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 }