Skip to content

Commit

Permalink
make sure, equivalentValues is used if available
Browse files Browse the repository at this point in the history
  • Loading branch information
rbri committed Nov 7, 2024
1 parent 59eb197 commit abd0eb1
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 24 deletions.
10 changes: 9 additions & 1 deletion rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -3737,7 +3737,15 @@ public static boolean eq(Object x, Object y) {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3934,34 +3934,18 @@ private void visitIfJumpEqOp(Node node, Node child, int trueGOTO, int falseGOTO)
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}

0 comments on commit abd0eb1

Please sign in to comment.