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 }