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    }