package com.oracle.truffle.js.test.instrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.LiteralTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.ReadElementTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.ReadPropertyTag;
import com.oracle.truffle.js.nodes.instrumentation.JSTags.WriteElementTag;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.builtins.JSFunction;
import com.oracle.truffle.js.runtime.objects.Undefined;
public class ElementsAccessTest extends FineGrainedAccessTest {
@Test
public void read() {
evalAllTags("var a = [1]; a[0];");
assertGlobalArrayLiteralDeclaration("a");
enter(ReadElementTag.class, (e, elem) -> {
enter(ReadPropertyTag.class).input(assertGlobalObjectInput).exit();
elem.input((e1) -> {
assertTrue(JSArray.isJSArray(e1.val));
});
enter(LiteralTag.class).exit();
elem.input(0);
}).exit();
}
@Test
public void nestedRead() {
evalAllTags("var a = [0]; a[a[0]];");
assertGlobalArrayLiteralDeclaration("a");
enter(ReadElementTag.class, (e, elem) -> {
enter(ReadPropertyTag.class).input(assertGlobalObjectInput).exit();
elem.input((e1) -> {
assertTrue(JSArray.isJSArray(e1.val));
});
enter(ReadElementTag.class, (e1, elem1) -> {
enter(ReadPropertyTag.class).input(assertGlobalObjectInput).exit();
elem1.input((e2) -> {
assertTrue(JSArray.isJSArray(e2.val));
});
enter(LiteralTag.class).exit();
elem1.input(0);
}).exit();
elem.input(0);
}).exit();
}
@Test
public void write() {
evalAllTags("var a = []; a[1] = 'foo';");
assertGlobalArrayLiteralDeclaration("a");
enter(WriteElementTag.class, (e, elem) -> {
enter(ReadPropertyTag.class).input(assertGlobalObjectInput).exit();
elem.input((e1) -> {
assertTrue(JSArray.isJSArray(e1.val));
});
enter(LiteralTag.class).exit();
elem.input(1);
enter(LiteralTag.class).exit();
elem.input("foo");
}).exit();
}
@Test
public void elementWriteIncDec() {
evalWithTag("var u=[2,4,6]; var p = 1; u[p] -= 42", WriteElementTag.class);
enter(WriteElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(1);
b.input(-38);
}).exit();
}
@Test
public void elementReadInvoke() {
evalWithTag("var u={x:[function(){}]}; u.x[0]()", ReadElementTag.class);
enter(ReadElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(0);
}).exit((res) -> {
Object[] val = (Object[]) res.val;
assertEquals(3, val.length);
assertTrue(JSFunction.isJSFunction(val[0]));
assertTrue(JSArray.isJSArray(val[1]));
assertEquals(0, val[2]);
});
}
@Test
public void targetTest() {
evalWithTag("var Box2d = {};Box2d.postDefs = [function(){}];function test(){var i = 0;Box2d.postDefs[i]();}; test();", ReadElementTag.class);
enter(ReadElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(0);
}).exit();
}
@Test
public void exprBlockTestIncPost() {
assertNestedIncDecElementRead("self.cursorState.cursorIndex++", 1, 1);
}
@Test
public void exprBlockTestIncPre() {
assertNestedIncDecElementRead("++self.cursorState.cursorIndex", 0, 1);
}
@Test
public void exprBlockTestDecPost() {
assertNestedIncDecElementRead("self.cursorState.cursorIndex--", 1, 1);
}
@Test
public void exprBlockTestDecPre() {
assertNestedIncDecElementRead("--self.cursorState.cursorIndex", 1, 0);
}
private void assertNestedIncDecElementRead(String op, int initial, int returned) {
evalWithTag("(function() {" +
" var self = {" +
" cursorState : {" +
" documents : ['foo', 'bar']," +
" cursorIndex : " + initial +
" }" +
" };" +
" var doc = self.cursorState.documents[" + op + "];" +
"})()", ReadElementTag.class);
enter(ReadElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(returned);
}).exit();
}
@Test
public void nestedInvokeReads() {
evalWithTag("function setKey(obj, keys) {" +
" obj.a;" +
" keys.slice[0][1][2](0, -1).forEach(function(key) {});" +
"};" +
"const callable = {" +
" slice : [['',['','',function fakeslice() { return [1,2]; }]]]" +
"};" +
"setKey({}, callable);" +
"for (var i = 0; i < 2; i++) {" +
" setKey({" +
" a: 1" +
" }, callable);" +
"}", ReadElementTag.class);
for (int i = 0; i < 3; i++) {
enter(ReadElementTag.class, (e, elem) -> {
enter(ReadElementTag.class, (e1, elem1) -> {
elem1.input(assertJSArrayInput);
elem1.input(0);
}).exit(assertJSObjectReturn);
elem.input(assertJSArrayInput);
elem.input(1);
}).exit(assertJSObjectReturn);
enter(ReadElementTag.class, (e, elem) -> {
elem.input(assertJSArrayInput);
elem.input(2);
}).exit(assertJSFunctionReturn);
}
}
@Test
public void elementWriteIndexConvert() {
evalWithTag("var a = []; a[true] = 0; a[undefined] = 0; a[{}] = 0;", WriteElementTag.class);
enter(WriteElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(true);
b.input(0);
}).exit();
enter(WriteElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(Undefined.instance);
b.input(0);
}).exit();
enter(WriteElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(assertJSObjectInput);
b.input(0);
}).exit();
}
@Test
public void elementReadIndexConvert() {
evalWithTag("var a = []; a[true]; a[undefined]; a[{}];", ReadElementTag.class);
enter(ReadElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(true);
}).exit();
enter(ReadElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(Undefined.instance);
}).exit();
enter(ReadElementTag.class, (e, b) -> {
b.input(assertJSArrayInput);
b.input(assertJSObjectInput);
}).exit();
}
}