001: /*=============================================================================
002: * Copyright Texas Instruments 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.util;
022:
023: import java.io.BufferedReader;
024: import java.io.InputStreamReader;
025: import java.util.*;
026:
027: import oscript.exceptions.*;
028: import oscript.data.*;
029: import oscript.NodeEvaluator;
030: import oscript.fs.AbstractFile;
031:
032: /**
033: * The "chain" of stack frames is used to track execution context of a
034: * particular thread and, when debugging is enabled, give the debugger a
035: * chance to run breakpoints.
036: * <p>
037: * Where possible, the head of the chain of stack frames is passed on the
038: * stack, but in cases where it cannot be, such as when control passes to
039: * java code and back, a hashtable is used to map the current thread to
040: * a <code>StackFrame</code>. To access the current stack frame, or
041: * create one if needed, use {@link #currentStackFrame}.
042: * <p>
043: * While on the interface, the stack frame behaves as a chain of
044: * <code>StackFrame</code> objects, behind the scenes an array is used
045: * for the stack, and a fly-weight pattern is used for the stack frame
046: * objects. This way we (1) avoid extra memory allocations, and (2)
047: * can have different implementations of {@link #setLineNumber} depending
048: * on whether debugging is enabled or not. (Debugging is automatically
049: * enabled when a breakpoint is set.)
050: * <p>
051: * In order to maintain this allusion, calls to {@link NodeEvaluator#evalNode}
052: * must go through the {@link #evalNode} call-gate.
053: * <p>
054: * The stack frame object is intentionally not thread safe, since it is only
055: * accessed from a single thread context. Because of the use of the fly-
056: * weight pattern, a stack frame object no longer validly represents a stack
057: * frame that has exited, either by normally or via an exception. Because
058: * of this, any code that wishes to save a reference to a stack frame object
059: * must {@link #clone} it.
060: * <p>
061: * Because the <code>StackFrame</code> is only accessed from a single thread
062: * context, it can provide a lightweight mechanism to allocate storage for
063: * {@link BasicScope} objects. This can be used in cases where the scope
064: * object only exists on the stack, and is not held after the program
065: * enclosed by the scope has finished execution, ie. there is no function
066: * enclosed by the scope. For cases of an enclosed function, the scope
067: * storage must be allocated from the heap so that it can be valid at some
068: * point in the future when the enclosed function is potentially called.
069: *
070: * @author Rob Clark (rob@ti.com)
071: * @version 1
072: */
073: public abstract class StackFrame {
074: /**
075: * note impericially derived stack size number based on
076: * a value sufficiently large that a StackOverFlowException
077: * occurs before the stack frame index reaches this value.
078: */
079: private static final int STACK_SIZE = 808; // reduct when growing stack implemented??
080:
081: /**
082: * it would be nice to support paging the member stack, so we can keep
083: * it smaller, and possible save/restore it thru the same call gate as
084: * <code>idx[0]</code> and <code>membersIdx[0]</code> are saved/restored.
085: */
086: private static final int MEMBERS_STACK_SIZE = 4096;
087:
088: /**
089: * Maps thread to stack frame. Because of the fly-weight pattern, there
090: * is only one stack frame object (well, actually two), so all we need
091: * is to map the current frame to a stack frame... no need to go
092: * searching for the tail, or do any extra book-keeping to track the
093: * tail.
094: */
095: private static final Hashtable stackFrameTable = new Hashtable();
096:
097: /**
098: * Get the stack frame for the current thread. If one does not already
099: * exist, this will create a new one, otherwise it will return the
100: * current top of the stack.
101: */
102: public static synchronized StackFrame currentStackFrame() {
103: Thread t = Thread.currentThread();
104: StackFrame stackFrame = (StackFrame) (stackFrameTable.get(t));
105:
106: if (stackFrame == null) {
107: stackFrame = new RegularStackFrame();
108: stackFrameTable.put(t, stackFrame);
109: }
110:
111: return stackFrame;
112: }
113:
114: /**
115: * StackFrame to use when not debugging. This one's {@link #setLineNumber}
116: * does not have extra checks for breakpoints for better performance. This
117: * should be treated as final.
118: */
119: protected StackFrame regularStackFrame;
120:
121: /**
122: * StackFrame to use whe debugging. This one's {@link #setLineNumber} does
123: * have extra checks for breakpoints, so breakpoints can work properly.
124: * This should be treated as final.
125: */
126: protected StackFrame debugStackFrame;
127:
128: /**
129: * The current index, boxed in array so it can be shared between the
130: * two stack-frame instances (regular & debug)
131: */
132: protected final short[] idx;
133:
134: /**
135: * The node evaluator, which has file, and id info needed when filling
136: * in stack trace.
137: */
138: protected final NodeEvaluator[] nes;
139:
140: /**
141: * The current line number at each stack frame.
142: */
143: protected final int[] lines;
144:
145: /**
146: * The current scopes at each stack frame.
147: */
148: protected final Scope[] scopes;
149:
150: /**
151: * The list of scopes allocated at the current frame, which should be
152: * recycled once the stack frame is released.
153: */
154: protected final StackFrameBasicScope[] scopeLists;
155:
156: /**
157: * The pool of members that can be used (and re-used) by scope objects
158: * that only ever exist on the stack.
159: */
160: private final Reference[] members;
161:
162: /**
163: * The index of the first available slot in the members stack, boxed in
164: * an array so it can be shared between the two stack-frame instances
165: * (regular & debug)
166: */
167: private final short[] membersIdx;
168:
169: /**
170: * The number of slots in <code>members</code>.
171: */
172: private final short membersCount;
173:
174: /**
175: * Pool of available, pre-allocated SFA's. Whenever possible, allocating
176: * a SFA will re-use a SFA from the pool, to avoid dynamic memory allocation
177: * @see #allocateMemberTable(short)
178: */
179: private StackFrameMemberTable sfaPool = null; // XXX should be shared between both StackFrame objects...
180:
181: /**
182: * Pool of available, pre-allocated scope objects. Whenever possible,
183: * allocating a new scope will re-use one from the pool, in order to avoid
184: * dynamic memory allocation.
185: * @see #allocateBasicScope(Scope, SymbolTable)
186: */
187: private StackFrameBasicScope basicScopePool = null; // XXX should be shared between both StackFrame objects
188:
189: /**
190: * Pool of available, pre-allocated fxn-scope objects. Whenever possible,
191: * allocating a new scope will re-use one from the pool, in order to avoid
192: * dynamic memory allocation.
193: * @see #allocateFunctionScope(Function, Scope, SymbolTable, MemberTable)
194: */
195: private StackFrameFunctionScope functionScopePool = null; // XXX should be shared between both StackFrame objects
196:
197: /**
198: * Class Constructor.
199: */
200: private StackFrame(short[] idx, NodeEvaluator[] nes, int[] lines,
201: Scope[] scopes, StackFrameBasicScope[] scopeLists,
202: Reference[] members, short[] membersIdx) {
203: this .idx = idx;
204: this .nes = nes;
205: this .lines = lines;
206: this .scopes = scopes;
207: this .scopeLists = scopeLists;
208:
209: this .members = members;
210: this .membersIdx = membersIdx;
211:
212: membersCount = (short) ((members != null) ? members.length : 0);
213: }
214:
215: /**
216: * Push a new stack frame onto the stack, and pass it to <code>ne</code>'s
217: * {@link #evalNode} method, returning the result.
218: *
219: * @param ne the node-evaluator for the node to evaluate
220: * @param scope the scope to evalute in
221: * @return the result
222: */
223: public final Object evalNode(NodeEvaluator ne, Scope scope) {
224: StackFrame sf = Debugger.mayHaveBreakpoints(ne) ? debugStackFrame
225: : regularStackFrame;
226:
227: short idx = ++this .idx[0];
228: short membersIdx = this .membersIdx[0];
229:
230: // grow stack, if needed:
231: if (idx >= nes.length)
232: throw new ProgrammingErrorException(
233: "growing stack is not implemented yet, so STACK OVERFLOW! (idx="
234: + idx + ")"); // XXX
235:
236: nes[idx] = ne;
237:
238: try {
239: return ne.evalNode(sf, scope);
240: } catch (PackagedScriptObjectException e) {
241: if (e.val instanceof OException)
242: ((OException) (e.val)).preserveStackFrame();
243: throw e;
244: } finally {
245: // reset members, since it is possible this didn't happen in the
246: // compiler... if the compiler gets better about always resetting,
247: // then we won't need this here:
248: for (int i = this .membersIdx[0] - 1; i >= membersIdx; i--)
249: if (this .members[i] != null)
250: this .members[i].reset();
251:
252: StackFrameBasicScope scopeList = scopeLists[idx];
253: scopeLists[idx] = null;
254: while (scopeList != null) {
255: StackFrameBasicScope head = scopeList;
256: scopeList = scopeList.next;
257: head.next = basicScopePool;
258: basicScopePool = head;
259: }
260:
261: // so things can be GC'd:
262: scopes[idx] = null;
263: nes[idx] = null; // is this needed??
264: this .idx[0] = (short) (idx - 1);
265: this .membersIdx[0] = membersIdx;
266: }
267: }
268:
269: /**
270: * Called by node evaluator to store line number info, and to give the
271: * debugger a chance to see if we've hit a breakpoint.
272: *
273: * @param scope the current scope
274: * @param line the current line number
275: */
276: public void setLineNumber(Scope scope, int line) {
277: short idx = this .idx[0];
278: scopes[idx] = scope;
279: lines[idx] = line;
280: if (VERBOSE)
281: logSetLineNumber(getFile(), line);
282: }
283:
284: /**
285: * Called by node evaluator to store line number info, and to give the
286: * debugger a chance to see if we've hit a breakpoint. This method
287: * is used by the compiler in cases where the scope hasn't changed
288: * sinced last line number, to save a few instructions.
289: *
290: * @param scope the current scope
291: * @param line the current line number
292: */
293: public void setLineNumber(int line) {
294: lines[idx[0]] = line;
295: if (VERBOSE)
296: logSetLineNumber(getFile(), line);
297: }
298:
299: /**
300: * Allocate a scope from the stack. The basic-scope is freed automatically
301: * when the stack-frame is disposed
302: */
303: public final BasicScope allocateBasicScope(Scope prev,
304: SymbolTable smit) {
305: short idx = this .idx[0];
306: StackFrameBasicScope scope;
307: if (basicScopePool != null) {
308: scope = basicScopePool;
309: scope.reinit(prev, smit);
310: basicScopePool = basicScopePool.next;
311: if (VERBOSE)
312: log("recycle StackFrameBasicScope");
313: } else {
314: scope = new StackFrameBasicScope(prev, smit);
315: if (VERBOSE)
316: log("allocate StackFrameBasicScope");
317: }
318: scope.next = scopeLists[idx];
319: scopeLists[idx] = scope;
320: return scope;
321: }
322:
323: /**
324: * A scope allocated by the stack-frame, which can be recycled
325: */
326: private final class StackFrameBasicScope extends BasicScope {
327: StackFrameBasicScope next;
328:
329: StackFrameBasicScope(Scope previous, SymbolTable smit) {
330: super (previous, smit, allocateMemberTable((short) (smit
331: .size())));
332: }
333:
334: final void reinit(Scope previous, SymbolTable smit) {
335: this .previous = previous;
336: this .smit = smit;
337: if (members instanceof StackFrameMemberTable)
338: ((StackFrameMemberTable) members).reinit((short) (smit
339: .size()));
340: else
341: members = allocateMemberTable((short) (smit.size()));
342: }
343: }
344:
345: /**
346: * Allocate a fxn-scope from the stack. The fxn-scope must be
347: * freed by the caller.
348: */
349: public final FunctionScope allocateFunctionScope(Function fxn,
350: Scope prev, SymbolTable smit, MemberTable members) {
351: StackFrameFunctionScope scope;
352: if (functionScopePool != null) {
353: scope = functionScopePool;
354: scope.reinit(fxn, prev, smit, members);
355: functionScopePool = functionScopePool.next;
356: if (VERBOSE)
357: log("recycle StackFrameFunctionScope");
358: } else {
359: scope = new StackFrameFunctionScope(fxn, prev, smit,
360: members);
361: if (VERBOSE)
362: log("allocate StackFrameFunctionScope");
363: }
364: return scope;
365: }
366:
367: /**
368: * A scope allocated by the stack-frame, which can be recycled
369: */
370: private final class StackFrameFunctionScope extends FunctionScope {
371: StackFrameFunctionScope next;
372:
373: StackFrameFunctionScope(Function fxn, Scope prev,
374: SymbolTable smit, MemberTable members) {
375: super (fxn, prev, smit, members);
376: }
377:
378: final void reinit(Function fxn, Scope prev, SymbolTable smit,
379: MemberTable members) {
380: this .fxn = fxn;
381: this .previous = prev;
382: this .smit = smit;
383: this .members = members;
384: }
385:
386: public final void free() {
387: this .members = null;
388: this .next = functionScopePool;
389: functionScopePool = this ;
390: }
391: }
392:
393: /**
394: * Allocate from the stack.
395: */
396: public final MemberTable allocateMemberTable(short sz) {
397: /* note: not synchronized because only a single thread context
398: * should be allocating/freeing from this stack...
399: */
400: StackFrameMemberTable sfa;
401: if (sfaPool != null) {
402: sfa = sfaPool;
403: sfaPool = sfaPool.next;
404: if (VERBOSE)
405: log("recycle StackFrameMemberTable: " + sfa);
406: } else {
407: sfa = new StackFrameMemberTable();
408: if (VERBOSE)
409: log("allocate StackFrameMemberTable: " + sfa);
410: }
411: sfa.reinit(sz);
412: return sfa;
413: }
414:
415: /**
416: * An array object which uses the pre-allocated stack. Note that this array
417: * is not thread safe, because it is only intended to be used from the thread
418: * associated with this stack.
419: */
420: private final class StackFrameMemberTable implements MemberTable {
421: private short off; // offset into 'members'
422: private short len; // length of our part of 'members'
423: private short sz; // the actual size of the array, sz <= len
424:
425: StackFrameMemberTable next;
426:
427: private Reference[] members;
428:
429: final void reinit(short len) {
430: this .members = StackFrame.this .members;
431: this .off = membersIdx[0];
432: this .len = len;
433: this .sz = 0;
434:
435: if ((len + off) > membersCount)
436: throw new ProgrammingErrorException(
437: "failed to allocate from stack for " + this
438: + ", idx=" + idx[0]);
439:
440: membersIdx[0] = (short) (off + len);
441: }
442:
443: private final void checkIndex(int idx) {
444: if ((idx < 0) || (idx >= length())) {
445: Thread.dumpStack(); // XXX
446: throw PackagedScriptObjectException
447: .makeExceptionWrapper(new OIllegalArgumentException(
448: "invalid array index: " + idx));
449: }
450: }
451:
452: /**
453: * grow the member table by copying out of stack (or out of current array)
454: * @param grow the number of elements to grow the table by
455: */
456: private final void copyOutOfStack(int grow) {
457: Reference[] newMembers = new Reference[len + grow];
458: Reference r;
459: if (members == StackFrame.this .members) {
460: if (VERBOSE)
461: log("copy out of stack: " + this + ", grow=" + grow);
462: System.arraycopy(members, off, newMembers, 0, sz);
463: // need to null out entries from shared stack to ensure
464: // that no-one else tries to recycle a Reference object
465: // while it is still used by the safe-copy
466: for (int i = off + sz - 1; i >= off; i--)
467: members[i] = null;
468:
469: if ((off + len) == membersIdx[0])
470: membersIdx[0] = off;
471: else if (DEBUG)
472: System.err.println(" *** cant free"); // XXXXX
473: } else {
474: if (VERBOSE)
475: log("grow: " + this + ", grow=" + grow);
476: System.arraycopy(members, off, newMembers, 0, sz);
477: }
478: members = newMembers;
479: off = 0;
480: len += grow;
481: }
482:
483: public void reset() {
484: if (VERBOSE)
485: log("reset: " + this );
486: for (int i = off + sz - 1; i >= off; i--)
487: members[i].reset();
488: }
489:
490: public void free() {
491: if (VERBOSE)
492: log("free: " + this );
493:
494: if (members == StackFrame.this .members) {
495: reset();
496: membersIdx[0] = off;
497: }
498:
499: // free the StackFrameMemberTable to the pool:
500: members = null;
501: next = sfaPool;
502: sfaPool = this ;
503: }
504:
505: public Reference referenceAt(int idx) {
506: if (DEBUG)
507: checkIndex(idx);
508: idx += off;
509: if (members[idx] == null)
510: members[idx] = new Reference();
511: return members[idx];
512: }
513:
514: public int length() {
515: return sz;
516: }
517:
518: public void ensureCapacity(int sz) {
519: sz++;
520: int grow = sz - len;
521: if (grow > 0) {
522: if (members == StackFrame.this .members) {
523: // if off+len == membersIdx, then we are the topmost array on the stack
524: // so it is safe to grow up:
525: if (membersIdx[0] == (off + len)) {
526: if (VERBOSE)
527: log("growing table on stack: " + this );
528: len += grow;
529: membersIdx[0] += grow;
530: } else {
531: if (VERBOSE)
532: log("cannot grow table (shouldn't normally happen): "
533: + this );
534: copyOutOfStack(grow);
535: }
536: } else if ((sz + off) > members.length) {
537: copyOutOfStack(sz + off - members.length);
538: }
539: }
540:
541: if (sz > this .sz)
542: this .sz = (short) sz;
543: }
544:
545: public MemberTable safeCopy() {
546: if (VERBOSE)
547: log("safe copy: " + this );
548:
549: if (members == StackFrame.this .members)
550: copyOutOfStack(0);
551:
552: // we need to use this OArray constructor to ensure that the same Reference
553: // objects in the members table get used... otherwise there could be problems
554: // with the compiler's cached Reference's (as local vars) getting out of
555: // sync:
556: return new OArray(members, sz);
557: }
558:
559: public void push1(Value val) {
560: int idx = sz;
561: ensureCapacity(idx);
562: referenceAt(idx).reset(val);
563: }
564:
565: public void push2(Value val1, Value val2) {
566: int idx = sz;
567: ensureCapacity(idx + 1);
568: referenceAt(idx++).reset(val1);
569: referenceAt(idx).reset(val2);
570: }
571:
572: public void push3(Value val1, Value val2, Value val3) {
573: int idx = sz;
574: ensureCapacity(idx + 2);
575: referenceAt(idx++).reset(val1);
576: referenceAt(idx++).reset(val2);
577: referenceAt(idx).reset(val3);
578: }
579:
580: public void push4(Value val1, Value val2, Value val3, Value val4) {
581: int idx = sz;
582: ensureCapacity(idx + 3);
583: referenceAt(idx++).reset(val1);
584: referenceAt(idx++).reset(val2);
585: referenceAt(idx++).reset(val3);
586: referenceAt(idx).reset(val4);
587: }
588:
589: public String toString() {
590: return "[" + hashCode() + ": off=" + off + ", sz=" + sz
591: + ", len=" + len + "]";
592: }
593: }
594:
595: /**
596: * Convenience wrapper for {@link #getId}, mainly provided for the benefit
597: * of script code that probably doesn't want to know about ids, and just
598: * wants to think in terms of names.
599: */
600: public oscript.data.Value getName() {
601: int id = getId();
602: if (id == -1)
603: return null;
604: return Symbol.getSymbol(id);
605: }
606:
607: /**
608: * The function name for the current stack frame, if there is one, otherwise
609: * <code>-1</code>.
610: */
611: public final int getId() {
612: return nes[idx[0]].getId();
613: }
614:
615: /**
616: * The file corresponding to the current stack frame.
617: */
618: public final AbstractFile getFile() {
619: return nes[idx[0]].getFile();
620: }
621:
622: /**
623: * The current line number in the current stack frame.
624: */
625: public final int getLineNumber() {
626: return lines[idx[0]];
627: }
628:
629: /**
630: * The current scope in the current line number.
631: */
632: public final Scope getScope() {
633: return scopes[idx[0]];
634: }
635:
636: /**
637: * Return an iterator of stack frames, starting with the top of the
638: * stack, and iterating to root of stack.
639: */
640: public Iterator iterator() {
641: return new CollectionIterator(new Iterator() {
642:
643: private short idx = StackFrame.this .idx[0];
644:
645: public boolean hasNext() {
646: return idx > 0;
647: }
648:
649: public Object next() {
650: return new ReadOnlyStackFrame(idx--, nes, lines, scopes);
651: }
652:
653: public void remove() {
654: throw new UnsupportedOperationException("remove");
655: }
656:
657: });
658: }
659:
660: public StackFrame getSafeCopy() {
661: return (StackFrame) clone();
662: }
663:
664: /**
665: * Clone the stack frame, which is necessary in cases where you need to keep
666: * a reference to the stack frame (and it's parents) after the original
667: * stack frame has exited, such as to store in an exception.
668: */
669: public final Object clone() {
670: short idx = this .idx[0];
671:
672: NodeEvaluator[] nes = new NodeEvaluator[idx + 1];
673: System.arraycopy(this .nes, 0, nes, 0, idx + 1);
674:
675: int[] lines = new int[idx + 1];
676: System.arraycopy(this .lines, 0, lines, 0, idx + 1);
677:
678: Scope[] scopes = new Scope[idx + 1];
679: for (int i = 0; i < scopes.length; i++)
680: if (this .scopes[i] != null)
681: scopes[i] = this .scopes[i].getSafeCopy();
682:
683: return new ReadOnlyStackFrame(idx, nes, lines, scopes);
684: }
685:
686: /**
687: * Convert to string, to print out a line in the stack-trace.
688: */
689: public String toString() {
690: String fileline = getFile() + ":" + getLineNumber();
691: int id = getId();
692: if (id == -1)
693: return fileline;
694: return Symbol.getSymbol(id) + " (" + fileline + ")";
695: }
696:
697: public int hashCode() {
698: return getId() ^ getLineNumber();
699: }
700:
701: public boolean equals(Object obj) {
702: if (obj instanceof StackFrame) {
703: StackFrame other = (StackFrame) obj;
704: AbstractFile file = getFile();
705: return (other.getId() == getId())
706: && (other.getLineNumber() == getLineNumber())
707: && ((file == null) ? (other.getFile() == null)
708: : other.getFile().equals(getFile()));
709: }
710: return false;
711: }
712:
713: /*=======================================================================*/
714: /**
715: * StackFrame fly-weight to use when not debugging.
716: */
717: private static class RegularStackFrame extends StackFrame {
718: RegularStackFrame(short[] idx, NodeEvaluator[] nes,
719: int[] lines, Scope[] scopes,
720: StackFrameBasicScope[] scopeLists, Reference[] members,
721: short[] membersIdx) {
722: super (idx, nes, lines, scopes, scopeLists, members,
723: membersIdx);
724:
725: regularStackFrame = this ;
726: debugStackFrame = new DebugStackFrame(this , idx, nes,
727: lines, scopes, scopeLists, members, membersIdx);
728: }
729:
730: RegularStackFrame() {
731: this (new short[] { 0 }, new NodeEvaluator[STACK_SIZE],
732: new int[STACK_SIZE], new Scope[STACK_SIZE],
733: new StackFrameBasicScope[STACK_SIZE],
734: new Reference[MEMBERS_STACK_SIZE],
735: new short[] { 1 } // XXX for debugging, need to not start at 0
736: );
737: }
738: }
739:
740: /*=======================================================================*/
741: /**
742: * StackFrame fly-weight object to use while debugging. This functions
743: * essentially as a facade for the regularStackFrame, but with the calls
744: * out to the Debugger subsystem.
745: */
746: private final static class DebugStackFrame extends StackFrame {
747: DebugStackFrame(StackFrame sf, short[] idx,
748: NodeEvaluator[] nes, int[] lines, Scope[] scopes,
749: StackFrameBasicScope[] scopeLists, Reference[] members,
750: short[] membersIdx) {
751: super (idx, nes, lines, scopes, scopeLists, members,
752: membersIdx);
753:
754: regularStackFrame = sf;
755: debugStackFrame = this ;
756: }
757:
758: public void setLineNumber(Scope scope, int line) {
759: super .setLineNumber(scope, line);
760: Debugger.runBreakpoints(scope, getFile(), line);
761: }
762:
763: public void setLineNumber(int line) {
764: super .setLineNumber(line);
765: Debugger.runBreakpoints(getScope(), getFile(), line);
766: }
767: }
768:
769: /*=======================================================================*/
770: /**
771: * A read-only copy of a stack frame, used for a variety of purposes.
772: */
773: private static class ReadOnlyStackFrame extends RegularStackFrame {
774: ReadOnlyStackFrame(short idx, NodeEvaluator[] nes, int[] lines,
775: Scope[] scopes) {
776: super (new short[] { idx }, nes, lines, scopes, null, null,
777: null);
778: }
779:
780: public void setLineNumber(Scope scope, int line) {
781: throw new ProgrammingErrorException(
782: "cloned stack frames are read-only");
783: }
784:
785: public void setLineNumber(int line) {
786: throw new ProgrammingErrorException(
787: "cloned stack frames are read-only");
788: }
789:
790: public StackFrame getSafeCopy() {
791: return this ;
792: }
793: }
794:
795: /*=======================================================================*/
796: /**
797: * For debugging
798: */
799:
800: public static final void dumpStack(java.io.OutputStream out) {
801: dumpStack(new java.io.OutputStreamWriter(out));
802: }
803:
804: public static final void dumpStack(java.io.Writer out) {
805: java.io.PrintWriter ps;
806:
807: if (out instanceof java.io.PrintWriter)
808: ps = (java.io.PrintWriter) out;
809: else
810: ps = new java.io.PrintWriter(out);
811:
812: for (Iterator itr = currentStackFrame().iterator(); itr
813: .hasNext();)
814: ps.println(" at " + itr.next());
815:
816: ps.flush();
817: }
818:
819: private static final boolean DEBUG = false;
820: private static final boolean VERBOSE = false || DEBUG;
821:
822: public static final void log(String str) {
823: if (lastLine != -1) {
824: String[] lines = (String[]) (fileTable.get(lastFile));
825: if (lines == null) {
826: LinkedList linesList = new LinkedList();
827: try {
828: BufferedReader br = new BufferedReader(
829: new InputStreamReader(lastFile
830: .getInputStream()));
831: String tmp;
832: while ((tmp = br.readLine()) != null)
833: linesList.add(tmp);
834: } catch (java.io.IOException e) {
835: }
836: lines = (String[]) (linesList
837: .toArray(new String[linesList.size()]));
838: fileTable.put(lastFile, lines);
839: }
840: lastLine--;
841: if (lastLine >= 0 && lastLine < lines.length)
842: System.err.println(lastLine + ": "
843: + lines[lastLine - 1]);
844: lastLine = -1;
845: }
846: if (VERBOSE)
847: System.err.println(" " + str);
848: }
849:
850: private static AbstractFile lastFile = null;
851: private static int lastLine = -1;
852: private static Hashtable fileTable = new Hashtable();
853:
854: private static final void logSetLineNumber(AbstractFile file,
855: int line) {
856: if (file == null)
857: return; // ???
858:
859: if (file != lastFile) {
860: System.err.println("file: " + file);
861: lastFile = file;
862: }
863:
864: lastLine = line;
865: }
866: }
867:
868: /*
869: * Local Variables:
870: * tab-width: 2
871: * indent-tabs-mode: nil
872: * mode: java
873: * c-indentation-style: java
874: * c-basic-offset: 2
875: * eval: (c-set-offset 'substatement-open '0)
876: * eval: (c-set-offset 'case-label '+)
877: * eval: (c-set-offset 'inclass '+)
878: * eval: (c-set-offset 'inline-open '0)
879: * End:
880: */
|