4e2d0b08960eb66ed7416308ef5dea834eed664e
[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
27     JSFunction f = null;          ///< the currently-executing JSFunction
28     JSScope scope = null;
29     Vec stack = new Vec();        ///< the object stack
30     int pc = 0;                   ///< the program counter
31     boolean pauseable;
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         stack.push(new JSFunction.CallMarker(this));
43         stack.push(args);
44         resume();
45     }
46     
47     /** returns a callback which will restart the context, or null if this context is not pauseable */
48     public static Callback pause() { return current().pause_(); }
49     private Callback pause_() {
50         if (!pauseable) return null;
51         pausecount++;
52         return new Callback() { public Object call(Object o) {
53             stack.push(o);
54             resume();
55             return null;
56         } };
57     }
58     
59     private void resume() {
60         Thread t = Thread.currentThread();
61         JSContext old = (JSContext)threadToJSContext.get(t);
62         threadToJSContext.put(t, this);
63         try {
64             JSFunction.eval(this);
65         } finally {
66             if (old == null) threadToJSContext.remove(t);
67             else threadToJSContext.put(t, old);
68         }
69     }
70 }