Skip to content

Commit

Permalink
improvements to abstract toPrototype using Symbol.toPrototype
Browse files Browse the repository at this point in the history
  • Loading branch information
tonygermano committed Sep 5, 2024
1 parent 400292c commit 9664e77
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 18 deletions.
3 changes: 2 additions & 1 deletion rhino/src/main/java/org/mozilla/javascript/NativeDate.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)) {
Expand Down
62 changes: 55 additions & 7 deletions rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <a href="https://262.ecma-international.org/15.0/index.html#sec-toprimitive"></a>
*/
public static Object toPrimitive(Object input, Optional<Class<?>> 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;
Expand Down Expand Up @@ -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();
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
35 changes: 25 additions & 10 deletions tests/testsrc/test262.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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%)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 9664e77

Please sign in to comment.