diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/ScriptRuntime.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/ScriptRuntime.java | 259 |
1 files changed, 156 insertions, 103 deletions
diff --git a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java index 6df047d8..e927d4fc 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java @@ -27,13 +27,14 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; +import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.SwitchPoint; import java.lang.reflect.Array; import java.util.Collections; import java.util.Iterator; @@ -45,9 +46,12 @@ import java.util.Objects; import jdk.internal.dynalink.beans.StaticClass; import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.api.scripting.ScriptObjectMirror; +import jdk.nashorn.internal.codegen.ApplySpecialization; +import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.CompilerConstants.Call; import jdk.nashorn.internal.ir.debug.JSONWriter; import jdk.nashorn.internal.objects.Global; +import jdk.nashorn.internal.objects.NativeObject; import jdk.nashorn.internal.parser.Lexer; import jdk.nashorn.internal.runtime.linker.Bootstrap; @@ -81,9 +85,6 @@ public final class ScriptRuntime { /** Method handle used to enter a {@code with} scope at runtime. */ public static final Call OPEN_WITH = staticCallNoLookup(ScriptRuntime.class, "openWith", ScriptObject.class, ScriptObject.class, Object.class); - /** Method handle used to exit a {@code with} scope at runtime. */ - public static final Call CLOSE_WITH = staticCallNoLookup(ScriptRuntime.class, "closeWith", ScriptObject.class, ScriptObject.class); - /** * Method used to place a scope's variable into the Global scope, which has to be done for the * properties declared at outermost script level. @@ -108,6 +109,16 @@ public final class ScriptRuntime { public static final Call APPLY = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "apply", Object.class, ScriptFunction.class, Object.class, Object[].class); /** + * Throws a reference error for an undefined variable. + */ + public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class); + + /** + * Used to invalidate builtin names, e.g "Function" mapping to all properties in Function.prototype and Function.prototype itself. + */ + public static final Call INVALIDATE_RESERVED_BUILTIN_NAME = staticCallNoLookup(ScriptRuntime.class, "invalidateReservedBuiltinName", void.class, String.class); + + /** * Converts a switch tag value to a simple integer. deflt value if it can't. * * @param tag Switch statement tag value. @@ -121,7 +132,6 @@ public final class ScriptRuntime { return (int)d; } } - return deflt; } @@ -169,7 +179,7 @@ public final class ScriptRuntime { // But we don't need to -- all we need is the right class name // of the corresponding primitive wrapper type. - final JSType type = JSType.of(self); + final JSType type = JSType.ofNoFunction(self); switch (type) { case BOOLEAN: @@ -189,7 +199,6 @@ public final class ScriptRuntime { className = "Undefined"; break; case OBJECT: - case FUNCTION: if (self instanceof ScriptObject) { className = ((ScriptObject)self).getClassName(); } else if (self instanceof JSObject) { @@ -271,7 +280,7 @@ public final class ScriptRuntime { private final int length; private int index; - RangeIterator(int length) { + RangeIterator(final int length) { this.length = length; } @@ -287,7 +296,7 @@ public final class ScriptRuntime { @Override public void remove() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("remove"); } } @@ -325,7 +334,7 @@ public final class ScriptRuntime { @Override public void remove() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("remove"); } }; } @@ -384,6 +393,15 @@ public final class ScriptRuntime { } /** + * Throws a reference error for an undefined variable. + * + * @param name the variable name + */ + public static void throwReferenceError(final String name) { + throw referenceError("not.defined", name); + } + + /** * Call a script function as a constructor with given args. * * @param target ScriptFunction object. @@ -409,8 +427,8 @@ public final class ScriptRuntime { * @return true if both objects have the same value */ public static boolean sameValue(final Object x, final Object y) { - final JSType xType = JSType.of(x); - final JSType yType = JSType.of(y); + final JSType xType = JSType.ofNoFunction(x); + final JSType yType = JSType.ofNoFunction(y); if (xType != yType) { return false; @@ -429,7 +447,7 @@ public final class ScriptRuntime { } // checking for xVal == -0.0 and yVal == +0.0 or vice versa - if (xVal == 0.0 && (Double.doubleToLongBits(xVal) != Double.doubleToLongBits(yVal))) { + if (xVal == 0.0 && Double.doubleToLongBits(xVal) != Double.doubleToLongBits(yVal)) { return false; } @@ -440,7 +458,7 @@ public final class ScriptRuntime { return x.equals(y); } - return (x == y); + return x == y; } /** @@ -453,7 +471,7 @@ public final class ScriptRuntime { * @return JSON string representation of AST of the supplied code */ public static String parse(final String code, final String name, final boolean includeLoc) { - return JSONWriter.parse(Context.getContextTrusted().getEnv(), code, name, includeLoc); + return JSONWriter.parse(Context.getContextTrusted(), code, name, includeLoc); } /** @@ -466,7 +484,8 @@ public final class ScriptRuntime { } /** - * Entering a {@code with} node requires new scope. This is the implementation + * Entering a {@code with} node requires new scope. This is the implementation. When exiting the with statement, + * use {@link ScriptObject#getProto()} on the scope. * * @param scope existing scope * @param expression expression in with @@ -481,6 +500,17 @@ public final class ScriptRuntime { throw typeError(global, "cant.apply.with.to.null"); } + if (expression instanceof ScriptObjectMirror) { + final Object unwrapped = ScriptObjectMirror.unwrap(expression, global); + if (unwrapped instanceof ScriptObject) { + return new WithObject(scope, (ScriptObject)unwrapped); + } + // foreign ScriptObjectMirror + final ScriptObject exprObj = global.newObject(); + NativeObject.bindAllProperties(exprObj, (ScriptObjectMirror)expression); + return new WithObject(scope, exprObj); + } + final Object wrappedExpr = JSType.toScriptObject(global, expression); if (wrappedExpr instanceof ScriptObject) { return new WithObject(scope, (ScriptObject)wrappedExpr); @@ -490,20 +520,6 @@ public final class ScriptRuntime { } /** - * Exiting a {@code with} node requires restoring scope. This is the implementation - * - * @param scope existing scope - * - * @return restored scope - */ - public static ScriptObject closeWith(final ScriptObject scope) { - if (scope instanceof WithObject) { - return ((WithObject)scope).getParentScope(); - } - return scope; - } - - /** * ECMA 11.6.1 - The addition operator (+) - generic implementation * Compiler specializes using {@link jdk.nashorn.internal.codegen.RuntimeCallSite} * if any type information is available for any of the operands @@ -525,7 +541,7 @@ public final class ScriptRuntime { final boolean xIsUndefined = x == UNDEFINED; final boolean yIsUndefined = y == UNDEFINED; - if ((xIsNumber && yIsUndefined) || (xIsUndefined && yIsNumber) || (xIsUndefined && yIsUndefined)) { + if (xIsNumber && yIsUndefined || xIsUndefined && yIsNumber || xIsUndefined && yIsUndefined) { return Double.NaN; } @@ -535,7 +551,11 @@ public final class ScriptRuntime { if (xPrim instanceof String || yPrim instanceof String || xPrim instanceof ConsString || yPrim instanceof ConsString) { - return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim)); + try { + return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim)); + } catch (final IllegalArgumentException iae) { + throw rangeError(iae, "concat.string.too.big"); + } } return JSType.toNumber(xPrim) + JSType.toNumber(yPrim); @@ -577,6 +597,13 @@ public final class ScriptRuntime { if (property != null) { if (obj instanceof ScriptObject) { obj = ((ScriptObject)obj).get(property); + if(Global.isLocationPropertyPlaceholder(obj)) { + if(CompilerConstants.__LINE__.name().equals(property)) { + obj = Integer.valueOf(0); + } else { + obj = ""; + } + } } else if (object instanceof Undefined) { obj = ((Undefined)obj).get(property); } else if (object == null) { @@ -689,67 +716,100 @@ public final class ScriptRuntime { /** ECMA 11.9.3 The Abstract Equality Comparison Algorithm */ private static boolean equals(final Object x, final Object y) { - final JSType xType = JSType.of(x); - final JSType yType = JSType.of(y); + if (x == y) { + return true; + } + if (x instanceof ScriptObject && y instanceof ScriptObject) { + return x == y; + } + if (x instanceof ScriptObjectMirror || y instanceof ScriptObjectMirror) { + return ScriptObjectMirror.identical(x, y); + } + return equalValues(x, y); + } - if (xType == yType) { + /** + * Extracted portion of {@code equals()} that compares objects by value (or by reference, if no known value + * comparison applies). + * @param x one value + * @param y another value + * @return true if they're equal according to 11.9.3 + */ + private static boolean equalValues(final Object x, final Object y) { + final JSType xType = JSType.ofNoFunction(x); + final JSType yType = JSType.ofNoFunction(y); - if (xType == JSType.UNDEFINED || xType == JSType.NULL) { - return true; - } + if (xType == yType) { + return equalSameTypeValues(x, y, xType); + } - if (xType == JSType.NUMBER) { - final double xVal = ((Number)x).doubleValue(); - final double yVal = ((Number)y).doubleValue(); - if (Double.isNaN(xVal) || Double.isNaN(yVal)) { - return false; - } + return equalDifferentTypeValues(x, y, xType, yType); + } - return xVal == yVal; - } + /** + * Extracted portion of {@link #equals(Object, Object)} and {@link #strictEquals(Object, Object)} that compares + * values belonging to the same JSType. + * @param x one value + * @param y another value + * @param type the common type for the values + * @return true if they're equal + */ + private static boolean equalSameTypeValues(final Object x, final Object y, final JSType type) { + if (type == JSType.UNDEFINED || type == JSType.NULL) { + return true; + } - if (xType == JSType.STRING) { - // String may be represented by ConsString - return x.toString().equals(y.toString()); - } + if (type == JSType.NUMBER) { + return ((Number)x).doubleValue() == ((Number)y).doubleValue(); + } - if (xType == JSType.BOOLEAN) { - // Boolean comparison - return x.equals(y); - } + if (type == JSType.STRING) { + // String may be represented by ConsString + return x.toString().equals(y.toString()); + } - return x == y; + if (type == JSType.BOOLEAN) { + return ((Boolean)x).booleanValue() == ((Boolean)y).booleanValue(); } - if ((xType == JSType.UNDEFINED && yType == JSType.NULL) || - (xType == JSType.NULL && yType == JSType.UNDEFINED)) { + return x == y; + } + + /** + * Extracted portion of {@link #equals(Object, Object)} that compares values belonging to different JSTypes. + * @param x one value + * @param y another value + * @param xType the type for the value x + * @param yType the type for the value y + * @return true if they're equal + */ + private static boolean equalDifferentTypeValues(final Object x, final Object y, final JSType xType, final JSType yType) { + if (xType == JSType.UNDEFINED && yType == JSType.NULL || xType == JSType.NULL && yType == JSType.UNDEFINED) { return true; } if (xType == JSType.NUMBER && yType == JSType.STRING) { - return EQ(x, JSType.toNumber(y)); + return equals(x, JSType.toNumber(y)); } if (xType == JSType.STRING && yType == JSType.NUMBER) { - return EQ(JSType.toNumber(x), y); + return equals(JSType.toNumber(x), y); } if (xType == JSType.BOOLEAN) { - return EQ(JSType.toNumber(x), y); + return equals(JSType.toNumber(x), y); } if (yType == JSType.BOOLEAN) { - return EQ(x, JSType.toNumber(y)); + return equals(x, JSType.toNumber(y)); } - if ((xType == JSType.STRING || xType == JSType.NUMBER) && - (y instanceof ScriptObject)) { - return EQ(x, JSType.toPrimitive(y)); + if ((xType == JSType.STRING || xType == JSType.NUMBER) && y instanceof ScriptObject) { + return equals(x, JSType.toPrimitive(y)); } - if ((x instanceof ScriptObject) && - (yType == JSType.STRING || yType == JSType.NUMBER)) { - return EQ(JSType.toPrimitive(x), y); + if (x instanceof ScriptObject && (yType == JSType.STRING || yType == JSType.NUMBER)) { + return equals(JSType.toPrimitive(x), y); } return false; @@ -781,39 +841,17 @@ public final class ScriptRuntime { /** ECMA 11.9.6 The Strict Equality Comparison Algorithm */ private static boolean strictEquals(final Object x, final Object y) { - final JSType xType = JSType.of(x); - final JSType yType = JSType.of(y); + // NOTE: you might be tempted to do a quick x == y comparison. Remember, though, that any Double object having + // NaN value is not equal to itself by value even though it is referentially. + + final JSType xType = JSType.ofNoFunction(x); + final JSType yType = JSType.ofNoFunction(y); if (xType != yType) { return false; } - if (xType == JSType.UNDEFINED || xType == JSType.NULL) { - return true; - } - - if (xType == JSType.NUMBER) { - final double xVal = ((Number)x).doubleValue(); - final double yVal = ((Number)y).doubleValue(); - - if (Double.isNaN(xVal) || Double.isNaN(yVal)) { - return false; - } - - return xVal == yVal; - } - - if (xType == JSType.STRING) { - // String may be represented by ConsString - return x.toString().equals(y.toString()); - } - - if (xType == JSType.BOOLEAN) { - return x.equals(y); - } - - // finally, the object identity comparison - return x == y; + return equalSameTypeValues(x, y, xType); } /** @@ -825,9 +863,9 @@ public final class ScriptRuntime { * @return true if objects are equal */ public static boolean IN(final Object property, final Object obj) { - final JSType rvalType = JSType.of(obj); + final JSType rvalType = JSType.ofNoFunction(obj); - if (rvalType == JSType.OBJECT || rvalType == JSType.FUNCTION) { + if (rvalType == JSType.OBJECT) { if (obj instanceof ScriptObject) { return ((ScriptObject)obj).has(property); } @@ -884,7 +922,7 @@ public final class ScriptRuntime { */ public static boolean LT(final Object x, final Object y) { final Object value = lessThan(x, y, true); - return (value == UNDEFINED) ? false : (Boolean)value; + return value == UNDEFINED ? false : (Boolean)value; } /** @@ -897,7 +935,7 @@ public final class ScriptRuntime { */ public static boolean GT(final Object x, final Object y) { final Object value = lessThan(y, x, false); - return (value == UNDEFINED) ? false : (Boolean)value; + return value == UNDEFINED ? false : (Boolean)value; } /** @@ -910,7 +948,7 @@ public final class ScriptRuntime { */ public static boolean LE(final Object x, final Object y) { final Object value = lessThan(y, x, false); - return (!(Boolean.TRUE.equals(value) || value == UNDEFINED)); + return !(Boolean.TRUE.equals(value) || value == UNDEFINED); } /** @@ -923,7 +961,7 @@ public final class ScriptRuntime { */ public static boolean GE(final Object x, final Object y) { final Object value = lessThan(x, y, true); - return (!(Boolean.TRUE.equals(value) || value == UNDEFINED)); + return !(Boolean.TRUE.equals(value) || value == UNDEFINED); } /** ECMA 11.8.5 The Abstract Relational Comparison Algorithm */ @@ -939,9 +977,9 @@ public final class ScriptRuntime { px = JSType.toPrimitive(x, Number.class); } - if (JSType.of(px) == JSType.STRING && JSType.of(py) == JSType.STRING) { + if (JSType.ofNoFunction(px) == JSType.STRING && JSType.ofNoFunction(py) == JSType.STRING) { // May be String or ConsString - return (px.toString()).compareTo(py.toString()) < 0; + return px.toString().compareTo(py.toString()) < 0; } final double nx = JSType.toNumber(px); @@ -966,4 +1004,19 @@ public final class ScriptRuntime { return nx < ny; } + /** + * Tag a reserved name as invalidated - used when someone writes + * to a property with this name - overly conservative, but link time + * is too late to apply e.g. apply->call specialization + * @param name property name + */ + public static void invalidateReservedBuiltinName(final String name) { + final Context context = Context.getContextTrusted(); + final SwitchPoint sp = context.getBuiltinSwitchPoint(name); + assert sp != null; + if (sp != null) { + context.getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint"); + SwitchPoint.invalidateAll(new SwitchPoint[] { sp }); + } + } } |