001: /*=============================================================================
002: * Copyright Texas Instruments 2000-2004. 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.data;
022:
023: import java.util.*;
024:
025: import oscript.exceptions.*;
026: import oscript.util.*;
027:
028: /**
029: * Scope is an internal object use to represent a scope of execution. This
030: * class implements a basic scope which can have members created and accessed
031: * via <code>lookupInScope</code> or <code>getMember</code>.
032: * <p>
033: * Different instances of a scope that represent the same part of the syntax
034: * tree can share the hashtable that maps member name to member index. This
035: * reduces the number of hashtables that need to be created as scopes are
036: * created, to improve performance. The member index is an index into each
037: * scope instances array of members.
038: * <p>
039: * In cases where it is safe to allocate storage for this scope from the
040: * stack, the current {@link StackFrame} may be passed to the constructor,
041: * in which case this object will try and allocate it's member storage from
042: * the stack. It is only safe to use the stack for storage allocation in
043: * cases where this scope does not enclose a function instance, so if the
044: * scope does enclose a function declaration, <code>null</code> must be
045: * passed in to the constructor instead of the current stack frame.
046: *
047: * @author Rob Clark (rob@ti.com)
048: */
049: public class BasicScope extends Scope {
050: /**
051: * The shared member index table maps member name to an index into the
052: * <code>members</code> array.
053: */
054: protected SymbolTable smit;
055:
056: /**
057: * The table of members of the scope. This is unique to each scope
058: * instance.
059: */
060: protected MemberTable members;
061:
062: /**
063: * List of mixed in vars, or <code>null</code> if none.
064: */
065: protected Value[] mixins = null;
066:
067: /*=======================================================================*/
068: /*=======================================================================*/
069: /*=======================================================================*/
070:
071: /**
072: * Class Constructor. Construct a new element in the scope chain. This
073: * constructs a "regular" element in the scope chain, as opposed to the
074: * element that is created when a function is called.
075: *
076: * @param previous previous in environment scope chain
077: */
078: public BasicScope(Scope previous) {
079: this (previous, new OpenHashSymbolTable(3, 0.67f));
080: }
081:
082: /**
083: * Class Constructor. Construct a new element in the scope chain. This
084: * constructs a "regular" element in the scope chain, as opposed to the
085: * element that is created when a function is called.
086: *
087: * @param previous previous in environment scope chain
088: * @param smit shared member idx table
089: */
090: public BasicScope(Scope previous, SymbolTable smit) {
091: this (previous, smit, new OArray(smit.size()));
092: }
093:
094: /**
095: * Class Constructor.
096: *
097: * @param previous previous scope
098: * @param smit shared-member-index-table
099: * @param members members, can be used by function scope to directly
100: * map arguments to the function into the function's scope
101: */
102: protected BasicScope(Scope previous, SymbolTable smit,
103: MemberTable members) {
104: super (previous);
105:
106: // if( smit == null )
107: // {
108: //System.err.println("yes, SMIT can be null"); // XXX
109: // smit = new OpenHashSymbolTable( 3, 0.67f );
110: // }
111:
112: this .smit = smit;
113: this .members = members;
114: }
115:
116: /*=======================================================================*/
117: /**
118: * Get the type of this object. The returned type doesn't have to take
119: * into account the possibility of a script type extending a built-in
120: * type, since that is handled by {@link #getType}.
121: *
122: * @return the object's type
123: */
124: protected Value getTypeImpl() {
125: return Type.HIDDEN_TYPE;
126: }
127:
128: /*=======================================================================*/
129: /**
130: * In case a scope has any resource allocated from a source which will
131: * no long be valid after a stack frame has returned (ie. resource
132: * allocated from stack), return a copy of the scope that is safe to
133: * keep after the stack frame returns.
134: */
135: public Scope getSafeCopy() {
136: members = members.safeCopy();
137: return super .getSafeCopy();
138: }
139:
140: // XXX for debugging, determine if this is a scope that is safe to hold
141: // a reference to after it is exited
142: public boolean isSafe() {
143: // StringBuffer sb = new StringBuffer();
144: // sb.append(this);
145: // sb.append(": ");
146: // sb.append( getClass().getName() );
147: // sb.append(", ");
148: // sb.append( findDesc(this) );
149: // sb.append(", ");
150: //
151: // Set memberSet = new HashSet();
152: // populateMemberSet( memberSet, true );
153: // sb.append(memberSet);
154: //
155: // if( members == StackFrame.currentStackFrame().members )
156: // {
157: // System.err.println("scope not safe: " + sb);
158: // return false;
159: // }
160: // else if( (previous != null) && !previous.isSafe() )
161: // {
162: // System.err.println(" -> " + sb);
163: // return false;
164: // }
165: return true;
166: }
167:
168: /*=======================================================================*/
169: /*=======================================================================*/
170: /*=======================================================================*/
171:
172: protected Value getMemberImpl(int id) {
173: Value val = getInstanceMemberImpl(id);
174:
175: if (val == null)
176: val = getType().getTypeMember(this , id);
177:
178: if (val == null && (mixins != null))
179: for (int i = 0; (i < mixins.length) && (val == null); i++)
180: val = mixins[i].getMember(id, false);
181:
182: return val;
183: }
184:
185: // only for use by wrapper class (oscript.classwrap.ClassWrapGen)
186: public Value __getInstanceMember(int id) {
187: return getInstanceMemberImpl(id);
188: }
189:
190: protected Value getInstanceMemberImpl(int id) {
191: int idx = smit.get(id);
192:
193: if ((idx < 0) || (idx >= members.length()))
194: return null;
195:
196: Reference ref = members.referenceAt(idx);
197: if ((ref == null) || (ref.getAttr() == Reference.ATTR_INVALID))
198: return null;
199:
200: return ref;
201: }
202:
203: /**
204: * Reset this scope object. When program execution has left this scope
205: * block, it must be reset to ensure that any reference to it's members
206: * be freed.
207: */
208: public final void reset() {
209: members.reset();
210: mixins = null;
211: }
212:
213: /*=======================================================================*/
214: /*=======================================================================*/
215: /*=======================================================================*/
216:
217: /**
218: * Get the type of this object. This is overloaded so that mixed in
219: * objects have an appropriate effect on <code>instanceof</code>
220: *
221: * @return the object's type
222: */
223: public Value getType() {
224: final Value type = super .getType();
225:
226: if (mixins == null) {
227: return type;
228: } else {
229: return new AbstractReference() {
230:
231: public boolean isA(Value type) {
232: if (get().isA(type))
233: return true;
234:
235: for (int i = 0; i < mixins.length; i++)
236: if (mixins[i].getType().isA(type))
237: return true;
238:
239: return false;
240: }
241:
242: protected Value get() {
243: return type;
244: }
245:
246: };
247: }
248: }
249:
250: /**
251: * Create a member of this object with the specified value.
252: * <p>
253: * Note that the theory behind not synchronizing this is that it can only
254: * be a race condition against itself, not against getMember, etc, and that
255: * this will only be called from a single thread context.
256: *
257: * @param id the id of the symbol that maps to the member
258: * @param attr the attributes of the object (see <code>Reference</code>.)
259: * @see Reference
260: */
261: public Value createMember(int id, int attr) {
262: int idx = smit.create(id);
263: members.ensureCapacity(idx);
264: Reference ref = members.referenceAt(idx);
265: ref.reset(attr);
266: return ref;
267: }
268:
269: /**
270: * "mixin" the specified variable into the current scope.
271: *
272: * @param val the value to mixin to this scope
273: */
274: public void mixin(Value val) {
275: int oldLength = (this .mixins == null) ? 0 : this .mixins.length;
276: Value[] mixins = new Value[oldLength + 1];
277:
278: if (this .mixins != null)
279: System.arraycopy(this .mixins, 0, mixins, 0, oldLength);
280:
281: // NOTE: important to do this before overwriting this.mixins or
282: // there will be a race condition
283: mixins[oldLength] = val;
284:
285: this .mixins = mixins;
286: }
287:
288: /**
289: * Get a member of this object.
290: *
291: * @param id the id of the symbol that maps to the member
292: * @param exception whether an exception should be thrown if the
293: * member object is not resolved
294: * @return a reference to the member
295: * @throws PackagedScriptObjectException(NoSuchMethodException)
296: * @throws PackagedScriptObjectException(NoSuchMemberException)
297: */
298: public Value getMember(int id, boolean exception)
299: throws PackagedScriptObjectException {
300: Value val = getMemberImpl(id);
301:
302: if (val == null)
303: return super .getMember(id, exception);
304:
305: if ((val instanceof Reference) && !((Reference) val).isPublic()) {
306: if (exception)
307: throw PackagedScriptObjectException
308: .makeExceptionWrapper(new OException(getType()
309: .castToString()
310: + ": "
311: + Symbol.getSymbol(id)
312: + " is not public"));
313: else
314: val = null;
315: }
316:
317: return val;
318: }
319:
320: /**
321: * Get a member from this scope. This is used to access local variables
322: * and object attributes from methods of the object. If the attribute
323: * isn't in this node in the scope chain, then the <code>previous</code>
324: * node in the scope chain is checked.
325: *
326: * @param id the id of the symbol that maps to the member
327: * @throws PackagedScriptObjectException(NoSuchMemberException)
328: */
329: public Value lookupInScope(int id)
330: throws PackagedScriptObjectException {
331: Value val = getMemberImpl(id);
332:
333: if (val == null)
334: val = previous.lookupInScope(id);
335:
336: return val;
337: }
338:
339: /**
340: * Indicate that this scope is no longer needed
341: */
342: public void free() {
343: // XXX currently members are freed elsewhere... should investigate
344: // consolodating freeing members and scope somehow
345: }
346:
347: /*=======================================================================*/
348: /*=======================================================================*/
349: /*=======================================================================*/
350:
351: // protected String convertToString()
352: // {
353: // throw new ProgrammingErrorException("unimplemented");
354: // // String str = "";
355: // // if( memberTable != null )
356: // // {
357: // // for( Enumeration e = memberTable.keys();
358: // // e.hasMoreElements(); )
359: // // {
360: // // Object key = e.nextElement();
361: // // str += "(" + ((Value)key).castToString() + ")";
362: // // }
363: // // }
364: // // return str;
365: // }
366: /*=======================================================================*/
367: /*=======================================================================*/
368: /*=======================================================================*/
369:
370: /*=======================================================================*/
371: /**
372: * Derived classes that implement {@link #getMember} should also
373: * implement this.
374: *
375: * @param s the set to populate
376: * @param debugger <code>true</code> if being used by debugger, in
377: * which case both public and private/protected field names should
378: * be returned
379: * @see #getMember
380: */
381: protected void populateMemberSet(Set s, boolean debugger) {
382: if (members == null)
383: return;
384: for (Iterator itr = smit.symbols(); itr.hasNext();) {
385: int id = ((Integer) (itr.next())).intValue();
386: int idx = smit.get(id);
387:
388: if (idx < members.length()) {
389: Reference ref = members.referenceAt(idx);
390: if ((ref.getAttr() != Reference.ATTR_INVALID)
391: && (debugger || ref.isPublic()))
392: s.add(Symbol.getSymbol(id));
393: }
394: }
395:
396: Value[] mixins = this .mixins;
397: if (mixins != null)
398: for (int i = 0; i < mixins.length; i++)
399: mixins[i].populateMemberSet(s, debugger);
400: }
401:
402: // used by Debugger::getMemberAccessor, allows access to non-public members
403: Debugger.MemberAccessor _getInstanceMemberAccessor(Value name) {
404: Value val = getMemberImpl(Symbol.getSymbol(name).getId());
405:
406: if (val != null)
407: return new BasicScopeMemberAccessor(val);
408: else
409: return null;
410: }
411:
412: private static class BasicScopeMemberAccessor implements
413: Debugger.MemberAccessor {
414: private Value val;
415:
416: /**
417: * It is safe to use val instead of name, because we never replace an
418: * entry in the member table.
419: */
420: BasicScopeMemberAccessor(Value val) {
421: this .val = val;
422: }
423:
424: public int getAttr() {
425: if (val instanceof Reference)
426: return ((Reference) val).getAttr();
427: else
428: return Reference.ATTR_PUBLIC | Reference.ATTR_CONST;
429: }
430:
431: public Value getValue() {
432: return val;
433: }
434: }
435: }
436:
437: /*
438: * Local Variables:
439: * tab-width: 2
440: * indent-tabs-mode: nil
441: * mode: java
442: * c-indentation-style: java
443: * c-basic-offset: 2
444: * eval: (c-set-offset 'substatement-open '0)
445: * eval: (c-set-offset 'case-label '+)
446: * eval: (c-set-offset 'inclass '+)
447: * eval: (c-set-offset 'inline-open '0)
448: * End:
449: */
|