make sure, equivalentValues is used if available

This commit is contained in:
Ronald Brill 2024-11-07 14:04:00 +01:00 коммит произвёл Greg Brail
Родитель 59eb197681
Коммит 740299852f
3 изменённых файлов: 102 добавлений и 24 удалений

Просмотреть файл

@ -3737,7 +3737,15 @@ public class ScriptRuntime {
if (isSymbol(y) && isObject(x)) {
return eq(toPrimitive(x), y);
}
if (y instanceof Scriptable) {
if (y == null || Undefined.isUndefined(y)) {
if (x instanceof ScriptableObject) {
Object test = ((ScriptableObject) x).equivalentValues(y);
if (test != Scriptable.NOT_FOUND) {
return ((Boolean) test).booleanValue();
}
}
return false;
} else if (y instanceof Scriptable) {
if (x instanceof ScriptableObject) {
Object test = ((ScriptableObject) x).equivalentValues(y);
if (test != Scriptable.NOT_FOUND) {

Просмотреть файл

@ -3934,34 +3934,18 @@ class BodyCodegen {
int type = node.getType();
Node rChild = child.getNext();
// Optimize if one of operands is null
if (child.getType() == Token.NULL || rChild.getType() == Token.NULL) {
// Optimize if one of operands is null; but we can't do this
// for EQ/NEQ because a ScripableObject might overwrite equivalentValues()
if (type != Token.EQ
&& type != Token.NE
&& (child.getType() == Token.NULL || rChild.getType() == Token.NULL)) {
// eq is symmetric in this case
if (child.getType() == Token.NULL) {
child = rChild;
}
generateExpression(child, node);
if (type == Token.SHEQ || type == Token.SHNE) {
int testCode = (type == Token.SHEQ) ? ByteCode.IFNULL : ByteCode.IFNONNULL;
cfw.add(testCode, trueGOTO);
} else {
if (type != Token.EQ) {
// swap false/true targets for !=
if (type != Token.NE) throw Codegen.badTree();
int tmp = trueGOTO;
trueGOTO = falseGOTO;
falseGOTO = tmp;
}
cfw.add(ByteCode.DUP);
int undefCheckLabel = cfw.acquireLabel();
cfw.add(ByteCode.IFNONNULL, undefCheckLabel);
int stack = cfw.getStackTop();
cfw.add(ByteCode.POP);
cfw.add(ByteCode.GOTO, trueGOTO);
cfw.markLabel(undefCheckLabel, stack);
Codegen.pushUndefined(cfw);
cfw.add(ByteCode.IF_ACMPEQ, trueGOTO);
}
int testCode = (type == Token.SHEQ) ? ByteCode.IFNULL : ByteCode.IFNONNULL;
cfw.add(testCode, trueGOTO);
cfw.add(ByteCode.GOTO, falseGOTO);
} else {
int child_dcp_register = nodeIsDirectCallParameter(child);

Просмотреть файл

@ -0,0 +1,86 @@
package org.mozilla.javascript.tests;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.mozilla.javascript.*;
import org.mozilla.javascript.annotations.JSConstructor;
/**
* Test cases for the {@link ScriptRuntime} support for ScriptableObject#equivalentValues(Object)
* method.
*
* @author Ronald Brill
*/
public class ScriptRuntimeEquivalentValuesTest {
@Test
public void equivalentValuesUndefined() throws Exception {
Utils.runWithAllOptimizationLevels(
cx -> {
final Scriptable scope = cx.initStandardObjects();
try {
ScriptableObject.defineClass(scope, EquivalentTesterObject.class);
} catch (Exception e) {
}
Object result =
cx.evaluateString(
scope,
"var o = new EquivalentTesterObject();"
+ "'' + (o == undefined) + ' ' + (undefined == o)",
"test",
1,
null);
assertEquals("" + cx.getOptimizationLevel(), "true true", result);
return null;
});
}
@Test
public void equivalentValuesNull() throws Exception {
Utils.runWithAllOptimizationLevels(
cx -> {
final Scriptable scope = cx.initStandardObjects();
try {
ScriptableObject.defineClass(scope, EquivalentTesterObject.class);
} catch (Exception e) {
}
Object result =
cx.evaluateString(
scope,
"var o = new EquivalentTesterObject();"
+ "'' + (o == null) + ' ' + (null == o)",
"test",
1,
null);
assertEquals("" + cx.getOptimizationLevel(), "true true", result);
return null;
});
}
public static class EquivalentTesterObject extends ScriptableObject {
public EquivalentTesterObject() {}
@Override
public String getClassName() {
return "EquivalentTesterObject";
}
@JSConstructor
public void jsConstructorMethod() {}
@Override
protected Object equivalentValues(final Object value) {
if (value == null || Undefined.isUndefined(value)) {
return Boolean.TRUE;
}
return super.equivalentValues(value);
}
}
}