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 oscript.fs.*;
024: import oscript.*;
025: import oscript.exceptions.*;
026: import oscript.classwrap.ClassWrapGen;
027: import oscript.syntaxtree.FunctionCallExpressionList;
028:
029: import java.util.*;
030:
031: /**
032: * The <code>Debugger</code> object provides access to objects, including (not
033: * for java objects) access to the object's private members. Also, some sort
034: * of security model should be provided.
035: *
036: * @author Rob Clark (rob@ti.com)
037: */
038: public class Debugger extends Value {
039: /**
040: * The type object for an instance of Object.
041: */
042: public final static Value TYPE = BuiltinType
043: .makeBuiltinType("oscript.data.Debugger");
044: public final static String PARENT_TYPE_NAME = "oscript.data.OObject";
045: public final static String TYPE_NAME = "Debugger";
046: public final static String[] MEMBER_NAMES = new String[] { // XXX these are out of date
047: "castToString", "enumerateMembers", "getMemberAccessor",
048: "setBreakpoint", "getBreakpointFiles",
049: "getBreakpointLines", "Breakpoint" };
050:
051: /**
052: * Table mapping file to breakpoint table (which maps line number to
053: * break-point). If there are no breakpoints, this will be null. If
054: * there are no breakpoints in a given file, the table for that file
055: * will be null.
056: */
057: private static Hashtable bpTable = null;
058:
059: /**
060: * If we are in single-step mode, there will be an entry mapping the
061: * thread that is single-stepping to the step-bp handler.
062: */
063: private static Hashtable stepBpTable = null;
064:
065: /**
066: * We can't call the step-bp re-entrantly, in case it is itself
067: * implemented as script code. Otherwise we can get into an infinite-
068: * recursion situation.
069: * <p>
070: * XXX this should probably be a table mapping thread -> boolean... but I
071: * need to investigate the implications of stepping thru multiple threads
072: * at once.
073: */
074: private static boolean inBp = false;
075:
076: /*=======================================================================*/
077: /**
078: * Class Constructor.
079: */
080: public Debugger() {
081: this (new OArray());
082: }
083:
084: /*=======================================================================*/
085: /**
086: * Class Constructor. This is the constructor that gets called via an
087: * BuiltinType instance.
088: *
089: * @param args arguments to this constructor
090: * @throws PackagedScriptObjectException(Exception) if wrong number of args
091: */
092: public Debugger(oscript.util.MemberTable args) {
093: super ();
094:
095: if (args.length() != 0)
096: throw PackagedScriptObjectException
097: .makeExceptionWrapper(new OIllegalArgumentException(
098: "wrong number of args!"));
099: }
100:
101: /*=======================================================================*/
102: /**
103: * Get the type of this object. The returned type doesn't have to take
104: * into account the possibility of a script type extending a built-in
105: * type, since that is handled by {@link #getType}.
106: *
107: * @return the object's type
108: */
109: protected Value getTypeImpl() {
110: return TYPE;
111: }
112:
113: /*=======================================================================*/
114: /**
115: * Convert this object to a native java <code>String</code> value.
116: *
117: * @return a String value
118: * @throws PackagedObjectException(NoSuchMethodException)
119: */
120: public String castToString() throws PackagedScriptObjectException {
121: return "[debugger]";
122: }
123:
124: /*=======================================================================*/
125: /**
126: * Determine if this node evaluator might have one or more breakpoints
127: * within it's body. This may return <code>true</code> when there are
128: * no break points, but should never return <code>false</code> if there
129: * are.
130: */
131: public static final boolean mayHaveBreakpoints(NodeEvaluator ne) {
132: AbstractFile file;
133: return (bpTable != null) && ((file = ne.getFile()) != null)
134: && (bpTable.get(file) != null);
135: }
136:
137: /*=======================================================================*/
138: /**
139: * Called by the stack-frame as the program is evaluated. Runs any
140: * breakpoints set for the specified file:lineno
141: */
142: public static final void runBreakpoints(Scope scope,
143: AbstractFile file, int line) {
144: // XXX this method should probably be synchronized, but it can get
145: // called for every line of script code, so I need to evaluate the
146: // performance impact... and see if necessary see if there is a
147: // way to avoid synchronization in the common case...
148:
149: // NOTE that making bpTable and stepBpTable null if empty makes the
150: // code more complicated, and it does more memory allocation, but I
151: // think that optimizing for the case where the debugger isn't used
152: // is more important
153:
154: if (!inBp) {
155: Breakpoint stepBp = null;
156:
157: do {
158:
159: // first check for breakpoint set at current file:line
160: if (bpTable != null) {
161: Hashtable lineTable = (Hashtable) (bpTable
162: .get(file));
163:
164: if (lineTable != null) {
165: Breakpoint bp = (Breakpoint) (lineTable
166: .get(new Integer(line)));
167:
168: if (bp != null) {
169: inBp = true;
170: stepBp = bp.handle(scope, file, line);
171: inBp = false;
172: break; // if there is a bp, don't go on to check for step-bp
173: }
174: }
175: }
176:
177: // if no breakpoint at file:line, check for step-bp for current thread:
178: if (stepBpTable != null) {
179: stepBp = (Breakpoint) (stepBpTable.remove(Thread
180: .currentThread()));
181: if (stepBpTable.size() == 0)
182: stepBpTable = null;
183: }
184:
185: if (stepBp != null) {
186: inBp = true;
187: stepBp = stepBp.handle(scope, file, line);
188: inBp = false;
189: }
190:
191: } while (false);
192:
193: if (stepBp != null) {
194: if (stepBpTable == null)
195: stepBpTable = new Hashtable();
196: stepBpTable.put(Thread.currentThread(), stepBp);
197: }
198: }
199: }
200:
201: private final static Collection EMPTY_COLLECTION = new LinkedList();
202:
203: /*=======================================================================*/
204: /**
205: * Return a collection view of the files with breakpoints set in them.
206: *
207: * @return a collection of {@link AbstractFile}
208: */
209: public static Collection getBreakpointFiles() {
210: if (bpTable == null)
211: return EMPTY_COLLECTION;
212: return bpTable.keySet();
213: }
214:
215: /*=======================================================================*/
216: /**
217: * Return a collection of line numbers of lines with breakpoints set
218: * in the specified file.
219: *
220: * @return a collection of {@link Integer}
221: */
222: public static Collection getBreakpointLines(AbstractFile file) {
223: if (bpTable == null)
224: return EMPTY_COLLECTION;
225:
226: Hashtable lineTable = (Hashtable) (bpTable.get(file));
227: if (lineTable == null)
228: return EMPTY_COLLECTION;
229:
230: return lineTable.keySet();
231: }
232:
233: /*=======================================================================*/
234: /**
235: * Set the breakpoint for the specified file:lineno. If <code>bp</code>
236: * is <code>null</code> then this clears the breakpoint. Returns the
237: * previous breakpoint on this file:lineno or <code>null</code> if none.
238: *
239: * @param file the file
240: * @param line the line number
241: * @param bp the breakpoint handler or <code>null</code>
242: */
243: public static Breakpoint setBreakpoint(AbstractFile file,
244: int linei, Breakpoint bp) {
245: Integer line = new Integer(linei);
246:
247: if (bpTable == null)
248: bpTable = new Hashtable();
249:
250: Hashtable lineTable = (Hashtable) (bpTable.get(file));
251:
252: if (lineTable == null)
253: bpTable.put(file, (lineTable = new Hashtable()));
254:
255: Breakpoint oldBp = (Breakpoint) (lineTable.get(line));
256:
257: if (bp != null)
258: lineTable.put(line, bp);
259: else
260: lineTable.remove(line);
261:
262: if (lineTable.size() == 0)
263: bpTable.remove(file);
264:
265: if (bpTable.size() == 0)
266: bpTable = null;
267:
268: return oldBp;
269: }
270:
271: /*=======================================================================*/
272: /**
273: * An interface to be implemented by a breakpoint handler. If a break-
274: * point is set for a particular file:lineno then when execution hits
275: * that particular file:lineno, the breakpoint handler will be invoked,
276: * and passed the current scope, which can be used to access variables,
277: * etc.
278: */
279: public interface Breakpoint {
280: /**
281: * Called when breakpoint is hit. The <code>file</code> and
282: * <code>line</code> are passed back to handler so that the
283: * same handler can be reused at many points.
284: *
285: * @param scope the current scope of execution
286: * @param file the current file
287: * @param line the current line
288: * @return step-breakpoint. If not <code>null</code>, this will
289: * be called at the next "step" in evaluating the program. This
290: * can be used to implement single-stepping.
291: */
292: public Breakpoint handle(Scope scope, AbstractFile file,
293: int line);
294: }
295:
296: /*=======================================================================*/
297: /**
298: * @deprecated use {@link #memberSet}
299: */
300: public static Enumeration enumerateMembers(final Value obj) {
301: return new Enumeration() {
302:
303: private Iterator itr = obj.memberSet().iterator();
304:
305: public boolean hasMoreElements() {
306: return itr.hasNext();
307: }
308:
309: public Object nextElement() {
310: return itr.next();
311: }
312:
313: };
314: }
315:
316: // wrappers for script environment, which can't call these directly:
317:
318: public static Set _memberSet(Value val) {
319: Set s = new HashSet();
320: val.populateMemberSet(s, true);
321: val.getType().populateTypeMemberSet(s, true);
322: return s;
323: }
324:
325: public static Value _getMember(Value val, Value name) {
326: return val.getMember(name);
327: }
328:
329: public static Class _getClass(Value val) {
330: return val.getClass();
331: }
332:
333: public static Value _getType(Value val) {
334: return val.getType();
335: }
336:
337: public static Scope _getPreviousScope(Scope scope) {
338: return scope.getPreviousScope();
339: }
340:
341: public static Value _getCallee(FunctionScope scope) {
342: return scope.getCallee();
343: }
344:
345: public static AbstractFile _getFile(FileScope scope) {
346: return scope.getFile();
347: }
348:
349: /*=======================================================================*/
350: /**
351: * Access a member of an object. By accessing a member this way, rather
352: * than directly, access can be had to non-public members. Also, it is
353: * possible to determine the attributes of that object.
354: *
355: * @param obj the object to access a member of
356: * @param name the name of the member to access
357: * @return an object wrapping the member
358: * @see #enumerateMembers
359: * @see #MemberAccessor
360: */
361: public static MemberAccessor getMemberAccessor(Value obj, Value name) {
362: // XXX should _getTypeMemberAccessor and _getInstanceMemberAccessor be
363: // part of the Value interface???
364:
365: Value type = obj.getType();
366: BasicScope scriptObj = getScriptObject(obj);
367:
368: MemberAccessor ma = null;
369:
370: if (scriptObj != null)
371: ma = scriptObj._getInstanceMemberAccessor(name);
372:
373: if ((ma == null) && (type instanceof JavaClassWrapper))
374: ma = ((JavaClassWrapper) type)._getTypeMemberAccessor(obj,
375: name);
376:
377: return ma;
378: }
379:
380: /*=======================================================================*/
381: /**
382: * An object implementing this interface is implemented to provide access
383: * to members of some object.
384: */
385: public interface MemberAccessor {
386: /**
387: * Return a bitmask of the member's attributes.
388: *
389: * @return a bitmask
390: * @see Reference#ATTR_CONST
391: * @see Reference#ATTR_PUBLIC
392: */
393: public int getAttr();
394:
395: /**
396: * Get the actual value of the member. If the member is not const,
397: * this should actually be a reference to the member, so that the
398: * member's value itself can be modified.
399: *
400: * @return the actual member
401: */
402: public Value getValue();
403: }
404:
405: private static final BasicScope getScriptObject(Value obj) {
406: if (obj instanceof BasicScope)
407: return (BasicScope) obj;
408: else
409: return (BasicScope) (ClassWrapGen.getScriptObject(obj));
410: }
411: }
412:
413: /*
414: * Local Variables:
415: * tab-width: 2
416: * indent-tabs-mode: nil
417: * mode: java
418: * c-indentation-style: java
419: * c-basic-offset: 2
420: * eval: (c-set-offset 'substatement-open '0)
421: * eval: (c-set-offset 'case-label '+)
422: * eval: (c-set-offset 'inclass '+)
423: * eval: (c-set-offset 'inline-open '0)
424: * End:
425: */
|