2003/11/13 09:15:12
[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             resume();
58             return null;
59         } };
60     }
61     
62     private void resume() {
63         Thread t = Thread.currentThread();
64         JSContext old = (JSContext)threadToJSContext.get(t);
65         threadToJSContext.put(t, this);
66         try {
67             JSFunction.eval(this);
68         } finally {
69             if (old == null) threadToJSContext.remove(t);
70             else threadToJSContext.put(t, old);
71         }
72     }
73 }