2003/11/16 08:28:10
[org.ibex.core.git] / src / org / xwt / js / JSContext.java
1 package org.xwt.js;
2 import java.util.*;
3 import org.xwt.util.*;
4
5 /** encapsulates the state of a JavaScript "thread" (no relation to Java threads) */
6 public class JSContext {
7
8     // Statics //////////////////////////////////////////////////////////////////////
9     private int getLine_() { return current().f == null || (pc < 0 || pc >= f.size) ? -1 : f.line[pc]; }
10     public static int getLine() { return current().getLine_(); }
11     public static String getSourceName() { return current().f == null ? null : current().f.sourceName; } 
12     
13     /** fetches the currently-executing javascript function */
14     private static JSContext current() { return (JSContext)threadToJSContext.get(Thread.currentThread()); }
15     private static Hashtable threadToJSContext = new Hashtable();
16     
17     // Instance members and methods //////////////////////////////////////////////////////////////////////
18     
19     /**
20      *  the number of times this context has been paused; used by
21      *  Function.eval() to make sure it behaves properly even if the
22      *  pause Callback is invoked *before* control is returned to
23      *  eval()
24      */
25     int pausecount = 0;
26     boolean pauseable;
27
28     JSFunction f;                 ///< the currently-executing JSFunction
29     JSScope scope;
30     Vec stack = new Vec();        ///< the object stack
31     int pc = 0;                   ///< the program counter
32
33     /** can be paused */
34     public static void invokePauseable(JSFunction function) {
35         new JSContext(function, true).invoke(new JSArray());
36     }
37
38     /** cannot be paused */
39     public static void invokeTrap(JSTrap.JSTrappable t, Object key, Object value) {
40         JSFunction invoker = new JSFunction("trap invoker", 0, null);
41         invoker.add(-1, ByteCodes.PUT, null);
42         JSContext cx = new JSContext(invoker, false);
43         cx.stack.push(t);
44         cx.stack.push(key);
45         cx.stack.push(value);
46         cx.resume();
47     }
48
49     JSContext(JSFunction f, boolean pauseable) {
50         this.pauseable = pauseable;
51         this.f = f;
52         scope = new JSScope(f.parentJSScope);
53     }
54
55     void invoke(JSArray args) {
56         JSFunction tf = f;
57         f = null;
58         stack.push(new Interpreter.CallMarker(this));
59         f = tf;
60         stack.push(args);
61         resume();
62     }
63     
64     /** returns a callback which will restart the context, or null if this context is not pauseable */
65     public static Callback pause() { return current().pause_(); }
66     private Callback pause_() {
67         if (!pauseable) return null;
68         pausecount++;
69         return new Callback() { public Object call(Object o) {
70             stack.push(o);
71             pc++;
72             resume();
73             return null;
74         } };
75     }
76     
77     private void resume() {
78         Thread t = Thread.currentThread();
79         JSContext old = (JSContext)threadToJSContext.get(t);
80         threadToJSContext.put(t, this);
81         try {
82             Interpreter.eval(this);
83         } finally {
84             if (old == null) threadToJSContext.remove(t);
85             else threadToJSContext.put(t, old);
86         }
87     }
88 }