Browse files

- support special GetValue and PutValue on primitives in call express…

…ions

- handle primitive thisObject in NativeString, NativeBoolean and NativeNumber functions
  • Loading branch information...
1 parent 7026596 commit f5329f629e6bc61a75d40b45b6a09440d801d0d9 @anba committed Mar 21, 2012
View
2 src/org/mozilla/javascript/Context.java
@@ -2665,7 +2665,7 @@ public void removeActivationName(String name)
long scratchUint32;
// It can be used to return the second Scriptable result from function
- Scriptable scratchScriptable;
+ Object scratchThis;
// Generate an observer count on compiled code
public boolean generateObserverCount = false;
View
17 src/org/mozilla/javascript/Interpreter.java
@@ -1540,7 +1540,7 @@ private static Object interpretLoop(Context cx, CallFrame frame,
stack[stackTop] = ScriptRuntime.getNameObjectAndThis(stringReg,
cx, frame.scope);
++stackTop;
- Scriptable thisObj = ScriptRuntime.lastStoredScriptable(cx);
+ Object thisObj = ScriptRuntime.lastStoredThis(cx);
stack[stackTop] = (thisObj != null ? thisObj : Undefined.instance);
continue Loop;
}
@@ -1551,25 +1551,30 @@ private static Object interpretLoop(Context cx, CallFrame frame,
stack[stackTop] = ScriptRuntime.getPropObjectAndThis(obj, stringReg,
cx, frame.scope);
++stackTop;
- stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
+ // ignore stored this
+ ScriptRuntime.lastStoredThis(cx);
+ stack[stackTop] = obj;
continue Loop;
}
case Icode_ELEM_AND_THIS: {
Object obj = stack[stackTop - 1];
if (obj == DBL_MRK) obj = ScriptRuntime.wrapNumber(sDbl[stackTop - 1]);
Object id = stack[stackTop];
if (id == DBL_MRK) id = ScriptRuntime.wrapNumber(sDbl[stackTop]);
- stack[stackTop - 1] = ScriptRuntime.getElemObjectAndThis(obj, id, cx);
- stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx);
+ stack[stackTop - 1] = ScriptRuntime.getElemObjectAndThis(obj, id, cx,
+ frame.scope);
+ // ignore stored this
+ ScriptRuntime.lastStoredThis(cx);
+ stack[stackTop] = obj;
continue Loop;
}
case Icode_VALUE_AND_THIS : {
Object value = stack[stackTop];
if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
stack[stackTop] = ScriptRuntime.getValueObjectAndThis(value, cx);
++stackTop;
- // ignore stored scriptable
- ScriptRuntime.lastStoredScriptable(cx);
+ // ignore stored this
+ ScriptRuntime.lastStoredThis(cx);
stack[stackTop] = Undefined.instance;
continue Loop;
}
View
9 src/org/mozilla/javascript/NativeBoolean.java
@@ -121,9 +121,14 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
// The rest of Boolean.prototype methods require thisObj to be Boolean
- if (!(thisObj instanceof NativeBoolean))
+ boolean value;
+ if (thisObj instanceof Boolean) {
+ value = ((Boolean)thisObj).booleanValue();
+ } else if (thisObj instanceof NativeBoolean) {
+ value = ((NativeBoolean)thisObj).booleanValue;
+ } else {
throw incompatibleCallError(f);
- boolean value = ((NativeBoolean)thisObj).booleanValue;
+ }
switch (id) {
View
9 src/org/mozilla/javascript/NativeNumber.java
@@ -136,9 +136,14 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
// The rest of Number.prototype methods require thisObj to be Number
- if (!(thisObj instanceof NativeNumber))
+ double value;
+ if (thisObj instanceof Number) {
+ value = ((Number)thisObj).doubleValue();
+ } else if (thisObj instanceof NativeNumber) {
+ value = ((NativeNumber)thisObj).doubleValue;
+ } else {
throw incompatibleCallError(f);
- double value = ((NativeNumber)thisObj).doubleValue;
+ }
switch (id) {
View
15 src/org/mozilla/javascript/NativeString.java
@@ -272,11 +272,11 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
case Id_toString:
case Id_valueOf:
// ECMA 15.5.4.2: 'the toString function is not generic.
- CharSequence cs = realThis(thisObj, f).string;
+ CharSequence cs = realThis(thisObj, f);
return cs instanceof String ? cs : cs.toString();
case Id_toSource: {
- CharSequence s = realThis(thisObj, f).string;
+ CharSequence s = realThis(thisObj, f);
return "(new String(\""+ScriptRuntime.escapeString(s.toString())+"\"))";
}
@@ -452,11 +452,14 @@ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
}
}
- private static NativeString realThis(Object thisObj, IdFunctionObject f)
+ private static CharSequence realThis(Object thisObj, IdFunctionObject f)
{
- if (!(thisObj instanceof NativeString))
- throw incompatibleCallError(f);
- return (NativeString)thisObj;
+ if (thisObj instanceof CharSequence) {
+ return (CharSequence)thisObj;
+ } else if (thisObj instanceof NativeString) {
+ return ((NativeString)thisObj).string;
+ }
+ throw incompatibleCallError(f);
}
/*
View
160 src/org/mozilla/javascript/ScriptRuntime.java
@@ -1513,7 +1513,9 @@ private static Object getPrimitiveValue(Object base, Scriptable obj,
String property, Context cx,
Scriptable scope) {
// TODO: could need some optimization
- assert obj instanceof ScriptableObject;
+ assert obj instanceof NativeString
+ || obj instanceof NativeNumber
+ || obj instanceof NativeBoolean;
ScriptableObject sobj = (ScriptableObject) obj;
PropertyDescriptor desc = sobj.$getProperty(property);
if (desc == null) {
@@ -1537,7 +1539,9 @@ private static void putPrimitiveValue(Object base, Scriptable obj,
Object value, Context cx,
Scriptable scope) {
// TODO: could need some optimization
- assert obj instanceof ScriptableObject;
+ assert obj instanceof NativeString
+ || obj instanceof NativeNumber
+ || obj instanceof NativeBoolean;
ScriptableObject sobj = (ScriptableObject) obj;
if (!sobj.$canPut(property)) {
if (checked) {
@@ -1563,6 +1567,7 @@ private static void putPrimitiveValue(Object base, Scriptable obj,
return;
}
Object setter = desc.getSetter();
+ assert setter instanceof Callable;
((Callable) setter).call(cx, scope, base, new Object[]{ value });
}
@@ -2154,7 +2159,7 @@ private static Object nameOrFunction(Context cx, Scriptable scope,
result = topScopeName(cx, scope, name);
if (result == Scriptable.NOT_FOUND) {
if (asFunctionCall) {
- storeScriptable(cx, BAD_SCRIPTABLE);
+ storeThis(cx, BAD_SCRIPTABLE);
return createNotFoundError(scope, name);
} else if (firstXMLObject == null) {
throw notFoundError(scope, name);
@@ -2173,10 +2178,10 @@ private static Object nameOrFunction(Context cx, Scriptable scope,
if (asFunctionCall) {
if (!(result instanceof Callable)) {
- storeScriptable(cx, BAD_SCRIPTABLE);
+ storeThis(cx, BAD_SCRIPTABLE);
return notFunctionError(result, name);
}
- storeScriptable(cx, thisObj);
+ storeThis(cx, thisObj);
}
return result;
@@ -2547,8 +2552,8 @@ public static Callable ensureCallable(Object value) throws RuntimeException {
*/
private static Callable ensureCallable(Context cx, Object value) {
if (!(value instanceof Callable)) {
- // clear stored scriptable before throwing
- lastStoredScriptable(cx);
+ // clear stored this before throwing
+ lastStoredThis(cx);
throw (RuntimeException)value;
}
return (Callable)value;
@@ -2567,11 +2572,11 @@ public static Callable getNameFunctionAndThis(String name,
{
Object value = getNameObjectAndThis(name, cx, scope);
// restore old behaviour
- scope = lastStoredScriptable(cx);
- if (scope == null) {
- scope = cx.topCallScope;
+ Object thisObj = lastStoredThis(cx);
+ if (thisObj == null) {
+ thisObj = cx.topCallScope;
}
- storeScriptable(cx, scope);
+ storeThis(cx, thisObj);
return ensureCallable(cx, value);
}
@@ -2586,7 +2591,7 @@ public static Callable getElemFunctionAndThis(Object obj,
Object elem,
Context cx)
{
- Object value = getElemObjectAndThis(obj, elem, cx);
+ Object value = getElemObjectAndThis(obj, elem, cx, getTopCallScope(cx));
return ensureCallable(cx, value);
}
@@ -2603,7 +2608,7 @@ public static Callable getPropFunctionAndThis(Object obj,
String property,
Context cx)
{
- Object value = getPropObjectAndThis(obj, property, cx);
+ Object value = getPropObjectAndThis(obj, property, cx, getTopCallScope(cx));
return ensureCallable(cx, value);
}
@@ -2616,7 +2621,7 @@ public static Callable getPropFunctionAndThis(Object obj,
*/
public static Callable getPropFunctionAndThis(Object obj,
String property,
- Context cx, final Scriptable scope)
+ Context cx, Scriptable scope)
{
Object value = getPropObjectAndThis(obj, property, cx, scope);
return ensureCallable(cx, value);
@@ -2676,15 +2681,15 @@ public static Object getNameObjectAndThis(String name,
if (parent == null) {
Object result = topScopeName(cx, scope, name);
if (!(result instanceof Callable)) {
- storeScriptable(cx, BAD_SCRIPTABLE);
+ storeThis(cx, BAD_SCRIPTABLE);
if (result == Scriptable.NOT_FOUND) {
return createNotFoundError(scope, name);
} else {
return notFunctionError(result, name);
}
}
// Top scope is not NativeWith or NativeCall => thisObj == scope
- storeScriptable(cx, null);
+ storeThis(cx, null);
return result;
}
@@ -2701,44 +2706,15 @@ public static Object getNameObjectAndThis(String name,
*/
public static Object getElemObjectAndThis(Object obj,
Object elem,
- Context cx)
+ Context cx, Scriptable scope)
{
String str = toStringIdOrIndex(cx, elem);
if (str != null) {
- return getPropObjectAndThis(obj, str, cx);
- }
- int index = lastIndexResult(cx);
-
- Scriptable thisObj = toObjectOrNull(cx, obj);
- if (thisObj == null) {
- throw undefCallError(obj, String.valueOf(index));
- }
-
- Object value = ScriptableObject.getProperty(thisObj, index);
- if (!(value instanceof Callable)) {
- storeScriptable(cx, BAD_SCRIPTABLE);
- return notFunctionError(value, elem);
+ return getObjectAndThisHelper(obj, str, -1, cx, scope);
+ } else {
+ int index = lastIndexResult(cx);
+ return getObjectAndThisHelper(obj, null, index, cx, scope);
}
-
- storeScriptable(cx, thisObj);
- return 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.
- * Warning: this doesn't allow to resolve primitive prototype properly when
- * many top scopes are involved.
- */
- public static Object getPropObjectAndThis(Object obj,
- String property,
- Context cx)
- {
- Scriptable thisObj = toObjectOrNull(cx, obj);
- return getPropFunctionAndThisHelper(obj, property, cx, thisObj);
}
/**
@@ -2750,32 +2726,60 @@ public static Object getPropObjectAndThis(Object obj,
*/
public static Object getPropObjectAndThis(Object obj,
String property,
- Context cx, final Scriptable scope)
+ Context cx, Scriptable scope)
{
- Scriptable thisObj = toObjectOrNull(cx, obj, scope);
- return getPropFunctionAndThisHelper(obj, property, cx, thisObj);
+ assert property != null;
+ return getObjectAndThisHelper(obj, property, -1, cx, scope);
}
- private static Object getPropFunctionAndThisHelper(Object obj,
- String property, Context cx, Scriptable thisObj)
- {
- if (thisObj == null) {
+ private static Object getObjectAndThisHelper(Object obj, String property,
+ int index, Context cx,
+ Scriptable scope) {
+ Scriptable sobj;
+ Object value;
+ // spidermonkey also tries noSuchMethod for index properties, rhino bug?
+ boolean tryNoSuchMethod = property != null;
+ if (obj == null || obj == Undefined.instance) {
throw undefCallError(obj, property);
+ } else if (obj instanceof Scriptable) {
+ sobj = (Scriptable) obj;
+ if (property == null) {
+ value = ScriptableObject.getProperty(sobj, index);
+ } else {
+ value = ScriptableObject.getProperty(sobj, property);
+ }
+ } else if ((sobj = tryPrimitive(cx, scope, obj)) != null) {
+ // follow spidermonkey (possible bug?)
+ tryNoSuchMethod = false;
+ if (property == null) { property = toString(index); }
+ value = getPrimitiveValue(obj, sobj, property, cx, scope);
+ } else if ((sobj = tryWrap(cx, scope, obj)) != null) {
+ if (property == null) {
+ value = ScriptableObject.getProperty(sobj, index);
+ } else {
+ value = ScriptableObject.getProperty(sobj, property);
+ }
+ } else {
+ throw errorWithClassName("msg.invalid.type", obj);
}
- Object value = ScriptableObject.getProperty(thisObj, property);
- if (!(value instanceof Callable)) {
- Object noSuchMethod = ScriptableObject.getProperty(thisObj, "__noSuchMethod__");
- if (noSuchMethod instanceof Callable)
- value = new NoSuchMethodShim((Callable)noSuchMethod, property);
+ boolean valueCallable = value instanceof Callable;
+ if (tryNoSuchMethod && !valueCallable) {
+ Object noSuchMethod = ScriptableObject.getProperty(sobj, "__noSuchMethod__");
+ if (noSuchMethod instanceof Callable) {
+ if (property == null) { property = toString(index); }
+ valueCallable = true;
+ value = new NoSuchMethodShim((Callable) noSuchMethod, property);
+ }
}
- if (!(value instanceof Callable)) {
- storeScriptable(cx, BAD_SCRIPTABLE);
- return notFunctionError(thisObj, value, property);
+ if (!valueCallable) {
+ if (property == null) { property = toString(index); }
+ storeThis(cx, BAD_SCRIPTABLE);
+ return notFunctionError(sobj, value, property);
}
- storeScriptable(cx, thisObj);
+ storeThis(cx, sobj);
return value;
}
@@ -2789,7 +2793,7 @@ private static Object getPropFunctionAndThisHelper(Object obj,
public static Object getValueObjectAndThis(Object value, Context cx)
{
if (!(value instanceof Callable)) {
- storeScriptable(cx, BAD_SCRIPTABLE);
+ storeThis(cx, BAD_SCRIPTABLE);
return notFunctionError(value);
}
Scriptable thisObj = null;
@@ -2809,7 +2813,7 @@ public static Object getValueObjectAndThis(Object value, Context cx)
thisObj = ScriptableObject.getTopLevelScope(thisObj);
}
}
- storeScriptable(cx, thisObj);
+ storeThis(cx, thisObj);
return value;
}
@@ -4555,21 +4559,27 @@ public static long lastUint32Result(Context cx)
return value;
}
- private static void storeScriptable(Context cx, Scriptable value)
+ private static void storeThis(Context cx, Object value)
{
- // The previously stored scratchScriptable should be consumed
- if (cx.scratchScriptable != null)
+ // The previously stored scratchThis should be consumed
+ if (cx.scratchThis != null)
throw new IllegalStateException();
- cx.scratchScriptable = value;
+ cx.scratchThis = value;
}
- public static Scriptable lastStoredScriptable(Context cx)
+ public static Object lastStoredThis(Context cx)
{
- Scriptable result = cx.scratchScriptable;
- cx.scratchScriptable = null;
+ Object result = cx.scratchThis;
+ cx.scratchThis = null;
return result;
}
+ @Deprecated
+ public static Scriptable lastStoredScriptable(Context cx)
+ {
+ return toObjectOrNull(cx, lastStoredThis(cx));
+ }
+
static String makeUrlForGeneratedScript
(boolean isEval, String masterScriptUrl, int masterScriptLine)
{
View
13 src/org/mozilla/javascript/optimizer/Codegen.java
@@ -3645,6 +3645,7 @@ private void generateObjectAndThisObj(Node node, Node parent)
case Token.GETELEM: {
Node target = node.getFirstChild();
generateExpression(target, node);
+ cfw.add(ByteCode.DUP); // dup b/c we ignore lastStoredThis
Node id = target.getNext();
if (type == Token.GETPROP) {
String property = id.getString();
@@ -3664,11 +3665,13 @@ private void generateObjectAndThisObj(Node node, Node parent)
throw Codegen.badTree();
generateExpression(id, node); // id
cfw.addALoad(contextLocal);
+ cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"getElemObjectAndThis",
"(Ljava/lang/Object;"
+"Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Context;"
+ +"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
}
break;
@@ -3701,13 +3704,17 @@ private void generateObjectAndThisObj(Node node, Node parent)
// Get thisObj prepared by get(Name|Prop|Elem|Value)ObjectAndThis
cfw.addALoad(contextLocal);
addScriptRuntimeInvoke(
- "lastStoredScriptable",
+ "lastStoredThis",
"(Lorg/mozilla/javascript/Context;"
- +")Lorg/mozilla/javascript/Scriptable;");
+ +")Ljava/lang/Object;");
switch (type) {
case Token.GETPROP:
case Token.GETELEM:
- // no further changes needed
+ // ignore stored scriptable, obj is still on stack (see DUP above)
+ // stack: ... obj value stored -> ... obj value
+ cfw.add(ByteCode.POP);
+ // stack: ... obj value -> ... value obj
+ cfw.add(ByteCode.SWAP);
break;
case Token.NAME: {
// replace null with undefined
View
8 src/org/mozilla/javascript/optimizer/OptRuntime.java
@@ -97,7 +97,7 @@ public static Object callName(Object[] args, String name,
Context cx, Scriptable scope)
{
Object f = getNameObjectAndThis(name, cx, scope);
- Object thisObj = lastStoredScriptable(cx);
+ Object thisObj = lastStoredThis(cx);
thisObj = (thisObj != null ? thisObj : Undefined.instance);
Callable c = ensureCallable(f);
return c.call(cx, scope, thisObj, args);
@@ -110,7 +110,7 @@ public static Object callName0(String name,
Context cx, Scriptable scope)
{
Object f = getNameObjectAndThis(name, cx, scope);
- Object thisObj = lastStoredScriptable(cx);
+ Object thisObj = lastStoredThis(cx);
thisObj = (thisObj != null ? thisObj : Undefined.instance);
Callable c = ensureCallable(f);
return c.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
@@ -123,7 +123,9 @@ public static Object callProp0(Object value, String property,
Context cx, Scriptable scope)
{
Object f = getPropObjectAndThis(value, property, cx, scope);
- Scriptable thisObj = lastStoredScriptable(cx);
+ // ignore stored this
+ Object thisObj = lastStoredThis(cx);
+ thisObj = value;
Callable c = ensureCallable(f);
return c.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
}

0 comments on commit f5329f6

Please sign in to comment.