From a6c36685517b36da833e5e1319af866e943d870e Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Fri, 30 Aug 2024 22:40:18 -0400 Subject: [PATCH 01/12] Sort well-known Symbol definitions --- .../main/java/org/mozilla/javascript/NativeSymbol.java | 8 ++++---- rhino/src/main/java/org/mozilla/javascript/SymbolKey.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java b/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java index 8add15dae2..1416980902 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java @@ -32,18 +32,18 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { cx.putThreadLocal(CONSTRUCTOR_SLOT, Boolean.TRUE); try { - createStandardSymbol(cx, scope, ctor, "iterator", SymbolKey.ITERATOR); - createStandardSymbol(cx, scope, ctor, "species", SymbolKey.SPECIES); - createStandardSymbol(cx, scope, ctor, "toStringTag", SymbolKey.TO_STRING_TAG); createStandardSymbol(cx, scope, ctor, "hasInstance", SymbolKey.HAS_INSTANCE); createStandardSymbol( cx, scope, ctor, "isConcatSpreadable", SymbolKey.IS_CONCAT_SPREADABLE); createStandardSymbol(cx, scope, ctor, "isRegExp", SymbolKey.IS_REGEXP); - createStandardSymbol(cx, scope, ctor, "toPrimitive", SymbolKey.TO_PRIMITIVE); + createStandardSymbol(cx, scope, ctor, "iterator", SymbolKey.ITERATOR); createStandardSymbol(cx, scope, ctor, "match", SymbolKey.MATCH); createStandardSymbol(cx, scope, ctor, "replace", SymbolKey.REPLACE); createStandardSymbol(cx, scope, ctor, "search", SymbolKey.SEARCH); + createStandardSymbol(cx, scope, ctor, "species", SymbolKey.SPECIES); createStandardSymbol(cx, scope, ctor, "split", SymbolKey.SPLIT); + createStandardSymbol(cx, scope, ctor, "toPrimitive", SymbolKey.TO_PRIMITIVE); + createStandardSymbol(cx, scope, ctor, "toStringTag", SymbolKey.TO_STRING_TAG); createStandardSymbol(cx, scope, ctor, "unscopables", SymbolKey.UNSCOPABLES); } finally { diff --git a/rhino/src/main/java/org/mozilla/javascript/SymbolKey.java b/rhino/src/main/java/org/mozilla/javascript/SymbolKey.java index 4649777cf2..a49cba51e2 100644 --- a/rhino/src/main/java/org/mozilla/javascript/SymbolKey.java +++ b/rhino/src/main/java/org/mozilla/javascript/SymbolKey.java @@ -12,17 +12,17 @@ public class SymbolKey implements Symbol, Serializable { // These are common SymbolKeys that are equivalent to well-known symbols // defined in ECMAScript. - public static final SymbolKey ITERATOR = new SymbolKey("Symbol.iterator"); - public static final SymbolKey TO_STRING_TAG = new SymbolKey("Symbol.toStringTag"); - public static final SymbolKey SPECIES = new SymbolKey("Symbol.species"); public static final SymbolKey HAS_INSTANCE = new SymbolKey("Symbol.hasInstance"); public static final SymbolKey IS_CONCAT_SPREADABLE = new SymbolKey("Symbol.isConcatSpreadable"); public static final SymbolKey IS_REGEXP = new SymbolKey("Symbol.isRegExp"); - public static final SymbolKey TO_PRIMITIVE = new SymbolKey("Symbol.toPrimitive"); + public static final SymbolKey ITERATOR = new SymbolKey("Symbol.iterator"); public static final SymbolKey MATCH = new SymbolKey("Symbol.match"); public static final SymbolKey REPLACE = new SymbolKey("Symbol.replace"); public static final SymbolKey SEARCH = new SymbolKey("Symbol.search"); + public static final SymbolKey SPECIES = new SymbolKey("Symbol.species"); public static final SymbolKey SPLIT = new SymbolKey("Symbol.split"); + public static final SymbolKey TO_PRIMITIVE = new SymbolKey("Symbol.toPrimitive"); + public static final SymbolKey TO_STRING_TAG = new SymbolKey("Symbol.toStringTag"); public static final SymbolKey UNSCOPABLES = new SymbolKey("Symbol.unscopables"); private String name; From 400292c9448b91e6f19c3ed4defb34286116c2cc Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Fri, 30 Aug 2024 22:55:30 -0400 Subject: [PATCH 02/12] Add asyncIterator and matchAll well-known Symbols --- .../src/main/java/org/mozilla/javascript/NativeSymbol.java | 2 ++ rhino/src/main/java/org/mozilla/javascript/SymbolKey.java | 2 ++ .../org/mozilla/javascript/tests/Test262SuiteTest.java | 1 - tests/testsrc/test262.properties | 7 ++++--- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java b/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java index 1416980902..0ff9d650d7 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java @@ -32,12 +32,14 @@ public static void init(Context cx, Scriptable scope, boolean sealed) { cx.putThreadLocal(CONSTRUCTOR_SLOT, Boolean.TRUE); try { + createStandardSymbol(cx, scope, ctor, "asyncIterator", SymbolKey.ASYNC_ITERATOR); createStandardSymbol(cx, scope, ctor, "hasInstance", SymbolKey.HAS_INSTANCE); createStandardSymbol( cx, scope, ctor, "isConcatSpreadable", SymbolKey.IS_CONCAT_SPREADABLE); createStandardSymbol(cx, scope, ctor, "isRegExp", SymbolKey.IS_REGEXP); createStandardSymbol(cx, scope, ctor, "iterator", SymbolKey.ITERATOR); createStandardSymbol(cx, scope, ctor, "match", SymbolKey.MATCH); + createStandardSymbol(cx, scope, ctor, "matchAll", SymbolKey.MATCH_ALL); createStandardSymbol(cx, scope, ctor, "replace", SymbolKey.REPLACE); createStandardSymbol(cx, scope, ctor, "search", SymbolKey.SEARCH); createStandardSymbol(cx, scope, ctor, "species", SymbolKey.SPECIES); diff --git a/rhino/src/main/java/org/mozilla/javascript/SymbolKey.java b/rhino/src/main/java/org/mozilla/javascript/SymbolKey.java index a49cba51e2..5547bc10d2 100644 --- a/rhino/src/main/java/org/mozilla/javascript/SymbolKey.java +++ b/rhino/src/main/java/org/mozilla/javascript/SymbolKey.java @@ -12,11 +12,13 @@ public class SymbolKey implements Symbol, Serializable { // These are common SymbolKeys that are equivalent to well-known symbols // defined in ECMAScript. + public static final SymbolKey ASYNC_ITERATOR = new SymbolKey("Symbol.asyncIterator"); public static final SymbolKey HAS_INSTANCE = new SymbolKey("Symbol.hasInstance"); public static final SymbolKey IS_CONCAT_SPREADABLE = new SymbolKey("Symbol.isConcatSpreadable"); public static final SymbolKey IS_REGEXP = new SymbolKey("Symbol.isRegExp"); public static final SymbolKey ITERATOR = new SymbolKey("Symbol.iterator"); public static final SymbolKey MATCH = new SymbolKey("Symbol.match"); + public static final SymbolKey MATCH_ALL = new SymbolKey("Symbol.matchAll"); public static final SymbolKey REPLACE = new SymbolKey("Symbol.replace"); public static final SymbolKey SEARCH = new SymbolKey("Symbol.search"); public static final SymbolKey SPECIES = new SymbolKey("Symbol.species"); diff --git a/tests/src/test/java/org/mozilla/javascript/tests/Test262SuiteTest.java b/tests/src/test/java/org/mozilla/javascript/tests/Test262SuiteTest.java index aade747ea9..98eccb17b7 100644 --- a/tests/src/test/java/org/mozilla/javascript/tests/Test262SuiteTest.java +++ b/tests/src/test/java/org/mozilla/javascript/tests/Test262SuiteTest.java @@ -112,7 +112,6 @@ public class Test262SuiteTest { "regexp-unicode-property-escapes", "super", "String.prototype.matchAll", - "Symbol.matchAll", "tail-call-optimization", "u180e")); diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 734e5b37e0..3d4f83573b 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -2299,8 +2299,8 @@ built-ins/String 140/1182 (11.84%) built-ins/StringIteratorPrototype 0/7 (0.0%) -built-ins/Symbol 34/92 (36.96%) - asyncIterator/prop-desc.js +built-ins/Symbol 33/92 (35.87%) + asyncIterator/cross-realm.js for/cross-realm.js for/description.js for/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2310,7 +2310,7 @@ built-ins/Symbol 34/92 (36.96%) keyFor/arg-non-symbol.js keyFor/cross-realm.js keyFor/not-a-constructor.js {unsupported: [Reflect.construct]} - matchAll 2/2 (100.0%) + matchAll/cross-realm.js match/cross-realm.js prototype/description/description-symboldescriptivestring.js prototype/description/descriptor.js @@ -2334,6 +2334,7 @@ built-ins/Symbol 34/92 (36.96%) unscopables/cross-realm.js is-constructor.js {unsupported: [Reflect.construct]} + built-ins/ThrowTypeError 8/14 (57.14%) extensible.js forbidden-arguments.js From 9664e7753b4645c716b61ff75b4d777b4f458922 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Sun, 1 Sep 2024 20:13:57 -0400 Subject: [PATCH 03/12] improvements to abstract toPrototype using Symbol.toPrototype --- .../org/mozilla/javascript/NativeDate.java | 3 +- .../org/mozilla/javascript/ScriptRuntime.java | 62 ++++++++++++++++--- .../javascript/resources/Messages.properties | 3 + tests/testsrc/test262.properties | 35 ++++++++--- 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeDate.java b/rhino/src/main/java/org/mozilla/javascript/NativeDate.java index dc7601510c..e93480518a 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeDate.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeDate.java @@ -15,6 +15,7 @@ import java.util.Date; import java.util.List; import java.util.Locale; +import java.util.Optional; /** * This class implements the Date native object. See ECMA 15.9. @@ -293,7 +294,7 @@ public Object execIdCall( final String toISOString = "toISOString"; Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - Object tv = ScriptRuntime.toPrimitive(o, ScriptRuntime.NumberClass); + Object tv = ScriptRuntime.toPrimitive(o, Optional.of(ScriptRuntime.NumberClass)); if (tv instanceof Number) { double d = ((Number) tv).doubleValue(); if (Double.isNaN(d) || Double.isInfinite(d)) { diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 337f7e0bba..b04245fd6d 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -18,6 +18,7 @@ import java.util.Optional; import java.util.ResourceBundle; import java.util.function.BiConsumer; + import org.mozilla.javascript.ast.FunctionNode; import org.mozilla.javascript.v8dtoa.DoubleConversion; import org.mozilla.javascript.v8dtoa.FastDtoa; @@ -3474,16 +3475,57 @@ public static Number negate(Number val) { return -val.doubleValue(); } - public static Object toPrimitive(Object val) { - return toPrimitive(val, null); + public static Object toPrimitive(Object input) { + return toPrimitive(input, Optional.empty()); } - public static Object toPrimitive(Object val, Class typeHint) { - if (!(val instanceof Scriptable)) { - return val; + /** + * 1. If input is an Object, then + * a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). + * b. If exoticToPrim is not undefined, then + * i. If preferredType is not present, then + * 1. Let hint be "default". + * ii. Else if preferredType is string, then + * 1. Let hint be "string". + * iii. Else, + * 1. Assert: preferredType is number. + * 2. Let hint be "number". + * iv. Let result be ? Call(exoticToPrim, input, « hint »). + * v. If result is not an Object, return result. + * vi. Throw a TypeError exception. + * c. If preferredType is not present, let preferredType be number. + * d. Return ? OrdinaryToPrimitive(input, preferredType). + * 2. Return input. + * @param input + * @param preferredType + * @return + * @see + */ + public static Object toPrimitive(Object input, Optional> preferredType) { + if (!isObject(input)) { + return input; + } + final Scriptable s = (Scriptable) input; + final Object exoticToPrim = ScriptableObject.getProperty(s, SymbolKey.TO_PRIMITIVE); + if (exoticToPrim instanceof Function) { + final Function func = (Function) exoticToPrim; + final Context cx = Context.getCurrentContext(); + final Scriptable scope = func.getParentScope(); + final String hint = preferredType + .map(type -> type == StringClass ? "string" : "number") + .orElse("default"); + final Object[] args = new Object[] {hint}; + final Object result = func.call(cx, scope, s, args); + if (isObject(result)) { + throw typeErrorById("msg.cant.convert.to.primitive"); + } + return result; + } + if (!Undefined.isUndefined(exoticToPrim) && exoticToPrim != Scriptable.NOT_FOUND) { + throw notFunctionError(exoticToPrim); } - Scriptable s = (Scriptable) val; - Object result = s.getDefaultValue(typeHint); + final Class defaultValueHint = preferredType.orElse(NumberClass); + final Object result = s.getDefaultValue(defaultValueHint); if ((result instanceof Scriptable) && !isSymbol(result)) throw typeErrorById("msg.bad.default.value"); return result; @@ -3526,6 +3568,8 @@ public static boolean eq(Object x, Object y) { } } return eqNumber(b ? 1.0 : 0.0, y); + } else if (isSymbol(x) && isObject(y)) { + return eq(x, toPrimitive(y)); } else if (x instanceof Scriptable) { if (x instanceof Delegator) { x = ((Delegator) x).getDelegee(); @@ -3540,6 +3584,10 @@ public static boolean eq(Object x, Object y) { return true; } + if (isSymbol(y) && isObject(x)) { + return eq(toPrimitive(x), y); + } + if (y instanceof Scriptable) { if (x instanceof ScriptableObject) { Object test = ((ScriptableObject) x).equivalentValues(y); diff --git a/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties b/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties index 642491e89c..a7c2f5243c 100644 --- a/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties +++ b/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties @@ -668,6 +668,9 @@ msg.bigint.negative.exponent = \ msg.bigint.out.of.range.arithmetic = \ BigInt is too large. +msg.cant.convert.to.primitive = \ + Cannot convert object to primitive value. + # ScriptableObject msg.default.value =\ Cannot find default value for object. diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 3d4f83573b..92d56a3a69 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -1739,7 +1739,7 @@ built-ins/Promise 429/631 (67.99%) ~built-ins/Reflect -built-ins/RegExp 1169/1853 (63.09%) +built-ins/RegExp 1166/1853 (62.92%) CharacterClassEscapes 24/24 (100.0%) dotall 4/4 (100.0%) escape 20/20 (100.0%) @@ -1818,7 +1818,29 @@ built-ins/RegExp 1169/1853 (63.09%) prototype/sticky/name.js prototype/sticky/prop-desc.js prototype/sticky/this-val-regexp-prototype.js - prototype/Symbol.matchAll 26/26 (100.0%) + prototype/Symbol.matchAll/isregexp-called-once.js + prototype/Symbol.matchAll/isregexp-this-throws.js + prototype/Symbol.matchAll/length.js + prototype/Symbol.matchAll/name.js + prototype/Symbol.matchAll/not-a-constructor.js {unsupported: [Reflect.construct]} + prototype/Symbol.matchAll/prop-desc.js + prototype/Symbol.matchAll/regexpcreate-this-throws.js + prototype/Symbol.matchAll/species-constructor.js + prototype/Symbol.matchAll/species-constructor-get-constructor-throws.js + prototype/Symbol.matchAll/species-constructor-get-species-throws.js + prototype/Symbol.matchAll/species-constructor-is-undefined.js + prototype/Symbol.matchAll/species-constructor-species-is-null-or-undefined.js + prototype/Symbol.matchAll/species-constructor-species-throws.js + prototype/Symbol.matchAll/species-regexp-get-global-throws.js + prototype/Symbol.matchAll/species-regexp-get-unicode-throws.js + prototype/Symbol.matchAll/string-tostring.js + prototype/Symbol.matchAll/string-tostring-throws.js + prototype/Symbol.matchAll/this-get-flags.js + prototype/Symbol.matchAll/this-get-flags-throws.js + prototype/Symbol.matchAll/this-lastindex-cached.js + prototype/Symbol.matchAll/this-tolength-lastindex-throws.js + prototype/Symbol.matchAll/this-tostring-flags.js + prototype/Symbol.matchAll/this-tostring-flags-throws.js prototype/Symbol.match/builtin-infer-unicode.js prototype/Symbol.match/builtin-success-g-set-lastindex.js prototype/Symbol.match/builtin-success-g-set-lastindex-err.js @@ -2334,7 +2356,6 @@ built-ins/Symbol 33/92 (35.87%) unscopables/cross-realm.js is-constructor.js {unsupported: [Reflect.construct]} - built-ins/ThrowTypeError 8/14 (57.14%) extensible.js forbidden-arguments.js @@ -4638,13 +4659,7 @@ language/expressions/does-not-equals 0/38 (0.0%) ~language/expressions/dynamic-import -language/expressions/equals 6/47 (12.77%) - coerce-symbol-to-prim-err.js - coerce-symbol-to-prim-invocation.js - coerce-symbol-to-prim-return-obj.js - coerce-symbol-to-prim-return-prim.js - get-symbol-to-prim-err.js - to-prim-hint.js +language/expressions/equals 0/47 (0.0%) language/expressions/exponentiation 3/44 (6.82%) bigint-toprimitive.js From e17cc255a0e31355dab4aaee20af739eabd8d584 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Mon, 2 Sep 2024 22:14:42 -0400 Subject: [PATCH 04/12] Update compare to use toPrimitive. Improve symbol and bigint handling. --- .../org/mozilla/javascript/ScriptRuntime.java | 28 ++++++++++++++----- tests/testsrc/test262.properties | 14 +++------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index b04245fd6d..9e7a040600 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -3926,18 +3926,32 @@ public static boolean compare(Object val1, Object val2, int op) { if (val1 instanceof Number && val2 instanceof Number) { return compare((Number) val1, (Number) val2, op); } else { - if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { + if ((isSymbol(val1)) || (isSymbol(val2))) { throw typeErrorById("msg.compare.symbol"); } - if (val1 instanceof Scriptable) { - val1 = ((Scriptable) val1).getDefaultValue(NumberClass); - } - if (val2 instanceof Scriptable) { - val2 = ((Scriptable) val2).getDefaultValue(NumberClass); - } + val1 = toPrimitive(val1, Optional.of(NumberClass)); + val2 = toPrimitive(val2, Optional.of(NumberClass)); if (val1 instanceof CharSequence && val2 instanceof CharSequence) { return compareTo(val1.toString(), val2.toString(), op); } + if (val1 instanceof BigInteger && val2 instanceof CharSequence) { + final BigInteger ny; + try { + ny = toBigInt(val2.toString()); + } catch (EcmaError e) { + return false; + } + return compareTo((BigInteger) val1, ny, op); + } + if (val1 instanceof CharSequence && val2 instanceof BigInteger) { + final BigInteger nx; + try { + nx = toBigInt(val1.toString()); + } catch (EcmaError e) { + return false; + } + return compareTo(nx, (BigInteger) val2, op); + } return compare(toNumeric(val1), toNumeric(val2), op); } } diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 92d56a3a69..2d4bca63ca 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -5112,12 +5112,9 @@ language/expressions/generators 232/290 (80.0%) yield-star-after-newline.js yield-star-before-newline.js -language/expressions/greater-than 2/49 (4.08%) - bigint-and-incomparable-string.js - bigint-and-string.js +language/expressions/greater-than 0/49 (0.0%) -language/expressions/greater-than-or-equal 1/43 (2.33%) - bigint-and-incomparable-string.js +language/expressions/greater-than-or-equal 0/43 (0.0%) language/expressions/grouping 0/9 (0.0%) @@ -5160,12 +5157,9 @@ language/expressions/left-shift 4/45 (8.89%) bigint-wrapped-values.js order-of-evaluation.js -language/expressions/less-than 1/45 (2.22%) - bigint-and-incomparable-string.js +language/expressions/less-than 0/45 (0.0%) -language/expressions/less-than-or-equal 2/47 (4.26%) - bigint-and-incomparable-string.js - bigint-and-string.js +language/expressions/less-than-or-equal 0/47 (0.0%) language/expressions/logical-and 1/18 (5.56%) tco-right.js {unsupported: [tail-call-optimization]} From 97a5d7f9e2d9223e7841dccbfa3b1d30b19cd1a8 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Tue, 3 Sep 2024 02:34:25 -0400 Subject: [PATCH 05/12] Update toNumber, toBigInt, toString, toNumeric to use toPrimitive Also improved Symbol handling in these methods. Removed checks, warnings, and return values for non-JS objects. The warnings must have been going unnoticed, because NativeArray::js_includes had been trying to convert UniqueTag.NOT_FOUND to a length for non-array-like 'this' values, so that was corrected as well. --- .../org/mozilla/javascript/NativeArray.java | 2 +- .../org/mozilla/javascript/ScriptRuntime.java | 115 ++++++++---------- tests/testsrc/test262.properties | 89 ++++---------- 3 files changed, 72 insertions(+), 134 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeArray.java b/rhino/src/main/java/org/mozilla/javascript/NativeArray.java index d0276715d7..37447b9a37 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeArray.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeArray.java @@ -1949,7 +1949,7 @@ private static Boolean js_includes( Object compareTo = args.length > 0 ? args[0] : Undefined.instance; Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - long len = ScriptRuntime.toLength(new Object[] {getProperty(thisObj, "length")}, 0); + long len = getLengthProperty(cx, o); if (len == 0) return Boolean.FALSE; long k; diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 9e7a040600..af5fea656e 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -437,15 +437,10 @@ public static double toNumber(Object val) { if (val instanceof String) return toNumber((String) val); if (val instanceof CharSequence) return toNumber(val.toString()); if (val instanceof Boolean) return ((Boolean) val).booleanValue() ? 1 : +0.0; - if (val instanceof Symbol) throw typeErrorById("msg.not.a.number"); - if (val instanceof Scriptable) { - val = ((Scriptable) val).getDefaultValue(NumberClass); - if ((val instanceof Scriptable) && !isSymbol(val)) - throw errorWithClassName("msg.primitive.expected", val); - continue; - } - warnAboutNonJSObject(val); - return NaN; + if (isSymbol(val)) throw typeErrorById("msg.not.a.number"); + // Assert: val is an Object + val = toPrimitive(val, Optional.of(NumberClass)); + // Assert: val is a primitive } } @@ -714,56 +709,47 @@ public static double toNumber(String s) { /** Convert the value to a BigInt. */ public static BigInteger toBigInt(Object val) { - for (; ; ) { - if (val instanceof BigInteger) { - return (BigInteger) val; - } - if (val instanceof BigDecimal) { - return ((BigDecimal) val).toBigInteger(); - } - if (val instanceof Number) { - if (val instanceof Long) { - return BigInteger.valueOf(((Long) val)); - } else { - double d = ((Number) val).doubleValue(); - if (Double.isNaN(d) || Double.isInfinite(d)) { - throw rangeErrorById( - "msg.cant.convert.to.bigint.isnt.integer", toString(val)); - } - BigDecimal bd = new BigDecimal(d, MathContext.UNLIMITED); - try { - return bd.toBigIntegerExact(); - } catch (ArithmeticException e) { - throw rangeErrorById( - "msg.cant.convert.to.bigint.isnt.integer", toString(val)); - } + val = toPrimitive(val, Optional.of(NumberClass)); + if (val instanceof BigInteger) { + return (BigInteger) val; + } + if (val instanceof BigDecimal) { + return ((BigDecimal) val).toBigInteger(); + } + if (val instanceof Number) { + if (val instanceof Long) { + return BigInteger.valueOf(((Long) val)); + } else { + double d = ((Number) val).doubleValue(); + if (Double.isNaN(d) || Double.isInfinite(d)) { + throw rangeErrorById( + "msg.cant.convert.to.bigint.isnt.integer", toString(val)); } - } - if (val == null || Undefined.isUndefined(val)) { - throw typeErrorById("msg.cant.convert.to.bigint", toString(val)); - } - if (val instanceof String) { - return toBigInt((String) val); - } - if (val instanceof CharSequence) { - return toBigInt(val.toString()); - } - if (val instanceof Boolean) { - return ((Boolean) val).booleanValue() ? BigInteger.ONE : BigInteger.ZERO; - } - if (val instanceof Symbol) { - throw typeErrorById("msg.cant.convert.to.bigint", toString(val)); - } - if (val instanceof Scriptable) { - val = ((Scriptable) val).getDefaultValue(BigIntegerClass); - if ((val instanceof Scriptable) && !isSymbol(val)) { - throw errorWithClassName("msg.primitive.expected", val); + BigDecimal bd = new BigDecimal(d, MathContext.UNLIMITED); + try { + return bd.toBigIntegerExact(); + } catch (ArithmeticException e) { + throw rangeErrorById( + "msg.cant.convert.to.bigint.isnt.integer", toString(val)); } - continue; } - warnAboutNonJSObject(val); - return BigInteger.ZERO; } + if (val == null || Undefined.isUndefined(val)) { + throw typeErrorById("msg.cant.convert.to.bigint", toString(val)); + } + if (val instanceof String) { + return toBigInt((String) val); + } + if (val instanceof CharSequence) { + return toBigInt(val.toString()); + } + if (val instanceof Boolean) { + return ((Boolean) val).booleanValue() ? BigInteger.ONE : BigInteger.ZERO; + } + if (isSymbol(val)) { + throw typeErrorById("msg.cant.convert.to.bigint", toString(val)); + } + throw errorWithClassName("msg.primitive.expected", val); } /** ToBigInt applied to the String type */ @@ -842,6 +828,7 @@ public static BigInteger toBigInt(String s) { *

See ECMA 7.1.3 (v11.0). */ public static Number toNumeric(Object val) { + val = toPrimitive(val, Optional.of(NumberClass)); if (val instanceof Number) { return (Number) val; } @@ -1028,24 +1015,22 @@ public static String toString(Object val) { return val.toString(); } if (val instanceof BigInteger) { - return val.toString(); + return ((BigInteger) val).toString(10); } if (val instanceof Number) { // XXX should we just teach NativeNumber.stringValue() // about Numbers? return numberToString(((Number) val).doubleValue(), 10); } - if (val instanceof Symbol) { - throw typeErrorById("msg.not.a.string"); + if (val instanceof Boolean) { + return val.toString(); } - if (val instanceof Scriptable) { - val = ((Scriptable) val).getDefaultValue(StringClass); - if ((val instanceof Scriptable) && !isSymbol(val)) { - throw errorWithClassName("msg.primitive.expected", val); - } - continue; + if (isSymbol(val)) { + throw typeErrorById("msg.not.a.string"); } - return val.toString(); + // Assert: val is an Object + val = toPrimitive(val, Optional.of(StringClass)); + // Assert: val is a primitive } } diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 2d4bca63ca..26f4370a61 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -26,7 +26,7 @@ harness 22/115 (19.13%) isConstructor.js {unsupported: [Reflect.construct]} nativeFunctionMatcher.js -built-ins/Array 362/3055 (11.85%) +built-ins/Array 361/3055 (11.82%) fromAsync 94/94 (100.0%) from/calling-from-valid-1-noStrict.js non-strict Spec pretty clearly says this should be undefined from/elements-deleted-after.js Checking to see if length changed, but spec says it should not @@ -134,7 +134,6 @@ built-ins/Array 362/3055 (11.85%) prototype/find/resizable-buffer.js prototype/find/resizable-buffer-grow-mid-iteration.js prototype/find/resizable-buffer-shrink-mid-iteration.js - prototype/flatMap/array-like-objects-poisoned-length.js prototype/flatMap/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/flatMap/proxy-access-count.js prototype/flatMap/target-array-non-extensible.js @@ -361,17 +360,15 @@ built-ins/ArrayIteratorPrototype 1/27 (3.7%) ~built-ins/Atomics -built-ins/BigInt 21/75 (28.0%) +built-ins/BigInt 18/75 (24.0%) asIntN/bigint-tobigint-errors.js asIntN/bigint-tobigint-toprimitive.js - asIntN/bigint-tobigint-wrapped-values.js asIntN/bits-toindex-errors.js asIntN/bits-toindex-toprimitive.js asIntN/bits-toindex-wrapped-values.js asIntN/not-a-constructor.js {unsupported: [Reflect.construct]} asUintN/bigint-tobigint-errors.js asUintN/bigint-tobigint-toprimitive.js - asUintN/bigint-tobigint-wrapped-values.js asUintN/bits-toindex-errors.js asUintN/bits-toindex-toprimitive.js asUintN/bits-toindex-wrapped-values.js @@ -380,7 +377,6 @@ built-ins/BigInt 21/75 (28.0%) prototype/toString/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/toString/prototype-call.js Check IsInteger in ES2020, not IsSafeInteger, https://github.com/tc39/test262/commit/bf1b79d65a760a5f03df1198557da2d010f8f397#diff-3ecd6a0c50da5c8f8eff723afb6182a889b7315d99545b055559e22d302cc453 prototype/valueOf/not-a-constructor.js {unsupported: [Reflect.construct]} - constructor-coercion.js is-a-constructor.js {unsupported: [Reflect.construct]} wrapper-object-ordinary-toprimitive.js @@ -1098,7 +1094,7 @@ built-ins/Number 24/335 (7.16%) S9.3.1_A3_T1_U180E.js {unsupported: [u180e]} S9.3.1_A3_T2_U180E.js {unsupported: [u180e]} -built-ins/Object 218/3403 (6.41%) +built-ins/Object 217/3403 (6.38%) assign/assignment-to-readonly-property-of-target-must-throw-a-typeerror-exception.js assign/not-a-constructor.js {unsupported: [Reflect.construct]} assign/source-own-prop-desc-missing.js {unsupported: [Proxy]} @@ -1163,7 +1159,6 @@ built-ins/Object 218/3403 (6.41%) freeze/proxy-with-defineProperty-handler.js {unsupported: [Proxy, Reflect]} freeze/throws-when-false.js fromEntries/not-a-constructor.js {unsupported: [Reflect.construct]} - fromEntries/to-property-key.js fromEntries/uses-keys-not-iterator.js getOwnPropertyDescriptors/not-a-constructor.js {unsupported: [Reflect.construct]} getOwnPropertyDescriptors/observable-operations.js {unsupported: [Proxy]} @@ -2210,7 +2205,7 @@ built-ins/SetIteratorPrototype 0/11 (0.0%) ~built-ins/SharedArrayBuffer -built-ins/String 140/1182 (11.84%) +built-ins/String 130/1182 (11.0%) fromCharCode/not-a-constructor.js {unsupported: [Reflect.construct]} fromCodePoint/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/charAt/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2222,12 +2217,9 @@ built-ins/String 140/1182 (11.84%) prototype/includes/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/includes/return-abrupt-from-searchstring-regexp-test.js prototype/indexOf/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype/indexOf/position-tointeger-bigint.js prototype/indexOf/position-tointeger-errors.js prototype/indexOf/position-tointeger-toprimitive.js prototype/indexOf/position-tointeger-wrapped-values.js - prototype/indexOf/searchstring-tostring-bigint.js - prototype/indexOf/searchstring-tostring-errors.js prototype/indexOf/searchstring-tostring-toprimitive.js prototype/indexOf/searchstring-tostring-wrapped-values.js prototype/isWellFormed 8/8 (100.0%) @@ -2250,7 +2242,6 @@ built-ins/String 140/1182 (11.84%) prototype/replaceAll/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/replaceAll/replaceValue-call-each-match-position.js prototype/replaceAll/replaceValue-call-matching-empty.js - prototype/replaceAll/replaceValue-value-tostring.js prototype/replaceAll/searchValue-flags-no-g-throws.js prototype/replaceAll/searchValue-flags-null-undefined-throws.js prototype/replaceAll/searchValue-flags-toString-abrupt.js @@ -2263,7 +2254,6 @@ built-ins/String 140/1182 (11.84%) prototype/replaceAll/searchValue-replacer-RegExp-call.js {unsupported: [class]} prototype/replaceAll/searchValue-replacer-RegExp-call-fn.js {unsupported: [class]} prototype/replaceAll/searchValue-tostring-regexp.js - prototype/replaceAll/this-tostring.js prototype/replace/cstm-replace-get-err.js prototype/replace/cstm-replace-invocation.js prototype/replace/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2276,7 +2266,6 @@ built-ins/String 140/1182 (11.84%) prototype/slice/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/split/cstm-split-get-err.js prototype/split/cstm-split-invocation.js - prototype/split/limit-touint32-error.js prototype/split/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/split/separator-regexp.js prototype/split/separator-tostring-error.js @@ -2299,16 +2288,12 @@ built-ins/String 140/1182 (11.84%) prototype/toWellFormed 8/8 (100.0%) prototype/trimEnd/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/trimEnd/this-value-object-toprimitive-call-err.js - prototype/trimEnd/this-value-object-toprimitive-meth-err.js prototype/trimEnd/this-value-object-toprimitive-meth-priority.js - prototype/trimEnd/this-value-object-toprimitive-returns-object-err.js prototype/trimEnd/this-value-object-tostring-meth-priority.js prototype/trimEnd/this-value-object-valueof-meth-priority.js prototype/trimStart/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/trimStart/this-value-object-toprimitive-call-err.js - prototype/trimStart/this-value-object-toprimitive-meth-err.js prototype/trimStart/this-value-object-toprimitive-meth-priority.js - prototype/trimStart/this-value-object-toprimitive-returns-object-err.js prototype/trimStart/this-value-object-tostring-meth-priority.js prototype/trimStart/this-value-object-valueof-meth-priority.js prototype/trim/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2947,7 +2932,7 @@ built-ins/TypedArray 1053/1386 (75.97%) name.js prototype.js -built-ins/TypedArrayConstructors 582/721 (80.72%) +built-ins/TypedArrayConstructors 580/721 (80.44%) BigInt64Array/prototype 4/4 (100.0%) BigInt64Array 8/8 (100.0%) BigUint64Array/prototype 4/4 (100.0%) @@ -3018,8 +3003,6 @@ built-ins/TypedArrayConstructors 582/721 (80.72%) ctors/object-arg/proto-from-ctor-realm.js {unsupported: [Reflect]} ctors/object-arg/returns.js ctors/object-arg/throws-from-property.js - ctors/object-arg/throws-setting-obj-to-primitive.js - ctors/object-arg/throws-setting-obj-to-primitive-typeerror.js ctors/object-arg/throws-setting-property.js ctors/object-arg/throws-setting-symbol-property.js ctors/object-arg/use-custom-proto-if-object.js {unsupported: [Reflect]} @@ -3267,25 +3250,13 @@ built-ins/eval 3/10 (30.0%) built-ins/global 0/29 (0.0%) -built-ins/isFinite 8/17 (47.06%) +built-ins/isFinite 2/17 (11.76%) length.js not-a-constructor.js {unsupported: [Reflect.construct]} - toprimitive-call-abrupt.js - toprimitive-get-abrupt.js - toprimitive-not-callable-throws.js - toprimitive-result-is-object-throws.js - toprimitive-result-is-symbol-throws.js - toprimitive-valid-result.js - -built-ins/isNaN 8/17 (47.06%) + +built-ins/isNaN 2/17 (11.76%) length.js not-a-constructor.js {unsupported: [Reflect.construct]} - toprimitive-call-abrupt.js - toprimitive-get-abrupt.js - toprimitive-not-callable-throws.js - toprimitive-result-is-object-throws.js - toprimitive-result-is-symbol-throws.js - toprimitive-valid-result.js built-ins/parseFloat 3/59 (5.08%) not-a-constructor.js {unsupported: [Reflect.construct]} @@ -4407,25 +4378,18 @@ language/expressions/async-arrow-function 44/60 (73.33%) ~language/expressions/await -language/expressions/bitwise-and 4/30 (13.33%) - bigint-non-primitive.js +language/expressions/bitwise-and 2/30 (6.67%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js -language/expressions/bitwise-not 1/16 (6.25%) - bigint-non-primitive.js +language/expressions/bitwise-not 0/16 (0.0%) -language/expressions/bitwise-or 4/30 (13.33%) - bigint-non-primitive.js +language/expressions/bitwise-or 2/30 (6.67%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js -language/expressions/bitwise-xor 4/30 (13.33%) - bigint-non-primitive.js +language/expressions/bitwise-xor 2/30 (6.67%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js language/expressions/call 60/92 (65.22%) @@ -4650,9 +4614,8 @@ language/expressions/delete 5/67 (7.46%) super-property-method.js {unsupported: [class]} super-property-null-base.js {unsupported: [class]} -language/expressions/division 3/45 (6.67%) +language/expressions/division 2/45 (4.44%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js language/expressions/does-not-equals 0/38 (0.0%) @@ -4661,9 +4624,8 @@ language/expressions/does-not-equals 0/38 (0.0%) language/expressions/equals 0/47 (0.0%) -language/expressions/exponentiation 3/44 (6.82%) +language/expressions/exponentiation 2/44 (4.55%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js language/expressions/function 214/264 (81.06%) @@ -5151,10 +5113,8 @@ language/expressions/instanceof 7/43 (16.28%) symbol-hasinstance-not-callable.js symbol-hasinstance-to-boolean.js -language/expressions/left-shift 4/45 (8.89%) - bigint-non-primitive.js +language/expressions/left-shift 2/45 (4.44%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js language/expressions/less-than 0/45 (0.0%) @@ -5226,14 +5186,12 @@ language/expressions/logical-not 0/19 (0.0%) language/expressions/logical-or 1/18 (5.56%) tco-right.js {unsupported: [tail-call-optimization]} -language/expressions/modulus 3/40 (7.5%) +language/expressions/modulus 2/40 (5.0%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js -language/expressions/multiplication 3/40 (7.5%) +language/expressions/multiplication 2/40 (5.0%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js language/expressions/new 41/59 (69.49%) @@ -6199,19 +6157,16 @@ language/expressions/property-accessors 0/21 (0.0%) language/expressions/relational 0/1 (0.0%) -language/expressions/right-shift 4/37 (10.81%) - bigint-non-primitive.js +language/expressions/right-shift 2/37 (5.41%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js language/expressions/strict-does-not-equals 0/30 (0.0%) language/expressions/strict-equals 0/30 (0.0%) -language/expressions/subtraction 3/38 (7.89%) +language/expressions/subtraction 2/38 (5.26%) bigint-toprimitive.js - bigint-wrapped-values.js order-of-evaluation.js ~language/expressions/super @@ -6231,13 +6186,11 @@ language/expressions/typeof 2/16 (12.5%) built-in-ordinary-objects-no-call.js proxy.js {unsupported: [Proxy]} -language/expressions/unary-minus 1/14 (7.14%) - bigint-non-primitive.js +language/expressions/unary-minus 0/14 (0.0%) language/expressions/unary-plus 0/17 (0.0%) -language/expressions/unsigned-right-shift 3/45 (6.67%) - bigint-non-primitive.js +language/expressions/unsigned-right-shift 2/45 (4.44%) bigint-toprimitive.js order-of-evaluation.js From eb066d0ba30ef40d5e32ccfa4313fa817166a466 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Tue, 3 Sep 2024 04:48:25 -0400 Subject: [PATCH 06/12] Fix a bunch of instance checks for Symbol to isSymbol() And fix a spelling error. --- .../mozilla/javascript/AbstractEcmaObjectOperations.java | 2 +- .../java/org/mozilla/javascript/IdScriptableObject.java | 4 ++-- .../src/main/java/org/mozilla/javascript/NativeObject.java | 4 ++-- .../src/main/java/org/mozilla/javascript/ScriptRuntime.java | 6 +++--- rhino/src/main/java/org/mozilla/javascript/Symbol.java | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java b/rhino/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java index 26633b6428..acde62d55b 100644 --- a/rhino/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java +++ b/rhino/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java @@ -46,7 +46,7 @@ enum KEY_COERCION { static boolean hasOwnProperty(Context cx, Object o, Object property) { Scriptable obj = ScriptableObject.ensureScriptable(o); boolean result; - if (property instanceof Symbol) { + if (ScriptRuntime.isSymbol(property)) { result = ScriptableObject.ensureSymbolScriptable(o).has((Symbol) property, obj); } else { ScriptRuntime.StringIdOrIndex s = ScriptRuntime.toStringIdOrIndex(property); diff --git a/rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java b/rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java index d617123811..c65c446e96 100644 --- a/rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java +++ b/rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java @@ -182,7 +182,7 @@ final void set(int id, Scriptable start, Object value) { } else { int nameSlot = (id - 1) * SLOT_SPAN + NAME_SLOT; Object name = valueArray[nameSlot]; - if (name instanceof Symbol) { + if (ScriptRuntime.isSymbol(name)) { if (start instanceof SymbolScriptable) { ((SymbolScriptable) start).put((Symbol) name, start, value); } @@ -241,7 +241,7 @@ final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntrie names = new Object[maxId]; } names[count++] = name; - } else if (getSymbols && (name instanceof Symbol)) { + } else if (getSymbols && (ScriptRuntime.isSymbol(name))) { if (names == null) { names = new Object[maxId]; } diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeObject.java b/rhino/src/main/java/org/mozilla/javascript/NativeObject.java index ce316bbe22..721d1c0568 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeObject.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeObject.java @@ -361,7 +361,7 @@ public Object execIdCall( Integer.toString(args.length)); } Scriptable proto = (args[1] == null) ? null : ensureScriptable(args[1]); - if (proto instanceof Symbol) { + if (ScriptRuntime.isSymbol(proto)) { throw ScriptRuntime.typeErrorById( "msg.arg.not.object", ScriptRuntime.typeof(proto)); } @@ -440,7 +440,7 @@ public Object execIdCall( (key, value) -> { if (key instanceof Integer) { obj.put((Integer) key, obj, value); - } else if (key instanceof Symbol + } else if (ScriptRuntime.isSymbol(key) && obj instanceof SymbolScriptable) { ((SymbolScriptable) obj).put((Symbol) key, obj, value); } else { diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index af5fea656e..57dd13ed66 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -3000,7 +3000,7 @@ public static Object add(Object val1, Object val2, Context cx) { return test; } } - if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { + if (isSymbol(val1) || isSymbol(val2)) { throw typeErrorById("msg.not.a.number"); } if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(null); @@ -3911,7 +3911,7 @@ public static boolean compare(Object val1, Object val2, int op) { if (val1 instanceof Number && val2 instanceof Number) { return compare((Number) val1, (Number) val2, op); } else { - if ((isSymbol(val1)) || (isSymbol(val2))) { + if (isSymbol(val1) || isSymbol(val2)) { throw typeErrorById("msg.compare.symbol"); } val1 = toPrimitive(val1, Optional.of(NumberClass)); @@ -4640,7 +4640,7 @@ public static Scriptable newObjectLiteral( Object value = propertyValues[i]; if (getterSetter == 0) { - if (id instanceof Symbol) { + if (isSymbol(id)) { Symbol sym = (Symbol) id; SymbolScriptable so = (SymbolScriptable) object; so.put(sym, object, value); diff --git a/rhino/src/main/java/org/mozilla/javascript/Symbol.java b/rhino/src/main/java/org/mozilla/javascript/Symbol.java index 181708401b..8370c58d39 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Symbol.java +++ b/rhino/src/main/java/org/mozilla/javascript/Symbol.java @@ -7,7 +7,7 @@ package org.mozilla.javascript; /** - * A Symbol is a JavaScript objecy that obeys the special properties of the Symbol prototype. This + * A Symbol is a JavaScript object that obeys the special properties of the Symbol prototype. This * interface lets us possibly support multiple implementations of Symbol. * * @since 1.7.8 From 04a4d534d9f2c60680091f3e0bd804054af0a5a6 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Tue, 3 Sep 2024 07:16:52 -0400 Subject: [PATCH 07/12] rewrite plus operator to use toPrimitive --- .../javascript/IdScriptableObject.java | 2 +- .../org/mozilla/javascript/ScriptRuntime.java | 76 +++++++++---------- tests/testsrc/test262.properties | 8 +- 3 files changed, 40 insertions(+), 46 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java b/rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java index c65c446e96..62679a10e7 100644 --- a/rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java +++ b/rhino/src/main/java/org/mozilla/javascript/IdScriptableObject.java @@ -241,7 +241,7 @@ final Object[] getNames(boolean getAll, boolean getSymbols, Object[] extraEntrie names = new Object[maxId]; } names[count++] = name; - } else if (getSymbols && (ScriptRuntime.isSymbol(name))) { + } else if (getSymbols && ScriptRuntime.isSymbol(name)) { if (names == null) { names = new Object[maxId]; } diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 57dd13ed66..924f97d335 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -2969,55 +2969,53 @@ public static boolean isObject(Object value) { // implement the '~' operator inline in the caller // as "~toInt32(val)" - public static Object add(Object val1, Object val2, Context cx) { - if (val1 instanceof BigInteger && val2 instanceof BigInteger) { - return ((BigInteger) val1).add((BigInteger) val2); - } - if ((val1 instanceof Number && val2 instanceof BigInteger) - || (val1 instanceof BigInteger && val2 instanceof Number)) { - throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); - } - if (val1 instanceof Integer && val2 instanceof Integer) { - return add((Integer) val1, (Integer) val2); - } - if (val1 instanceof Number && val2 instanceof Number) { - return wrapNumber(((Number) val1).doubleValue() + ((Number) val2).doubleValue()); - } - if (val1 instanceof CharSequence && val2 instanceof CharSequence) { - // If we let this happen later, then the "getDefaultValue" logic - // undoes many optimizations - return new ConsString((CharSequence) val1, (CharSequence) val2); - } - if (val1 instanceof XMLObject) { - Object test = ((XMLObject) val1).addValues(cx, true, val2); + public static Object add(Object lval, Object rval, Context cx) { + // if lval and rval are primitive numerics of the same type, give them priority + if (lval instanceof Integer && rval instanceof Integer) { + return add((Integer) lval, (Integer) rval); + } + if (lval instanceof BigInteger && rval instanceof BigInteger) { + return ((BigInteger) lval).add((BigInteger) rval); + } + if (lval instanceof Number && !(lval instanceof BigInteger) + && rval instanceof Number && !(rval instanceof BigInteger)) { + return wrapNumber(((Number) lval).doubleValue() + ((Number) rval).doubleValue()); + } + + // spec starts here for abstract operation ApplyStringOrNumericBinaryOperator + // where opText is "+". + final Object lprim = toPrimitive(lval); + final Object rprim = toPrimitive(rval); + if (lprim instanceof CharSequence || rprim instanceof CharSequence) { + final CharSequence lstr = (lprim instanceof CharSequence) + ? (CharSequence) lprim : toString(lprim); + final CharSequence rstr = (rprim instanceof CharSequence) + ? (CharSequence) rprim : toString(rprim); + return new ConsString(lstr, rstr); + } + // e4x extension start + if (lval instanceof XMLObject) { + Object test = ((XMLObject) lval).addValues(cx, true, rval); if (test != Scriptable.NOT_FOUND) { return test; } } - if (val2 instanceof XMLObject) { - Object test = ((XMLObject) val2).addValues(cx, false, val1); + if (rval instanceof XMLObject) { + Object test = ((XMLObject) rval).addValues(cx, false, lval); if (test != Scriptable.NOT_FOUND) { return test; } } - if (isSymbol(val1) || isSymbol(val2)) { - throw typeErrorById("msg.not.a.number"); + // e4x extension end + final Number lnum = toNumeric(lval); + final Number rnum = toNumeric(rval); + if (lnum instanceof BigInteger && rnum instanceof BigInteger) { + return ((BigInteger) lnum).add((BigInteger) rnum); } - if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(null); - if (val2 instanceof Scriptable) val2 = ((Scriptable) val2).getDefaultValue(null); - if (!(val1 instanceof CharSequence) && !(val2 instanceof CharSequence)) { - Number num1 = val1 instanceof Number ? (Number) val1 : toNumeric(val1); - Number num2 = val2 instanceof Number ? (Number) val2 : toNumeric(val2); - - if (num1 instanceof BigInteger && num2 instanceof BigInteger) { - return ((BigInteger) num1).add((BigInteger) num2); - } - if (num1 instanceof BigInteger || num2 instanceof BigInteger) { - throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); - } - return num1.doubleValue() + num2.doubleValue(); + if (lnum instanceof BigInteger || rnum instanceof BigInteger) { + throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } - return new ConsString(toCharSequence(val1), toCharSequence(val2)); + return lnum.doubleValue() + rnum.doubleValue(); } /** diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 26f4370a61..7263fbc7ea 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -3852,16 +3852,12 @@ language/eval-code 257/347 (74.06%) ~language/export -language/expressions/addition 9/48 (18.75%) +language/expressions/addition 5/48 (10.42%) bigint-errors.js bigint-toprimitive.js - bigint-wrapped-values.js - coerce-symbol-to-prim-err.js coerce-symbol-to-prim-invocation.js - coerce-symbol-to-prim-return-obj.js - coerce-symbol-to-prim-return-prim.js - get-symbol-to-prim-err.js order-of-evaluation.js + S11.6.1_A2.2_T2.js language/expressions/array 41/52 (78.85%) spread-err-mult-err-expr-throws.js From 45bdf4c84f89c7a13b71eef2d17a58175eef3d3e Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Tue, 3 Sep 2024 18:48:59 -0400 Subject: [PATCH 08/12] toPrimitive related bug fixes --- .../org/mozilla/javascript/ScriptRuntime.java | 9 ++-- tests/testsrc/test262.properties | 44 ++++++------------- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 924f97d335..36d3071c1e 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -2993,6 +2993,7 @@ public static Object add(Object lval, Object rval, Context cx) { ? (CharSequence) rprim : toString(rprim); return new ConsString(lstr, rstr); } + // e4x extension start if (lval instanceof XMLObject) { Object test = ((XMLObject) lval).addValues(cx, true, rval); @@ -3007,8 +3008,10 @@ public static Object add(Object lval, Object rval, Context cx) { } } // e4x extension end - final Number lnum = toNumeric(lval); - final Number rnum = toNumeric(rval); + + // Skipping (lval = lprim, rval = rprim) and using xprim values directly. + final Number lnum = toNumeric(lprim); + final Number rnum = toNumeric(rprim); if (lnum instanceof BigInteger && rnum instanceof BigInteger) { return ((BigInteger) lnum).add((BigInteger) rnum); } @@ -3504,7 +3507,7 @@ public static Object toPrimitive(Object input, Optional> preferredType) } return result; } - if (!Undefined.isUndefined(exoticToPrim) && exoticToPrim != Scriptable.NOT_FOUND) { + if (!Undefined.isUndefined(exoticToPrim) && exoticToPrim != null && exoticToPrim != Scriptable.NOT_FOUND) { throw notFunctionError(exoticToPrim); } final Class defaultValueHint = preferredType.orElse(NumberClass); diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 7263fbc7ea..2806d9682e 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -360,17 +360,13 @@ built-ins/ArrayIteratorPrototype 1/27 (3.7%) ~built-ins/Atomics -built-ins/BigInt 18/75 (24.0%) +built-ins/BigInt 14/75 (18.67%) asIntN/bigint-tobigint-errors.js - asIntN/bigint-tobigint-toprimitive.js asIntN/bits-toindex-errors.js - asIntN/bits-toindex-toprimitive.js asIntN/bits-toindex-wrapped-values.js asIntN/not-a-constructor.js {unsupported: [Reflect.construct]} asUintN/bigint-tobigint-errors.js - asUintN/bigint-tobigint-toprimitive.js asUintN/bits-toindex-errors.js - asUintN/bits-toindex-toprimitive.js asUintN/bits-toindex-wrapped-values.js asUintN/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/toLocaleString/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2205,7 +2201,7 @@ built-ins/SetIteratorPrototype 0/11 (0.0%) ~built-ins/SharedArrayBuffer -built-ins/String 130/1182 (11.0%) +built-ins/String 128/1182 (10.83%) fromCharCode/not-a-constructor.js {unsupported: [Reflect.construct]} fromCodePoint/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/charAt/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2218,9 +2214,7 @@ built-ins/String 130/1182 (11.0%) prototype/includes/return-abrupt-from-searchstring-regexp-test.js prototype/indexOf/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/indexOf/position-tointeger-errors.js - prototype/indexOf/position-tointeger-toprimitive.js prototype/indexOf/position-tointeger-wrapped-values.js - prototype/indexOf/searchstring-tostring-toprimitive.js prototype/indexOf/searchstring-tostring-wrapped-values.js prototype/isWellFormed 8/8 (100.0%) prototype/lastIndexOf/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -3852,10 +3846,8 @@ language/eval-code 257/347 (74.06%) ~language/export -language/expressions/addition 5/48 (10.42%) +language/expressions/addition 3/48 (6.25%) bigint-errors.js - bigint-toprimitive.js - coerce-symbol-to-prim-invocation.js order-of-evaluation.js S11.6.1_A2.2_T2.js @@ -4374,18 +4366,15 @@ language/expressions/async-arrow-function 44/60 (73.33%) ~language/expressions/await -language/expressions/bitwise-and 2/30 (6.67%) - bigint-toprimitive.js +language/expressions/bitwise-and 1/30 (3.33%) order-of-evaluation.js language/expressions/bitwise-not 0/16 (0.0%) -language/expressions/bitwise-or 2/30 (6.67%) - bigint-toprimitive.js +language/expressions/bitwise-or 1/30 (3.33%) order-of-evaluation.js -language/expressions/bitwise-xor 2/30 (6.67%) - bigint-toprimitive.js +language/expressions/bitwise-xor 1/30 (3.33%) order-of-evaluation.js language/expressions/call 60/92 (65.22%) @@ -4610,8 +4599,7 @@ language/expressions/delete 5/67 (7.46%) super-property-method.js {unsupported: [class]} super-property-null-base.js {unsupported: [class]} -language/expressions/division 2/45 (4.44%) - bigint-toprimitive.js +language/expressions/division 1/45 (2.22%) order-of-evaluation.js language/expressions/does-not-equals 0/38 (0.0%) @@ -4620,8 +4608,7 @@ language/expressions/does-not-equals 0/38 (0.0%) language/expressions/equals 0/47 (0.0%) -language/expressions/exponentiation 2/44 (4.55%) - bigint-toprimitive.js +language/expressions/exponentiation 1/44 (2.27%) order-of-evaluation.js language/expressions/function 214/264 (81.06%) @@ -5109,8 +5096,7 @@ language/expressions/instanceof 7/43 (16.28%) symbol-hasinstance-not-callable.js symbol-hasinstance-to-boolean.js -language/expressions/left-shift 2/45 (4.44%) - bigint-toprimitive.js +language/expressions/left-shift 1/45 (2.22%) order-of-evaluation.js language/expressions/less-than 0/45 (0.0%) @@ -5182,12 +5168,10 @@ language/expressions/logical-not 0/19 (0.0%) language/expressions/logical-or 1/18 (5.56%) tco-right.js {unsupported: [tail-call-optimization]} -language/expressions/modulus 2/40 (5.0%) - bigint-toprimitive.js +language/expressions/modulus 1/40 (2.5%) order-of-evaluation.js -language/expressions/multiplication 2/40 (5.0%) - bigint-toprimitive.js +language/expressions/multiplication 1/40 (2.5%) order-of-evaluation.js language/expressions/new 41/59 (69.49%) @@ -6153,16 +6137,14 @@ language/expressions/property-accessors 0/21 (0.0%) language/expressions/relational 0/1 (0.0%) -language/expressions/right-shift 2/37 (5.41%) - bigint-toprimitive.js +language/expressions/right-shift 1/37 (2.7%) order-of-evaluation.js language/expressions/strict-does-not-equals 0/30 (0.0%) language/expressions/strict-equals 0/30 (0.0%) -language/expressions/subtraction 2/38 (5.26%) - bigint-toprimitive.js +language/expressions/subtraction 1/38 (2.63%) order-of-evaluation.js ~language/expressions/super From 8584fcc3bd346f7e142a160d92419f07ec06f3b8 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Tue, 3 Sep 2024 23:25:26 -0400 Subject: [PATCH 09/12] Update NativeDate::jsConstructor to call toPrimitive ScriptRuntime.toPrimitive(Object, Class) with a nullable instead of Optional type hint has also been added back to ScriptRuntime as a deprecated method. --- .../org/mozilla/javascript/NativeDate.java | 18 ++++++++---------- .../org/mozilla/javascript/ScriptRuntime.java | 6 ++++++ tests/testsrc/test262.properties | 13 +------------ 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeDate.java b/rhino/src/main/java/org/mozilla/javascript/NativeDate.java index e93480518a..0b38628560 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeDate.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeDate.java @@ -1374,21 +1374,19 @@ private static Object jsConstructor(Context cx, Object[] args) { // if called with just one arg - if (args.length == 1) { - Object arg0 = args[0]; - if (arg0 instanceof NativeDate) { - obj.date = ((NativeDate) arg0).date; + final Object value = args[0]; + if (value instanceof NativeDate) { + obj.date = ((NativeDate) value).date; return obj; } - if (arg0 instanceof Scriptable) { - arg0 = ((Scriptable) arg0).getDefaultValue(null); - } - double date; - if (arg0 instanceof CharSequence) { + final Object v = ScriptRuntime.toPrimitive(value); + final double date; + if (v instanceof CharSequence) { // it's a string; parse it. - date = date_parseString(cx, arg0.toString()); + date = date_parseString(cx, v.toString()); } else { // if it's not a string, use it as a millisecond date - date = ScriptRuntime.toNumber(arg0); + date = ScriptRuntime.toNumber(v); } obj.date = TimeClip(date); return obj; diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 36d3071c1e..ea09453b45 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -3465,6 +3465,12 @@ public static Object toPrimitive(Object input) { return toPrimitive(input, Optional.empty()); } + /** @deprecated Use {@link #toPrimitive(Object, Optional)} instead */ + @Deprecated + public static Object toPrimitive(Object input, Class preferredType) { + return toPrimitive(input, Optional.ofNullable(preferredType)); + } + /** * 1. If input is an Object, then * a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 2806d9682e..5eac9ee2ca 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -637,7 +637,7 @@ built-ins/DataView 254/550 (46.18%) toindex-bytelength-sab.js {unsupported: [SharedArrayBuffer]} toindex-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} -built-ins/Date 90/770 (11.69%) +built-ins/Date 79/770 (10.26%) now/not-a-constructor.js {unsupported: [Reflect.construct]} parse/not-a-constructor.js {unsupported: [Reflect.construct]} parse/year-zero.js @@ -716,17 +716,6 @@ built-ins/Date 90/770 (11.69%) proto-from-ctor-realm-two.js {unsupported: [Reflect]} proto-from-ctor-realm-zero.js {unsupported: [Reflect]} subclassing.js {unsupported: [Reflect]} - value-get-symbol-to-prim-err.js - value-symbol-to-prim-err.js - value-symbol-to-prim-invocation.js - value-symbol-to-prim-return-obj.js - value-symbol-to-prim-return-prim.js - value-to-primitive-call.js - value-to-primitive-call-err.js - value-to-primitive-get-meth-err.js - value-to-primitive-result-faulty.js - value-to-primitive-result-non-string-prim.js - value-to-primitive-result-string.js year-zero.js built-ins/Error 6/41 (14.63%) From e31e8938cb01ff21e6b55115a0599237bd1699ef Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Thu, 5 Sep 2024 00:49:13 -0400 Subject: [PATCH 10/12] Implement Date.prototype[Symbol.toPrimitive] Also, updated Symbol.prototype[Symbol.toPrimitive] to standardize the function name to what is in the spec. --- .../org/mozilla/javascript/NativeDate.java | 35 ++++++++++++++++++- .../org/mozilla/javascript/NativeSymbol.java | 2 +- .../javascript/resources/Messages.properties | 4 +++ tests/testsrc/test262.properties | 21 +++-------- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeDate.java b/rhino/src/main/java/org/mozilla/javascript/NativeDate.java index 0b38628560..f5b1029177 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeDate.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeDate.java @@ -255,6 +255,10 @@ protected void initPrototypeId(int id) { arity = 1; s = "toJSON"; break; + case SymbolId_toPrimitive: + initPrototypeMethod( + DATE_TAG, id, SymbolKey.TO_PRIMITIVE, "[Symbol.toPrimitive]", 1); + return; default: throw new IllegalArgumentException(String.valueOf(id)); } @@ -323,6 +327,26 @@ public Object execIdCall( } return result; } + case SymbolId_toPrimitive: + { + Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); + final Object arg0 = args.length > 0 ? args[0] : Undefined.instance; + final Optional hint = + Optional.ofNullable(arg0 instanceof CharSequence ? arg0.toString() : null); + final Class tryFirst = hint + .map(h -> { + if (h.equals("string") || h.equals("default")) { + return ScriptRuntime.StringClass; + } else if (h.equals("number")) { + return ScriptRuntime.NumberClass; + } + return null; + }) + .orElseThrow(() -> ScriptRuntime.typeErrorById( + "msg.invalid.toprimitive.hint", + ScriptRuntime.toString(arg0))); + return ScriptableObject.getDefaultValue(o, tryFirst); + } } // The rest of Date.prototype methods require thisObj to be Date @@ -1898,6 +1922,14 @@ protected int findPrototypeId(String s) { return id; } + @Override + protected int findPrototypeId(Symbol key) { + if (SymbolKey.TO_PRIMITIVE.equals(key)) { + return SymbolId_toPrimitive; + } + return 0; + } + private static final int ConstructorId_now = -3, ConstructorId_parse = -2, ConstructorId_UTC = -1, @@ -1948,7 +1980,8 @@ protected int findPrototypeId(String s) { Id_setYear = 45, Id_toISOString = 46, Id_toJSON = 47, - MAX_PROTOTYPE_ID = Id_toJSON; + SymbolId_toPrimitive = 48, + MAX_PROTOTYPE_ID = SymbolId_toPrimitive; private static final int Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6 diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java b/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java index 0ff9d650d7..c19479707b 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeSymbol.java @@ -166,7 +166,7 @@ protected void initPrototypeId(int id) { break; case SymbolId_toPrimitive: initPrototypeMethod( - CLASS_NAME, id, SymbolKey.TO_PRIMITIVE, "Symbol.toPrimitive", 1); + CLASS_NAME, id, SymbolKey.TO_PRIMITIVE, "[Symbol.toPrimitive]", 1); break; default: super.initPrototypeId(id); diff --git a/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties b/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties index a7c2f5243c..003588f044 100644 --- a/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties +++ b/rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties @@ -257,6 +257,10 @@ msg.invalid.date =\ msg.toisostring.must.return.primitive =\ toISOString must return a primitive value, but instead returned "{0}" +# NativeSymbol +msg.invalid.toprimitive.hint =\ + [Symbol.toPrimitive]: expected "string", "number", or "default", but got {0} + # NativeJSON msg.json.cant.serialize =\ Do not know how to serialize a {0} diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 5eac9ee2ca..0e0263a971 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -637,7 +637,7 @@ built-ins/DataView 254/550 (46.18%) toindex-bytelength-sab.js {unsupported: [SharedArrayBuffer]} toindex-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} -built-ins/Date 79/770 (10.26%) +built-ins/Date 68/770 (8.83%) now/not-a-constructor.js {unsupported: [Reflect.construct]} parse/not-a-constructor.js {unsupported: [Reflect.construct]} parse/year-zero.js @@ -675,18 +675,7 @@ built-ins/Date 79/770 (10.26%) prototype/setUTCMinutes/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/setUTCMonth/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/setUTCSeconds/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype/Symbol.toPrimitive/hint-default-first-invalid.js - prototype/Symbol.toPrimitive/hint-default-first-non-callable.js - prototype/Symbol.toPrimitive/hint-default-first-valid.js - prototype/Symbol.toPrimitive/hint-invalid.js - prototype/Symbol.toPrimitive/hint-number-first-invalid.js - prototype/Symbol.toPrimitive/hint-number-first-non-callable.js - prototype/Symbol.toPrimitive/hint-number-first-valid.js - prototype/Symbol.toPrimitive/hint-string-first-invalid.js - prototype/Symbol.toPrimitive/hint-string-first-non-callable.js - prototype/Symbol.toPrimitive/hint-string-first-valid.js - prototype/Symbol.toPrimitive/length.js - prototype/Symbol.toPrimitive/name.js + prototype/Symbol.toPrimitive/called-as-function.js prototype/Symbol.toPrimitive/prop-desc.js prototype/Symbol.toPrimitive/this-val-non-obj.js prototype/toDateString/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2289,7 +2278,7 @@ built-ins/String 128/1182 (10.83%) built-ins/StringIteratorPrototype 0/7 (0.0%) -built-ins/Symbol 33/92 (35.87%) +built-ins/Symbol 32/92 (34.78%) asyncIterator/cross-realm.js for/cross-realm.js for/description.js @@ -2308,7 +2297,6 @@ built-ins/Symbol 33/92 (35.87%) prototype/description/this-val-non-symbol.js prototype/description/this-val-symbol.js prototype/description/wrapper.js - prototype/Symbol.toPrimitive/name.js prototype/Symbol.toPrimitive/prop-desc.js prototype/Symbol.toPrimitive/redefined-symbol-wrapper-ordinary-toprimitive.js prototype/Symbol.toPrimitive/removed-symbol-wrapper-ordinary-toprimitive.js @@ -3835,10 +3823,9 @@ language/eval-code 257/347 (74.06%) ~language/export -language/expressions/addition 3/48 (6.25%) +language/expressions/addition 2/48 (4.17%) bigint-errors.js order-of-evaluation.js - S11.6.1_A2.2_T2.js language/expressions/array 41/52 (78.85%) spread-err-mult-err-expr-throws.js From 835cac35faefcb38cd7d10cf3b98f444549067e4 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Thu, 5 Sep 2024 15:49:05 -0400 Subject: [PATCH 11/12] spotlessApply --- .../org/mozilla/javascript/NativeDate.java | 33 ++++++++------- .../org/mozilla/javascript/ScriptRuntime.java | 42 ++++++++++--------- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/NativeDate.java b/rhino/src/main/java/org/mozilla/javascript/NativeDate.java index f5b1029177..b231de28b2 100644 --- a/rhino/src/main/java/org/mozilla/javascript/NativeDate.java +++ b/rhino/src/main/java/org/mozilla/javascript/NativeDate.java @@ -298,7 +298,8 @@ public Object execIdCall( final String toISOString = "toISOString"; Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); - Object tv = ScriptRuntime.toPrimitive(o, Optional.of(ScriptRuntime.NumberClass)); + Object tv = + ScriptRuntime.toPrimitive(o, Optional.of(ScriptRuntime.NumberClass)); if (tv instanceof Number) { double d = ((Number) tv).doubleValue(); if (Double.isNaN(d) || Double.isInfinite(d)) { @@ -332,19 +333,23 @@ public Object execIdCall( Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj); final Object arg0 = args.length > 0 ? args[0] : Undefined.instance; final Optional hint = - Optional.ofNullable(arg0 instanceof CharSequence ? arg0.toString() : null); - final Class tryFirst = hint - .map(h -> { - if (h.equals("string") || h.equals("default")) { - return ScriptRuntime.StringClass; - } else if (h.equals("number")) { - return ScriptRuntime.NumberClass; - } - return null; - }) - .orElseThrow(() -> ScriptRuntime.typeErrorById( - "msg.invalid.toprimitive.hint", - ScriptRuntime.toString(arg0))); + Optional.ofNullable( + arg0 instanceof CharSequence ? arg0.toString() : null); + final Class tryFirst = + hint.map( + h -> { + if (h.equals("string") || h.equals("default")) { + return ScriptRuntime.StringClass; + } else if (h.equals("number")) { + return ScriptRuntime.NumberClass; + } + return null; + }) + .orElseThrow( + () -> + ScriptRuntime.typeErrorById( + "msg.invalid.toprimitive.hint", + ScriptRuntime.toString(arg0))); return ScriptableObject.getDefaultValue(o, tryFirst); } } diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index ea09453b45..611f02a7ca 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -18,7 +18,6 @@ import java.util.Optional; import java.util.ResourceBundle; import java.util.function.BiConsumer; - import org.mozilla.javascript.ast.FunctionNode; import org.mozilla.javascript.v8dtoa.DoubleConversion; import org.mozilla.javascript.v8dtoa.FastDtoa; @@ -722,15 +721,13 @@ public static BigInteger toBigInt(Object val) { } else { double d = ((Number) val).doubleValue(); if (Double.isNaN(d) || Double.isInfinite(d)) { - throw rangeErrorById( - "msg.cant.convert.to.bigint.isnt.integer", toString(val)); + throw rangeErrorById("msg.cant.convert.to.bigint.isnt.integer", toString(val)); } BigDecimal bd = new BigDecimal(d, MathContext.UNLIMITED); try { return bd.toBigIntegerExact(); } catch (ArithmeticException e) { - throw rangeErrorById( - "msg.cant.convert.to.bigint.isnt.integer", toString(val)); + throw rangeErrorById("msg.cant.convert.to.bigint.isnt.integer", toString(val)); } } } @@ -2977,23 +2974,25 @@ public static Object add(Object lval, Object rval, Context cx) { if (lval instanceof BigInteger && rval instanceof BigInteger) { return ((BigInteger) lval).add((BigInteger) rval); } - if (lval instanceof Number && !(lval instanceof BigInteger) - && rval instanceof Number && !(rval instanceof BigInteger)) { + if (lval instanceof Number + && !(lval instanceof BigInteger) + && rval instanceof Number + && !(rval instanceof BigInteger)) { return wrapNumber(((Number) lval).doubleValue() + ((Number) rval).doubleValue()); } - + // spec starts here for abstract operation ApplyStringOrNumericBinaryOperator - // where opText is "+". + // where opText is "+". final Object lprim = toPrimitive(lval); final Object rprim = toPrimitive(rval); if (lprim instanceof CharSequence || rprim instanceof CharSequence) { - final CharSequence lstr = (lprim instanceof CharSequence) - ? (CharSequence) lprim : toString(lprim); - final CharSequence rstr = (rprim instanceof CharSequence) - ? (CharSequence) rprim : toString(rprim); + final CharSequence lstr = + (lprim instanceof CharSequence) ? (CharSequence) lprim : toString(lprim); + final CharSequence rstr = + (rprim instanceof CharSequence) ? (CharSequence) rprim : toString(rprim); return new ConsString(lstr, rstr); } - + // e4x extension start if (lval instanceof XMLObject) { Object test = ((XMLObject) lval).addValues(cx, true, rval); @@ -3471,7 +3470,7 @@ public static Object toPrimitive(Object input, Class preferredType) { return toPrimitive(input, Optional.ofNullable(preferredType)); } - /** + /* * 1. If input is an Object, then * a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). * b. If exoticToPrim is not undefined, then @@ -3488,6 +3487,8 @@ public static Object toPrimitive(Object input, Class preferredType) { * c. If preferredType is not present, let preferredType be number. * d. Return ? OrdinaryToPrimitive(input, preferredType). * 2. Return input. + / + /** * @param input * @param preferredType * @return @@ -3503,9 +3504,10 @@ public static Object toPrimitive(Object input, Optional> preferredType) final Function func = (Function) exoticToPrim; final Context cx = Context.getCurrentContext(); final Scriptable scope = func.getParentScope(); - final String hint = preferredType - .map(type -> type == StringClass ? "string" : "number") - .orElse("default"); + final String hint = + preferredType + .map(type -> type == StringClass ? "string" : "number") + .orElse("default"); final Object[] args = new Object[] {hint}; final Object result = func.call(cx, scope, s, args); if (isObject(result)) { @@ -3513,7 +3515,9 @@ public static Object toPrimitive(Object input, Optional> preferredType) } return result; } - if (!Undefined.isUndefined(exoticToPrim) && exoticToPrim != null && exoticToPrim != Scriptable.NOT_FOUND) { + if (!Undefined.isUndefined(exoticToPrim) + && exoticToPrim != null + && exoticToPrim != Scriptable.NOT_FOUND) { throw notFunctionError(exoticToPrim); } final Class defaultValueHint = preferredType.orElse(NumberClass); From 40b7494ccff7455df6a5f82ed20259561f1e99a1 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Fri, 6 Sep 2024 09:50:49 -0400 Subject: [PATCH 12/12] guard Symbol.toPrimitive lookup in toPrimitive AO by language version >= ES6 --- .../org/mozilla/javascript/ScriptRuntime.java | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 611f02a7ca..9215f8d915 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -3499,26 +3499,28 @@ public static Object toPrimitive(Object input, Optional> preferredType) return input; } final Scriptable s = (Scriptable) input; - final Object exoticToPrim = ScriptableObject.getProperty(s, SymbolKey.TO_PRIMITIVE); - if (exoticToPrim instanceof Function) { - final Function func = (Function) exoticToPrim; - final Context cx = Context.getCurrentContext(); - final Scriptable scope = func.getParentScope(); - final String hint = - preferredType - .map(type -> type == StringClass ? "string" : "number") - .orElse("default"); - final Object[] args = new Object[] {hint}; - final Object result = func.call(cx, scope, s, args); - if (isObject(result)) { - throw typeErrorById("msg.cant.convert.to.primitive"); + final Context cx = Context.getCurrentContext(); + if (cx.getLanguageVersion() >= Context.VERSION_ES6) { + final Object exoticToPrim = ScriptableObject.getProperty(s, SymbolKey.TO_PRIMITIVE); + if (exoticToPrim instanceof Function) { + final Function func = (Function) exoticToPrim; + final Scriptable scope = func.getParentScope(); + final String hint = + preferredType + .map(type -> type == StringClass ? "string" : "number") + .orElse("default"); + final Object[] args = new Object[] {hint}; + final Object result = func.call(cx, scope, s, args); + if (isObject(result)) { + throw typeErrorById("msg.cant.convert.to.primitive"); + } + return result; + } + if (!Undefined.isUndefined(exoticToPrim) + && exoticToPrim != null + && exoticToPrim != Scriptable.NOT_FOUND) { + throw notFunctionError(exoticToPrim); } - return result; - } - if (!Undefined.isUndefined(exoticToPrim) - && exoticToPrim != null - && exoticToPrim != Scriptable.NOT_FOUND) { - throw notFunctionError(exoticToPrim); } final Class defaultValueHint = preferredType.orElse(NumberClass); final Object result = s.getDefaultValue(defaultValueHint);