Skip to content

Commit b5ffa7f

Browse files
committed
add test for active prototype, and feature disabled
1 parent 0b7e0ff commit b5ffa7f

File tree

2 files changed

+218
-1
lines changed

2 files changed

+218
-1
lines changed

graal-js/src/com.oracle.truffle.js.test/src/com/oracle/truffle/js/test/interop/ForeignObjectPrototypeTest.java

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import static org.junit.Assert.assertFalse;
4747
import static org.junit.Assert.assertTrue;
4848

49+
import java.time.Instant;
4950
import java.util.Arrays;
5051
import java.util.List;
5152

@@ -54,8 +55,13 @@
5455
import org.graalvm.polyglot.Value;
5556
import org.graalvm.polyglot.proxy.ProxyArray;
5657
import org.graalvm.polyglot.proxy.ProxyExecutable;
58+
import org.junit.Assert;
5759
import org.junit.Test;
5860

61+
import com.oracle.truffle.api.interop.InteropLibrary;
62+
import com.oracle.truffle.api.interop.TruffleObject;
63+
import com.oracle.truffle.api.library.ExportLibrary;
64+
import com.oracle.truffle.api.library.ExportMessage;
5965
import com.oracle.truffle.js.test.JSTest;
6066

6167
public class ForeignObjectPrototypeTest {
@@ -120,4 +126,215 @@ public void testHostMethodStatic() {
120126
}
121127
}
122128

129+
@Test
130+
public void testPrototype() {
131+
testPrototypeIntl("Array", ProxyArray.fromArray("fun", "with", "proxy", "array"));
132+
testPrototypeIntl("Date", Instant.now());
133+
testPrototypeIntl("Map", new TestTruffleHash());
134+
// testPrototypeIntl("String", new TestTruffleString());
135+
// testPrototypeIntl("Boolean", new TestTruffleBoolean());
136+
// testPrototypeIntl("Number", new TestTruffleNumber());
137+
testPrototypeIntl("Function", (ProxyExecutable) v -> true);
138+
testPrototypeIntl("Object", new Object());
139+
}
140+
141+
private static void testPrototypeIntl(String prototype, Object obj) {
142+
String code = "(obj) => { var proto = Object.getPrototypeOf(obj); \n" +
143+
" var protoProto = Object.getPrototypeOf(proto); \n" +
144+
" return protoProto === " + prototype + ".prototype; \n" +
145+
"}";
146+
try (Context context = JSTest.newContextBuilder(ID).build()) {
147+
Value result = context.eval(ID, code).execute(obj);
148+
Assert.assertTrue(result.asBoolean());
149+
}
150+
}
151+
152+
@Test
153+
public void testPrototypeDisabled() {
154+
testDisabled(ProxyArray.fromArray("fun", "with", "proxy", "array"));
155+
testDisabled((ProxyExecutable) args -> "hi");
156+
testDisabled(Instant.now());
157+
}
158+
159+
private static void testDisabled(Object array) {
160+
String code = "(obj) => { return Object.getPrototypeOf(obj) === null; }";
161+
try (Context context = JSTest.newContextBuilder(ID).option(FOREIGN_OBJECT_PROTOTYPE_NAME, "false").build()) {
162+
Value result = context.eval(ID, code).execute(array);
163+
Assert.assertTrue(result.asBoolean());
164+
}
165+
}
166+
167+
@Test
168+
public void testHostMethodHasPrecedence() {
169+
String codeGetEpochSecond = "(obj) => { return obj.getEpochSecond(); }";
170+
try (Context context = JSTest.newContextBuilder(ID).allowHostAccess(HostAccess.ALL).build()) {
171+
Instant inst = Instant.now();
172+
173+
// check getter from Java can be called
174+
long second = context.eval(ID, codeGetEpochSecond).execute(inst).asLong();
175+
Assert.assertEquals(inst.getEpochSecond(), second);
176+
177+
// provide your own getter
178+
context.eval(ID, "(obj) => { Object.getPrototypeOf(obj).getEpochSecond = () => { return 666; }; };").execute(inst);
179+
180+
// verify that still the host getter is called
181+
Instant inst2 = Instant.now();
182+
Value result = context.eval(ID, codeGetEpochSecond).execute(inst2);
183+
Assert.assertEquals(inst2.getEpochSecond(), result.asLong());
184+
}
185+
}
186+
187+
@Test
188+
public void testJSBuiltinCanBeOverwritten() {
189+
String codeGetUTCString = "(obj) => { return obj.toUTCString(); }";
190+
try (Context context = JSTest.newContextBuilder(ID).allowHostAccess(HostAccess.ALL).build()) {
191+
Instant inst = Instant.now();
192+
193+
// check JS Prototype method can be called
194+
String utcString = context.eval(ID, codeGetUTCString).execute(inst).asString();
195+
Assert.assertTrue(utcString.length() > 10);
196+
197+
// provide your own getter
198+
context.eval(ID, "(obj) => { Object.getPrototypeOf(obj).toUTCString = () => { return 'special'; }; };").execute(inst);
199+
200+
// verify that on another object this overwritten method is called
201+
Instant inst2 = Instant.now();
202+
Value result = context.eval(ID, codeGetUTCString).execute(inst2);
203+
Assert.assertEquals("special", result.asString());
204+
}
205+
}
206+
207+
@ExportLibrary(InteropLibrary.class)
208+
public static class TestTruffleHash implements TruffleObject {
209+
210+
@ExportMessage
211+
@SuppressWarnings("static-method")
212+
boolean hasHashEntries() {
213+
return true;
214+
}
215+
216+
@ExportMessage
217+
@SuppressWarnings("static-method")
218+
long getHashSize() {
219+
return 0;
220+
}
221+
222+
@ExportMessage
223+
@SuppressWarnings("static-method")
224+
Object getHashEntriesIterator() {
225+
return null;
226+
}
227+
}
228+
229+
@ExportLibrary(InteropLibrary.class)
230+
public static class TestTruffleString implements TruffleObject {
231+
@ExportMessage
232+
@SuppressWarnings("static-method")
233+
boolean isString() {
234+
return true;
235+
}
236+
237+
@ExportMessage
238+
@SuppressWarnings("static-method")
239+
String asString() {
240+
return "";
241+
}
242+
}
243+
244+
@ExportLibrary(InteropLibrary.class)
245+
public static class TestTruffleBoolean implements TruffleObject {
246+
@ExportMessage
247+
@SuppressWarnings("static-method")
248+
boolean isBoolean() {
249+
return true;
250+
}
251+
252+
@ExportMessage
253+
@SuppressWarnings("static-method")
254+
boolean asBoolean() {
255+
return true;
256+
}
257+
}
258+
259+
@ExportLibrary(InteropLibrary.class)
260+
public static class TestTruffleNumber implements TruffleObject {
261+
@ExportMessage
262+
@SuppressWarnings("static-method")
263+
boolean isNumber() {
264+
return true;
265+
}
266+
267+
@ExportMessage
268+
@SuppressWarnings("static-method")
269+
final boolean fitsInByte() {
270+
return true;
271+
}
272+
273+
@ExportMessage
274+
@SuppressWarnings("static-method")
275+
final boolean fitsInShort() {
276+
return true;
277+
}
278+
279+
@ExportMessage
280+
@SuppressWarnings("static-method")
281+
final boolean fitsInInt() {
282+
return true;
283+
}
284+
285+
@ExportMessage
286+
@SuppressWarnings("static-method")
287+
final boolean fitsInLong() {
288+
return true;
289+
}
290+
291+
@ExportMessage
292+
@SuppressWarnings("static-method")
293+
final boolean fitsInFloat() {
294+
return true;
295+
}
296+
297+
@ExportMessage
298+
@SuppressWarnings("static-method")
299+
final boolean fitsInDouble() {
300+
return true;
301+
}
302+
303+
@ExportMessage
304+
@SuppressWarnings("static-method")
305+
final byte asByte() {
306+
return (byte) 0;
307+
}
308+
309+
@ExportMessage
310+
@SuppressWarnings("static-method")
311+
final short asShort() {
312+
return (short) 0;
313+
}
314+
315+
@ExportMessage
316+
@SuppressWarnings("static-method")
317+
final int asInt() {
318+
return 0;
319+
}
320+
321+
@ExportMessage
322+
@SuppressWarnings("static-method")
323+
final long asLong() {
324+
return 0L;
325+
}
326+
327+
@ExportMessage
328+
@SuppressWarnings("static-method")
329+
final float asFloat() {
330+
return 0.0F;
331+
}
332+
333+
@ExportMessage
334+
@SuppressWarnings("static-method")
335+
final double asDouble() {
336+
return 0.0D;
337+
}
338+
339+
}
123340
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/runtime/JSContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,7 @@ public boolean isOptionAsyncStackTraces() {
14981498
return contextOptions.isAsyncStackTraces();
14991499
}
15001500

1501-
public boolean isOptionForeignObjectPrototype () {
1501+
public boolean isOptionForeignObjectPrototype() {
15021502
return contextOptions.hasForeignObjectPrototype();
15031503
}
15041504

0 commit comments

Comments
 (0)