package com.oracle.truffle.js.test.interop;
import static com.oracle.truffle.js.lang.JavaScriptLanguage.ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.PolyglotAccess;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyExecutable;
import org.graalvm.polyglot.proxy.ProxyInstantiable;
import org.graalvm.polyglot.proxy.ProxyObject;
import org.junit.Test;
import com.oracle.truffle.js.runtime.JSContextOptions;
import com.oracle.truffle.js.test.JSTest;
import com.oracle.truffle.js.test.polyglot.PolyglotBuiltinTest;
public class JSProxyTest {
@Test
public void testProxySet() {
try (Context context = JSTest.newContextBuilder().build()) {
String source = "var target= { isTarget: true};" +
"var handler={ set: function(t,v) { t.setCalled=true; } };" +
"var p = new Proxy(target, handler);" +
"function set(t,v) { t.value=v; };" +
"set({},38); set({},39); set({},40); set({}, 42); set(p,42);" +
"target.setCalled;";
assertTrue(context.eval("js", source).asBoolean());
}
}
@Test
public void testProxyOwnKeysForeignTarget() {
try (Context context = JSTest.newContextBuilder().allowPolyglotAccess(PolyglotAccess.ALL).option(JSContextOptions.DEBUG_BUILTIN_NAME, "true").build()) {
PolyglotBuiltinTest.addTestPolyglotBuiltins(context);
String source = "var target = Polyglot.createForeignObject();" +
"var handler = { ownKeys: function(t) { target.ownKeysCalled=true; return []; } };" +
"var p = new Proxy(target, handler);" +
"Object.keys(p);" +
"target.ownKeysCalled;";
assertTrue(context.eval("js", source).asBoolean());
}
}
@Test
public void testProxyInstantiate() {
try (Context context = JSTest.newContextBuilder().build()) {
Value proxyFactory = context.eval(ID, "(target) => new Proxy(target, {});");
Value constructorProxy;
Value result;
Value constructor = context.eval(ID, "(function(arg) {return {expected: arg};});");
constructorProxy = proxyFactory.execute(constructor);
result = constructorProxy.newInstance("hello");
assertTrue(result.hasMembers());
assertTrue(result.hasMember("expected"));
assertEquals("hello", result.getMember("expected").asString());
constructorProxy = proxyFactory.execute(constructorProxy);
result = constructorProxy.newInstance(42);
assertTrue(result.hasMembers());
assertTrue(result.hasMember("expected"));
assertEquals(42, result.getMember("expected").asInt());
constructorProxy = proxyFactory.execute((ProxyInstantiable) args -> {
assertEquals(1, args.length);
return ProxyObject.fromMap(Collections.singletonMap("expected", 42));
});
result = constructorProxy.newInstance("hello");
assertTrue(result.hasMembers());
assertTrue(result.hasMember("expected"));
assertEquals(42, result.getMember("expected").asInt());
}
}
@Test
public void testProxyInstantiateWithTrap() {
try (Context context = JSTest.newContextBuilder().build()) {
Value proxyFactory = context.eval(ID, "" +
"(target) => new Proxy(target, {\n" +
" construct(target, args) {return {expected: args[0]};}\n" +
"});\n");
Value constructorProxy;
Value result;
constructorProxy = proxyFactory.execute(context.eval(ID, "(function(arg) {return {notExpected: arg};});"));
result = constructorProxy.newInstance("hello");
assertTrue(result.hasMembers());
assertTrue(result.hasMember("expected"));
assertEquals("hello", result.getMember("expected").asString());
constructorProxy = proxyFactory.execute(constructorProxy);
result = constructorProxy.newInstance(42);
assertTrue(result.hasMembers());
assertTrue(result.hasMember("expected"));
assertEquals(42, result.getMember("expected").asInt());
constructorProxy = proxyFactory.execute((ProxyInstantiable) args -> {
assertEquals(1, args.length);
return ProxyObject.fromMap(Collections.singletonMap("notExpected", 42));
});
result = constructorProxy.newInstance(42);
assertTrue(result.hasMembers());
assertTrue(result.hasMember("expected"));
assertEquals(42, result.getMember("expected").asInt());
}
}
@Test
public void testProxy() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final Value proxy = ctx.eval("js", "" +
"(function(){\n" +
"return new Proxy(function() {}, {\n" +
" apply: function(target, thisArg, argumentsList) {return 'a';}\n" +
"});\n" +
"})").execute();
assertEquals("a", proxy.execute().asString());
}
}
@Test
public void testProxyConstruct() {
try (Context ctx = JSTest.newContextBuilder().allowHostAccess(HostAccess.ALL).allowHostClassLookup(s -> true).build()) {
String src = "const AL = Java.type('java.util.ArrayList');\n" +
"const PAL = new Proxy(AL, {});\n" +
"var ar = new PAL();\n" +
"ar.add('test');\n" +
"ar.size() === 1 && ar.get(0)==='test'";
org.graalvm.polyglot.Source source = org.graalvm.polyglot.Source.newBuilder("js", src, "testProxy").buildLiteral();
final Value proxy = ctx.eval(source);
assertTrue(proxy.isBoolean() && proxy.asBoolean() == true);
}
}
@Test
public void testForeignGetTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
Value value = ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { get: foreignTrap });\n" +
"return proxy.foo;\n" +
"})").execute((ProxyExecutable) args -> 42);
assertTrue(value.isNumber());
assertSame(42, value.asInt());
}
}
@Test
public void testForeignSetTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { set: foreignTrap });\n" +
"proxy.foo = 42;\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return true;
});
assertTrue(called[0]);
}
}
@Test
public void testForeignHasTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { has: foreignTrap });\n" +
"with (proxy) foo;\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return true;
});
assertTrue(called[0]);
}
}
@Test
public void testForeignConstructTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy(function() {}, { construct: foreignTrap });\n" +
"new proxy();\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return args[0];
});
assertTrue(called[0]);
}
}
@Test
public void testForeignApplyTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
Value value = ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy(function() {}, { apply: foreignTrap });\n" +
"return proxy();\n" +
"})").execute((ProxyExecutable) args -> {
return 42;
});
assertTrue(value.isNumber());
assertSame(42, value.asInt());
}
}
@Test
public void testForeignDeletePropertyTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { deleteProperty: foreignTrap });\n" +
"delete proxy.foo;\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return true;
});
assertTrue(called[0]);
}
}
@Test
public void testForeignDefinePropertyTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { defineProperty: foreignTrap });\n" +
"proxy.foo = 42;\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return true;
});
assertTrue(called[0]);
}
}
@Test
public void testForeignPreventExtensionsTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
" var proxy = new Proxy({}, { preventExtensions: foreignTrap });\n" +
" try {\n" +
" Object.freeze(proxy);\n" +
" throw new Error('TypeError expected');\n" +
" } catch (e) {\n" +
" if (!(e instanceof TypeError)) {\n" +
" throw e;\n" +
" }\n" +
" }\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return false;
});
assertTrue(called[0]);
}
}
@Test
public void testForeignIsExtensibleTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { isExtensible: foreignTrap });\n" +
"Object.isExtensible(proxy);\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return true;
});
assertTrue(called[0]);
}
}
@Test
public void testForeignGetPrototypeOfTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
Value objectPrototype = ctx.eval("js", "Object.prototype");
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { getPrototypeOf: foreignTrap });\n" +
"Object.getPrototypeOf(proxy);\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return objectPrototype;
});
assertTrue(called[0]);
}
}
@Test
public void testForeignSetPrototypeOfTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { setPrototypeOf: foreignTrap });\n" +
"Object.setPrototypeOf(proxy, {});\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return true;
});
assertTrue(called[0]);
}
}
@Test
public void testForeignOwnKeysTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { ownKeys: foreignTrap });\n" +
"Object.keys(proxy);\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return args[0];
});
assertTrue(called[0]);
}
}
@Test
public void testForeignGetOwnPropertyDescriptorTrap() {
try (Context ctx = JSTest.newContextBuilder().build()) {
final boolean[] called = new boolean[1];
Value descriptor = ctx.eval("js", "({ configurable: true })");
ctx.eval("js", "(function(foreignTrap) {\n" +
"var proxy = new Proxy({}, { getOwnPropertyDescriptor: foreignTrap });\n" +
"Object.getOwnPropertyDescriptor(proxy, 'foo');\n" +
"})").execute((ProxyExecutable) args -> {
called[0] = true;
return descriptor;
});
assertTrue(called[0]);
}
}
}