e707326b5cfcc1cb75fb5cf3c9897a914a80285f
[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     public static void invokePauseable(JSFunction function) { new JSContext(function, true).invoke(new JSArray()); }
34
35     JSContext(JSFunction f, boolean pauseable) {
36         this.pauseable = pauseable;
37         this.f = f;
38         scope = new JSScope(f.parentJSScope);
39     }
40
41     void invoke(JSArray args) {
42         JSFunction tf = f;
43         f = null;
44         stack.push(new JSFunction.CallMarker(this));
45         f = tf;
46         stack.push(args);
47         resume();
48     }
49     
50     /** returns a callback which will restart the context, or null if this context is not pauseable */
51     public static Callback pause() { return current().pause_(); }
52     private Callback pause_() {
53         if (!pauseable) return null;
54         pausecount++;
55         return new Callback() { public Object call(Object o) {
56             stack.push(o);
57             pc++;
58             resume();
59             return null;
60         } };
61     }
62     
63     private void resume() {
64         Thread t = Thread.currentThread();
65         JSContext old = (JSContext)threadToJSContext.get(t);
66         threadToJSContext.put(t, this);
67         try {
68             JSFunction.eval(this);
69         } finally {
70             if (old == null) threadToJSContext.remove(t);
71             else threadToJSContext.put(t, old);
72         }
73     }
74 }