mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 18:26:15 +00:00
Explicit ScriptRuntime methods to get function and function this for all cases of method calls:
ScriptRuntime.get(Name|Prop|Elem|Value)FunctionAndThis provides uniform way to get function object and its this during function calls. It allowed to simplify handling of method calls both in interpreter and optimizer and opened a way to implement independent processing of function and property namespaces.
This commit is contained in:
parent
3441040ff5
commit
e32e12b401
@ -242,7 +242,8 @@ public class BaseFunction extends IdScriptableObject implements Function
|
||||
|
||||
case Id_apply:
|
||||
case Id_call:
|
||||
return applyOrCall(id, cx, scope, thisObj, args);
|
||||
return ScriptRuntime.applyOrCall(id == Id_apply,
|
||||
cx, scope, thisObj, args);
|
||||
}
|
||||
throw new IllegalArgumentException(String.valueOf(id));
|
||||
}
|
||||
@ -499,56 +500,6 @@ public class BaseFunction extends IdScriptableObject implements Function
|
||||
return fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function.prototype.apply and Function.prototype.call
|
||||
*
|
||||
* See Ecma 15.3.4.[34]
|
||||
*/
|
||||
private static Object applyOrCall(int id,
|
||||
Context cx, Scriptable scope,
|
||||
Scriptable thisObj, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
int L = args.length;
|
||||
Object function = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
|
||||
|
||||
Object callThis;
|
||||
if (L == 0 || args[0] == null || args[0] == Undefined.instance) {
|
||||
callThis = ScriptableObject.getTopLevelScope(scope);
|
||||
} else {
|
||||
callThis = ScriptRuntime.toObject(cx, scope, args[0]);
|
||||
}
|
||||
|
||||
Object[] callArgs;
|
||||
if (id == Id_apply) {
|
||||
// Follow Ecma 15.3.4.3
|
||||
if (L <= 1) {
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
} else {
|
||||
Object arg1 = args[1];
|
||||
if (arg1 == null || arg1 == Undefined.instance) {
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
} else if (arg1 instanceof NativeArray
|
||||
|| arg1 instanceof Arguments)
|
||||
{
|
||||
callArgs = cx.getElements((Scriptable) arg1);
|
||||
} else {
|
||||
throw ScriptRuntime.typeError0("msg.arg.isnt.array");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Follow Ecma 15.3.4.4
|
||||
if (L <= 1) {
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
} else {
|
||||
callArgs = new Object[L - 1];
|
||||
System.arraycopy(args, 1, callArgs, 0, L - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return ScriptRuntime.call(cx, function, callThis, callArgs, scope);
|
||||
}
|
||||
|
||||
protected int findPrototypeId(String s)
|
||||
{
|
||||
int id;
|
||||
|
@ -2661,4 +2661,7 @@ public class Context
|
||||
|
||||
// It can be used to return the second uint32 result from function
|
||||
long scratchUint32;
|
||||
|
||||
// It can be used to return the second Scriptable result from function
|
||||
Scriptable scratchScriptable;
|
||||
}
|
||||
|
@ -84,74 +84,75 @@ public class Interpreter
|
||||
Icode_TYPEOFNAME = -13,
|
||||
|
||||
// helper for function calls
|
||||
Icode_NAME_FAST_THIS = -14,
|
||||
Icode_NAME_SLOW_THIS = -15,
|
||||
Icode_PUSH_PARENT = -16,
|
||||
Icode_NAME_AND_THIS = -14,
|
||||
Icode_PROP_AND_THIS = -15,
|
||||
Icode_ELEM_AND_THIS = -16,
|
||||
Icode_VALUE_AND_THIS = -17,
|
||||
|
||||
// Create closure object for nested functions
|
||||
Icode_CLOSURE_EXPR = -17,
|
||||
Icode_CLOSURE_STMT = -18,
|
||||
Icode_CLOSURE_EXPR = -18,
|
||||
Icode_CLOSURE_STMT = -19,
|
||||
|
||||
// Special calls
|
||||
Icode_CALLSPECIAL = -19,
|
||||
Icode_CALLSPECIAL = -20,
|
||||
|
||||
// To return undefined value
|
||||
Icode_RETUNDEF = -20,
|
||||
Icode_RETUNDEF = -21,
|
||||
|
||||
// Exception handling implementation
|
||||
Icode_CATCH = -21,
|
||||
Icode_GOSUB = -22,
|
||||
Icode_RETSUB = -23,
|
||||
Icode_CATCH = -22,
|
||||
Icode_GOSUB = -23,
|
||||
Icode_RETSUB = -24,
|
||||
|
||||
// To indicating a line number change in icodes.
|
||||
Icode_LINE = -24,
|
||||
Icode_LINE = -25,
|
||||
|
||||
// To store shorts and ints inline
|
||||
Icode_SHORTNUMBER = -25,
|
||||
Icode_INTNUMBER = -26,
|
||||
Icode_SHORTNUMBER = -26,
|
||||
Icode_INTNUMBER = -27,
|
||||
|
||||
// To create and populate array to hold values for [] and {} literals
|
||||
Icode_LITERAL_NEW = -27,
|
||||
Icode_LITERAL_SET = -28,
|
||||
Icode_LITERAL_NEW = -28,
|
||||
Icode_LITERAL_SET = -29,
|
||||
|
||||
// Array literal with skipped index like [1,,2]
|
||||
Icode_SPARE_ARRAYLIT = -29,
|
||||
Icode_SPARE_ARRAYLIT = -30,
|
||||
|
||||
// Load index register to prepare for the following index operation
|
||||
Icode_REG_IND_C0 = -30,
|
||||
Icode_REG_IND_C1 = -31,
|
||||
Icode_REG_IND_C2 = -32,
|
||||
Icode_REG_IND_C3 = -33,
|
||||
Icode_REG_IND_C4 = -34,
|
||||
Icode_REG_IND_C5 = -35,
|
||||
Icode_REG_IND1 = -36,
|
||||
Icode_REG_IND2 = -37,
|
||||
Icode_REG_IND4 = -38,
|
||||
Icode_REG_IND_C0 = -31,
|
||||
Icode_REG_IND_C1 = -32,
|
||||
Icode_REG_IND_C2 = -33,
|
||||
Icode_REG_IND_C3 = -34,
|
||||
Icode_REG_IND_C4 = -35,
|
||||
Icode_REG_IND_C5 = -36,
|
||||
Icode_REG_IND1 = -37,
|
||||
Icode_REG_IND2 = -38,
|
||||
Icode_REG_IND4 = -39,
|
||||
|
||||
// Load string register to prepare for the following string operation
|
||||
Icode_REG_STR_C0 = -39,
|
||||
Icode_REG_STR_C1 = -40,
|
||||
Icode_REG_STR_C2 = -41,
|
||||
Icode_REG_STR_C3 = -42,
|
||||
Icode_REG_STR1 = -43,
|
||||
Icode_REG_STR2 = -44,
|
||||
Icode_REG_STR4 = -45,
|
||||
Icode_REG_STR_C0 = -40,
|
||||
Icode_REG_STR_C1 = -41,
|
||||
Icode_REG_STR_C2 = -42,
|
||||
Icode_REG_STR_C3 = -43,
|
||||
Icode_REG_STR1 = -44,
|
||||
Icode_REG_STR2 = -45,
|
||||
Icode_REG_STR4 = -46,
|
||||
|
||||
// Version of getvar/setvar that read var index directly from bytecode
|
||||
Icode_GETVAR1 = -46,
|
||||
Icode_SETVAR1 = -47,
|
||||
Icode_GETVAR1 = -47,
|
||||
Icode_SETVAR1 = -48,
|
||||
|
||||
// Load unefined
|
||||
Icode_UNDEF = -48,
|
||||
Icode_ZERO = -49,
|
||||
Icode_ONE = -50,
|
||||
Icode_UNDEF = -49,
|
||||
Icode_ZERO = -50,
|
||||
Icode_ONE = -51,
|
||||
|
||||
// entrance and exit from .()
|
||||
Icode_ENTERDQ = -51,
|
||||
Icode_LEAVEDQ = -52,
|
||||
Icode_ENTERDQ = -52,
|
||||
Icode_LEAVEDQ = -53,
|
||||
|
||||
// Last icode
|
||||
MIN_ICODE = -52;
|
||||
MIN_ICODE = -53;
|
||||
|
||||
static {
|
||||
// Checks for byte code consistencies, good compiler can eliminate them
|
||||
@ -196,9 +197,10 @@ public class Interpreter
|
||||
case Icode_REF_INC_DEC: return "REF_INC_DEC";
|
||||
case Icode_SCOPE: return "SCOPE";
|
||||
case Icode_TYPEOFNAME: return "TYPEOFNAME";
|
||||
case Icode_NAME_FAST_THIS: return "NAME_FAST_THIS";
|
||||
case Icode_NAME_SLOW_THIS: return "NAME_SLOW_THIS";
|
||||
case Icode_PUSH_PARENT: return "PUSH_PARENT";
|
||||
case Icode_NAME_AND_THIS: return "NAME_AND_THIS";
|
||||
case Icode_PROP_AND_THIS: return "PROP_AND_THIS";
|
||||
case Icode_ELEM_AND_THIS: return "ELEM_AND_THIS";
|
||||
case Icode_VALUE_AND_THIS: return "VALUE_AND_THIS";
|
||||
case Icode_CLOSURE_EXPR: return "CLOSURE_EXPR";
|
||||
case Icode_CLOSURE_STMT: return "CLOSURE_STMT";
|
||||
case Icode_CALLSPECIAL: return "CALLSPECIAL";
|
||||
@ -775,64 +777,21 @@ public class Interpreter
|
||||
|
||||
case Token.CALL:
|
||||
case Token.NEW:
|
||||
case Token.REF_CALL: {
|
||||
if (type == Token.NEW) {
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
} else {
|
||||
iCodeTop = generateCallFunAndThis(child, iCodeTop);
|
||||
if (itsStackDepth - savedStackDepth != 2)
|
||||
Kit.codeBug();
|
||||
}
|
||||
// To get better debugging output for undefined or null calls.
|
||||
int debugNameIndex = itsLastStringIndex;
|
||||
int argCount = 0;
|
||||
while ((child = child.getNext()) != null) {
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
++argCount;
|
||||
}
|
||||
int callType = node.getIntProp(Node.SPECIALCALL_PROP,
|
||||
Node.NON_SPECIALCALL);
|
||||
if (callType != Node.NON_SPECIALCALL) {
|
||||
// embed line number and source filename
|
||||
iCodeTop = addIndexOp(Icode_CALLSPECIAL, argCount, iCodeTop);
|
||||
iCodeTop = addByte(callType, iCodeTop);
|
||||
iCodeTop = addByte(type == Token.NEW ? 1 : 0, iCodeTop);
|
||||
iCodeTop = addShort(itsLineNumber, iCodeTop);
|
||||
} else {
|
||||
iCodeTop = addIndexOp(type, argCount, iCodeTop);
|
||||
if (debugNameIndex < 0xFFFF) {
|
||||
// Use only 2 bytes to store debug index
|
||||
iCodeTop = addShort(debugNameIndex, iCodeTop);
|
||||
} else {
|
||||
iCodeTop = addShort(0xFFFF, iCodeTop);
|
||||
}
|
||||
}
|
||||
// adjust stack
|
||||
if (type == Token.NEW) {
|
||||
// f, args -> results
|
||||
itsStackDepth -= argCount;
|
||||
} else {
|
||||
// f, thisObj, args -> results
|
||||
itsStackDepth -= (argCount + 1);
|
||||
}
|
||||
if (argCount > itsData.itsMaxCalleeArgs)
|
||||
itsData.itsMaxCalleeArgs = argCount;
|
||||
case Token.REF_CALL:
|
||||
iCodeTop = visitCall(node, iCodeTop);
|
||||
break;
|
||||
}
|
||||
|
||||
case Token.AND:
|
||||
case Token.OR: {
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
iCodeTop = addIcode(Icode_DUP, iCodeTop);
|
||||
itsStackDepth++;
|
||||
if (itsStackDepth > itsData.itsMaxStack)
|
||||
itsData.itsMaxStack = itsStackDepth;
|
||||
stackChange(1);
|
||||
int afterSecondJumpStart = iCodeTop;
|
||||
int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
|
||||
iCodeTop = addForwardGoto(jump, iCodeTop);
|
||||
itsStackDepth--;
|
||||
stackChange(-1);
|
||||
iCodeTop = addIcode(Icode_POP, iCodeTop);
|
||||
itsStackDepth--;
|
||||
stackChange(-1);
|
||||
child = child.getNext();
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
resolveForwardGoto(afterSecondJumpStart, iCodeTop);
|
||||
@ -857,11 +816,11 @@ public class Interpreter
|
||||
}
|
||||
|
||||
case Token.GETPROP:
|
||||
iCodeTop = visitGetProp(node, child, false, iCodeTop);
|
||||
iCodeTop = visitGetProp(node, child, iCodeTop);
|
||||
break;
|
||||
|
||||
case Token.GETELEM:
|
||||
iCodeTop = visitGetElem(node, child, false, iCodeTop);
|
||||
iCodeTop = visitGetElem(node, child, iCodeTop);
|
||||
break;
|
||||
|
||||
case Token.GET_REF:
|
||||
@ -1123,73 +1082,105 @@ public class Interpreter
|
||||
return iCodeTop;
|
||||
}
|
||||
|
||||
private int visitCall(Node node, int iCodeTop)
|
||||
{
|
||||
int type = node.getType();
|
||||
Node child = node.getFirstChild();
|
||||
if (type == Token.NEW) {
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
} else {
|
||||
iCodeTop = generateCallFunAndThis(child, iCodeTop);
|
||||
}
|
||||
// To get better debugging output for undefined or null calls.
|
||||
int debugNameIndex = itsLastStringIndex;
|
||||
int argCount = 0;
|
||||
while ((child = child.getNext()) != null) {
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
++argCount;
|
||||
}
|
||||
int callType = node.getIntProp(Node.SPECIALCALL_PROP,
|
||||
Node.NON_SPECIALCALL);
|
||||
if (callType != Node.NON_SPECIALCALL) {
|
||||
// embed line number and source filename
|
||||
iCodeTop = addIndexOp(Icode_CALLSPECIAL, argCount, iCodeTop);
|
||||
iCodeTop = addByte(callType, iCodeTop);
|
||||
iCodeTop = addByte(type == Token.NEW ? 1 : 0, iCodeTop);
|
||||
iCodeTop = addShort(itsLineNumber, iCodeTop);
|
||||
} else {
|
||||
iCodeTop = addIndexOp(type, argCount, iCodeTop);
|
||||
if (debugNameIndex < 0xFFFF) {
|
||||
// Use only 2 bytes to store debug index
|
||||
iCodeTop = addShort(debugNameIndex, iCodeTop);
|
||||
} else {
|
||||
iCodeTop = addShort(0xFFFF, iCodeTop);
|
||||
}
|
||||
}
|
||||
// adjust stack
|
||||
if (type == Token.NEW) {
|
||||
// f, args -> results
|
||||
stackChange(-argCount);
|
||||
} else {
|
||||
// f, thisObj, args -> results
|
||||
stackChange(-1 - argCount);
|
||||
}
|
||||
if (argCount > itsData.itsMaxCalleeArgs)
|
||||
itsData.itsMaxCalleeArgs = argCount;
|
||||
|
||||
return iCodeTop;
|
||||
}
|
||||
|
||||
private int generateCallFunAndThis(Node left, int iCodeTop)
|
||||
{
|
||||
// Generate code to place on stack function and thisObj
|
||||
switch (left.getType()) {
|
||||
int type = left.getType();
|
||||
switch (type) {
|
||||
case Token.NAME: {
|
||||
String name = left.getString();
|
||||
// Conditionally skip ScriptRuntime.getThis.
|
||||
// The getThis entry in the runtime will take a
|
||||
// Scriptable object intended to be used as a 'this'
|
||||
// and make sure that it is neither a With object or
|
||||
// an activation object.
|
||||
// Executing getThis requires at least two instanceof
|
||||
// tests, so we only include it if we are currently
|
||||
// inside a 'with' statement, or if we are executing
|
||||
// eval script (to protect against an eval inside a with).
|
||||
boolean skipGetThis = (itsWithDepth == 0
|
||||
&& (itsInFunctionFlag
|
||||
|| !itsData.itsFromEvalCode));
|
||||
int op = skipGetThis ? Icode_NAME_FAST_THIS : Icode_NAME_SLOW_THIS;
|
||||
iCodeTop = addStringOp(op, name, iCodeTop);
|
||||
// stack: ... -> ... function thisObj
|
||||
iCodeTop = addStringOp(Icode_NAME_AND_THIS, name, iCodeTop);
|
||||
stackChange(2);
|
||||
break;
|
||||
}
|
||||
case Token.GETPROP:
|
||||
// x.y(...)
|
||||
// -> tmp = x, (tmp.y, tmp)(...)
|
||||
iCodeTop = visitGetProp(left, left.getFirstChild(), true, iCodeTop);
|
||||
iCodeTop = addIcode(Icode_SWAP, iCodeTop);
|
||||
break;
|
||||
case Token.GETELEM:
|
||||
// x[y](...)
|
||||
// -> tmp = x, (tmp[y], tmp)(...)
|
||||
iCodeTop = visitGetElem(left, left.getFirstChild(), true, iCodeTop);
|
||||
iCodeTop = addIcode(Icode_SWAP, iCodeTop);
|
||||
case Token.GETELEM: {
|
||||
Node target = left.getFirstChild();
|
||||
iCodeTop = visitExpression(target, iCodeTop);
|
||||
Node id = target.getNext();
|
||||
if (type == Token.GETPROP) {
|
||||
String property = id.getString();
|
||||
// stack: ... target -> ... function thisObj
|
||||
iCodeTop = addStringOp(Icode_PROP_AND_THIS, property, iCodeTop);
|
||||
stackChange(1);
|
||||
} else {
|
||||
iCodeTop = visitExpression(id, iCodeTop);
|
||||
// stack: ... target id -> ... function thisObj
|
||||
iCodeTop = addIcode(Icode_ELEM_AND_THIS, iCodeTop);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Including Token.GETVAR
|
||||
iCodeTop = visitExpression(left, iCodeTop);
|
||||
iCodeTop = addIcode(Icode_PUSH_PARENT, iCodeTop);
|
||||
// stack: ... value -> ... function thisObj
|
||||
iCodeTop = addIcode(Icode_VALUE_AND_THIS, iCodeTop);
|
||||
stackChange(1);
|
||||
break;
|
||||
}
|
||||
return iCodeTop;
|
||||
}
|
||||
|
||||
private int visitGetProp(Node node, Node child, boolean dupObject,
|
||||
int iCodeTop)
|
||||
private int visitGetProp(Node node, Node child, int iCodeTop)
|
||||
{
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
if (dupObject) {
|
||||
iCodeTop = addIcode(Icode_DUP, iCodeTop);
|
||||
stackChange(1);
|
||||
}
|
||||
child = child.getNext();
|
||||
String property = child.getString();
|
||||
iCodeTop = addStringOp(Token.GETPROP, property, iCodeTop);
|
||||
return iCodeTop;
|
||||
}
|
||||
|
||||
private int visitGetElem(Node node, Node child, boolean dupObject,
|
||||
int iCodeTop)
|
||||
private int visitGetElem(Node node, Node child, int iCodeTop)
|
||||
{
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
if (dupObject) {
|
||||
iCodeTop = addIcode(Icode_DUP, iCodeTop);
|
||||
stackChange(1);
|
||||
}
|
||||
child = child.getNext();
|
||||
iCodeTop = visitExpression(child, iCodeTop);
|
||||
iCodeTop = addToken(Token.GETELEM, iCodeTop);
|
||||
@ -2574,6 +2565,42 @@ switch (op) {
|
||||
stack[stackTop] = stack[LOCAL_SHFT + indexReg];
|
||||
sDbl[stackTop] = sDbl[LOCAL_SHFT + indexReg];
|
||||
continue Loop;
|
||||
case Icode_NAME_AND_THIS :
|
||||
// stringReg: name
|
||||
++stackTop;
|
||||
stack[stackTop] = ScriptRuntime.getNameFunctionAndThis(stringReg,
|
||||
cx, scope);
|
||||
++stackTop;
|
||||
stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
|
||||
continue Loop;
|
||||
case Icode_PROP_AND_THIS: {
|
||||
Object obj = stack[stackTop];
|
||||
if (obj == DBL_MRK) obj = doubleWrap(sDbl[stackTop]);
|
||||
// stringReg: property
|
||||
stack[stackTop] = ScriptRuntime.getPropFunctionAndThis(obj, stringReg,
|
||||
cx, scope);
|
||||
++stackTop;
|
||||
stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
|
||||
continue Loop;
|
||||
}
|
||||
case Icode_ELEM_AND_THIS: {
|
||||
Object obj = stack[stackTop - 1];
|
||||
if (obj == DBL_MRK) obj = doubleWrap(sDbl[stackTop - 1]);
|
||||
Object id = stack[stackTop];
|
||||
if (id == DBL_MRK) id = doubleWrap(sDbl[stackTop]);
|
||||
stack[stackTop - 1] = ScriptRuntime.getElemFunctionAndThis(obj, id,
|
||||
cx, scope);
|
||||
stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
|
||||
continue Loop;
|
||||
}
|
||||
case Icode_VALUE_AND_THIS : {
|
||||
Object value = stack[stackTop];
|
||||
if (value == DBL_MRK) value = doubleWrap(sDbl[stackTop]);
|
||||
stack[stackTop] = ScriptRuntime.getValueFunctionAndThis(value, cx);
|
||||
++stackTop;
|
||||
stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
|
||||
continue Loop;
|
||||
}
|
||||
case Icode_CALLSPECIAL : {
|
||||
if (instructionCounting) {
|
||||
cx.instructionCount += INVOCATION_COST;
|
||||
@ -2584,22 +2611,22 @@ switch (op) {
|
||||
int sourceLine = getShort(iCode, pc + 2);
|
||||
stackTop -= indexReg;
|
||||
Object[] outArgs = getArgsArray(stack, sDbl, stackTop + 1, indexReg);
|
||||
Object functionThis;
|
||||
if (isNew) {
|
||||
functionThis = null;
|
||||
Object function = stack[stackTop];
|
||||
if (function == DBL_MRK) function = doubleWrap(sDbl[stackTop]);
|
||||
stack[stackTop] = ScriptRuntime.newSpecial(
|
||||
cx, function, outArgs, scope, callType);
|
||||
} else {
|
||||
functionThis = stack[stackTop];
|
||||
if (functionThis == DBL_MRK) {
|
||||
functionThis = doubleWrap(sDbl[stackTop]);
|
||||
}
|
||||
// Call code generation ensure that stack here
|
||||
// is ... Function Scriptable
|
||||
Scriptable functionThis = (Scriptable)stack[stackTop];
|
||||
--stackTop;
|
||||
Function function = (Function)stack[stackTop];
|
||||
stack[stackTop] = ScriptRuntime.callSpecial(
|
||||
cx, function, functionThis, outArgs,
|
||||
scope, thisObj, callType,
|
||||
idata.itsSourceFile, sourceLine);
|
||||
}
|
||||
Object function = stack[stackTop];
|
||||
if (function == DBL_MRK) function = doubleWrap(sDbl[stackTop]);
|
||||
stack[stackTop] = ScriptRuntime.callSpecial(
|
||||
cx, function, isNew, functionThis, outArgs,
|
||||
scope, thisObj, callType,
|
||||
idata.itsSourceFile, sourceLine);
|
||||
pc += 4;
|
||||
continue Loop;
|
||||
}
|
||||
@ -2610,37 +2637,29 @@ switch (op) {
|
||||
// indexReg: number of arguments
|
||||
stackTop -= indexReg;
|
||||
int calleeArgShft = stackTop + 1;
|
||||
Object rhs = stack[stackTop];
|
||||
if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);
|
||||
|
||||
// CALL generation ensures that funThisObj and fun
|
||||
// are already Scriptable and Function objects respectively
|
||||
Scriptable funThisObj = (Scriptable)stack[stackTop];
|
||||
--stackTop;
|
||||
Object lhs = stack[stackTop];
|
||||
Scriptable calleeScope = scope;
|
||||
Function fun = (Function)stack[stackTop];
|
||||
|
||||
Scriptable funScope = scope;
|
||||
if (idata.itsNeedsActivation) {
|
||||
calleeScope = ScriptableObject.getTopLevelScope(scope);
|
||||
funScope = ScriptableObject.getTopLevelScope(scope);
|
||||
}
|
||||
|
||||
Scriptable calleeThis;
|
||||
if (rhs instanceof Scriptable || rhs == null) {
|
||||
calleeThis = (Scriptable)rhs;
|
||||
} else {
|
||||
calleeThis = ScriptRuntime.toObject(cx, calleeScope, rhs);
|
||||
}
|
||||
|
||||
if (lhs instanceof InterpretedFunction) {
|
||||
if (fun instanceof InterpretedFunction) {
|
||||
// Inlining of InterpretedFunction.call not to create
|
||||
// argument array
|
||||
InterpretedFunction f = (InterpretedFunction)lhs;
|
||||
stack[stackTop] = interpret(cx, calleeScope, calleeThis,
|
||||
InterpretedFunction ifun = (InterpretedFunction)fun;
|
||||
stack[stackTop] = interpret(cx, funScope, funThisObj,
|
||||
stack, sDbl, calleeArgShft, indexReg,
|
||||
f, f.itsData);
|
||||
} else if (lhs instanceof Function) {
|
||||
Function f = (Function)lhs;
|
||||
ifun, ifun.itsData);
|
||||
} else {
|
||||
Object[] outArgs = getArgsArray(stack, sDbl, calleeArgShft,
|
||||
indexReg);
|
||||
stack[stackTop] = f.call(cx, calleeScope, calleeThis, outArgs);
|
||||
} else {
|
||||
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
|
||||
throw notAFunction(lhs, idata, pc);
|
||||
stack[stackTop] = fun.call(cx, funScope, funThisObj, outArgs);
|
||||
}
|
||||
pc += 2;
|
||||
continue Loop;
|
||||
@ -2686,17 +2705,20 @@ switch (op) {
|
||||
// indexReg: number of arguments
|
||||
stackTop -= indexReg;
|
||||
int calleeArgShft = stackTop + 1;
|
||||
Object rhs = stack[stackTop];
|
||||
if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);
|
||||
|
||||
// REF_CALL generation ensures that funThisObj and fun
|
||||
// are already Scriptable and Function objects respectively
|
||||
Scriptable funThisObj = (Scriptable)stack[stackTop];
|
||||
--stackTop;
|
||||
Object lhs = stack[stackTop];
|
||||
Scriptable calleeScope = scope;
|
||||
Function fun = (Function)stack[stackTop];
|
||||
|
||||
Scriptable funScope = scope;
|
||||
if (idata.itsNeedsActivation) {
|
||||
calleeScope = ScriptableObject.getTopLevelScope(scope);
|
||||
funScope = ScriptableObject.getTopLevelScope(scope);
|
||||
}
|
||||
Object[] outArgs = getArgsArray(stack, sDbl, calleeArgShft, indexReg);
|
||||
stack[stackTop] = ScriptRuntime.referenceCall(lhs, rhs, outArgs,
|
||||
cx, calleeScope);
|
||||
stack[stackTop] = ScriptRuntime.referenceCall(fun, funThisObj, outArgs,
|
||||
cx, funScope);
|
||||
pc += 2;
|
||||
continue Loop;
|
||||
}
|
||||
@ -2709,10 +2731,6 @@ switch (op) {
|
||||
case Icode_TYPEOFNAME :
|
||||
stack[++stackTop] = ScriptRuntime.typeofName(scope, stringReg);
|
||||
continue Loop;
|
||||
case Icode_NAME_FAST_THIS :
|
||||
case Icode_NAME_SLOW_THIS :
|
||||
stackTop = do_nameAndThis(stack, stackTop, scope, stringReg, op);
|
||||
continue Loop;
|
||||
case Token.STRING :
|
||||
stack[++stackTop] = stringReg;
|
||||
continue Loop;
|
||||
@ -2859,12 +2877,6 @@ switch (op) {
|
||||
: (Object)ScriptRuntime.enumId(val, cx);
|
||||
continue Loop;
|
||||
}
|
||||
case Icode_PUSH_PARENT : {
|
||||
Object lhs = stack[stackTop];
|
||||
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
|
||||
stack[++stackTop] = ScriptRuntime.getParent(lhs);
|
||||
continue Loop;
|
||||
}
|
||||
case Token.SPECIAL_REF : {
|
||||
//stringReg: name of special property
|
||||
Object lhs = stack[stackTop];
|
||||
@ -3385,7 +3397,7 @@ switch (op) {
|
||||
Object result;
|
||||
Object id = stack[stackTop];
|
||||
if (id != DBL_MRK) {
|
||||
result = ScriptRuntime.getObjectId(lhs, id, cx, scope);
|
||||
result = ScriptRuntime.getObjectElem(lhs, id, cx, scope);
|
||||
} else {
|
||||
double val = sDbl[stackTop];
|
||||
if (lhs == null || lhs == Undefined.instance) {
|
||||
@ -3419,7 +3431,7 @@ switch (op) {
|
||||
Object result;
|
||||
Object id = stack[stackTop - 1];
|
||||
if (id != DBL_MRK) {
|
||||
result = ScriptRuntime.setObjectId(lhs, id, rhs, cx, scope);
|
||||
result = ScriptRuntime.setObjectElem(lhs, id, rhs, cx, scope);
|
||||
} else {
|
||||
double val = sDbl[stackTop - 1];
|
||||
if (lhs == null || lhs == Undefined.instance) {
|
||||
@ -3442,31 +3454,6 @@ switch (op) {
|
||||
return stackTop;
|
||||
}
|
||||
|
||||
private static int do_nameAndThis(Object[] stack, int stackTop,
|
||||
Scriptable scope, String name,
|
||||
int op)
|
||||
{
|
||||
Object prop;
|
||||
Scriptable obj = scope;
|
||||
search: {
|
||||
while (obj != null) {
|
||||
prop = ScriptableObject.getProperty(obj, name);
|
||||
if (prop != Scriptable.NOT_FOUND) {
|
||||
break search;
|
||||
}
|
||||
obj = obj.getParentScope();
|
||||
}
|
||||
throw ScriptRuntime.notFoundError(scope, name);
|
||||
}
|
||||
|
||||
Scriptable thisArg = (op == Icode_NAME_FAST_THIS)
|
||||
? obj : ScriptRuntime.getThis(obj);
|
||||
stack[++stackTop] = prop;
|
||||
stack[++stackTop] = thisArg;
|
||||
|
||||
return stackTop;
|
||||
}
|
||||
|
||||
static RuntimeException notAFunction(Object notAFunction,
|
||||
InterpreterData idata, int namePC)
|
||||
{
|
||||
|
@ -547,13 +547,13 @@ public class NativeArray extends IdScriptableObject
|
||||
if (toLocale && elem != Undefined.instance &&
|
||||
elem != null)
|
||||
{
|
||||
Scriptable obj = ScriptRuntime.
|
||||
toObject(cx, thisObj, elem);
|
||||
Object tls = ScriptRuntime.
|
||||
getObjectProp(obj, "toLocaleString", cx);
|
||||
elem = ScriptRuntime.call(cx, tls, elem,
|
||||
ScriptRuntime.emptyArgs,
|
||||
scope);
|
||||
Function fun;
|
||||
Scriptable funThis;
|
||||
fun = ScriptRuntime.getPropFunctionAndThis(
|
||||
elem, "toLocaleString", cx, scope);
|
||||
funThis = ScriptRuntime.lastStoredScriptable(cx);
|
||||
elem = fun.call(cx, scope, funThis,
|
||||
ScriptRuntime.emptyArgs);
|
||||
}
|
||||
result.append(ScriptRuntime.toString(elem));
|
||||
}
|
||||
@ -720,7 +720,10 @@ public class NativeArray extends IdScriptableObject
|
||||
// assemble args and call supplied JS cmp function
|
||||
cmpBuf[0] = x;
|
||||
cmpBuf[1] = y;
|
||||
Object ret = ScriptRuntime.call(cx, cmp, null, cmpBuf, scope);
|
||||
Function fun = ScriptRuntime.getValueFunctionAndThis(cmp, cx);
|
||||
Scriptable funThis = ScriptRuntime.lastStoredScriptable(cx);
|
||||
|
||||
Object ret = fun.call(cx, scope, funThis, cmpBuf);
|
||||
double d = ScriptRuntime.toNumber(ret);
|
||||
|
||||
// XXX what to do when cmp function returns NaN? ECMA states
|
||||
|
@ -1059,14 +1059,6 @@ public class ScriptRuntime {
|
||||
return s.getPrototype();
|
||||
}
|
||||
|
||||
public static Scriptable getParent(Object obj) {
|
||||
if (!(obj instanceof Scriptable)) {
|
||||
return null;
|
||||
}
|
||||
Scriptable s = (Scriptable)obj;
|
||||
return getThis(s.getParentScope());
|
||||
}
|
||||
|
||||
public static Scriptable getParent(Object obj, Scriptable scope) {
|
||||
Scriptable s;
|
||||
if (obj instanceof Scriptable) {
|
||||
@ -1172,31 +1164,6 @@ public class ScriptRuntime {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void storeIndexResult(Context cx, int index)
|
||||
{
|
||||
cx.scratchIndex = index;
|
||||
}
|
||||
|
||||
static int lastIndexResult(Context cx)
|
||||
{
|
||||
return cx.scratchIndex;
|
||||
}
|
||||
|
||||
public static void storeUint32Result(Context cx, long value)
|
||||
{
|
||||
if ((value >>> 32) != 0)
|
||||
throw new IllegalArgumentException();
|
||||
cx.scratchUint32 = value;
|
||||
}
|
||||
|
||||
public static long lastUint32Result(Context cx)
|
||||
{
|
||||
long value = cx.scratchUint32;
|
||||
if ((value >>> 32) != 0)
|
||||
throw new IllegalStateException();
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If s represents index, then return index value wrapped as Integer
|
||||
* and othewise return s.
|
||||
@ -1254,11 +1221,14 @@ public class ScriptRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getObjectId(Object obj, Object id,
|
||||
Context cx, Scriptable scope)
|
||||
/**
|
||||
* Call obj.[[Get]](id)
|
||||
*/
|
||||
public static Object getObjectElem(Object obj, Object elem,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
if (obj == null || obj == Undefined.instance) {
|
||||
throw undefReadError(obj, id);
|
||||
throw undefReadError(obj, elem);
|
||||
}
|
||||
Scriptable sobj;
|
||||
if (obj instanceof Scriptable) {
|
||||
@ -1266,20 +1236,20 @@ public class ScriptRuntime {
|
||||
} else {
|
||||
sobj = toObject(cx, scope, obj);
|
||||
}
|
||||
return getObjectId(sobj, id, cx);
|
||||
return getObjectElem(sobj, elem, cx);
|
||||
}
|
||||
|
||||
public static Object getObjectId(Scriptable obj, Object id,
|
||||
Context cx)
|
||||
public static Object getObjectElem(Scriptable obj, Object elem,
|
||||
Context cx)
|
||||
{
|
||||
if (obj instanceof XMLObject) {
|
||||
XMLObject xmlObject = (XMLObject)obj;
|
||||
return xmlObject.ecmaGet(cx, id);
|
||||
return xmlObject.ecmaGet(cx, elem);
|
||||
}
|
||||
|
||||
Object result;
|
||||
|
||||
String s = toStringIdOrIndex(cx, id);
|
||||
String s = toStringIdOrIndex(cx, elem);
|
||||
if (s == null) {
|
||||
int index = lastIndexResult(cx);
|
||||
result = ScriptableObject.getProperty(obj, index);
|
||||
@ -1295,7 +1265,7 @@ public class ScriptRuntime {
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of getObjectId when id is a valid JS identifier name.
|
||||
* Version of getObjectElem when elem is a valid JS identifier name.
|
||||
*/
|
||||
public static Object getObjectProp(Object obj, String property,
|
||||
Context cx, Scriptable scope)
|
||||
@ -1349,13 +1319,13 @@ public class ScriptRuntime {
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic assignment to member element.
|
||||
* Call obj.[[Put]](id, value)
|
||||
*/
|
||||
public static Object setObjectId(Object obj, Object id, Object value,
|
||||
Context cx, Scriptable scope)
|
||||
public static Object setObjectElem(Object obj, Object elem, Object value,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
if (obj == null || obj == Undefined.instance) {
|
||||
throw undefWriteError(obj, id, value);
|
||||
throw undefWriteError(obj, elem, value);
|
||||
}
|
||||
Scriptable sobj;
|
||||
if (obj instanceof Scriptable) {
|
||||
@ -1363,19 +1333,19 @@ public class ScriptRuntime {
|
||||
} else {
|
||||
sobj = toObject(cx, scope, obj);
|
||||
}
|
||||
return setObjectId(sobj, id, value, cx);
|
||||
return setObjectElem(sobj, elem, value, cx);
|
||||
}
|
||||
|
||||
public static Object setObjectId(Scriptable obj, Object id, Object value,
|
||||
Context cx)
|
||||
public static Object setObjectElem(Scriptable obj, Object elem,
|
||||
Object value, Context cx)
|
||||
{
|
||||
if (obj instanceof XMLObject) {
|
||||
XMLObject xmlObject = (XMLObject)obj;
|
||||
xmlObject.ecmaPut(cx, id, value);
|
||||
xmlObject.ecmaPut(cx, elem, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
String s = toStringIdOrIndex(cx, id);
|
||||
String s = toStringIdOrIndex(cx, elem);
|
||||
if (s == null) {
|
||||
int index = lastIndexResult(cx);
|
||||
ScriptableObject.putProperty(obj, index, value);
|
||||
@ -1387,7 +1357,7 @@ public class ScriptRuntime {
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of setObjectId when id is a valid JS identifier name.
|
||||
* Version of setObjectElem when elem is a valid JS identifier name.
|
||||
*/
|
||||
public static Object setObjectProp(Object obj, String property,
|
||||
Object value,
|
||||
@ -1563,11 +1533,11 @@ public class ScriptRuntime {
|
||||
*/
|
||||
public static Object name(Context cx, Scriptable scopeChain, String id)
|
||||
{
|
||||
Scriptable obj = scopeChain;
|
||||
Scriptable scope = scopeChain;
|
||||
XMLObject firstXMLObject = null;
|
||||
while (obj != null) {
|
||||
if (obj instanceof NativeWith) {
|
||||
Scriptable withObj = obj.getPrototype();
|
||||
do {
|
||||
if (scope instanceof NativeWith) {
|
||||
Scriptable withObj = scope.getPrototype();
|
||||
if (withObj instanceof XMLObject) {
|
||||
XMLObject xmlObj = (XMLObject)withObj;
|
||||
if (xmlObj.ecmaHas(cx, id)) {
|
||||
@ -1583,13 +1553,13 @@ public class ScriptRuntime {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Object result = ScriptableObject.getProperty(obj, id);
|
||||
Object result = ScriptableObject.getProperty(scope, id);
|
||||
if (result != Scriptable.NOT_FOUND) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
obj = obj.getParentScope();
|
||||
}
|
||||
scope = scope.getParentScope();
|
||||
} while (scope != null);
|
||||
|
||||
if (firstXMLObject != null) {
|
||||
// The name was not found, but we did find an XML object in the
|
||||
@ -1816,12 +1786,127 @@ public class ScriptRuntime {
|
||||
x.index = 0;
|
||||
}
|
||||
|
||||
public static Object call(Context cx, Object fun, Object thisArg,
|
||||
Object[] args, Scriptable scope)
|
||||
/**
|
||||
* Prepare for calling name(...): return function corresponding to
|
||||
* name and make current top scope available
|
||||
* as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
|
||||
* The caller must call ScriptRuntime.lastStoredScriptable() immediately
|
||||
* after calling this method.
|
||||
*/
|
||||
public static Function getNameFunctionAndThis(String name,
|
||||
Context cx,
|
||||
Scriptable scope)
|
||||
{
|
||||
Object value = name(cx, scope, name);
|
||||
if (!(value instanceof Function)) {
|
||||
throw notFunctionError(value, name);
|
||||
}
|
||||
|
||||
Scriptable thisObj = scope;
|
||||
if (scope.getParentScope() != null) {
|
||||
// Check for with and activation:
|
||||
while (thisObj instanceof NativeWith) {
|
||||
thisObj = thisObj.getPrototype();
|
||||
}
|
||||
if (thisObj instanceof NativeCall) {
|
||||
thisObj = ScriptableObject.getTopLevelScope(thisObj);
|
||||
}
|
||||
}
|
||||
|
||||
storeScriptable(cx, thisObj);
|
||||
return (Function)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for calling obj[id](...): return function corresponding to
|
||||
* obj[id] and make obj properly converted to Scriptable available
|
||||
* as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
|
||||
* The caller must call ScriptRuntime.lastStoredScriptable() immediately
|
||||
* after calling this method.
|
||||
*/
|
||||
public static Function getElemFunctionAndThis(Object obj,
|
||||
Object elem,
|
||||
Context cx,
|
||||
Scriptable scope)
|
||||
{
|
||||
if (obj == null || obj == Undefined.instance) {
|
||||
throw undefReadError(obj, elem);
|
||||
}
|
||||
Scriptable thisObj;
|
||||
if (obj instanceof Scriptable) {
|
||||
thisObj = (Scriptable)obj;
|
||||
} else {
|
||||
thisObj = toObject(cx, scope, obj);
|
||||
}
|
||||
|
||||
// Do NOT check for XMLObject: x.elem() calls are resolved via normal
|
||||
// Scriptable machinery
|
||||
|
||||
Object value = getObjectElem(thisObj, elem, cx);
|
||||
if (!(value instanceof Function)) {
|
||||
throw notFunctionError(value, elem);
|
||||
}
|
||||
|
||||
storeScriptable(cx, thisObj);
|
||||
return (Function)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for calling obj.property(...): return function corresponding to
|
||||
* obj.property and make obj properly converted to Scriptable available
|
||||
* as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
|
||||
* The caller must call ScriptRuntime.lastStoredScriptable() immediately
|
||||
* after calling this method.
|
||||
*/
|
||||
public static Function getPropFunctionAndThis(Object obj,
|
||||
String property,
|
||||
Context cx,
|
||||
Scriptable scope)
|
||||
{
|
||||
if (obj == null || obj == Undefined.instance) {
|
||||
throw undefReadError(obj, property);
|
||||
}
|
||||
Scriptable thisObj;
|
||||
if (obj instanceof Scriptable) {
|
||||
thisObj = (Scriptable)obj;
|
||||
} else {
|
||||
thisObj = toObject(cx, scope, obj);
|
||||
}
|
||||
|
||||
Object value = getObjectProp(thisObj, property, cx);
|
||||
if (!(value instanceof Function)) {
|
||||
throw notFunctionError(value, property);
|
||||
}
|
||||
|
||||
storeScriptable(cx, thisObj);
|
||||
return (Function)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for calling <expression>(...): return function corresponding to
|
||||
* <expression> and make parent scope of the function available
|
||||
* as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
|
||||
* The caller must call ScriptRuntime.lastStoredScriptable() immediately
|
||||
* after calling this method.
|
||||
*/
|
||||
public static Function getValueFunctionAndThis(Object value, Context cx)
|
||||
{
|
||||
if (!(value instanceof Function)) {
|
||||
throw notFunctionError(value);
|
||||
}
|
||||
|
||||
Function f = (Function)value;
|
||||
Scriptable thisObj = f.getParentScope();
|
||||
storeScriptable(cx, thisObj);
|
||||
return f;
|
||||
}
|
||||
|
||||
private static Object call(Object fun, Context cx, Scriptable scope,
|
||||
Object thisArg, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
if (!(fun instanceof Function)) {
|
||||
throw typeError1("msg.isnt.function", toString(fun));
|
||||
throw notFunctionError(fun);
|
||||
}
|
||||
Function function = (Function)fun;
|
||||
Scriptable thisObj;
|
||||
@ -1842,21 +1927,10 @@ public class ScriptRuntime {
|
||||
* can be GC-reachable after this method returns. If this is necessary,
|
||||
* store args.clone(), not args array itself.
|
||||
*/
|
||||
public static Object referenceCall(Object fun, Object thisArg,
|
||||
public static Object referenceCall(Function function, Scriptable thisObj,
|
||||
Object[] args,
|
||||
Context cx, Scriptable scope)
|
||||
throws JavaScriptException
|
||||
{
|
||||
if (!(fun instanceof Function)) {
|
||||
throw typeError1("msg.isnt.function", toString(fun));
|
||||
}
|
||||
Function function = (Function)fun;
|
||||
Scriptable thisObj;
|
||||
if (thisArg instanceof Scriptable || thisArg == null) {
|
||||
thisObj = (Scriptable) thisArg;
|
||||
} else {
|
||||
thisObj = ScriptRuntime.toObject(cx, scope, thisArg);
|
||||
}
|
||||
if (function instanceof BaseFunction) {
|
||||
BaseFunction bf = (BaseFunction)function;
|
||||
Reference ref = bf.referenceCall(cx, scope, thisObj, args);
|
||||
@ -1864,7 +1938,7 @@ public class ScriptRuntime {
|
||||
}
|
||||
// No runtime support for now
|
||||
String msg = getMessage1("msg.no.ref.from.function",
|
||||
toString(fun));
|
||||
toString(function));
|
||||
throw constructError("ReferenceError", msg);
|
||||
}
|
||||
|
||||
@ -1873,19 +1947,19 @@ public class ScriptRuntime {
|
||||
*
|
||||
* See ECMA 11.2.2
|
||||
*/
|
||||
public static Scriptable newObject(Context cx, Object fun,
|
||||
Object[] args, Scriptable scope)
|
||||
public static Scriptable newObject(Object fun, Context cx,
|
||||
Scriptable scope, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
if (!(fun instanceof Function)) {
|
||||
throw typeError1("msg.isnt.function", toString(fun));
|
||||
throw notFunctionError(fun);
|
||||
}
|
||||
Function function = (Function)fun;
|
||||
return function.construct(cx, scope, args);
|
||||
}
|
||||
|
||||
public static Object callSpecial(Context cx, Object fun,
|
||||
boolean isNew, Object thisObj,
|
||||
public static Object callSpecial(Context cx, Function fun,
|
||||
Scriptable thisObj,
|
||||
Object[] args, Scriptable scope,
|
||||
Scriptable callerThis, int callType,
|
||||
String filename, int lineNumber)
|
||||
@ -1893,29 +1967,98 @@ public class ScriptRuntime {
|
||||
{
|
||||
if (callType == Node.SPECIALCALL_EVAL) {
|
||||
if (NativeGlobal.isEvalFunction(fun)) {
|
||||
if (isNew) {
|
||||
throw typeError1("msg.not.ctor", "eval");
|
||||
}
|
||||
return evalSpecial(cx, scope, callerThis, args,
|
||||
filename, lineNumber);
|
||||
}
|
||||
} else if (callType == Node.SPECIALCALL_WITH) {
|
||||
if (NativeWith.isWithFunction(fun)) {
|
||||
if (!isNew) {
|
||||
throw Context.reportRuntimeError1("msg.only.from.new",
|
||||
"With");
|
||||
}
|
||||
throw Context.reportRuntimeError1("msg.only.from.new",
|
||||
"With");
|
||||
}
|
||||
} else {
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
|
||||
return fun.call(cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
public static Object newSpecial(Context cx, Object fun,
|
||||
Object[] args, Scriptable scope,
|
||||
int callType)
|
||||
throws JavaScriptException
|
||||
{
|
||||
if (callType == Node.SPECIALCALL_EVAL) {
|
||||
if (NativeGlobal.isEvalFunction(fun)) {
|
||||
throw typeError1("msg.not.ctor", "eval");
|
||||
}
|
||||
} else if (callType == Node.SPECIALCALL_WITH) {
|
||||
if (NativeWith.isWithFunction(fun)) {
|
||||
return NativeWith.newWithSpecial(cx, scope, args);
|
||||
}
|
||||
} else {
|
||||
Kit.codeBug();
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
return newObject(cx, fun, args, scope);
|
||||
return newObject(fun, cx, scope, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function.prototype.apply and Function.prototype.call
|
||||
*
|
||||
* See Ecma 15.3.4.[34]
|
||||
*/
|
||||
public static Object applyOrCall(boolean isApply,
|
||||
Context cx, Scriptable scope,
|
||||
Scriptable thisObj, Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
int L = args.length;
|
||||
Function function;
|
||||
if (thisObj instanceof Function) {
|
||||
function = (Function)thisObj;
|
||||
} else {
|
||||
return call(cx, fun, thisObj, args, scope);
|
||||
Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
|
||||
if (!(value instanceof Function)) {
|
||||
throw ScriptRuntime.notFunctionError(value, thisObj);
|
||||
}
|
||||
function = (Function)value;
|
||||
}
|
||||
|
||||
Scriptable callThis;
|
||||
if (L == 0 || args[0] == null || args[0] == Undefined.instance) {
|
||||
callThis = ScriptableObject.getTopLevelScope(scope);
|
||||
} else {
|
||||
callThis = ScriptRuntime.toObject(cx, scope, args[0]);
|
||||
}
|
||||
|
||||
Object[] callArgs;
|
||||
if (isApply) {
|
||||
// Follow Ecma 15.3.4.3
|
||||
if (L <= 1) {
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
} else {
|
||||
Object arg1 = args[1];
|
||||
if (arg1 == null || arg1 == Undefined.instance) {
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
} else if (arg1 instanceof NativeArray
|
||||
|| arg1 instanceof Arguments)
|
||||
{
|
||||
callArgs = cx.getElements((Scriptable) arg1);
|
||||
} else {
|
||||
throw ScriptRuntime.typeError0("msg.arg.isnt.array");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Follow Ecma 15.3.4.4
|
||||
if (L <= 1) {
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
} else {
|
||||
callArgs = new Object[L - 1];
|
||||
System.arraycopy(args, 1, callArgs, 0, L - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return function.call(cx, scope, callThis, callArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2134,7 +2277,7 @@ public class ScriptRuntime {
|
||||
Context cx, Scriptable scope,
|
||||
int incrDecrMask)
|
||||
{
|
||||
Object value = getObjectId(obj, index, cx, scope);
|
||||
Object value = getObjectElem(obj, index, cx, scope);
|
||||
boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
|
||||
double number;
|
||||
if (value instanceof Number) {
|
||||
@ -2152,7 +2295,7 @@ public class ScriptRuntime {
|
||||
--number;
|
||||
}
|
||||
Number result = new Double(number);
|
||||
setObjectId(obj, index, result, cx, scope);
|
||||
setObjectElem(obj, index, result, cx, scope);
|
||||
if (post) {
|
||||
return value;
|
||||
} else {
|
||||
@ -2621,6 +2764,8 @@ public class ScriptRuntime {
|
||||
if (cx.currentActivationCall != null)
|
||||
throw new IllegalStateException();
|
||||
cx.topActivationScope = null;
|
||||
|
||||
// Cleanup references
|
||||
cx.cachedXMLLib = null;
|
||||
}
|
||||
}
|
||||
@ -2902,13 +3047,28 @@ public class ScriptRuntime {
|
||||
return typeError2(messageId, valueStr, msg);
|
||||
}
|
||||
|
||||
public static RuntimeException notFoundError(Scriptable object, String property)
|
||||
public static RuntimeException notFoundError(Scriptable object,
|
||||
String property)
|
||||
{
|
||||
// XXX: use object to improve the error message
|
||||
String msg = getMessage1("msg.is.not.defined", property);
|
||||
throw constructError("ReferenceError", msg);
|
||||
}
|
||||
|
||||
public static RuntimeException notFunctionError(Object value)
|
||||
{
|
||||
return notFunctionError(value, value);
|
||||
}
|
||||
|
||||
public static RuntimeException notFunctionError(Object value,
|
||||
Object messageHelper)
|
||||
{
|
||||
// XXX Use value for better error reporting
|
||||
String msg = (messageHelper == null)
|
||||
? "null" : messageHelper.toString();
|
||||
return typeError1("msg.isnt.function", msg);
|
||||
}
|
||||
|
||||
public static RegExpProxy getRegExpProxy(Context cx)
|
||||
{
|
||||
return cx.getRegExpProxy();
|
||||
@ -3003,6 +3163,51 @@ public class ScriptRuntime {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void storeIndexResult(Context cx, int index)
|
||||
{
|
||||
cx.scratchIndex = index;
|
||||
}
|
||||
|
||||
static int lastIndexResult(Context cx)
|
||||
{
|
||||
return cx.scratchIndex;
|
||||
}
|
||||
|
||||
public static void storeUint32Result(Context cx, long value)
|
||||
{
|
||||
if ((value >>> 32) != 0)
|
||||
throw new IllegalArgumentException();
|
||||
cx.scratchUint32 = value;
|
||||
}
|
||||
|
||||
public static long lastUint32Result(Context cx)
|
||||
{
|
||||
long value = cx.scratchUint32;
|
||||
if ((value >>> 32) != 0)
|
||||
throw new IllegalStateException();
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void storeScriptable(Context cx, Scriptable value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException();
|
||||
// The previosly stored scratchScriptable should be consumed
|
||||
if (cx.scratchScriptable != null)
|
||||
throw new IllegalStateException();
|
||||
cx.scratchScriptable = value;
|
||||
}
|
||||
|
||||
public static Scriptable lastStoredScriptable(Context cx)
|
||||
{
|
||||
Scriptable result = cx.scratchScriptable;
|
||||
if (result == null)
|
||||
throw new IllegalStateException();
|
||||
// Consume the result
|
||||
cx.scratchScriptable = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
static String makeUrlForGeneratedScript
|
||||
(boolean isEval, String masterScriptUrl, int masterScriptLine)
|
||||
{
|
||||
|
@ -1559,9 +1559,16 @@ class BodyCodegen
|
||||
break;
|
||||
|
||||
case Token.CALL:
|
||||
case Token.NEW:
|
||||
visitCall(node, type, child);
|
||||
case Token.NEW: {
|
||||
int specialType = node.getIntProp(Node.SPECIALCALL_PROP,
|
||||
Node.NON_SPECIALCALL);
|
||||
if (specialType == Node.NON_SPECIALCALL) {
|
||||
visitCall(node, type, child);
|
||||
} else {
|
||||
visitSpecialCall(node, type, specialType, child);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Token.REF_CALL:
|
||||
visitRefCall(node, type, child);
|
||||
@ -1838,11 +1845,11 @@ class BodyCodegen
|
||||
}
|
||||
|
||||
case Token.GETPROP:
|
||||
visitGetProp(node, child, false);
|
||||
visitGetProp(node, child);
|
||||
break;
|
||||
|
||||
case Token.GETELEM:
|
||||
visitGetElem(node, child, false);
|
||||
visitGetElem(node, child);
|
||||
break;
|
||||
|
||||
case Token.GET_REF:
|
||||
@ -2248,47 +2255,118 @@ class BodyCodegen
|
||||
|
||||
private void visitCall(Node node, int type, Node child)
|
||||
{
|
||||
/*
|
||||
* Generate code for call.
|
||||
*/
|
||||
Node firstArgChild = child.getNext();
|
||||
|
||||
OptFunctionNode
|
||||
target = (OptFunctionNode)node.getProp(Node.DIRECTCALL_PROP);
|
||||
int callType = (target == null)
|
||||
? node.getIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL)
|
||||
: Node.NON_SPECIALCALL;
|
||||
|
||||
cfw.addALoad(contextLocal);
|
||||
if (target == null && type == Token.CALL) {
|
||||
int childType = child.getType();
|
||||
if (firstArgChild == null) {
|
||||
if (childType == Token.NAME) {
|
||||
String name = child.getString();
|
||||
cfw.addPush(name);
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addOptRuntimeInvoke(
|
||||
"callName0",
|
||||
"(Ljava/lang/String;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Ljava/lang/Object;");
|
||||
} else if (childType == Token.GETPROP) {
|
||||
// x.name() call
|
||||
Node propTarget = child.getFirstChild();
|
||||
generateExpression(propTarget, node);
|
||||
Node id = propTarget.getNext();
|
||||
String property = id.getString();
|
||||
cfw.addPush(property);
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addOptRuntimeInvoke(
|
||||
"callProp0",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Ljava/lang/String;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Ljava/lang/Object;");
|
||||
} else {
|
||||
generateFunctionAndThisObj(child, node);
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addOptRuntimeInvoke(
|
||||
"call0",
|
||||
"(Lorg/mozilla/javascript/Function;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Ljava/lang/Object;");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == Token.CALL && child.getType() == Token.NAME
|
||||
&& target == null && callType == Node.NON_SPECIALCALL)
|
||||
{
|
||||
// Optimize common case of name(arguments) calls
|
||||
String simpleCallName = child.getString();
|
||||
cfw.addPush(simpleCallName);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
generateCallArgArray(node, child.getNext(), false);
|
||||
addOptRuntimeInvoke("callSimple",
|
||||
"(Lorg/mozilla/javascript/Context;"
|
||||
+"Ljava/lang/String;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+")Ljava/lang/Object;");
|
||||
return;
|
||||
if (childType == Token.NAME) {
|
||||
// XXX: this optimization is only possible if name
|
||||
// resolution
|
||||
// is not affected by arguments evaluation and currently
|
||||
// there are no checks for it
|
||||
String name = child.getString();
|
||||
generateCallArgArray(node, firstArgChild, false);
|
||||
cfw.addPush(name);
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addOptRuntimeInvoke(
|
||||
"callName",
|
||||
"([Ljava/lang/Object;"
|
||||
+"Ljava/lang/String;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Ljava/lang/Object;");
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstArgChild.getNext() == null) {
|
||||
// ...(something) call
|
||||
generateFunctionAndThisObj(child, node);
|
||||
// stack: ... functionObj thisObj
|
||||
generateExpression(firstArgChild, node);
|
||||
// stack: ... functionObj thisObj arg0
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addOptRuntimeInvoke(
|
||||
"call1",
|
||||
"(Lorg/mozilla/javascript/Function;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Ljava/lang/Object;");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == Token.NEW) {
|
||||
generateExpression(child, node);
|
||||
// stack: ... cx functionObj
|
||||
// stack: ... functionObj
|
||||
} else {
|
||||
generateFunctionAndThisObj(child, node);
|
||||
// stack: ... cx functionObj thisObj
|
||||
// stack: ... functionObj thisObj
|
||||
}
|
||||
child = child.getNext();
|
||||
|
||||
int beyond = 0;
|
||||
if (target == null) {
|
||||
generateCallArgArray(node, child, false);
|
||||
if (type == Token.NEW) {
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
// stack: ... functionObj cx scope
|
||||
} else {
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.add(ByteCode.SWAP);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
cfw.add(ByteCode.SWAP);
|
||||
// stack: ... functionObj cx scope thisObj
|
||||
}
|
||||
generateCallArgArray(node, firstArgChild, false);
|
||||
} else {
|
||||
beyond = cfw.acquireLabel();
|
||||
|
||||
@ -2297,7 +2375,7 @@ class BodyCodegen
|
||||
thisObjLocal = getNewWordLocal();
|
||||
cfw.addAStore(thisObjLocal);
|
||||
}
|
||||
// stack: ... cx functionObj
|
||||
// stack: ... functionObj
|
||||
|
||||
int directTargetIndex = target.getDirectTargetIndex();
|
||||
if (isTopLevel) {
|
||||
@ -2313,25 +2391,30 @@ class BodyCodegen
|
||||
codegen.mainClassSignature);
|
||||
|
||||
cfw.add(ByteCode.DUP2);
|
||||
// stack: ... cx functionObj directFunct functionObj directFunct
|
||||
// stack: ... functionObj directFunct functionObj directFunct
|
||||
|
||||
int regularCall = cfw.acquireLabel();
|
||||
cfw.add(ByteCode.IF_ACMPNE, regularCall);
|
||||
|
||||
// stack: ... functionObj directFunct
|
||||
short stackHeight = cfw.getStackTop();
|
||||
cfw.add(ByteCode.SWAP);
|
||||
cfw.add(ByteCode.POP);
|
||||
// stack: ... cx directFunct
|
||||
// stack: ... directFunct
|
||||
if (compilerEnv.isUseDynamicScope()) {
|
||||
cfw.add(ByteCode.SWAP);
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
} else {
|
||||
cfw.add(ByteCode.DUP_X1);
|
||||
// stack: ... directFunct cx directFunct
|
||||
cfw.add(ByteCode.DUP);
|
||||
// stack: ... directFunct directFunct
|
||||
cfw.addInvoke(ByteCode.INVOKEINTERFACE,
|
||||
"org/mozilla/javascript/Scriptable",
|
||||
"getParentScope",
|
||||
"()Lorg/mozilla/javascript/Scriptable;");
|
||||
// stack: ... directFunct scope
|
||||
cfw.addALoad(contextLocal);
|
||||
// stack: ... directFunct scope cx
|
||||
cfw.add(ByteCode.SWAP);
|
||||
}
|
||||
// stack: ... directFunc cx scope
|
||||
|
||||
@ -2348,25 +2431,25 @@ class BodyCodegen
|
||||
in the aReg and the number is the dReg
|
||||
Else pass the JS object in the aReg and 0.0 in the dReg.
|
||||
*/
|
||||
Node firstArgChild = child;
|
||||
while (child != null) {
|
||||
int dcp_register = nodeIsDirectCallParameter(child);
|
||||
Node argChild = firstArgChild;
|
||||
while (argChild != null) {
|
||||
int dcp_register = nodeIsDirectCallParameter(argChild);
|
||||
if (dcp_register >= 0) {
|
||||
cfw.addALoad(dcp_register);
|
||||
cfw.addDLoad(dcp_register + 1);
|
||||
} else if (child.getIntProp(Node.ISNUMBER_PROP, -1)
|
||||
} else if (argChild.getIntProp(Node.ISNUMBER_PROP, -1)
|
||||
== Node.BOTH)
|
||||
{
|
||||
cfw.add(ByteCode.GETSTATIC,
|
||||
"java/lang/Void",
|
||||
"TYPE",
|
||||
"Ljava/lang/Class;");
|
||||
generateExpression(child, node);
|
||||
generateExpression(argChild, node);
|
||||
} else {
|
||||
generateExpression(child, node);
|
||||
generateExpression(argChild, node);
|
||||
cfw.addPush(0.0);
|
||||
}
|
||||
child = child.getNext();
|
||||
argChild = argChild.getNext();
|
||||
}
|
||||
|
||||
cfw.add(ByteCode.GETSTATIC,
|
||||
@ -2382,82 +2465,98 @@ class BodyCodegen
|
||||
cfw.add(ByteCode.GOTO, beyond);
|
||||
|
||||
cfw.markLabel(regularCall, stackHeight);
|
||||
// stack: ... functionObj directFunct
|
||||
cfw.add(ByteCode.POP);
|
||||
// stack: functionObj, cx
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
// stack: ... functionObj cx scope
|
||||
if (type != Token.NEW) {
|
||||
cfw.addALoad(thisObjLocal);
|
||||
releaseWordLocal(thisObjLocal);
|
||||
// stack: ... functionObj cx scope thisObj
|
||||
}
|
||||
// XXX: this will generate code for the child array the second time,
|
||||
// so the code better not to alter tree structure...
|
||||
generateCallArgArray(node, firstArgChild, true);
|
||||
}
|
||||
|
||||
String className;
|
||||
String methodName;
|
||||
String callSignature;
|
||||
|
||||
if (callType != Node.NON_SPECIALCALL) {
|
||||
className = "org/mozilla/javascript/optimizer/OptRuntime";
|
||||
if (type == Token.NEW) {
|
||||
methodName = "newObjectSpecial";
|
||||
callSignature = "(Lorg/mozilla/javascript/Context;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"I" // call type
|
||||
+")Ljava/lang/Object;";
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
cfw.addALoad(thisObjLocal);
|
||||
cfw.addPush(callType);
|
||||
} else {
|
||||
methodName = "callSpecial";
|
||||
callSignature = "(Lorg/mozilla/javascript/Context;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"I" // call type
|
||||
+"Ljava/lang/String;I" // filename, linenumber
|
||||
+")Ljava/lang/Object;";
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
cfw.addALoad(thisObjLocal);
|
||||
cfw.addPush(callType);
|
||||
String sourceName = scriptOrFn.getSourceName();
|
||||
cfw.addPush(sourceName == null ? "" : sourceName);
|
||||
cfw.addPush(itsLineNumber);
|
||||
}
|
||||
if (type == Token.NEW) {
|
||||
addScriptRuntimeInvoke(
|
||||
"newObject",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+")Lorg/mozilla/javascript/Scriptable;");
|
||||
} else {
|
||||
className = "org/mozilla/javascript/ScriptRuntime";
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
if (type == Token.NEW) {
|
||||
methodName = "newObject";
|
||||
callSignature = "(Lorg/mozilla/javascript/Context;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Lorg/mozilla/javascript/Scriptable;";
|
||||
} else {
|
||||
methodName = "call";
|
||||
callSignature = "(Lorg/mozilla/javascript/Context;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Ljava/lang/Object;";
|
||||
}
|
||||
cfw.addInvoke(ByteCode.INVOKEINTERFACE,
|
||||
"org/mozilla/javascript/Function",
|
||||
"call",
|
||||
"(Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+")Ljava/lang/Object;");
|
||||
}
|
||||
|
||||
cfw.addInvoke(ByteCode.INVOKESTATIC,
|
||||
className, methodName, callSignature);
|
||||
|
||||
if (target != null) {
|
||||
cfw.markLabel(beyond);
|
||||
}
|
||||
}
|
||||
|
||||
private void visitSpecialCall(Node node, int type, int specialType,
|
||||
Node child)
|
||||
{
|
||||
cfw.addALoad(contextLocal);
|
||||
|
||||
if (type == Token.NEW) {
|
||||
generateExpression(child, node);
|
||||
// stack: ... cx functionObj
|
||||
} else {
|
||||
generateFunctionAndThisObj(child, node);
|
||||
// stack: ... cx functionObj thisObj
|
||||
}
|
||||
child = child.getNext();
|
||||
|
||||
generateCallArgArray(node, child, false);
|
||||
|
||||
String methodName;
|
||||
String callSignature;
|
||||
|
||||
if (type == Token.NEW) {
|
||||
methodName = "newObjectSpecial";
|
||||
callSignature = "(Lorg/mozilla/javascript/Context;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"I" // call type
|
||||
+")Ljava/lang/Object;";
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
cfw.addALoad(thisObjLocal);
|
||||
cfw.addPush(specialType);
|
||||
} else {
|
||||
methodName = "callSpecial";
|
||||
callSignature = "(Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Function;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"I" // call type
|
||||
+"Ljava/lang/String;I" // filename, linenumber
|
||||
+")Ljava/lang/Object;";
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
cfw.addALoad(thisObjLocal);
|
||||
cfw.addPush(specialType);
|
||||
String sourceName = scriptOrFn.getSourceName();
|
||||
cfw.addPush(sourceName == null ? "" : sourceName);
|
||||
cfw.addPush(itsLineNumber);
|
||||
}
|
||||
|
||||
addOptRuntimeInvoke(methodName, callSignature);
|
||||
}
|
||||
|
||||
private void visitRefCall(Node node, int type, Node child)
|
||||
{
|
||||
generateFunctionAndThisObj(child, node);
|
||||
@ -2467,8 +2566,8 @@ class BodyCodegen
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addScriptRuntimeInvoke("referenceCall",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
"(Lorg/mozilla/javascript/Function;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"[Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
@ -2519,74 +2618,73 @@ class BodyCodegen
|
||||
private void generateFunctionAndThisObj(Node node, Node parent)
|
||||
{
|
||||
// Place on stack (function object, function this) pair
|
||||
int type = node.getType();
|
||||
switch (node.getType()) {
|
||||
case Token.GETPROP:
|
||||
// x.y(...)
|
||||
// -> tmp = x, (tmp.y, tmp)(...)
|
||||
visitGetProp(node, node.getFirstChild(), true);
|
||||
cfw.add(ByteCode.SWAP);
|
||||
break;
|
||||
|
||||
case Token.GETELEM:
|
||||
// x[y](...)
|
||||
// -> tmp = x, (tmp[y], tmp)(...)
|
||||
visitGetElem(node, node.getFirstChild(), true);
|
||||
cfw.add(ByteCode.SWAP);
|
||||
break;
|
||||
|
||||
case Token.NAME: {
|
||||
// name()(...)
|
||||
// -> base = getBase("name"), (base.name, getThis(base))(...)
|
||||
String name = node.getString();
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
cfw.addPush(name);
|
||||
addScriptRuntimeInvoke("getBase",
|
||||
"(Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Ljava/lang/String;"
|
||||
+")Lorg/mozilla/javascript/Scriptable;");
|
||||
cfw.add(ByteCode.DUP);
|
||||
cfw.addPush(name);
|
||||
cfw.addALoad(contextLocal);
|
||||
addScriptRuntimeInvoke(
|
||||
"getObjectProp",
|
||||
"(Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Ljava/lang/String;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+")Ljava/lang/Object;");
|
||||
// swap property and base to call getThis(base)
|
||||
cfw.add(ByteCode.SWAP);
|
||||
|
||||
// Conditionally call getThis.
|
||||
// The getThis entry in the runtime will take a
|
||||
// Scriptable object intended to be used as a 'this'
|
||||
// and make sure that it is neither a With object or
|
||||
// an activation object.
|
||||
// Executing getThis requires at least two instanceof
|
||||
// tests, so we only include it if we are currently
|
||||
// inside a 'with' statement, or if we are executing
|
||||
// a script (to protect against an eval inside a with).
|
||||
if (withNesting != 0
|
||||
|| (fnCurrent == null && compilerEnv.isFromEval()))
|
||||
{
|
||||
addScriptRuntimeInvoke("getThis",
|
||||
"(Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Lorg/mozilla/javascript/Scriptable;");
|
||||
case Token.GETELEM: {
|
||||
Node target = node.getFirstChild();
|
||||
generateExpression(target, node);
|
||||
Node id = target.getNext();
|
||||
if (type == Token.GETPROP) {
|
||||
String property = id.getString();
|
||||
cfw.addPush(property);
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addScriptRuntimeInvoke(
|
||||
"getPropFunctionAndThis",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Ljava/lang/String;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Lorg/mozilla/javascript/Function;");
|
||||
} else {
|
||||
// Optimizer do not optimize this case for now
|
||||
if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
|
||||
throw Codegen.badTree();
|
||||
generateExpression(id, node); // id
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addScriptRuntimeInvoke(
|
||||
"getElemFunctionAndThis",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Lorg/mozilla/javascript/Function;");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Token.NAME: {
|
||||
String name = node.getString();
|
||||
cfw.addPush(name);
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addScriptRuntimeInvoke(
|
||||
"getNameFunctionAndThis",
|
||||
"(Ljava/lang/String;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Lorg/mozilla/javascript/Scriptable;"
|
||||
+")Lorg/mozilla/javascript/Function;");
|
||||
break;
|
||||
}
|
||||
|
||||
default: // including GETVAR
|
||||
// something(...)
|
||||
// -> tmp = something, (tmp, getParent(tmp))(...)
|
||||
generateExpression(node, parent);
|
||||
cfw.add(ByteCode.DUP);
|
||||
addScriptRuntimeInvoke("getParent",
|
||||
"(Ljava/lang/Object;"
|
||||
+")Lorg/mozilla/javascript/Scriptable;");
|
||||
cfw.addALoad(contextLocal);
|
||||
addScriptRuntimeInvoke(
|
||||
"getValueFunctionAndThis",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+")Lorg/mozilla/javascript/Function;");
|
||||
break;
|
||||
}
|
||||
// Get thisObj prepared by get(Name|Prop|Elem|Value)FunctionAndThis
|
||||
cfw.addALoad(contextLocal);
|
||||
addScriptRuntimeInvoke(
|
||||
"lastStoredScriptable",
|
||||
"(Lorg/mozilla/javascript/Context;"
|
||||
+")Lorg/mozilla/javascript/Scriptable;");
|
||||
}
|
||||
|
||||
private void updateLineNumber(Node node)
|
||||
@ -3432,12 +3530,9 @@ class BodyCodegen
|
||||
cfw.add(ByteCode.POP);
|
||||
}
|
||||
|
||||
private void visitGetProp(Node node, Node child, boolean dupObject)
|
||||
private void visitGetProp(Node node, Node child)
|
||||
{
|
||||
generateExpression(child, node); //object
|
||||
if (dupObject) {
|
||||
cfw.add(ByteCode.DUP);
|
||||
}
|
||||
Node nameChild = child.getNext();
|
||||
generateExpression(nameChild, node); // the name
|
||||
/*
|
||||
@ -3466,12 +3561,9 @@ class BodyCodegen
|
||||
}
|
||||
}
|
||||
|
||||
private void visitGetElem(Node node, Node child, boolean dupObject)
|
||||
private void visitGetElem(Node node, Node child)
|
||||
{
|
||||
generateExpression(child, node); // object
|
||||
if (dupObject) {
|
||||
cfw.add(ByteCode.DUP);
|
||||
}
|
||||
generateExpression(child.getNext(), node); // id
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
@ -3485,7 +3577,7 @@ class BodyCodegen
|
||||
}
|
||||
else {
|
||||
addScriptRuntimeInvoke(
|
||||
"getObjectId",
|
||||
"getObjectElem",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
@ -3582,7 +3674,7 @@ class BodyCodegen
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addALoad(variableObjectLocal);
|
||||
addScriptRuntimeInvoke(
|
||||
"getObjectId",
|
||||
"getObjectElem",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
@ -3604,7 +3696,7 @@ class BodyCodegen
|
||||
+")Ljava/lang/Object;");
|
||||
} else {
|
||||
addScriptRuntimeInvoke(
|
||||
"setObjectId",
|
||||
"setObjectElem",
|
||||
"(Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"Ljava/lang/Object;"
|
||||
|
@ -91,6 +91,58 @@ public final class OptRuntime extends ScriptRuntime
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement ....() call. This is mostly to shrink optimizer code
|
||||
*/
|
||||
public static Object call0(Function fun, Scriptable thisObj,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement ....(arg) call. This is mostly to shrink optimizer code
|
||||
*/
|
||||
public static Object call1(Function fun, Scriptable thisObj, Object arg0, Context cx, Scriptable scope)
|
||||
{
|
||||
Object[] args = new Object[1];
|
||||
args[0] = arg0;
|
||||
return fun.call(cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement name(args) call. This is mostly to shrink optimizer code
|
||||
*/
|
||||
public static Object callName(Object[] args, String name,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
Function f = getNameFunctionAndThis(name, cx, scope);
|
||||
Scriptable thisObj = lastStoredScriptable(cx);
|
||||
return f.call(cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement name() call. This is mostly to shrink optimizer code
|
||||
*/
|
||||
public static Object callName0(String name,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
Function f = getNameFunctionAndThis(name, cx, scope);
|
||||
Scriptable thisObj = lastStoredScriptable(cx);
|
||||
return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement x.property() call. This is mostly to shrink optimizer code
|
||||
*/
|
||||
public static Object callProp0(Object value, String property,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
Function f = getPropFunctionAndThis(value, property, cx, scope);
|
||||
Scriptable thisObj = lastStoredScriptable(cx);
|
||||
return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
|
||||
}
|
||||
|
||||
public static Object add(Object val1, double val2)
|
||||
{
|
||||
if (val1 instanceof Scriptable)
|
||||
@ -109,35 +161,6 @@ public final class OptRuntime extends ScriptRuntime
|
||||
return toString(val1).concat((String)val2);
|
||||
}
|
||||
|
||||
public static Object callSimple(Context cx, String id, Scriptable scope,
|
||||
Object[] args)
|
||||
throws JavaScriptException
|
||||
{
|
||||
Object prop;
|
||||
Scriptable obj = scope;
|
||||
search: {
|
||||
while (obj != null) {
|
||||
prop = ScriptableObject.getProperty(obj, id);
|
||||
if (prop != Scriptable.NOT_FOUND) {
|
||||
break search;
|
||||
}
|
||||
obj = obj.getParentScope();
|
||||
}
|
||||
throw ScriptRuntime.notFoundError(scope, id);
|
||||
}
|
||||
|
||||
Scriptable thisArg = ScriptRuntime.getThis(obj);
|
||||
|
||||
if (!(prop instanceof Function)) {
|
||||
Object[] errorArgs = { toString(prop) };
|
||||
throw cx.reportRuntimeError(
|
||||
getMessage("msg.isnt.function", errorArgs));
|
||||
}
|
||||
|
||||
Function function = (Function)prop;
|
||||
return function.call(cx, scope, thisArg, args);
|
||||
}
|
||||
|
||||
public static Object[] padStart(Object[] currentArgs, int count) {
|
||||
Object[] result = new Object[currentArgs.length + count];
|
||||
System.arraycopy(currentArgs, 0, result, count, currentArgs.length);
|
||||
@ -150,14 +173,14 @@ public final class OptRuntime extends ScriptRuntime
|
||||
ScriptRuntime.initFunction(cx, scope, fn, functionType, false);
|
||||
}
|
||||
|
||||
public static Object callSpecial(Context cx, Object fun,
|
||||
Object thisObj, Object[] args,
|
||||
public static Object callSpecial(Context cx, Function fun,
|
||||
Scriptable thisObj, Object[] args,
|
||||
Scriptable scope,
|
||||
Scriptable callerThis, int callType,
|
||||
String fileName, int lineNumber)
|
||||
throws JavaScriptException
|
||||
{
|
||||
return ScriptRuntime.callSpecial(cx, fun, false, thisObj, args, scope,
|
||||
return ScriptRuntime.callSpecial(cx, fun, thisObj, args, scope,
|
||||
callerThis, callType,
|
||||
fileName, lineNumber);
|
||||
}
|
||||
@ -167,9 +190,7 @@ public final class OptRuntime extends ScriptRuntime
|
||||
Scriptable callerThis, int callType)
|
||||
throws JavaScriptException
|
||||
{
|
||||
return ScriptRuntime.callSpecial(cx, fun, true, null, args, scope,
|
||||
callerThis, callType,
|
||||
"", -1);
|
||||
return ScriptRuntime.newSpecial(cx, fun, args, scope, callType);
|
||||
}
|
||||
|
||||
public static Double wrapDouble(double num)
|
||||
|
@ -419,6 +419,17 @@ class Optimizer
|
||||
}
|
||||
case Token.CALL :
|
||||
{
|
||||
Node child = n.getFirstChild(); // the function node
|
||||
if (child.getType() == Token.GETELEM) {
|
||||
// Optimization of x[0]() is not supported
|
||||
// so bypass GETELEM optimization that
|
||||
// rewriteForNumberVariables would trigger
|
||||
rewriteAsObjectChildren(child, child.getFirstChild());
|
||||
} else {
|
||||
rewriteForNumberVariables(child);
|
||||
}
|
||||
child = child.getNext(); // the first arg
|
||||
|
||||
OptFunctionNode target
|
||||
= (OptFunctionNode)n.getProp(Node.DIRECTCALL_PROP);
|
||||
if (target != null) {
|
||||
@ -426,9 +437,6 @@ class Optimizer
|
||||
we leave each child as a Number if it can be. The codegen will
|
||||
handle moving the pairs of parameters.
|
||||
*/
|
||||
Node child = n.getFirstChild(); // the function node
|
||||
rewriteForNumberVariables(child);
|
||||
child = child.getNext(); // the first arg
|
||||
while (child != null) {
|
||||
int type = rewriteForNumberVariables(child);
|
||||
if (type == NumberType) {
|
||||
@ -436,29 +444,35 @@ class Optimizer
|
||||
}
|
||||
child = child.getNext();
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
// else fall thru...
|
||||
}
|
||||
default : {
|
||||
Node child = n.getFirstChild();
|
||||
while (child != null) {
|
||||
Node nextChild = child.getNext();
|
||||
int type = rewriteForNumberVariables(child);
|
||||
if (type == NumberType) {
|
||||
if (!convertParameter(child)) {
|
||||
n.removeChild(child);
|
||||
Node nuChild = new Node(TO_OBJECT, child);
|
||||
if (nextChild == null)
|
||||
n.addChildToBack(nuChild);
|
||||
else
|
||||
n.addChildBefore(nuChild, nextChild);
|
||||
}
|
||||
}
|
||||
child = nextChild;
|
||||
} else {
|
||||
rewriteAsObjectChildren(n, child);
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
default : {
|
||||
rewriteAsObjectChildren(n, n.getFirstChild());
|
||||
return NoType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rewriteAsObjectChildren(Node n, Node child)
|
||||
{
|
||||
// Force optimized children to be objects
|
||||
while (child != null) {
|
||||
Node nextChild = child.getNext();
|
||||
int type = rewriteForNumberVariables(child);
|
||||
if (type == NumberType) {
|
||||
if (!convertParameter(child)) {
|
||||
n.removeChild(child);
|
||||
Node nuChild = new Node(TO_OBJECT, child);
|
||||
if (nextChild == null)
|
||||
n.addChildToBack(nuChild);
|
||||
else
|
||||
n.addChildBefore(nuChild, nextChild);
|
||||
}
|
||||
}
|
||||
child = nextChild;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1533,52 +1533,7 @@ class XMLList extends XMLObjectImpl implements Function
|
||||
throw ScriptRuntime.typeError1("msg.isnt.function",
|
||||
methodName);
|
||||
|
||||
int L = args.length;
|
||||
Object callThis =
|
||||
(L == 0 || args[0] == null || args[0] == Undefined.instance)
|
||||
? ScriptableObject.getTopLevelScope(scope)
|
||||
: ScriptRuntime.toObject(cx, scope, args[0]);
|
||||
|
||||
Object[] callArgs;
|
||||
if (isApply)
|
||||
{
|
||||
// Follow Ecma 15.3.4.3
|
||||
if (L <= 1)
|
||||
{
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
}
|
||||
else
|
||||
{
|
||||
Object arg1 = args[1];
|
||||
if (arg1 == null || arg1 == Undefined.instance)
|
||||
{
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
}
|
||||
else if (ScriptRuntime.isArrayObject(arg1))
|
||||
{
|
||||
callArgs = ScriptRuntime.getArrayElements((Scriptable)arg1);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ScriptRuntime.typeError0("msg.arg.isnt.array");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Follow Ecma 15.3.4.4
|
||||
if (L <= 1)
|
||||
{
|
||||
callArgs = ScriptRuntime.emptyArgs;
|
||||
}
|
||||
else
|
||||
{
|
||||
callArgs = new Object[L - 1];
|
||||
System.arraycopy(args, 1, callArgs, 0, L - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return ScriptRuntime.call(cx, thisObj, callThis, callArgs, scope);
|
||||
return ScriptRuntime.applyOrCall(isApply, cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
protected Object jsConstructor(Context cx, boolean inNewExpr,
|
||||
|
Loading…
Reference in New Issue
Block a user