Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

New Java API for continuations in interpreter mode.

  • Loading branch information...
commit 6fba64615f28c5bdc8f002ad7047e978def4329a 1 parent 4462004
nboyd%atg.com authored
View
1  apiClasses.properties
@@ -37,6 +37,7 @@ apiClasses=\
src/org/mozilla/javascript/ClassCache.java,\
src/org/mozilla/javascript/ClassShutter.java,\
src/org/mozilla/javascript/CompilerEnvirons.java,\
+ src/org/mozilla/javascript/ContinuationPending.java,\
src/org/mozilla/javascript/Context.java,\
src/org/mozilla/javascript/ContextAction.java,\
src/org/mozilla/javascript/ContextFactory.java,\
View
114 src/org/mozilla/javascript/Context.java
@@ -1121,12 +1121,123 @@ public final Object evaluateReader(Scriptable scope, Reader in,
return null;
}
}
+
+ /**
+ * Execute script that may pause execution by capturing a continuation.
+ * Caller must be prepared to catch a ContinuationPending exception
+ * and resume execution by calling
+ * {@link #resumeContinuation(ContinuationPending, Object)}.
+ * @param script The script to execute. Script must have been compiled
+ * with interpreted mode (optimization level -1)
+ * @param scope The scope to execute the script against
+ * @throws ContinuationPending if the script calls a function that results
+ * in a call to {@link #captureContinuation()}
+ * @since 1.7 Release 2
+ */
+ public Object executeScriptWithContinuations(Script script,
+ Scriptable scope)
+ throws ContinuationPending
+ {
+ if (!(script instanceof InterpretedFunction) ||
+ !((InterpretedFunction)script).isScript())
+ {
+ // Can only be applied to scripts
+ throw new IllegalArgumentException("Script argument was not" +
+ " a script or was not created by interpreted mode ");
+ }
+ return callFunctionWithContinuations((InterpretedFunction) script,
+ scope, ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * Call function that may pause execution by capturing a continuation.
+ * Caller must be prepared to catch a ContinuationPending exception
+ * and resume execution by calling
+ * {@link #resumeContinuation(ContinuationPending, Object)}.
+ * @param function The function to call. The function must have been
+ * compiled with interpreted mode (optimization level -1)
+ * @param scope The scope to execute the script against
+ * @param args The arguments for the function
+ * @throws ContinuationPending if the script calls a function that results
+ * in a call to {@link #captureContinuation()}
+ * @since 1.7 Release 2
+ */
+ public Object callFunctionWithContinuations(Callable function,
+ Scriptable scope, Object[] args)
+ throws ContinuationPending
+ {
+ if (!(function instanceof InterpretedFunction)) {
+ // Can only be applied to scripts
+ throw new IllegalArgumentException("Function argument was not" +
+ " created by interpreted mode ");
+ }
+ if (ScriptRuntime.hasTopCall(this)) {
+ throw new IllegalStateException("Cannot have any pending top " +
+ "calls when executing a script with continuations");
+ }
+ // Annotate so we can check later to ensure no java code in
+ // intervening frames
+ isContinuationsTopCall = true;
+ try {
+ return ScriptRuntime.doTopCall(function, this, scope, scope,
+ args);
+ } catch (ContinuationPending continuation) {
+ throw continuation;
+ }
+ }
+
+ /**
+ * Capture a continuation from the current execution. The execution must
+ * have been started via a call to
+ * {@link #executeScriptWithContinuations(Script, Scriptable)} or
+ * {@link #callFunctionWithContinuations(Callable, Scriptable, Object[])}.
+ * This implies that the code calling
+ * this method must have been called as a function from the
+ * JavaScript script. Also, there cannot be any non-JavaScript code
+ * between the JavaScript frames (e.g., a call to eval()). The
+ * ContinuationPending exception returned must be thrown.
+ * @return A ContinuationPending exception that must be thrown
+ * @since 1.7 Release 2
+ */
+ public ContinuationPending captureContinuation() {
+ ContinuationPending pending = new ContinuationPending(
+ Interpreter.captureContinuation(this));
+ Scriptable scope = ScriptRuntime.getTopCallScope(this);
+ pending.setScope(scope);
+ return pending;
+ }
+
+ /**
+ * Restarts execution of the JavaScript suspended at the call
+ * to {@link #captureContinuation()}. Execution of the code will resume
+ * with the functionResult as the result of the call that captured the
+ * continuation.
+ * Execution of the script will either conclude normally and the
+ * result returned, another continuation will be captured and
+ * thrown, or the script will terminate abnormally and throw an exception.
+ * @param continuation The value returned by a previous call to
+ * {@link #captureContinuation()}
+ * @param functionResult This value will appear to the code being resumed
+ * as the result of the function that captured the continuation
+ * @throws ContinuationPending if another continuation is captured before
+ * the code terminates
+ * @since 1.7 Release 2
+ */
+ public Object resumeContinuation(ContinuationPending continuation,
+ Object functionResult)
+ throws ContinuationPending
+ {
+ Object[] args = { functionResult };
+ return Interpreter.restartContinuation(
+ continuation.getContinuationState(),
+ this, continuation.getScope(), args);
+ }
/**
* Check whether a string is ready to be compiled.
* <p>
* stringIsCompilableUnit is intended to support interactive compilation of
- * javascript. If compiling the string would result in an error
+ * JavaScript. If compiling the string would result in an error
* that might be fixed by appending more source, this method
* returns false. In every other case, it returns true.
* <p>
@@ -2456,6 +2567,7 @@ public void removeActivationName(String name)
private Object sealKey;
Scriptable topCallScope;
+ boolean isContinuationsTopCall;
NativeCall currentActivationCall;
XMLLib cachedXMLLib;
View
6 src/org/mozilla/javascript/InterpretedFunction.java
@@ -166,7 +166,7 @@ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
public Object exec(Context cx, Scriptable scope)
{
- if (idata.itsFunctionType != 0) {
+ if (!isScript()) {
// Can only be applied to scripts
throw new IllegalStateException();
}
@@ -178,6 +178,10 @@ public Object exec(Context cx, Scriptable scope)
return Interpreter.interpret(
this, cx, scope, scope, ScriptRuntime.emptyArgs);
}
+
+ public boolean isScript() {
+ return idata.itsFunctionType == 0;
+ }
@Override
public String getEncodedSource()
View
116 src/org/mozilla/javascript/Interpreter.java
@@ -253,6 +253,7 @@
DebugFrame debuggerFrame;
boolean useActivation;
+ boolean isContinuationsTopFrame;
Scriptable thisObj;
Scriptable[] scriptRegExps;
@@ -2480,6 +2481,8 @@ static Object interpret(InterpretedFunction ifun,
CallFrame frame = new CallFrame();
initFrame(cx, scope, thisObj, args, null, 0, args.length,
ifun, null, frame);
+ frame.isContinuationsTopFrame = cx.isContinuationsTopCall;
+ cx.isContinuationsTopCall = false;
return interpretLoop(cx, frame, null);
}
@@ -2795,37 +2798,7 @@ private static Object interpretLoop(Context cx, CallFrame frame,
case Token.SHEQ :
case Token.SHNE : {
--stackTop;
- Object rhs = stack[stackTop + 1];
- Object lhs = stack[stackTop];
- boolean valBln;
- shallow_compare: {
- double rdbl, ldbl;
- if (rhs == DBL_MRK) {
- rdbl = sDbl[stackTop + 1];
- if (lhs == DBL_MRK) {
- ldbl = sDbl[stackTop];
- } else if (lhs instanceof Number) {
- ldbl = ((Number)lhs).doubleValue();
- } else {
- valBln = false;
- break shallow_compare;
- }
- } else if (lhs == DBL_MRK) {
- ldbl = sDbl[stackTop];
- if (rhs == DBL_MRK) {
- rdbl = sDbl[stackTop + 1];
- } else if (rhs instanceof Number) {
- rdbl = ((Number)rhs).doubleValue();
- } else {
- valBln = false;
- break shallow_compare;
- }
- } else {
- valBln = ScriptRuntime.shallowEq(lhs, rhs);
- break shallow_compare;
- }
- valBln = (ldbl == rdbl);
- }
+ boolean valBln = shallowEquals(stack, sDbl, stackTop);
valBln ^= (op == Token.SHNE);
stack[stackTop] = ScriptRuntime.wrapBoolean(valBln);
continue Loop;
@@ -3313,16 +3286,17 @@ private static Object interpretLoop(Context cx, CallFrame frame,
if (fun instanceof IdFunctionObject) {
IdFunctionObject ifun = (IdFunctionObject)fun;
if (Continuation.isContinuationConstructor(ifun)) {
- captureContinuation(cx, frame, stackTop);
+ frame.stack[stackTop] = captureContinuation(cx,
+ frame.parentFrame, false);
continue Loop;
}
// Bug 405654 -- make best effort to keep Function.apply and
// Function.call within this interpreter loop invocation
- if(BaseFunction.isApplyOrCall(ifun)) {
+ if (BaseFunction.isApplyOrCall(ifun)) {
Callable applyCallable = ScriptRuntime.getCallable(funThisObj);
- if(applyCallable instanceof InterpretedFunction) {
+ if (applyCallable instanceof InterpretedFunction) {
InterpretedFunction iApplyCallable = (InterpretedFunction)applyCallable;
- if(frame.fnOrScript.securityDomain == iApplyCallable.securityDomain) {
+ if (frame.fnOrScript.securityDomain == iApplyCallable.securityDomain) {
frame = initFrameForApplyOrCall(cx, frame, indexReg,
stack, sDbl, stackTop, op, calleeScope, ifun,
iApplyCallable);
@@ -3332,8 +3306,12 @@ private static Object interpretLoop(Context cx, CallFrame frame,
}
}
+ cx.lastInterpreterFrame = frame;
+ frame.savedCallOp = op;
+ frame.savedStackTop = stackTop;
stack[stackTop] = fun.call(cx, calleeScope, funThisObj,
getArgsArray(stack, sDbl, stackTop + 2, indexReg));
+ cx.lastInterpreterFrame = null;
continue Loop;
}
@@ -3371,7 +3349,8 @@ private static Object interpretLoop(Context cx, CallFrame frame,
if (fun instanceof IdFunctionObject) {
IdFunctionObject ifun = (IdFunctionObject)fun;
if (Continuation.isContinuationConstructor(ifun)) {
- captureContinuation(cx, frame, stackTop);
+ frame.stack[stackTop] =
+ captureContinuation(cx, frame.parentFrame, false);
continue Loop;
}
}
@@ -4014,6 +3993,37 @@ private static Object interpretLoop(Context cx, CallFrame frame,
? interpreterResult
: ScriptRuntime.wrapNumber(interpreterResultDbl);
}
+
+ private static boolean shallowEquals(Object[] stack, double[] sDbl,
+ int stackTop)
+ {
+ Object rhs = stack[stackTop + 1];
+ Object lhs = stack[stackTop];
+ final Object DBL_MRK = UniqueTag.DOUBLE_MARK;
+ double rdbl, ldbl;
+ if (rhs == DBL_MRK) {
+ rdbl = sDbl[stackTop + 1];
+ if (lhs == DBL_MRK) {
+ ldbl = sDbl[stackTop];
+ } else if (lhs instanceof Number) {
+ ldbl = ((Number)lhs).doubleValue();
+ } else {
+ return false;
+ }
+ } else if (lhs == DBL_MRK) {
+ ldbl = sDbl[stackTop];
+ if (rhs == DBL_MRK) {
+ rdbl = sDbl[stackTop + 1];
+ } else if (rhs instanceof Number) {
+ rdbl = ((Number)rhs).doubleValue();
+ } else {
+ return false;
+ }
+ } else {
+ return ScriptRuntime.shallowEq(lhs, rhs);
+ }
+ return (ldbl == rdbl);
+ }
private static CallFrame processThrowable(Context cx, Object throwable,
CallFrame frame, int indexReg,
@@ -4469,16 +4479,26 @@ private static void setCallResult(CallFrame frame,
}
frame.savedCallOp = 0;
}
+
+ public static Continuation captureContinuation(Context cx) {
+ if (cx.lastInterpreterFrame == null ||
+ !(cx.lastInterpreterFrame instanceof CallFrame))
+ {
+ throw new IllegalStateException("Interpreter frames not found");
+ }
+ return captureContinuation(cx, (CallFrame)cx.lastInterpreterFrame, true);
+ }
- private static void captureContinuation(Context cx, CallFrame frame,
- int stackTop)
+ private static Continuation captureContinuation(Context cx, CallFrame frame,
+ boolean requireContinuationsTopFrame)
{
Continuation c = new Continuation();
ScriptRuntime.setObjectProtoAndParent(
c, ScriptRuntime.getTopCallScope(cx));
- // Make sure that all frames upstack frames are frozen
- CallFrame x = frame.parentFrame;
+ // Make sure that all frames are frozen
+ CallFrame x = frame;
+ CallFrame outermost = frame;
while (x != null && !x.frozen) {
x.frozen = true;
// Allow to GC unused stack space
@@ -4496,11 +4516,23 @@ private static void captureContinuation(Context cx, CallFrame frame,
// object so it shall not be cleared: see comments in
// setCallResult
}
+ outermost = x;
x = x.parentFrame;
}
+
+ while (outermost.parentFrame != null)
+ outermost = outermost.parentFrame;
- c.initImplementation(frame.parentFrame);
- frame.stack[stackTop] = c;
+ if (requireContinuationsTopFrame && !outermost.isContinuationsTopFrame)
+ {
+ throw new IllegalStateException("Cannot capture continuation " +
+ "from JavaScript code not called directly by " +
+ "executeScriptWithContinuations or " +
+ "callFunctionWithContinuations");
+ }
+
+ c.initImplementation(frame);
+ return c;
}
private static int stack_int32(CallFrame frame, int i)
View
9 src/org/mozilla/javascript/MemberBox.java
@@ -172,6 +172,15 @@ Object invoke(Object target, Object[] args)
// Retry after recovery
return method.invoke(target, args);
}
+ } catch (InvocationTargetException ite) {
+ // Must allow ContinuationPending exceptions to propagate unhindered
+ Throwable e = ite;
+ do {
+ e = ((InvocationTargetException) e).getTargetException();
+ } while ((e instanceof InvocationTargetException));
+ if (e instanceof ContinuationPending)
+ throw (ContinuationPending) e;
+ throw Context.throwAsScriptRuntimeEx(e);
} catch (Exception ex) {
throw Context.throwAsScriptRuntimeEx(ex);
}
Please sign in to comment.
Something went wrong with that request. Please try again.