001: /*=============================================================================
002: * Copyright Texas Instruments 2000-2003. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * $ProjectHeader: OSCRIPT 0.155 Fri, 20 Dec 2002 18:34:22 -0800 rclark $
019: */
020:
021: package oscript.compiler;
022:
023: import java.util.*;
024:
025: import oscript.data.*;
026: import oscript.syntaxtree.*;
027: import oscript.util.*;
028:
029: // The Bytecode Engineerign Library
030: import org.apache.bcel.generic.*;
031: import org.apache.bcel.Constants;
032: import org.apache.bcel.classfile.JavaClass;
033:
034: /**
035: * This class helps the compiler track declarations of variables within a
036: * scope, in order to optimize resolving references to variables by caching
037: * and, when possible, statically resolving the reference in order to avoid
038: * performing the normal hashtable lookup(s). It is also responsible for
039: * generating the {@link Scope#createMember} and {@link Scope#lookupInScope}
040: * code, so that the rest of the compiler does not need to worry about what
041: * optimization strategy (if any) is used to resolve references.
042: * <p>
043: * Note that this class is fairly tightly coupled to {@link CompilerVisitor}.
044: * It would be an inner class if it were not for file size issues.
045: *
046: * @author Rob Clark (rob@ti.com)
047: * @version 0
048: */
049: public class CompilerScope {
050: private static final org.apache.bcel.generic.Type BASIC_SCOPE_TYPE = new ObjectType(
051: "oscript.data.BasicScope");
052:
053: private SymbolTable smit = new OpenHashSymbolTable();
054: private SymbolTable privSmit;
055: private SymbolTable pubpSmit;
056:
057: private int slot;
058: private LocalVariableGen lg;
059: private CompilerScope prev;
060: private boolean hasFxnInScope;
061: private Hashtable memberTable = new Hashtable();
062: private int conditionalNestingCnt = 0;
063: private int smitIdx = -1;
064: private int privSmitIdx, pubpSmitIdx;
065:
066: /**
067: * The compiler for the compiled unit within which this scope is
068: * declared.
069: */
070: CompilerVisitor cv;
071:
072: /**
073: * Used to mark that the scope is open, meaning there may be variables
074: * declared in this scope that the compiler doesn't know about. This
075: * can be the result of an "eval", "import", or "mixin".
076: */
077: private boolean openScope = false;
078:
079: /**
080: * Constructor for scope to represent a scope passed in to the
081: * node-evaluator, rather than constructed by the node-evaluator
082: *
083: * @param cv the compiler that declares this scope
084: * @param slot the local variable slot of the externally constructed
085: * scope object
086: * @param argIds array of argument ids and attributes
087: */
088: CompilerScope(CompilerVisitor cv, int slot, int[] argIds) {
089: this .cv = cv;
090: this .slot = slot;
091:
092: // for top-level scopes, also construct private and public/protect
093: // index tables:
094: privSmit = new OpenHashSymbolTable();
095: pubpSmit = new OpenHashSymbolTable();
096:
097: if (argIds != null)
098: for (int i = 0; i < argIds.length; i += 2)
099: smit.create(argIds[i]);
100:
101: // create smit instance variable:
102: smitIdx = cv.ctx.getInstanceConstantIdx(smit);
103: privSmitIdx = cv.ctx.getInstanceConstantIdx(privSmit);
104: pubpSmitIdx = cv.ctx.getInstanceConstantIdx(pubpSmit);
105: }
106:
107: /**
108: * Constructor to represent a scope constructed by this node-evaluator.
109: *
110: * @param cv the compiler that declares this scope
111: * @param prev the enclosing scope
112: * @param hasFxnInScope a flag from the parser indicating if there is
113: * (or might be) a function declaration within this scope. If there
114: * is no function in this scope, scope storage can be allocated from
115: * the stack
116: */
117: CompilerScope(CompilerVisitor cv, CompilerScope prev,
118: boolean hasFxnInScope) {
119: this .cv = cv;
120: this .prev = prev;
121: this .hasFxnInScope = hasFxnInScope;
122:
123: // create local variable for scope object:
124: lg = cv.mg.addLocalVariable(cv.ctx
125: .makeUniqueIdentifierName("scope"), BASIC_SCOPE_TYPE,
126: null, null);
127: slot = lg.getIndex();
128:
129: // create smit instance variable:
130: smitIdx = cv.ctx.getInstanceConstantIdx(smit);
131:
132: // if no fxn in scope, we can re-use the scope, which entails
133: // extra logic to see if a scope has already been created,
134: // and to reset() when leaving the scope... also, we can allocate
135: // the scope from the stack:
136: if (!hasFxnInScope) {
137: BranchInstruction IFNONNULL = null;
138: // note insert into head of list, hence insert'd instructions are
139: // in reverse order:
140: cv.il.insert(new ASTORE(slot));
141: cv.il.insert(InstructionConstants.ACONST_NULL);
142: cv.il.append(new ALOAD(slot));
143: IFNONNULL = new IFNONNULL(null);
144: cv.il.append(IFNONNULL);
145:
146: // "sf.allocateScope(currentScope,smit)"
147: cv.il.append(InstructionConstants.ALOAD_1); // sf
148: cv.il.append(new ALOAD(prev.getSlot())); // currentScope
149: cv.il.append(new GETSTATIC(smitIdx)); // smit
150: cv.il
151: .append(new INVOKEVIRTUAL(
152: cv.ctx
153: .methodref(
154: "oscript.util.StackFrame",
155: "allocateBasicScope",
156: "(Loscript/data/Scope;Loscript/util/SymbolTable;)Loscript/data/BasicScope;")));
157:
158: // "scopeXX = " ...
159: cv.il.append(new ASTORE(slot));
160:
161: if (!hasFxnInScope)
162: cv.il.setNextAsTarget(IFNONNULL);
163: } else {
164: // "new BasicScope(currentScope,smit)"
165: cv.il.append(new NEW(cv.ctx.cp
166: .addClass("oscript.data.BasicScope")));
167: cv.il.append(InstructionConstants.DUP);
168: cv.il.append(new ALOAD(prev.getSlot())); // currentScope
169: cv.il.append(new GETSTATIC(smitIdx)); // smit
170: cv.il
171: .append(new INVOKESPECIAL(
172: cv.ctx
173: .methodref(
174: "oscript.data.BasicScope",
175: "<init>",
176: "(Loscript/data/Scope;Loscript/util/SymbolTable;)V")));
177:
178: // "scopeXX = " ...
179: cv.il.append(new ASTORE(slot));
180: }
181: }
182:
183: /**
184: * Get the instance-constant indexs of the SMIT... this is needed for the
185: * topmost CompilerScope in a function, so that the compiler can generate
186: * an accessor method.
187: */
188: int[] getSharedMemberIndexTableIdxs() {
189: return new int[] { smitIdx, pubpSmitIdx, privSmitIdx };
190: }
191:
192: /**
193: * Mark this scope as open. An open scope may possibly have members
194: * declared in it that the compiler does not know about, such as
195: * because of an <code>eval</code> or <code>import</code> statement.
196: */
197: void markOpen() {
198: openScope = true;
199:
200: // XXX should clear any cached lookups, since the lookup may
201: // change after this point
202: }
203:
204: /**
205: * Called when processing has reached the end of this scope, to pop
206: * this scope of the stack
207: */
208: CompilerScope pop() {
209: if (prev != null) {
210: if (!hasFxnInScope) {
211: cv.il.append(new ALOAD(slot));
212: cv.il.append(new INVOKEVIRTUAL(cv.ctx.methodref(
213: "oscript.data.BasicScope", "reset", "()V")));
214: }
215: }
216: lg.setEnd(cv.il.getLastInstructionHandle());
217: return prev;
218: }
219:
220: /**
221: * Get the slot for the local variable that refers to this scope object
222: */
223: int getSlot() {
224: return slot;
225: }
226:
227: /**
228: * Generate the {@link Scope#createMember} call. The code is generated
229: * at the current position in the compiler that this scope is declared
230: * in. Stack:
231: * <pre>
232: * ... -> ... , Value
233: * </pre>
234: *
235: * @param id the <IDENTIFIER> token
236: * @param attr the permissions attribute
237: */
238: void createMember(NodeToken id, int attr) {
239: cv.il.append(new ALOAD(getSlot()));
240: cv.handle(id);
241: cv.ctx.pushSymbol(cv.il, id.tokenImage);
242: cv.ctx.pushInt(cv.il, attr);
243: cv.il.append(new INVOKEVIRTUAL(cv.ctx.methodref(
244: "oscript.data.Scope", "createMember",
245: "(II)Loscript/data/Value;")));
246:
247: createMemberImpl(id.otokenImage).dumpInitializer();
248:
249: int iid = Symbol.getSymbol(id.otokenImage).getId();
250: smit.create(iid);
251: if (privSmit != null) {
252: if (attr == Reference.ATTR_PRIVATE)
253: privSmit.create(iid);
254: else
255: pubpSmit.create(iid);
256: }
257: }
258:
259: private Member createMemberImpl(Value name) {
260: Member member = new Member(this , name);
261: memberTable.put(name, member);
262: return member;
263: }
264:
265: /**
266: * Dump the code to perform a {@link Scope#lookupInScope}.
267: *
268: * @param cv the compiler instance within which to perform the lookup
269: * @param id the <IDENTIFIER> token
270: */
271: void lookupInScope(CompilerVisitor cv, NodeToken id) {
272: cv.handle(id);
273:
274: Value name = id.otokenImage;
275: Member member = (Member) (memberTable.get(name));
276:
277: if (member == null) {
278: if ((prev == null) || openScope)
279: createMemberImpl(name).dumpLookup(cv);
280: else
281: prev.lookupInScope(cv, id);
282: } else {
283: member.dumpLookup(cv);
284: }
285: }
286:
287: /**
288: * Called by the compiler to indicate that compilation has entered a
289: * potentially condional path within this scope. (It does not matter
290: * if this scope is entirely enclosed by a conditional path, what does
291: * matter is a conditional path enclosed by this scope.)
292: *
293: * @see #leaveConditional
294: */
295: void enterConditional() {
296: conditionalNestingCnt++;
297: }
298:
299: /**
300: * Called by the compiler to indicate that compilation has left a
301: * potentially conditional path within this scope.
302: *
303: * @see #enterConditional
304: */
305: void leaveConditional() {
306: conditionalNestingCnt--;
307: }
308:
309: /**
310: */
311: boolean inConditional() {
312: return cv.scope.inConditionalImpl(this );
313: }
314:
315: private boolean inConditionalImpl(CompilerScope terminator) {
316: if (conditionalNestingCnt > 0)
317: return true;
318: else if (this != terminator)
319: return prev.inConditionalImpl(terminator);
320: else
321: return false;
322: }
323: }
324:
325: /**
326: * An instance of this class is created for each variable that is declared
327: * or looked up, to track the status of that variable
328: */
329: class Member {
330: private int slot = -1;
331: private CompilerScope scope;
332: private Value name;
333: private boolean mayNeedToLoad;
334: private boolean definitelyNeedToLoad = true;
335: private CompilerVisitor cv;
336:
337: private InstructionHandle initializerHandle = null;
338:
339: /**
340: * Class Constructor for a member of a scope.
341: *
342: * @param scope the scope this member is declared in
343: * @param name the member name
344: */
345: Member(CompilerScope scope, Value name) {
346: this .scope = scope;
347: this .name = name;
348: cv = scope.cv;
349:
350: mayNeedToLoad = scope.inConditional();
351:
352: cv.defer(new Runnable() {
353: public void run() {
354: if (initializerHandle != null) {
355: try {
356: cv.il.delete(initializerHandle);
357: } catch (Throwable t) {
358: Thread.dumpStack();
359: }
360: initializerHandle = null;
361: }
362: }
363: });
364: }
365:
366: // protected void finalize()
367: // {
368: // System.err.println(name + ": slot=" + slot);
369: // }
370:
371: /**
372: * Called when we decide that this member should be cached.
373: */
374: private void createLocalVar() {
375: if (slot != -1)
376: return; // already created
377:
378: LocalVariableGen lg = cv.mg.addLocalVariable(cv.ctx
379: .makeUniqueIdentifierName(name.castToString()),
380: cv.ctx.VALUE_TYPE, null, // XXX setStart!
381: null);
382:
383: slot = lg.getIndex();
384:
385: //if(XXX)
386: {
387: cv.il.insert(new ASTORE(slot)); // insert at head in reverse order
388: cv.il.insert(InstructionConstants.ACONST_NULL);
389: }
390:
391: if (initializerHandle == null)
392: Thread.dumpStack();
393:
394: // replace initializerHandle:
395: InstructionHandle tmp = cv.il.append(initializerHandle,
396: InstructionConstants.DUP);
397: cv.il.append(tmp, new ASTORE(slot));
398: }
399:
400: /**
401: * can only be called by defining scope, after createMember
402: */
403: void dumpInitializer() {
404: if (initializerHandle != null)
405: Thread.dumpStack();
406: initializerHandle = cv.il.append(InstructionConstants.NOP);
407: definitelyNeedToLoad = false;
408: }
409:
410: /**
411: * can only be called by defining scope, after lookupInScope
412: */
413: void dumpLookup(CompilerVisitor cv) {
414: // XXX perhaps this could be cleaned up:
415: if (definitelyNeedToLoad) {
416: mayNeedToLoad = scope.inConditional();
417:
418: cv.il.append(new ALOAD(scope.getSlot()));
419:
420: cv.ctx.pushSymbol(cv.il, name.castToString());
421:
422: cv.il.append(new INVOKEVIRTUAL(cv.ctx.methodref(
423: "oscript.data.Scope", "lookupInScope",
424: "(I)Loscript/data/Value;")));
425:
426: dumpInitializer();
427: } else if (mayNeedToLoad) {
428: createLocalVar();
429:
430: mayNeedToLoad = scope.inConditional();
431:
432: cv.il.append(new ALOAD(slot));
433:
434: cv.il.append(InstructionConstants.DUP);
435:
436: BranchInstruction IFNONNULL = new IFNONNULL(null);
437: cv.il.append(IFNONNULL);
438: cv.il.append(InstructionConstants.POP);
439: cv.il.append(new ALOAD(scope.getSlot()));
440:
441: cv.ctx.pushSymbol(cv.il, name.castToString());
442:
443: cv.il.append(new INVOKEVIRTUAL(cv.ctx.methodref(
444: "oscript.data.Scope", "lookupInScope",
445: "(I)Loscript/data/Value;")));
446: cv.il.append(InstructionConstants.DUP);
447: cv.il.append(new ASTORE(slot));
448:
449: cv.il.setNextAsTarget(IFNONNULL);
450: } else {
451: createLocalVar();
452: cv.il.append(new ALOAD(slot));
453: }
454: }
455: }
456:
457: /*
458: * Local Variables:
459: * tab-width: 2
460: * indent-tabs-mode: nil
461: * mode: java
462: * c-indentation-style: java
463: * c-basic-offset: 2
464: * eval: (c-set-offset 'substatement-open '0)
465: * eval: (c-set-offset 'case-label '+)
466: * eval: (c-set-offset 'inclass '+)
467: * eval: (c-set-offset 'inline-open '0)
468: * End:
469: */
|