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:
igor%mir2.org 2004-08-01 23:21:17 +00:00
parent 3441040ff5
commit e32e12b401
9 changed files with 869 additions and 638 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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

View File

@ -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)
{

View File

@ -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;"

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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,