001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: Param.java 468650 2006-10-28 07:03:30Z minchau $
020 */
021
022 package org.apache.xalan.xsltc.compiler;
023
024 import org.apache.bcel.classfile.Field;
025 import org.apache.bcel.generic.BranchHandle;
026 import org.apache.bcel.generic.CHECKCAST;
027 import org.apache.bcel.generic.IFNONNULL;
028 import org.apache.bcel.generic.ConstantPoolGen;
029 import org.apache.bcel.generic.INVOKEVIRTUAL;
030 import org.apache.bcel.generic.Instruction;
031 import org.apache.bcel.generic.InstructionList;
032 import org.apache.bcel.generic.PUSH;
033 import org.apache.bcel.generic.PUTFIELD;
034 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
035 import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
036 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
037 import org.apache.xalan.xsltc.compiler.util.ReferenceType;
038 import org.apache.xalan.xsltc.compiler.util.Type;
039 import org.apache.xalan.xsltc.compiler.util.ObjectType;
040 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
041 import org.apache.xalan.xsltc.runtime.BasisLibrary;
042
043 /**
044 * @author Jacek Ambroziak
045 * @author Santiago Pericas-Geertsen
046 * @author Morten Jorgensen
047 * @author Erwin Bolwidt <ejb@klomp.org>
048 * @author John Howard <JohnH@schemasoft.com>
049 */
050 final class Param extends VariableBase {
051
052 /**
053 * True if this Param is declared in a simple named template.
054 * This is used to optimize codegen for parameter passing
055 * in named templates.
056 */
057 private boolean _isInSimpleNamedTemplate = false;
058
059 /**
060 * Display variable as single string
061 */
062 public String toString() {
063 return "param(" + _name + ")";
064 }
065
066 /**
067 * Set the instruction for loading the value of this variable onto the
068 * JVM stack and returns the old instruction.
069 */
070 public Instruction setLoadInstruction(Instruction instruction) {
071 Instruction tmp = _loadInstruction;
072 _loadInstruction = instruction;
073 return tmp;
074 }
075
076 /**
077 * Set the instruction for storing a value from the stack into this
078 * variable and returns the old instruction.
079 */
080 public Instruction setStoreInstruction(Instruction instruction) {
081 Instruction tmp = _storeInstruction;
082 _storeInstruction = instruction;
083 return tmp;
084 }
085
086 /**
087 * Display variable in a full AST dump
088 */
089 public void display(int indent) {
090 indent(indent);
091 System.out.println("param " + _name);
092 if (_select != null) {
093 indent(indent + IndentIncrement);
094 System.out.println("select " + _select.toString());
095 }
096 displayContents(indent + IndentIncrement);
097 }
098
099 /**
100 * Parse the contents of the <xsl:param> element. This method must read
101 * the 'name' (required) and 'select' (optional) attributes.
102 */
103 public void parseContents(Parser parser) {
104
105 // Parse 'name' and 'select' attributes plus parameter contents
106 super.parseContents(parser);
107
108 // Add a ref to this param to its enclosing construct
109 final SyntaxTreeNode parent = getParent();
110 if (parent instanceof Stylesheet) {
111 // Mark this as a global parameter
112 _isLocal = false;
113 // Check if a global variable with this name already exists...
114 Param param = parser.getSymbolTable().lookupParam(_name);
115 // ...and if it does we need to check import precedence
116 if (param != null) {
117 final int us = this.getImportPrecedence();
118 final int them = param.getImportPrecedence();
119 // It is an error if the two have the same import precedence
120 if (us == them) {
121 final String name = _name.toString();
122 reportError(this, parser, ErrorMsg.VARIABLE_REDEF_ERR,name);
123 }
124 // Ignore this if previous definition has higher precedence
125 else if (them > us) {
126 _ignore = true;
127 return;
128 }
129 else {
130 param.disable();
131 }
132 }
133 // Add this variable if we have higher precedence
134 ((Stylesheet)parent).addParam(this);
135 parser.getSymbolTable().addParam(this);
136 }
137 else if (parent instanceof Template) {
138 Template template = (Template) parent;
139 _isLocal = true;
140 template.addParameter(this);
141 if (template.isSimpleNamedTemplate()) {
142 _isInSimpleNamedTemplate = true;
143 }
144 }
145 }
146
147 /**
148 * Type-checks the parameter. The parameter type is determined by the
149 * 'select' expression (if present) or is a result tree if the parameter
150 * element has a body and no 'select' expression.
151 */
152 public Type typeCheck(SymbolTable stable) throws TypeCheckError {
153 if (_select != null) {
154 _type = _select.typeCheck(stable);
155 if (_type instanceof ReferenceType == false && !(_type instanceof ObjectType)) {
156 _select = new CastExpr(_select, Type.Reference);
157 }
158 }
159 else if (hasContents()) {
160 typeCheckContents(stable);
161 }
162 _type = Type.Reference;
163
164 // This element has no type (the parameter does, but the parameter
165 // element itself does not).
166 return Type.Void;
167 }
168
169 public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
170 final ConstantPoolGen cpg = classGen.getConstantPool();
171 final InstructionList il = methodGen.getInstructionList();
172
173 if (_ignore) return;
174 _ignore = true;
175
176 /*
177 * To fix bug 24518 related to setting parameters of the form
178 * {namespaceuri}localName which will get mapped to an instance
179 * variable in the class.
180 */
181 final String name = BasisLibrary.mapQNameToJavaName(_name.toString());
182 final String signature = _type.toSignature();
183 final String className = _type.getClassName();
184
185 if (isLocal()) {
186 /*
187 * If simple named template then generate a conditional init of the
188 * param using its default value:
189 * if (param == null) param = <default-value>
190 */
191 if (_isInSimpleNamedTemplate) {
192 il.append(loadInstruction());
193 BranchHandle ifBlock = il.append(new IFNONNULL(null));
194 translateValue(classGen, methodGen);
195 il.append(storeInstruction());
196 ifBlock.setTarget(il.append(NOP));
197 return;
198 }
199
200 il.append(classGen.loadTranslet());
201 il.append(new PUSH(cpg, name));
202 translateValue(classGen, methodGen);
203 il.append(new PUSH(cpg, true));
204
205 // Call addParameter() from this class
206 il.append(new INVOKEVIRTUAL(cpg.addMethodref(TRANSLET_CLASS,
207 ADD_PARAMETER,
208 ADD_PARAMETER_SIG)));
209 if (className != EMPTYSTRING) {
210 il.append(new CHECKCAST(cpg.addClass(className)));
211 }
212
213 _type.translateUnBox(classGen, methodGen);
214
215 if (_refs.isEmpty()) { // nobody uses the value
216 il.append(_type.POP());
217 _local = null;
218 }
219 else { // normal case
220 _local = methodGen.addLocalVariable2(name,
221 _type.toJCType(),
222 null);
223 // Cache the result of addParameter() in a local variable
224 _local.setStart(il.append(_type.STORE(_local.getIndex())));
225 }
226 }
227 else {
228 if (classGen.containsField(name) == null) {
229 classGen.addField(new Field(ACC_PUBLIC, cpg.addUtf8(name),
230 cpg.addUtf8(signature),
231 null, cpg.getConstantPool()));
232 il.append(classGen.loadTranslet());
233 il.append(DUP);
234 il.append(new PUSH(cpg, name));
235 translateValue(classGen, methodGen);
236 il.append(new PUSH(cpg, true));
237
238 // Call addParameter() from this class
239 il.append(new INVOKEVIRTUAL(cpg.addMethodref(TRANSLET_CLASS,
240 ADD_PARAMETER,
241 ADD_PARAMETER_SIG)));
242
243 _type.translateUnBox(classGen, methodGen);
244
245 // Cache the result of addParameter() in a field
246 if (className != EMPTYSTRING) {
247 il.append(new CHECKCAST(cpg.addClass(className)));
248 }
249 il.append(new PUTFIELD(cpg.addFieldref(classGen.getClassName(),
250 name, signature)));
251 }
252 }
253 }
254 }