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   }
89