1 /*
2  * Copyright (c) 2016, 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;
22 
23 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
24 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
25 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
26 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
27 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
28 import com.sun.org.apache.bcel.internal.generic.Instruction;
29 import com.sun.org.apache.bcel.internal.generic.InstructionList;
30 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
31 import com.sun.org.apache.bcel.internal.generic.NEW;
32 import com.sun.org.apache.bcel.internal.generic.PUSH;
33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
40 import com.sun.org.apache.xml.internal.utils.XML11Char;
41 import java.util.ArrayList;
42 import java.util.List;
43 
44 /**
45  * @author Jacek Ambroziak
46  * @author Santiago Pericas-Geertsen
47  * @author Morten Jorgensen
48  * @author Erwin Bolwidt <ejb@klomp.org>
49  * @author John Howard <JohnH@schemasoft.com>
50  * @LastModified: Oct 2017
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      */
disable()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      */
addReference(VariableRefBase vref)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      */
copyReferences(VariableBase var)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      */
mapRegister(MethodGenerator methodGen)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      */
unmapRegister(ClassGenerator classGen, MethodGenerator methodGen)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      */
loadInstruction()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      */
storeInstruction()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      */
getExpression()164     public Expression getExpression() {
165         return(_select);
166     }
167 
168     /**
169      * Display variable as single string
170      */
toString()171     public String toString() {
172         return("variable("+_name+")");
173     }
174 
175     /**
176      * Display variable in a full AST dump
177      */
display(int indent)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      */
getType()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      */
getName()199     public QName getName() {
200         return _name;
201     }
202 
203     /**
204      * Returns the escaped qname of the variable or parameter
205      */
getEscapedName()206     public String getEscapedName() {
207         return _escapedName;
208     }
209 
210     /**
211      * Set the name of the variable or paremeter. Escape all special chars.
212      */
setName(QName name)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      */
isLocal()221     public boolean isLocal() {
222         return _isLocal;
223     }
224 
225     /**
226      * Parse the contents of the <xsl:decimal-format> element.
227      */
parseContents(Parser parser)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      */
translateValue(ClassGenerator classGen, MethodGenerator methodGen)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 }
302