1 /*
   2  * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  23 
  24 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
  25 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  26 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  27 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  28 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  29 import com.sun.org.apache.bcel.internal.generic.Instruction;
  30 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  31 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  32 import com.sun.org.apache.bcel.internal.generic.NEW;
  33 import com.sun.org.apache.bcel.internal.generic.PUSH;
  34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
  38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
  39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  41 import com.sun.org.apache.xml.internal.utils.XML11Char;
  42 import java.util.ArrayList;
  43 import java.util.List;
  44 
  45 /**
  46  * @author Jacek Ambroziak
  47  * @author Santiago Pericas-Geertsen
  48  * @author Morten Jorgensen
  49  * @author Erwin Bolwidt <ejb@klomp.org>
  50  * @author John Howard <JohnH@schemasoft.com>
  51  */
  52 class VariableBase extends TopLevelElement {
  53 
  54     protected QName       _name;             // The name of the variable.
  55     protected String      _escapedName;      // The escaped qname of the variable.
  56     protected Type        _type;             // The type of this variable.
  57     protected boolean     _isLocal;          // True if the variable is local.
  58     protected LocalVariableGen _local;       // Reference to JVM variable
  59     protected Instruction _loadInstruction;  // Instruction to load JVM variable
  60     protected Instruction _storeInstruction; // Instruction to load JVM variable
  61     protected Expression  _select;           // Reference to variable expression
  62     protected String      select;            // Textual repr. of variable expr.
  63 
  64     // References to this variable (when local)
  65     protected List<VariableRefBase> _refs = new ArrayList<>(2);
  66 
  67     // Used to make sure parameter field is not added twice
  68     protected boolean    _ignore = false;
  69 
  70     /**
  71      * Disable this variable/parameter
  72      */
  73     public void disable() {
  74         _ignore = true;
  75     }
  76 
  77     /**
  78      * Add a reference to this variable. Called by VariableRef when an
  79      * expression contains a reference to this variable.
  80      */
  81     public void addReference(VariableRefBase vref) {
  82         _refs.add(vref);
  83     }
  84 
  85     /**
  86      * When a variable is overriden by another, e.g. via xsl:import,
  87      * its references need to be copied or otherwise it may be
  88      * compiled away as dead code. This method can be used for that
  89      * purpose.
  90      */
  91     public void copyReferences(VariableBase var) {
  92         final int size = _refs.size();
  93         for (int i = 0; i < size; i++) {
  94             var.addReference(_refs.get(i));
  95         }
  96     }
  97 
  98     /**
  99      * Map this variable to a register
 100      */
 101     public void mapRegister(MethodGenerator methodGen) {
 102         if (_local == null) {
 103             final InstructionList il = methodGen.getInstructionList();
 104             final String name = getEscapedName(); // TODO: namespace ?
 105             final com.sun.org.apache.bcel.internal.generic.Type varType = _type.toJCType();
 106             _local = methodGen.addLocalVariable2(name, varType, il.getEnd());
 107         }
 108     }
 109 
 110     /**
 111      * Remove the mapping of this variable to a register.
 112      * Called when we leave the AST scope of the variable's declaration
 113      */
 114     public void unmapRegister(ClassGenerator classGen, MethodGenerator methodGen) {
 115         if (_local != null) {
 116             if (_type instanceof ResultTreeType) {
 117                 final ConstantPoolGen cpg = classGen.getConstantPool();
 118                 final InstructionList il = methodGen.getInstructionList();
 119                 if (classGen.getStylesheet().callsNodeset() && classGen.getDOMClass().equals(MULTI_DOM_CLASS)) {
 120                     final int removeDA = cpg.addMethodref(MULTI_DOM_CLASS, "removeDOMAdapter", "(" + DOM_ADAPTER_SIG + ")V");
 121                     il.append(methodGen.loadDOM());
 122                     il.append(new CHECKCAST(cpg.addClass(MULTI_DOM_CLASS)));
 123                     il.append(loadInstruction());
 124                     il.append(new CHECKCAST(cpg.addClass(DOM_ADAPTER_CLASS)));
 125                     il.append(new INVOKEVIRTUAL(removeDA));
 126                 }
 127                 final int release = cpg.addInterfaceMethodref(DOM_IMPL_CLASS, "release", "()V");
 128                 il.append(loadInstruction());
 129                 il.append(new INVOKEINTERFACE(release, 1));
 130             }
 131 
 132             _local.setEnd(methodGen.getInstructionList().getEnd());
 133             methodGen.removeLocalVariable(_local);
 134             _refs = null;
 135             _local = null;
 136         }
 137     }
 138 
 139     /**
 140      * Returns an instruction for loading the value of this variable onto
 141      * the JVM stack.
 142      */
 143     public Instruction loadInstruction() {
 144         if (_loadInstruction == null) {
 145             _loadInstruction = _type.LOAD(_local.getIndex());
 146         }
 147         return _loadInstruction;
 148     }
 149 
 150     /**
 151      * Returns an instruction for storing a value from the JVM stack
 152      * into this variable.
 153      */
 154     public Instruction storeInstruction() {
 155         if (_storeInstruction == null) {
 156             _storeInstruction = _type.STORE(_local.getIndex());
 157         }
 158         return _storeInstruction;
 159     }
 160 
 161     /**
 162      * Returns the expression from this variable's select attribute (if any)
 163      */
 164     public Expression getExpression() {
 165         return(_select);
 166     }
 167 
 168     /**
 169      * Display variable as single string
 170      */
 171     public String toString() {
 172         return("variable("+_name+")");
 173     }
 174 
 175     /**
 176      * Display variable in a full AST dump
 177      */
 178     public void display(int indent) {
 179         indent(indent);
 180         System.out.println("Variable " + _name);
 181         if (_select != null) {
 182             indent(indent + IndentIncrement);
 183             System.out.println("select " + _select.toString());
 184         }
 185         displayContents(indent + IndentIncrement);
 186     }
 187 
 188     /**
 189      * Returns the type of the variable
 190      */
 191     public Type getType() {
 192         return _type;
 193     }
 194 
 195     /**
 196      * Returns the name of the variable or parameter as it will occur in the
 197      * compiled translet.
 198      */
 199     public QName getName() {
 200         return _name;
 201     }
 202 
 203     /**
 204      * Returns the escaped qname of the variable or parameter
 205      */
 206     public String getEscapedName() {
 207         return _escapedName;
 208     }
 209 
 210     /**
 211      * Set the name of the variable or paremeter. Escape all special chars.
 212      */
 213     public void setName(QName name) {
 214         _name = name;
 215         _escapedName = Util.escape(name.getStringRep());
 216     }
 217 
 218     /**
 219      * Returns the true if the variable is local
 220      */
 221     public boolean isLocal() {
 222         return _isLocal;
 223     }
 224 
 225     /**
 226      * Parse the contents of the <xsl:decimal-format> element.
 227      */
 228     public void parseContents(Parser parser) {
 229         // Get the 'name attribute
 230         String name = getAttribute("name");
 231 
 232         if (name.length() > 0) {
 233             if (!XML11Char.isXML11ValidQName(name)) {
 234                 ErrorMsg err = new ErrorMsg(ErrorMsg.INVALID_QNAME_ERR, name, this);
 235                 parser.reportError(Constants.ERROR, err);
 236             }
 237             setName(parser.getQNameIgnoreDefaultNs(name));
 238         }
 239         else
 240             reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "name");
 241 
 242         // Check whether variable/param of the same name is already in scope
 243         VariableBase other = parser.lookupVariable(_name);
 244         if ((other != null) && (other.getParent() == getParent())) {
 245             reportError(this, parser, ErrorMsg.VARIABLE_REDEF_ERR, name);
 246         }
 247 
 248         select = getAttribute("select");
 249         if (select.length() > 0) {
 250             _select = getParser().parseExpression(this, "select", null);
 251             if (_select.isDummy()) {
 252                 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "select");
 253                 return;
 254             }
 255         }
 256 
 257         // Children must be parsed first -> static scoping
 258         parseChildren(parser);
 259     }
 260 
 261     /**
 262      * Compile the value of the variable, which is either in an expression in
 263      * a 'select' attribute, or in the variable elements body
 264      */
 265     public void translateValue(ClassGenerator classGen,
 266                                MethodGenerator methodGen) {
 267         // Compile expression is 'select' attribute if present
 268         if (_select != null) {
 269             _select.translate(classGen, methodGen);
 270             // Create a CachedNodeListIterator for select expressions
 271             // in a variable or parameter.
 272             if (_select.getType() instanceof NodeSetType) {
 273                 final ConstantPoolGen cpg = classGen.getConstantPool();
 274                 final InstructionList il = methodGen.getInstructionList();
 275 
 276                 final int initCNI = cpg.addMethodref(CACHED_NODE_LIST_ITERATOR_CLASS,
 277                                             "<init>",
 278                                             "("
 279                                             +NODE_ITERATOR_SIG
 280                                             +")V");
 281                 il.append(new NEW(cpg.addClass(CACHED_NODE_LIST_ITERATOR_CLASS)));
 282                 il.append(DUP_X1);
 283                 il.append(SWAP);
 284 
 285                 il.append(new INVOKESPECIAL(initCNI));
 286             }
 287             _select.startIterator(classGen, methodGen);
 288         }
 289         // If not, compile result tree from parameter body if present.
 290         else if (hasContents()) {
 291             compileResultTree(classGen, methodGen);
 292         }
 293         // If neither are present then store empty string in variable
 294         else {
 295             final ConstantPoolGen cpg = classGen.getConstantPool();
 296             final InstructionList il = methodGen.getInstructionList();
 297             il.append(new PUSH(cpg, Constants.EMPTYSTRING));
 298         }
 299     }
 300 
 301 }