001: /*
002: * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.es.parser;
030:
031: import com.caucho.es.ESId;
032:
033: /**
034: * Expr is an intermediate form representing an expression.
035: */
036: class Variable {
037: private Function function;
038: private ESId id;
039: private boolean isLocal;
040: private boolean isScope;
041: // Is the variable initialized before it's used?
042: private boolean isInitialized;
043: // Is the variable ever used?
044: private boolean isUsed;
045: // Is the variable used by a closure?
046: private boolean isClosureVar;
047: // True if the variable is a global represented as a java field.
048: private boolean isJavaGlobal;
049: int type;
050:
051: Expr fullType;
052: // True if the type is declared.
053: private boolean isDeclared;
054:
055: Variable(Block block, ESId id, Expr type, boolean isLocal) {
056: this .function = block.function;
057: this .id = id;
058: this .isLocal = isLocal;
059: this .isScope = block.getDepth() > 0
060: || !function.isGlobalScope();
061: this .type = Expr.TYPE_UNKNOWN;
062: this .fullType = (TypeExpr) type;
063:
064: if (fullType != null) {
065: this .type = fullType.getType();
066: isDeclared = true;
067: }
068: }
069:
070: /**
071: * Returns the variable's name.
072: */
073: ESId getId() {
074: return id;
075: }
076:
077: /**
078: * Sets the JavaScript type.
079: */
080: void setType(int type) {
081: if (!isUsed)
082: this .isInitialized = true;
083:
084: if (fullType != null) {
085: // XXX: check type
086: // throw new RuntimeException("mismatch type");
087: } else
088: this .type = type;
089: }
090:
091: /**
092: * Declare the type of this variable.
093: */
094: void declare(int javaScriptType, Expr typeExpr) {
095: if (isDeclared && !typeExpr.equals(this .fullType))
096: throw new IllegalStateException("can't declare " + this
097: + " twice");
098:
099: setType(javaScriptType, typeExpr);
100:
101: isDeclared = true;
102: }
103:
104: /**
105: * Sets the type of this variable.
106: */
107: void setType(int newType, Expr expr) {
108: if (!isUsed)
109: this .isInitialized = true;
110:
111: if (isDeclared)
112: return;
113:
114: if (newType != Expr.TYPE_UNKNOWN && newType != Expr.TYPE_JAVA) {
115: this .type = Expr.TYPE_ES;
116: fullType = null;
117: return;
118: }
119:
120: if (fullType == null) {
121: } else if (!(fullType instanceof JavaTypeExpr)
122: || !(expr instanceof JavaTypeExpr)) {
123: this .type = Expr.TYPE_ES;
124: fullType = null;
125: return;
126: }
127:
128: JavaTypeExpr typeExpr = (JavaTypeExpr) expr;
129:
130: if (fullType != null
131: && !((JavaTypeExpr) fullType).getJavaClass().equals(
132: typeExpr.getJavaClass())) {
133: this .type = Expr.TYPE_ES;
134: fullType = null;
135: return;
136: }
137:
138: Class javaClass = typeExpr.getJavaClass();
139: if (javaClass.equals(byte.class)
140: || javaClass.equals(short.class)
141: || javaClass.equals(int.class)) {
142: this .type = Expr.TYPE_INTEGER;
143: } else if (javaClass.equals(float.class)
144: || javaClass.equals(double.class)) {
145: this .type = Expr.TYPE_NUMBER;
146: } else if (javaClass.equals(boolean.class)) {
147: this .type = Expr.TYPE_BOOLEAN;
148: } else if (javaClass.isPrimitive()) {
149: this .type = Expr.TYPE_ES;
150: this .fullType = null;
151: return;
152: } else {
153: this .type = newType;
154: this .fullType = typeExpr;
155: }
156: }
157:
158: boolean isLocal() {
159: return isLocal;
160: }
161:
162: void killLocal() {
163: isLocal = false;
164: }
165:
166: /**
167: * Returns true if this is a variable that can be used just like a
168: * java variable.
169: */
170: boolean isJavaLocal() {
171: return isLocal && !isClosureVar;
172: }
173:
174: /**
175: * True if the variable is a global represented by a global field.
176: */
177: boolean isJavaGlobal() {
178: return isJavaGlobal;
179: }
180:
181: /**
182: * Set the variable as a global.
183: */
184: void setJavaGlobal(boolean isGlobal) {
185: isJavaGlobal = isGlobal;
186: isLocal = false;
187: if (type == Expr.TYPE_UNKNOWN)
188: type = Expr.TYPE_ES;
189: }
190:
191: /**
192: * Returns the JavaScript type of the variable. If the type hasn't
193: * been inferred yet, the type must be ESBase.
194: */
195: int getType() {
196: if (type == Expr.TYPE_UNKNOWN) {
197: type = Expr.TYPE_ES;
198: isInitialized = false;
199: }
200:
201: return type;
202: }
203:
204: Expr getTypeExpr() {
205: return fullType;
206: }
207:
208: boolean hasInit() {
209: return type != Expr.TYPE_UNKNOWN && isLocal && isInitialized;
210: }
211:
212: boolean isScope() {
213: return isScope;
214: }
215:
216: void setUsed() {
217: isUsed = true;
218: if (type == Expr.TYPE_UNKNOWN)
219: type = Expr.TYPE_ES;
220: isInitialized = false;
221: }
222:
223: void setUsedByClosure() {
224: isClosureVar = true;
225: setUsed();
226: }
227:
228: boolean isUsed() {
229: return isUsed;
230: }
231:
232: void setLocal() {
233: isLocal = true;
234: }
235:
236: public String toString() {
237: return "[Variable " + id + " " + fullType + "]";
238: }
239: }
|