diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/ScriptRuntime.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/ScriptRuntime.java | 191 |
1 files changed, 111 insertions, 80 deletions
diff --git a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java index e927d4fc..ae46d253 100644 --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java @@ -32,6 +32,8 @@ 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 static jdk.nashorn.internal.runtime.JSType.isString; + import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.SwitchPoint; @@ -55,7 +57,6 @@ import jdk.nashorn.internal.objects.NativeObject; import jdk.nashorn.internal.parser.Lexer; import jdk.nashorn.internal.runtime.linker.Bootstrap; - /** * Utilities to be called by JavaScript runtime API and generated classes. */ @@ -114,6 +115,11 @@ public final class ScriptRuntime { public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class); /** + * Throws a reference error for an undefined variable. + */ + public static final Call THROW_CONST_TYPE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwConstTypeError", 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); @@ -367,9 +373,9 @@ public final class ScriptRuntime { * @return prototype object after merge */ public static ScriptObject mergeScope(final ScriptObject scope) { - final ScriptObject global = scope.getProto(); - global.addBoundProperties(scope); - return global; + final ScriptObject parentScope = scope.getProto(); + parentScope.addBoundProperties(scope); + return parentScope; } /** @@ -402,6 +408,15 @@ public final class ScriptRuntime { } /** + * Throws a type error for an assignment to a const. + * + * @param name the const name + */ + public static void throwConstTypeError(final String name) { + throw typeError("assign.constant", name); + } + + /** * Call a script function as a constructor with given args. * * @param target ScriptFunction object. @@ -521,8 +536,6 @@ public final class ScriptRuntime { /** * 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 * * @param x first term * @param y second term @@ -549,8 +562,7 @@ public final class ScriptRuntime { final Object xPrim = JSType.toPrimitive(x); final Object yPrim = JSType.toPrimitive(y); - if (xPrim instanceof String || yPrim instanceof String - || xPrim instanceof ConsString || yPrim instanceof ConsString) { + if (isString(xPrim) || isString(yPrim)) { try { return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim)); } catch (final IllegalArgumentException iae) { @@ -673,6 +685,33 @@ public final class ScriptRuntime { } /** + * ECMA 11.4.1 - delete operator, implementation for slow scopes + * + * This implementation of 'delete' walks the scope chain to find the scope that contains the + * property to be deleted, then invokes delete on it. + * + * @param obj top scope object + * @param property property to delete + * @param strict are we in strict mode + * + * @return true if property was successfully found and deleted + */ + public static boolean SLOW_DELETE(final Object obj, final Object property, final Object strict) { + if (obj instanceof ScriptObject) { + ScriptObject sobj = (ScriptObject) obj; + final String key = property.toString(); + while (sobj != null && sobj.isScope()) { + final FindProperty find = sobj.findProperty(key, false); + if (find != null) { + return sobj.delete(key, Boolean.TRUE.equals(strict)); + } + sobj = sobj.getProto(); + } + } + return DELETE(obj, property, strict); + } + + /** * ECMA 11.4.1 - delete operator, special case * * This is 'delete' that always fails. We have to check strict mode and throw error. @@ -720,7 +759,7 @@ public final class ScriptRuntime { return true; } if (x instanceof ScriptObject && y instanceof ScriptObject) { - return x == y; + return false; // x != y } if (x instanceof ScriptObjectMirror || y instanceof ScriptObjectMirror) { return ScriptObjectMirror.identical(x, y); @@ -784,35 +823,53 @@ public final class ScriptRuntime { * @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) { + if (isUndefinedAndNull(xType, yType) || isUndefinedAndNull(yType, xType)) { return true; + } else if (isNumberAndString(xType, yType)) { + return equalNumberToString(x, y); + } else if (isNumberAndString(yType, xType)) { + // Can reverse order as both are primitives + return equalNumberToString(y, x); + } else if (xType == JSType.BOOLEAN) { + return equalBooleanToAny(x, y); + } else if (yType == JSType.BOOLEAN) { + // Can reverse order as y is primitive + return equalBooleanToAny(y, x); + } else if (isNumberOrStringAndObject(xType, yType)) { + return equalNumberOrStringToObject(x, y); + } else if (isNumberOrStringAndObject(yType, xType)) { + // Can reverse order as y is primitive + return equalNumberOrStringToObject(y, x); } - if (xType == JSType.NUMBER && yType == JSType.STRING) { - return equals(x, JSType.toNumber(y)); - } + return false; + } - if (xType == JSType.STRING && yType == JSType.NUMBER) { - return equals(JSType.toNumber(x), y); - } + private static boolean isUndefinedAndNull(final JSType xType, final JSType yType) { + return xType == JSType.UNDEFINED && yType == JSType.NULL; + } - if (xType == JSType.BOOLEAN) { - return equals(JSType.toNumber(x), y); - } + private static boolean isNumberAndString(final JSType xType, final JSType yType) { + return xType == JSType.NUMBER && yType == JSType.STRING; + } - if (yType == JSType.BOOLEAN) { - return equals(x, JSType.toNumber(y)); - } + private static boolean isNumberOrStringAndObject(final JSType xType, final JSType yType) { + return (xType == JSType.NUMBER || xType == JSType.STRING) && yType == JSType.OBJECT; + } - if ((xType == JSType.STRING || xType == JSType.NUMBER) && y instanceof ScriptObject) { - return equals(x, JSType.toPrimitive(y)); - } + private static boolean equalNumberToString(final Object num, final Object str) { + // Specification says comparing a number to string should be done as "equals(num, JSType.toNumber(str))". We + // can short circuit it to this as we know that "num" is a number, so it'll end up being a number-number + // comparison. + return ((Number)num).doubleValue() == JSType.toNumber(str.toString()); + } - if (x instanceof ScriptObject && (yType == JSType.STRING || yType == JSType.NUMBER)) { - return equals(JSType.toPrimitive(x), y); - } + private static boolean equalBooleanToAny(final Object bool, final Object any) { + return equals(JSType.toNumber((Boolean)bool), any); + } - return false; + private static boolean equalNumberOrStringToObject(final Object numOrStr, final Object any) { + return equals(numOrStr, JSType.toPrimitive(any)); } /** @@ -921,8 +978,15 @@ public final class ScriptRuntime { * @return true if x is less than y */ public static boolean LT(final Object x, final Object y) { - final Object value = lessThan(x, y, true); - return value == UNDEFINED ? false : (Boolean)value; + final Object px = JSType.toPrimitive(x, Number.class); + final Object py = JSType.toPrimitive(y, Number.class); + + return areBothString(px, py) ? px.toString().compareTo(py.toString()) < 0 : + JSType.toNumber(px) < JSType.toNumber(py); + } + + private static boolean areBothString(final Object x, final Object y) { + return isString(x) && isString(y); } /** @@ -934,8 +998,11 @@ public final class ScriptRuntime { * @return true if x is greater than y */ public static boolean GT(final Object x, final Object y) { - final Object value = lessThan(y, x, false); - return value == UNDEFINED ? false : (Boolean)value; + final Object px = JSType.toPrimitive(x, Number.class); + final Object py = JSType.toPrimitive(y, Number.class); + + return areBothString(px, py) ? px.toString().compareTo(py.toString()) > 0 : + JSType.toNumber(px) > JSType.toNumber(py); } /** @@ -947,8 +1014,11 @@ public final class ScriptRuntime { * @return true if x is less than or equal to y */ public static boolean LE(final Object x, final Object y) { - final Object value = lessThan(y, x, false); - return !(Boolean.TRUE.equals(value) || value == UNDEFINED); + final Object px = JSType.toPrimitive(x, Number.class); + final Object py = JSType.toPrimitive(y, Number.class); + + return areBothString(px, py) ? px.toString().compareTo(py.toString()) <= 0 : + JSType.toNumber(px) <= JSType.toNumber(py); } /** @@ -960,48 +1030,11 @@ public final class ScriptRuntime { * @return true if x is greater than or equal to y */ public static boolean GE(final Object x, final Object y) { - final Object value = lessThan(x, y, true); - return !(Boolean.TRUE.equals(value) || value == UNDEFINED); - } - - /** ECMA 11.8.5 The Abstract Relational Comparison Algorithm */ - private static Object lessThan(final Object x, final Object y, final boolean leftFirst) { - Object px, py; - - //support e.g. x < y should throw exception correctly if x or y are not numeric - if (leftFirst) { - px = JSType.toPrimitive(x, Number.class); - py = JSType.toPrimitive(y, Number.class); - } else { - py = JSType.toPrimitive(y, Number.class); - px = JSType.toPrimitive(x, Number.class); - } + final Object px = JSType.toPrimitive(x, Number.class); + final Object py = JSType.toPrimitive(y, Number.class); - if (JSType.ofNoFunction(px) == JSType.STRING && JSType.ofNoFunction(py) == JSType.STRING) { - // May be String or ConsString - return px.toString().compareTo(py.toString()) < 0; - } - - final double nx = JSType.toNumber(px); - final double ny = JSType.toNumber(py); - - if (Double.isNaN(nx) || Double.isNaN(ny)) { - return UNDEFINED; - } - - if (nx == ny) { - return false; - } - - if (nx > 0 && ny > 0 && Double.isInfinite(nx) && Double.isInfinite(ny)) { - return false; - } - - if (nx < 0 && ny < 0 && Double.isInfinite(nx) && Double.isInfinite(ny)) { - return false; - } - - return nx < ny; + return areBothString(px, py) ? px.toString().compareTo(py.toString()) >= 0 : + JSType.toNumber(px) >= JSType.toNumber(py); } /** @@ -1014,9 +1047,7 @@ public final class ScriptRuntime { 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 }); - } + context.getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint"); + SwitchPoint.invalidateAll(new SwitchPoint[] { sp }); } } |