Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improved ScriptRuntime.toPrimitive() (taken from #1611 done by @tonygermano) #1661

Merged
merged 2 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 72 additions & 16 deletions rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -3547,16 +3547,53 @@ 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, null);
}

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, 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;
if (preferredType == null) {
hint = "default";
} else if (StringClass == preferredType) {
hint = "string";
} else {
hint = "number";
}
final Object result = func.call(cx, scope, s, new Object[] {hint});
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 == null ? preferredType : 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 @@ -3599,6 +3636,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 @@ -3612,7 +3651,9 @@ public static boolean eq(Object x, Object y) {
if (y instanceof Delegator && ((Delegator) y).getDelegee() == x) {
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 Expand Up @@ -3951,18 +3992,33 @@ 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, NumberClass);
val2 = toPrimitive(val2, NumberClass);

if (val1 instanceof CharSequence) {
if (val2 instanceof CharSequence) {
return compareTo(val1.toString(), val2.toString(), op);
}

if (val2 instanceof BigInteger) {
try {
return compareTo(toBigInt(val1.toString()), (BigInteger) val2, op);
} catch (EcmaError e) {
return false;
}
}
}
if (val1 instanceof CharSequence && val2 instanceof CharSequence) {
return compareTo(val1.toString(), val2.toString(), op);
if (val1 instanceof BigInteger && val2 instanceof CharSequence) {
try {
return compareTo((BigInteger) val1, toBigInt(val2.toString()), op);
} catch (EcmaError e) {
return false;
}
}

return compare(toNumeric(val1), toNumeric(val2), op);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,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
4 changes: 2 additions & 2 deletions tests/testsrc/jstests/harmony/v8-symbols.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ function TestEquality() {
assertTrue(symbols[i] == symbols[i])
assertFalse(symbols[i] === Object(symbols[i]))
assertFalse(Object(symbols[i]) === symbols[i])
assertFalse(symbols[i] == Object(symbols[i]))
assertFalse(Object(symbols[i]) == symbols[i])
assertTrue(symbols[i] == Object(symbols[i]))
assertTrue(Object(symbols[i]) == symbols[i])
assertTrue(symbols[i] === symbols[i].valueOf())
assertTrue(symbols[i].valueOf() === symbols[i])
assertTrue(symbols[i] == symbols[i].valueOf())
Expand Down
22 changes: 5 additions & 17 deletions tests/testsrc/test262.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4604,13 +4604,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 Expand Up @@ -4979,12 +4973,9 @@ language/expressions/generators 194/290 (66.9%)
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%)

Expand Down Expand Up @@ -5027,12 +5018,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]}
Expand Down
Loading