package com.oracle.truffle.js.test.instrumentation;
import org.junit.Test;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.ControlFlowBranchTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.FunctionCallTag;
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.ReadVariableTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.UnaryOperationTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.WritePropertyTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.WriteVariableTag;
import com.oracle.truffle.js.runtime.objects.Undefined;
public class IncDecOperationTest extends FineGrainedAccessTest {
@Test
public void inc() {
evalAllTags("var a = 42; a++;");
assertGlobalVarDeclaration("a", 42);
enter(WritePropertyTag.class, (e, write) -> {
assertAttribute(e, KEY, "a");
write.input(assertJSObjectInput);
enter(UnaryOperationTag.class, (e1, bin) -> {
assertAttribute(e1, OPERATOR, "++");
enter(ReadPropertyTag.class, (e2, p) -> {
assertAttribute(e2, KEY, "a");
p.input(assertGlobalObjectInput);
}).exit(assertReturnValue(42));
bin.input(42);
}).exit();
write.input(43);
}).exit();
}
@Test
public void dec() {
evalAllTags("var a = 42; a--;");
assertGlobalVarDeclaration("a", 42);
enter(WritePropertyTag.class, (e, write) -> {
assertAttribute(e, KEY, "a");
write.input(assertJSObjectInput);
enter(UnaryOperationTag.class, (e1, bin) -> {
assertAttribute(e1, OPERATOR, "--");
enter(ReadPropertyTag.class, (e2, p) -> {
assertAttribute(e2, KEY, "a");
p.input(assertGlobalObjectInput);
}).exit(assertReturnValue(42));
bin.input(42);
}).exit();
write.input(41);
}).exit();
}
@Test
public void decProperty() {
evalWithTag("var a = {x:42}; a.x--;", UnaryOperationTag.class);
enter(UnaryOperationTag.class, (e, b) -> {
assertAttribute(e, OPERATOR, "--");
b.input(42);
}).exit();
}
@Test
public void incDecVar() {
evalWithTag("function foo(a){var b = 0; b+=a.x;}; foo({x:42});", WriteVariableTag.class);
enter(WriteVariableTag.class, (e, b) -> {
assertAttribute(e, NAME, "b");
b.input(0);
}).exit();
enter(WriteVariableTag.class, (e, b) -> {
assertAttribute(e, NAME, "b");
b.input(42);
}).exit();
}
@Test
public void incLocal() {
evalAllTags("function foo(a) { var x = a++; return x; }; foo(42);");
assertAllLocalOperationsPost("++", 43, 42);
}
@Test
public void decLocal() {
evalAllTags("function foo(a) { var x = a--; return x; }; foo(42);");
assertAllLocalOperationsPost("--", 41, 42);
}
@Test
public void incLocalPre() {
evalAllTags("function foo(a) { var x = ++a; return x; }; foo(42);");
assertAllLocalOperationsPost("++", 43, 43);
}
@Test
public void decLocalPre() {
evalAllTags("function foo(a) { var x = --a; return x; }; foo(42);");
assertAllLocalOperationsPost("--", 41, 41);
}
private void assertAllLocalOperationsPost(String operator, int valueSet, int exprReturns) {
assertGlobalFunctionExpressionDeclaration("foo");
enter(FunctionCallTag.class, (e1, p1) -> {
enter(LiteralTag.class).exit(assertReturnValue(Undefined.instance));
p1.input(Undefined.instance);
enter(ReadPropertyTag.class, (e2, p2) -> {
assertAttribute(e2, KEY, "foo");
p2.input(assertGlobalObjectInput);
}).exit(assertJSFunctionReturn);
p1.input(assertJSFunctionInput);
enter(LiteralTag.class).exit(assertReturnValue(42));
p1.input(42);
enterDeclareTag("a");
enterDeclareTag("x");
enter(WriteVariableTag.class, (e3, p3) -> {
assertAttribute(e3, NAME, "a");
p3.input(42);
}).exit();
enter(WriteVariableTag.class, (e3, p3) -> {
assertAttribute(e3, NAME, "x");
enter(WriteVariableTag.class, (e4, p4) -> {
assertAttribute(e4, NAME, "a");
enter(UnaryOperationTag.class, (e5, p5) -> {
assertAttribute(e5, OPERATOR, operator);
enter(ReadVariableTag.class, (e6, p6) -> {
assertAttribute(e6, NAME, "a");
}).exit(assertReturnValue(42));
p5.input(42);
}).exit();
p4.input(valueSet);
}).exit();
p3.input(exprReturns);
}).exit();
enter(ControlFlowBranchTag.class, (e4, v) -> {
assertAttribute(e4, TYPE, ControlFlowBranchTag.Type.Return.name());
enter(ReadVariableTag.class, (e6, p6) -> {
assertAttribute(e6, NAME, "x");
}).exit(assertReturnValue(exprReturns));
v.input(exprReturns);
}).exitMaybeControlFlowException();
}).exit();
}
@Test
public void postfixToNumeric() {
evalWithTag("(function() { var x = {}; x++; })()", WriteVariableTag.class);
assertToNumericConversion();
}
@Test
public void prefixToNumeric() {
evalWithTag("(function() { var x = {}; ++x; })()", WriteVariableTag.class);
assertToNumericConversion();
}
private void assertToNumericConversion() {
enter(WriteVariableTag.class, (e, p) -> {
assertAttribute(e, NAME, "x");
p.input();
}).exit();
enter(WriteVariableTag.class, (e, p) -> {
assertAttribute(e, NAME, "x");
p.input(Double.NaN);
}).exit();
}
}