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: Variable.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.ACONST_NULL;
026    import org.apache.bcel.generic.ConstantPoolGen;
027    import org.apache.bcel.generic.DCONST;
028    import org.apache.bcel.generic.ICONST;
029    import org.apache.bcel.generic.InstructionHandle;
030    import org.apache.bcel.generic.InstructionList;
031    import org.apache.bcel.generic.PUTFIELD;
032    import org.apache.xalan.xsltc.compiler.util.BooleanType;
033    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
034    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
035    import org.apache.xalan.xsltc.compiler.util.IntType;
036    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
037    import org.apache.xalan.xsltc.compiler.util.NodeType;
038    import org.apache.xalan.xsltc.compiler.util.RealType;
039    import org.apache.xalan.xsltc.compiler.util.Type;
040    import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
041    
042    final class Variable extends VariableBase {
043    
044        public int getIndex() {
045            return (_local != null) ? _local.getIndex() : -1;
046        }
047    
048        /**
049         * Parse the contents of the variable
050         */
051        public void parseContents(Parser parser) {
052            // Parse 'name' and 'select' attributes plus parameter contents
053            super.parseContents(parser);
054    
055            // Add a ref to this var to its enclosing construct
056            SyntaxTreeNode parent = getParent();
057            if (parent instanceof Stylesheet) {
058                // Mark this as a global variable
059                _isLocal = false;
060                // Check if a global variable with this name already exists...
061                Variable var = parser.getSymbolTable().lookupVariable(_name);
062                // ...and if it does we need to check import precedence
063                if (var != null) {
064                    final int us = this.getImportPrecedence();
065                    final int them = var.getImportPrecedence();
066                    // It is an error if the two have the same import precedence
067                    if (us == them) {
068                        final String name = _name.toString();
069                        reportError(this, parser, ErrorMsg.VARIABLE_REDEF_ERR,name);
070                    }
071                    // Ignore this if previous definition has higher precedence
072                    else if (them > us) {
073                        _ignore = true;
074                        return;
075                    }
076                    else {
077                        var.disable();
078                    }
079                    // Add this variable if we have higher precedence
080                }
081                ((Stylesheet)parent).addVariable(this);
082                parser.getSymbolTable().addVariable(this);
083            }
084            else {
085                _isLocal = true;
086            }
087        }
088    
089        /**
090         * Runs a type check on either the variable element body or the
091         * expression in the 'select' attribute
092         */
093        public Type typeCheck(SymbolTable stable) throws TypeCheckError {
094    
095            // Type check the 'select' expression if present
096            if (_select != null) {
097                _type = _select.typeCheck(stable);
098            }
099            // Type check the element contents otherwise
100            else if (hasContents()) {
101                typeCheckContents(stable);
102                _type = Type.ResultTree;
103            }
104            else {
105                _type = Type.Reference;
106            }
107            // The return type is void as the variable element does not leave
108            // anything on the JVM's stack. The '_type' global will be returned
109            // by the references to this variable, and not by the variable itself.
110            return Type.Void;
111        }
112    
113        /**
114         * This method is part of a little trick that is needed to use local
115         * variables inside nested for-each loops. See the initializeVariables()
116         * method in the ForEach class for an explanation
117         */
118        public void initialize(ClassGenerator classGen, MethodGenerator methodGen) {
119            final ConstantPoolGen cpg = classGen.getConstantPool();
120            final InstructionList il = methodGen.getInstructionList();
121    
122            // This is only done for local variables that are actually used
123            if (isLocal() && !_refs.isEmpty()) {
124                // Create a variable slot if none is allocated
125                if (_local == null) {
126                    _local = methodGen.addLocalVariable2(getEscapedName(),
127                                                         _type.toJCType(),
128                                                         null);
129                }
130                // Push the default value on the JVM's stack
131                if ((_type instanceof IntType) ||
132                    (_type instanceof NodeType) ||
133                    (_type instanceof BooleanType))
134                    il.append(new ICONST(0)); // 0 for node-id, integer and boolean
135                else if (_type instanceof RealType)
136                    il.append(new DCONST(0)); // 0.0 for floating point numbers
137                else
138                    il.append(new ACONST_NULL()); // and 'null' for anything else
139    
140                // Mark the store as the start of the live range of the variable
141                _local.setStart(il.append(_type.STORE(_local.getIndex())));
142            }
143        }
144    
145        public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
146            final ConstantPoolGen cpg = classGen.getConstantPool();
147            final InstructionList il = methodGen.getInstructionList();
148    
149            // Don't generate code for unreferenced variables
150            if (_refs.isEmpty()) {
151                _ignore = true;
152            }
153    
154            // Make sure that a variable instance is only compiled once
155            if (_ignore) return;
156            _ignore = true;
157    
158            final String name = getEscapedName();
159    
160            if (isLocal()) {
161                // Compile variable value computation
162                translateValue(classGen, methodGen);
163    
164                // Add a new local variable and store value
165                boolean createLocal = _local == null;
166                if (createLocal) {
167                    mapRegister(methodGen);
168                }
169                InstructionHandle storeInst =
170                                      il.append(_type.STORE(_local.getIndex()));
171    
172                // If the local is just being created, mark the store as the start
173                // of its live range.  Note that it might have been created by
174                // initializeVariables already, which would have set the start of
175                // the live range already.
176                if (createLocal) {
177                    _local.setStart(storeInst);
178                }
179            }
180            else {
181                String signature = _type.toSignature();
182    
183                // Global variables are store in class fields
184                if (classGen.containsField(name) == null) {
185                    classGen.addField(new Field(ACC_PUBLIC, 
186                                                cpg.addUtf8(name),
187                                                cpg.addUtf8(signature),
188                                                null, cpg.getConstantPool()));
189    
190                    // Push a reference to "this" for putfield
191                    il.append(classGen.loadTranslet());
192                    // Compile variable value computation
193                    translateValue(classGen, methodGen);
194                    // Store the variable in the allocated field
195                    il.append(new PUTFIELD(cpg.addFieldref(classGen.getClassName(),
196                                                           name, signature)));
197                }
198            }
199        }
200    }