in rhino/src/main/java/org/mozilla/javascript/Interpreter.java [2806:3026]
private static final NewState doCallByteCode(
Context cx,
CallFrame frame,
boolean instructionCounting,
int op,
int stackTop,
int indexReg) {
Object[] stack = frame.stack;
double[] sDbl = frame.sDbl;
if (instructionCounting) {
cx.instructionCount += INVOCATION_COST;
}
// stack change: function thisObj arg0 .. argN -> result
// indexReg: number of arguments
stackTop -= 1 + indexReg;
// CALL generation ensures that fun and funThisObj
// are already Scriptable and Callable objects respectively
Callable fun = (Callable) stack[stackTop];
Scriptable funThisObj = (Scriptable) stack[stackTop + 1];
Scriptable funHomeObj =
(fun instanceof BaseFunction) ? ((BaseFunction) fun).getHomeObject() : null;
if (op == Icode_CALL_ON_SUPER) {
// funThisObj would have been the "super" object, which we
// used to lookup the function. Now that that's done, we
// discard it and invoke the function with the current
// "this".
funThisObj = frame.thisObj;
}
if (op == Token.REF_CALL) {
Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 2, indexReg);
stack[stackTop] =
ScriptRuntime.callRef(
fun, funThisObj,
outArgs, cx);
return new ContinueLoop(frame, stackTop, indexReg);
}
Scriptable calleeScope = frame.scope;
if (frame.useActivation) {
calleeScope = ScriptableObject.getTopLevelScope(frame.scope);
}
// Iteratively reduce known function types: arrows, lambdas,
// bound functions, call/apply, and no-such-method-handler in
// order to make a best-effort to keep them in this interpreter
// loop so continuations keep working. The loop initializer and
// condition are formulated so that they short-circuit the loop
// if the function is already an interpreted function, which
// should be the majority of cases.
for (; ; ) {
if (fun instanceof ArrowFunction) {
ArrowFunction afun = (ArrowFunction) fun;
fun = afun.getTargetFunction();
funThisObj = afun.getCallThis(cx);
funHomeObj = afun.getBoundHomeObject();
} else if (fun instanceof LambdaConstructor) {
break;
} else if (fun instanceof LambdaFunction) {
fun = ((LambdaFunction) fun).getTarget();
} else if (fun instanceof BoundFunction) {
BoundFunction bfun = (BoundFunction) fun;
fun = bfun.getTargetFunction();
funThisObj = bfun.getCallThis(cx, calleeScope);
Object[] boundArgs = bfun.getBoundArgs();
int blen = boundArgs.length;
if (blen > 0) {
stack = frame.ensureStackLength(blen + stackTop + 2 + indexReg);
sDbl = frame.sDbl;
System.arraycopy(stack, stackTop + 2, stack, stackTop + 2 + blen, indexReg);
System.arraycopy(sDbl, stackTop + 2, sDbl, stackTop + 2 + blen, indexReg);
System.arraycopy(boundArgs, 0, stack, stackTop + 2, blen);
indexReg += blen;
}
} else if (fun instanceof IdFunctionObject) {
IdFunctionObject ifun = (IdFunctionObject) fun;
// Bug 405654 -- make the best effort to keep
// Function.apply and Function.call within this
// interpreter loop invocation
if (BaseFunction.isApplyOrCall(ifun)) {
// funThisObj becomes fun
fun = ScriptRuntime.getCallable(funThisObj);
// first arg becomes thisObj
funThisObj = getApplyThis(cx, stack, sDbl, stackTop + 2, indexReg, fun, frame);
if (BaseFunction.isApply(ifun)) {
// Apply: second argument after new "this"
// should be array-like
// and we'll spread its elements on the stack
Object[] callArgs =
indexReg < 2
? ScriptRuntime.emptyArgs
: ScriptRuntime.getApplyArguments(cx, stack[stackTop + 3]);
int alen = callArgs.length;
stack = frame.ensureStackLength(alen + stackTop + 2);
sDbl = frame.sDbl;
System.arraycopy(callArgs, 0, stack, stackTop + 2, alen);
indexReg = alen;
} else {
// Call: shift args left, starting from 2nd
if (indexReg > 0) {
if (indexReg > 1) {
System.arraycopy(
stack, stackTop + 3, stack, stackTop + 2, indexReg - 1);
System.arraycopy(
sDbl, stackTop + 3, sDbl, stackTop + 2, indexReg - 1);
}
indexReg--;
}
}
} else {
// Some other IdFunctionObject we don't know how to
// reduce.
break;
}
} else if (fun instanceof NoSuchMethodShim) {
NoSuchMethodShim nsmfun = (NoSuchMethodShim) fun;
// Bug 447697 -- make best effort to keep
// __noSuchMethod__ within this interpreter loop
// invocation.
stack = frame.ensureStackLength(stackTop + 4);
sDbl = frame.sDbl;
Object[] elements = getArgsArray(stack, sDbl, stackTop + 2, indexReg);
fun = nsmfun.noSuchMethodMethod;
stack[stackTop + 2] = nsmfun.methodName;
stack[stackTop + 3] = cx.newArray(calleeScope, elements);
indexReg = 2;
} else if (fun == null) {
throw ScriptRuntime.notFunctionError(null, null);
} else {
// Current function is something that we can't reduce
// further.
break;
}
}
if (fun instanceof InterpretedFunction) {
InterpretedFunction ifun = (InterpretedFunction) fun;
if (frame.fnOrScript.securityDomain == ifun.securityDomain) {
CallFrame callParentFrame = frame;
if (op == Icode_TAIL_CALL) {
// In principle tail call can re-use the current
// frame and its stack arrays but it is hard to
// do properly. Any exceptions that can legally
// happen during frame re-initialization including
// StackOverflowException during innocent looking
// System.arraycopy may leave the current frame
// data corrupted leading to undefined behaviour
// in the catch code bellow that unwinds JS stack
// on exceptions. Then there is issue about frame
// release
// end exceptions there.
// To avoid frame allocation a released frame
// can be cached for re-use which would also benefit
// non-tail calls but it is not clear that this
// caching
// would gain in performance due to potentially
// bad interaction with GC.
callParentFrame = frame.parentFrame;
// Release the current frame. See Bug #344501 to see
// why
// it is being done here.
exitFrame(cx, frame, null);
}
CallFrame calleeFrame =
initFrame(
cx,
calleeScope,
funThisObj,
funHomeObj,
stack,
sDbl,
stackTop + 2,
indexReg,
ifun,
callParentFrame);
if (op != Icode_TAIL_CALL) {
frame.savedStackTop = stackTop;
frame.savedCallOp = op;
}
return new StateContinue(calleeFrame);
}
}
if (fun instanceof NativeContinuation) {
// Jump to the captured continuation
ContinuationJump cjump;
cjump = new ContinuationJump((NativeContinuation) fun, frame);
// continuation result is the first argument if any
// of continuation call
if (indexReg == 0) {
cjump.result = undefined;
} else {
cjump.result = stack[stackTop + 2];
cjump.resultDbl = sDbl[stackTop + 2];
}
// Start the real unwind job
return new NewThrowable(cjump);
}
if (fun instanceof IdFunctionObject) {
IdFunctionObject ifun = (IdFunctionObject) fun;
if (NativeContinuation.isContinuationConstructor(ifun)) {
frame.stack[stackTop] = captureContinuation(cx, frame.parentFrame, false);
return new ContinueLoop(frame, stackTop, indexReg);
}
}
cx.lastInterpreterFrame = frame;
frame.savedCallOp = op;
frame.savedStackTop = stackTop;
stack[stackTop] =
fun.call(
cx,
calleeScope,
funThisObj,
getArgsArray(stack, sDbl, stackTop + 2, indexReg));
return new ContinueLoop(frame, stackTop, indexReg);
}