package com.oracle.truffle.js.test.instrumentation;
import org.junit.Test;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.BinaryOperationTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.LiteralTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.ReadPropertyTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.UnaryOperationTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.WritePropertyTag;
import com.oracle.truffle.js.runtime.objects.Null;
import com.oracle.truffle.js.runtime.objects.Undefined;
public class BinaryOperationTest extends FineGrainedAccessTest {
@Test
public void fullAnd() {
evalAllTags("var a = 42; var b = 43; var c = a + b;");
assertGlobalVarDeclaration("a", 42);
assertGlobalVarDeclaration("b", 43);
enter(WritePropertyTag.class, (e, write) -> {
assertAttribute(e, KEY, "c");
write.input(assertGlobalObjectInput);
enter(BinaryOperationTag.class, (e2, binary) -> {
enter(ReadPropertyTag.class, (e3, prop) -> {
assertAttribute(e3, KEY, "a");
prop.input(assertGlobalObjectInput);
}).exit();
binary.input(42);
enter(ReadPropertyTag.class, (e3, prop) -> {
assertAttribute(e3, KEY, "b");
prop.input(assertGlobalObjectInput);
}).exit();
binary.input(43);
}).exit();
write.input(85);
}).exit();
}
@Test
public void leftConstantAnd() {
String src = "var a = 42; var c = 43 + a;";
evalWithTag(src, BinaryOperationTag.class);
enter(BinaryOperationTag.class, (e2, binary) -> {
binary.input(43);
binary.input(42);
}).exit();
}
@Test
public void rightConstantPlus() {
constantBinaryOperationTestRight(43, "+");
}
@Test
public void rightConstantAnd() {
constantBinaryOperationTestRight(43, "&");
}
@Test
public void rightConstantOr() {
constantBinaryOperationTestRight(43, "|");
}
@Test
public void rightConstantXor() {
constantBinaryOperationTestRight(43, "^");
}
@Test
public void rightConstantLeftShift() {
constantBinaryOperationTestRight(43, "<<");
}
@Test
public void rightConstantRightShift() {
constantBinaryOperationTestRight(43, ">>");
}
@Test
public void rightConstantUnsignedRightShift() {
constantBinaryOperationTestRight(43, ">>>");
}
@Test
public void eq() {
binaryOperationTest(42, 43, "==");
}
@Test
public void strictEq() {
binaryOperationTest(42, 43, "===");
}
@Test
public void plus() {
binaryOperationTest(42, 43, "+");
}
@Test
public void minus() {
binaryOperationTest(42, 43, "-");
}
@Test
public void mult() {
binaryOperationTest(42, 43, "*");
}
@Test
public void div() {
binaryOperationTest(42, 43, "/");
}
@Test
public void and() {
binaryOperationTest(42, 43, "&");
}
@Test
public void or() {
binaryOperationTest(42, 43, "|");
}
@Test
public void xor() {
binaryOperationTest(42, 43, "^");
}
@Test
public void leftShift() {
binaryOperationTest(42, 43, "<<");
}
@Test
public void rightShift() {
binaryOperationTest(42, 43, ">>");
}
@Test
public void unsignedRightShift() {
binaryOperationTest(42, 43, ">>>");
}
@Test
public void desugaredBinaryNeq() {
binaryOperationTest("!=", "!", "==");
}
@Test
public void desugaredBinarystrictNeq() {
binaryOperationTest("!==", "!", "===");
}
@Test
public void undefinedEqNullLeft() {
testBinExpOnly("var foo = undefined; foo == null;",
Undefined.instance,
Null.instance);
}
@Test
public void nullEqUndefinedLeft() {
testBinExpOnly("var foo = null; foo == undefined;",
Null.instance,
Undefined.instance);
}
@Test
public void nullEqUndefinedRight() {
testBinExpOnly("var foo = null; undefined == foo;",
Undefined.instance,
Null.instance);
}
@Test
public void undefinedEqNullRight() {
testBinExpOnly("var foo = undefined; null == foo;",
Null.instance,
Undefined.instance);
}
@Test
public void undefinedEqUndefined() {
testBinExpOnly("var foo = undefined; undefined == foo;",
Undefined.instance,
Undefined.instance);
}
@Test
public void nullEqNull() {
testBinExpOnly("var foo = null; null == foo;",
Null.instance,
Null.instance);
}
@Test
public void typeofIdenticalString1() {
typeofIdenticalType("string", "==", true);
}
@Test
public void typeofIdenticalString2() {
typeofIdenticalType("string", "==", false);
}
@Test
public void typeofIdenticalString3() {
typeofIdenticalType("string", "===", true);
}
@Test
public void typeofIdenticalString4() {
typeofIdenticalType("string", "===", false);
}
@Test
public void typeofIdenticalUnknown1() {
typeofIdenticalType("foo", "==", true);
}
@Test
public void typeofIdenticalUnknown2() {
typeofIdenticalType("foo", "==", false);
}
@Test
public void typeofIdenticalUnknown3() {
typeofIdenticalType("foo", "===", true);
}
public void typeofIdenticalUnknown4() {
typeofIdenticalType("foo", "===", false);
}
public void typeofIdenticalType(String type, String operator, boolean typeofAsLeftOperand) {
String lhs = "'" + type + "'";
String rhs = "(typeof a)";
if (typeofAsLeftOperand) {
String tmp = lhs;
lhs = rhs;
rhs = tmp;
}
String src = "var a = 42; var c = " + lhs + operator + rhs;
evalWithTags(src, new Class[]{BinaryOperationTag.class, UnaryOperationTag.class, LiteralTag.class});
enter(LiteralTag.class).exit(assertReturnValue(42));
enter(BinaryOperationTag.class, (e, binary) -> {
assertAttribute(e, OPERATOR, operator);
for (int eventNo = 0; eventNo < 2; eventNo++) {
if (typeofAsLeftOperand == (eventNo == 0)) {
enter(UnaryOperationTag.class, (e2, unary) -> {
assertAttribute(e2, OPERATOR, "typeof");
unary.input(42);
}).exit();
binary.input("number");
} else {
enter(LiteralTag.class).exit(assertReturnValue(type));
binary.input(type);
}
}
}).exit();
}
private void testBinExpOnly(String src, Object firstValue, Object secondValue) {
evalWithTag(src, BinaryOperationTag.class);
enter(BinaryOperationTag.class, (e, binary) -> {
binary.input(firstValue);
binary.input(secondValue);
}).exit();
}
private void binaryOperationTest(String srcOperator, String unOperator, String binOperator) {
String src = "var a = 42 " + srcOperator + " 41";
evalWithTags(src, new Class[]{BinaryOperationTag.class, UnaryOperationTag.class});
enter(UnaryOperationTag.class, (e, u) -> {
assertAttribute(e, OPERATOR, unOperator);
enter(BinaryOperationTag.class, (e2, b) -> {
assertAttribute(e2, OPERATOR, binOperator);
b.input(42);
b.input(41);
}).exit();
u.input(false);
}).exit();
}
private void binaryOperationTest(int leftValue, int rightValue, String operator) {
String src = "var a = " + leftValue + " ; var b = a " + operator + " " + rightValue + ";";
evalWithTag(src, BinaryOperationTag.class);
enter(BinaryOperationTag.class, (e, binary) -> {
assertAttribute(e, OPERATOR, operator);
binary.input(leftValue);
binary.input(rightValue);
}).exit();
}
private void constantBinaryOperationTestRight(int rightValue, String operator) {
String src = "var a = 42; var c = a " + operator + " " + rightValue + ";";
evalWithTag(src, BinaryOperationTag.class);
enter(BinaryOperationTag.class, (e, binary) -> {
assertAttribute(e, OPERATOR, operator);
binary.input(42);
binary.input(rightValue);
}).exit();
}
}