1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xalan.internal.xsltc.compiler.util;
  22 
  23 import com.sun.org.apache.bcel.internal.generic.ALOAD;
  24 import com.sun.org.apache.bcel.internal.generic.ASTORE;
  25 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
  26 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  27 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  28 import com.sun.org.apache.bcel.internal.generic.IFEQ;
  29 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  30 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  31 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  32 import com.sun.org.apache.bcel.internal.generic.Instruction;
  33 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  34 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  35 import com.sun.org.apache.bcel.internal.generic.NEW;
  36 import com.sun.org.apache.bcel.internal.generic.PUSH;
  37 import com.sun.org.apache.xalan.internal.xsltc.compiler.Constants;
  38 import com.sun.org.apache.xalan.internal.xsltc.compiler.FlowList;
  39 
  40 /**
  41  * @author Jacek Ambroziak
  42  * @author Santiago Pericas-Geertsen
  43  * @author Morten Jorgensen
  44  * @LastModified: Oct 2017
  45  */
  46 public final class ResultTreeType extends Type {
  47     private final String _methodName;
  48 
  49     protected ResultTreeType() {
  50         _methodName = null;
  51     }
  52 
  53     public ResultTreeType(String methodName) {
  54         _methodName = methodName;
  55     }
  56 
  57     public String toString() {
  58         return "result-tree";
  59     }
  60 
  61     public boolean identicalTo(Type other) {
  62         return (other instanceof ResultTreeType);
  63     }
  64 
  65     public String toSignature() {
  66         return DOM_INTF_SIG;
  67     }
  68 
  69     public com.sun.org.apache.bcel.internal.generic.Type toJCType() {
  70         return Util.getJCRefType(toSignature());
  71     }
  72 
  73     public String getMethodName() {
  74         return _methodName;
  75     }
  76 
  77     public boolean implementedAsMethod() {
  78         return _methodName != null;
  79     }
  80 
  81     /**
  82      * Translates a result tree to object of internal type <code>type</code>.
  83      * The translation to int is undefined since result trees
  84      * are always converted to reals in arithmetic expressions.
  85      *
  86      * @param classGen A BCEL class generator
  87      * @param methodGen A BCEL method generator
  88      * @param type An instance of the type to translate the result tree to
  89      * @see com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateTo
  90      */
  91     public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
  92                             Type type) {
  93         if (type == Type.String) {
  94             translateTo(classGen, methodGen, (StringType)type);
  95         }
  96         else if (type == Type.Boolean) {
  97             translateTo(classGen, methodGen, (BooleanType)type);
  98         }
  99         else if (type == Type.Real) {
 100             translateTo(classGen, methodGen, (RealType)type);
 101         }
 102         else if (type == Type.NodeSet) {
 103             translateTo(classGen, methodGen, (NodeSetType)type);
 104         }
 105         else if (type == Type.Reference) {
 106             translateTo(classGen, methodGen, (ReferenceType)type);
 107         }
 108         else if (type == Type.Object) {
 109             translateTo(classGen, methodGen, (ObjectType) type);
 110         }
 111         else {
 112             ErrorMsg err = new ErrorMsg(ErrorMsg.DATA_CONVERSION_ERR,
 113                                         toString(), type.toString());
 114             classGen.getParser().reportError(Constants.FATAL, err);
 115         }
 116     }
 117 
 118     /**
 119      * Expects an result tree on the stack and pushes a boolean.
 120      * Translates a result tree to a boolean by first converting it to string.
 121      *
 122      * @param classGen A BCEL class generator
 123      * @param methodGen A BCEL method generator
 124      * @param type An instance of BooleanType (any)
 125      * @see com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateTo
 126      */
 127     public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
 128                             BooleanType type) {
 129         // A result tree is always 'true' when converted to a boolean value,
 130         // since the tree always has at least one node (the root).
 131         final ConstantPoolGen cpg = classGen.getConstantPool();
 132         final InstructionList il = methodGen.getInstructionList();
 133         il.append(POP);      // don't need the DOM reference
 134         il.append(ICONST_1); // push 'true' on the stack
 135     }
 136 
 137     /**
 138      * Expects an result tree on the stack and pushes a string.
 139      *
 140      * @param classGen A BCEL class generator
 141      * @param methodGen A BCEL method generator
 142      * @param type An instance of StringType (any)
 143      * @see com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateTo
 144      */
 145     public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
 146                             StringType type) {
 147         final ConstantPoolGen cpg = classGen.getConstantPool();
 148         final InstructionList il = methodGen.getInstructionList();
 149 
 150         if (_methodName == null) {
 151             int index = cpg.addInterfaceMethodref(DOM_INTF,
 152                                                   "getStringValue",
 153                                                   "()"+STRING_SIG);
 154             il.append(new INVOKEINTERFACE(index, 1));
 155         }
 156         else {
 157             final String className = classGen.getClassName();
 158             final int current = methodGen.getLocalIndex("current");
 159 
 160             // Push required parameters
 161             il.append(classGen.loadTranslet());
 162             if (classGen.isExternal()) {
 163                 il.append(new CHECKCAST(cpg.addClass(className)));
 164             }
 165             il.append(DUP);
 166             il.append(new GETFIELD(cpg.addFieldref(className, "_dom",
 167                                                    DOM_INTF_SIG)));
 168 
 169             // Create a new instance of a StringValueHandler
 170             int index = cpg.addMethodref(STRING_VALUE_HANDLER, "<init>", "()V");
 171             il.append(new NEW(cpg.addClass(STRING_VALUE_HANDLER)));
 172             il.append(DUP);
 173             il.append(DUP);
 174             il.append(new INVOKESPECIAL(index));
 175 
 176             // Store new Handler into a local variable
 177             final LocalVariableGen handler =
 178                 methodGen.addLocalVariable("rt_to_string_handler",
 179                                            Util.getJCRefType(STRING_VALUE_HANDLER_SIG),
 180                                            null, null);
 181             handler.setStart(il.append(new ASTORE(handler.getIndex())));
 182 
 183             // Call the method that implements this result tree
 184             index = cpg.addMethodref(className, _methodName,
 185                                      "("+DOM_INTF_SIG+TRANSLET_OUTPUT_SIG+")V");
 186             il.append(new INVOKEVIRTUAL(index));
 187 
 188             // Restore new handler and call getValue()
 189             handler.setEnd(il.append(new ALOAD(handler.getIndex())));
 190             index = cpg.addMethodref(STRING_VALUE_HANDLER,
 191                                      "getValue",
 192                                      "()" + STRING_SIG);
 193             il.append(new INVOKEVIRTUAL(index));
 194         }
 195     }
 196 
 197     /**
 198      * Expects an result tree on the stack and pushes a real.
 199      * Translates a result tree into a real by first converting it to string.
 200      *
 201      * @param classGen A BCEL class generator
 202      * @param methodGen A BCEL method generator
 203      * @param type An instance of RealType (any)
 204      * @see com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateTo
 205      */
 206     public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
 207                             RealType type) {
 208         translateTo(classGen, methodGen, Type.String);
 209         Type.String.translateTo(classGen, methodGen, Type.Real);
 210     }
 211 
 212     /**
 213      * Expects a result tree on the stack and pushes a boxed result tree.
 214      * Result trees are already boxed so the translation is just a NOP.
 215      *
 216      * @param classGen A BCEL class generator
 217      * @param methodGen A BCEL method generator
 218      * @param type An instance of ReferenceType (any)
 219      * @see com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateTo
 220      */
 221     public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
 222                             ReferenceType type) {
 223         final ConstantPoolGen cpg = classGen.getConstantPool();
 224         final InstructionList il = methodGen.getInstructionList();
 225 
 226         if (_methodName == null) {
 227             il.append(NOP);
 228         }
 229         else {
 230             LocalVariableGen domBuilder, newDom;
 231             final String className = classGen.getClassName();
 232             final int current = methodGen.getLocalIndex("current");
 233 
 234             // Push required parameters
 235             il.append(classGen.loadTranslet());
 236             if (classGen.isExternal()) {
 237                 il.append(new CHECKCAST(cpg.addClass(className)));
 238             }
 239             il.append(methodGen.loadDOM());
 240 
 241             // Create new instance of DOM class (with RTF_INITIAL_SIZE nodes)
 242             il.append(methodGen.loadDOM());
 243             int index = cpg.addInterfaceMethodref(DOM_INTF,
 244                                  "getResultTreeFrag",
 245                                  "(IZ)" + DOM_INTF_SIG);
 246             il.append(new PUSH(cpg, RTF_INITIAL_SIZE));
 247             il.append(new PUSH(cpg, false));
 248             il.append(new INVOKEINTERFACE(index,3));
 249             il.append(DUP);
 250 
 251             // Store new DOM into a local variable
 252             newDom = methodGen.addLocalVariable("rt_to_reference_dom",
 253                                                 Util.getJCRefType(DOM_INTF_SIG),
 254                                                 null, null);
 255             il.append(new CHECKCAST(cpg.addClass(DOM_INTF_SIG)));
 256             newDom.setStart(il.append(new ASTORE(newDom.getIndex())));
 257 
 258             // Overwrite old handler with DOM handler
 259             index = cpg.addInterfaceMethodref(DOM_INTF,
 260                                  "getOutputDomBuilder",
 261                                  "()" + TRANSLET_OUTPUT_SIG);
 262 
 263             il.append(new INVOKEINTERFACE(index,1));
 264             //index = cpg.addMethodref(DOM_IMPL,
 265                 //                   "getOutputDomBuilder",
 266                 //                   "()" + TRANSLET_OUTPUT_SIG);
 267             //il.append(new INVOKEVIRTUAL(index));
 268             il.append(DUP);
 269             il.append(DUP);
 270 
 271             // Store DOM handler in a local in order to call endDocument()
 272             domBuilder =
 273                 methodGen.addLocalVariable("rt_to_reference_handler",
 274                                            Util.getJCRefType(TRANSLET_OUTPUT_SIG),
 275                                            null, null);
 276             domBuilder.setStart(il.append(new ASTORE(domBuilder.getIndex())));
 277 
 278             // Call startDocument on the new handler
 279             index = cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 280                                               "startDocument", "()V");
 281             il.append(new INVOKEINTERFACE(index, 1));
 282 
 283             // Call the method that implements this result tree
 284             index = cpg.addMethodref(className,
 285                                      _methodName,
 286                                      "("
 287                                      + DOM_INTF_SIG
 288                                      + TRANSLET_OUTPUT_SIG
 289                                      +")V");
 290             il.append(new INVOKEVIRTUAL(index));
 291 
 292             // Call endDocument on the DOM handler
 293             domBuilder.setEnd(il.append(new ALOAD(domBuilder.getIndex())));
 294             index = cpg.addInterfaceMethodref(TRANSLET_OUTPUT_INTERFACE,
 295                                               "endDocument", "()V");
 296             il.append(new INVOKEINTERFACE(index, 1));
 297 
 298             // Push the new DOM on the stack
 299             newDom.setEnd(il.append(new ALOAD(newDom.getIndex())));
 300         }
 301     }
 302 
 303     /**
 304      * Expects a result tree on the stack and pushes a node-set (iterator).
 305      * Note that the produced iterator is an iterator for the DOM that
 306      * contains the result tree, and not the DOM that is currently in use.
 307      * This conversion here will therefore not directly work with elements
 308      * such as <xsl:apply-templates> and <xsl:for-each> without the DOM
 309      * parameter/variable being updates as well.
 310      *
 311      * @param classGen A BCEL class generator
 312      * @param methodGen A BCEL method generator
 313      * @param type An instance of NodeSetType (any)
 314      * @see com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateTo
 315      */
 316     public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
 317                             NodeSetType type) {
 318         final ConstantPoolGen cpg = classGen.getConstantPool();
 319         final InstructionList il = methodGen.getInstructionList();
 320 
 321         // Put an extra copy of the result tree (DOM) on the stack
 322         il.append(DUP);
 323 
 324         // DOM adapters containing a result tree are not initialised with
 325         // translet-type to DOM-type mapping. This must be done now for
 326         // XPath expressions and patterns to work for the iterator we create.
 327         il.append(classGen.loadTranslet()); // get names array
 328         il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
 329                                                NAMES_INDEX,
 330                                                NAMES_INDEX_SIG)));
 331         il.append(classGen.loadTranslet()); // get uris array
 332         il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
 333                                                URIS_INDEX,
 334                                                URIS_INDEX_SIG)));
 335         il.append(classGen.loadTranslet()); // get types array
 336         il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
 337                                                TYPES_INDEX,
 338                                                TYPES_INDEX_SIG)));
 339         il.append(classGen.loadTranslet()); // get namespaces array
 340         il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
 341                                                NAMESPACE_INDEX,
 342                                                NAMESPACE_INDEX_SIG)));
 343         // Pass the type mappings to the DOM adapter
 344         final int mapping = cpg.addInterfaceMethodref(DOM_INTF,
 345                                                       "setupMapping",
 346                                                       "(["+STRING_SIG+
 347                                                       "["+STRING_SIG+
 348                                                       "[I" +
 349                                                       "["+STRING_SIG+")V");
 350         il.append(new INVOKEINTERFACE(mapping, 5));
 351         il.append(DUP);
 352 
 353         // Create an iterator for the root node of the DOM adapter
 354         final int iter = cpg.addInterfaceMethodref(DOM_INTF,
 355                                                    "getIterator",
 356                                                    "()"+NODE_ITERATOR_SIG);
 357         il.append(new INVOKEINTERFACE(iter, 1));
 358     }
 359 
 360     /**
 361      * Subsume result tree into ObjectType.
 362      *
 363      * @see     com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateTo
 364      */
 365     public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
 366                             ObjectType type) {
 367         methodGen.getInstructionList().append(NOP);
 368     }
 369 
 370     /**
 371      * Translates a result tree into a non-synthesized boolean.
 372      * It does not push a 0 or a 1 but instead returns branchhandle list
 373      * to be appended to the false list.
 374      *
 375      * @param classGen A BCEL class generator
 376      * @param methodGen A BCEL method generator
 377      * @param type An instance of BooleanType (any)
 378      * @see com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateToDesynthesized
 379      */
 380     public FlowList translateToDesynthesized(ClassGenerator classGen,
 381                                              MethodGenerator methodGen,
 382                                              BooleanType type) {
 383         final InstructionList il = methodGen.getInstructionList();
 384         translateTo(classGen, methodGen, Type.Boolean);
 385         return new FlowList(il.append(new IFEQ(null)));
 386     }
 387 
 388     /**
 389      * Translates a result tree to a Java type denoted by <code>clazz</code>.
 390      * Expects a result tree on the stack and pushes an object
 391      * of the appropriate type after coercion. Result trees are translated
 392      * to W3C Node or W3C NodeList and the translation is done
 393      * via node-set type.
 394      *
 395      * @param classGen A BCEL class generator
 396      * @param methodGen A BCEL method generator
 397      * @param clazz An reference to the Class to translate to
 398      * @see com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type#translateTo
 399      */
 400     public void translateTo(ClassGenerator classGen, MethodGenerator methodGen,
 401                             Class<?> clazz) {
 402         final String className = clazz.getName();
 403         final ConstantPoolGen cpg = classGen.getConstantPool();
 404         final InstructionList il = methodGen.getInstructionList();
 405 
 406         if (className.equals("org.w3c.dom.Node")) {
 407             translateTo(classGen, methodGen, Type.NodeSet);
 408             int index = cpg.addInterfaceMethodref(DOM_INTF,
 409                                                   MAKE_NODE,
 410                                                   MAKE_NODE_SIG2);
 411             il.append(new INVOKEINTERFACE(index, 2));
 412         }
 413         else if (className.equals("org.w3c.dom.NodeList")) {
 414             translateTo(classGen, methodGen, Type.NodeSet);
 415             int index = cpg.addInterfaceMethodref(DOM_INTF,
 416                                                   MAKE_NODE_LIST,
 417                                                   MAKE_NODE_LIST_SIG2);
 418             il.append(new INVOKEINTERFACE(index, 2));
 419         }
 420         else if (className.equals("java.lang.Object")) {
 421             il.append(NOP);
 422         }
 423         else if (className.equals("java.lang.String")) {
 424             translateTo(classGen, methodGen, Type.String);
 425         }
 426         else {
 427             ErrorMsg err = new ErrorMsg(ErrorMsg.DATA_CONVERSION_ERR,
 428                                         toString(), className);
 429             classGen.getParser().reportError(Constants.FATAL, err);
 430         }
 431     }
 432 
 433     /**
 434      * Translates an object of this type to its boxed representation.
 435      */
 436     public void translateBox(ClassGenerator classGen,
 437                              MethodGenerator methodGen) {
 438         translateTo(classGen, methodGen, Type.Reference);
 439     }
 440 
 441     /**
 442      * Translates an object of this type to its unboxed representation.
 443      */
 444     public void translateUnBox(ClassGenerator classGen,
 445                                MethodGenerator methodGen) {
 446         methodGen.getInstructionList().append(NOP);
 447     }
 448 
 449     /**
 450      * Returns the class name of an internal type's external representation.
 451      */
 452     public String getClassName() {
 453         return(DOM_INTF);
 454     }
 455 
 456     public Instruction LOAD(int slot) {
 457         return new ALOAD(slot);
 458     }
 459 
 460     public Instruction STORE(int slot) {
 461         return new ASTORE(slot);
 462     }
 463 }