ScriptObjectMirrorTest.java revision 1239:77609e069f9f
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.api.scripting.test;
27
28import static org.testng.Assert.assertEquals;
29import static org.testng.Assert.assertFalse;
30import static org.testng.Assert.assertTrue;
31import static org.testng.Assert.fail;
32
33import java.nio.ByteBuffer;
34import java.util.HashMap;
35import java.util.List;
36import java.util.Map;
37import java.util.function.Function;
38import javax.script.Bindings;
39import javax.script.Invocable;
40import javax.script.ScriptContext;
41import javax.script.ScriptEngine;
42import javax.script.ScriptEngineManager;
43import javax.script.ScriptException;
44import jdk.nashorn.api.scripting.JSObject;
45import jdk.nashorn.api.scripting.ScriptObjectMirror;
46import org.testng.annotations.Test;
47
48/**
49 * Tests to check jdk.nashorn.api.scripting.ScriptObjectMirror API.
50 */
51@SuppressWarnings("javadoc")
52public class ScriptObjectMirrorTest {
53
54    @SuppressWarnings("unchecked")
55    @Test
56    public void reflectionTest() throws ScriptException {
57        final ScriptEngineManager m = new ScriptEngineManager();
58        final ScriptEngine e = m.getEngineByName("nashorn");
59
60        e.eval("var obj = { x: 344, y: 'nashorn' }");
61
62        int count = 0;
63        Map<Object, Object> map = (Map<Object, Object>) e.get("obj");
64        assertFalse(map.isEmpty());
65        assertTrue(map.keySet().contains("x"));
66        assertTrue(map.containsKey("x"));
67        assertTrue(map.values().contains("nashorn"));
68        assertTrue(map.containsValue("nashorn"));
69        for (final Map.Entry<?, ?> ex : map.entrySet()) {
70            final Object key = ex.getKey();
71            if (key.equals("x")) {
72                assertTrue(344 == ((Number) ex.getValue()).doubleValue());
73                count++;
74            } else if (key.equals("y")) {
75                assertEquals(ex.getValue(), "nashorn");
76                count++;
77            }
78        }
79        assertEquals(2, count);
80        assertEquals(2, map.size());
81
82        // add property
83        map.put("z", "hello");
84        assertEquals(e.eval("obj.z"), "hello");
85        assertEquals(map.get("z"), "hello");
86        assertTrue(map.keySet().contains("z"));
87        assertTrue(map.containsKey("z"));
88        assertTrue(map.values().contains("hello"));
89        assertTrue(map.containsValue("hello"));
90        assertEquals(map.size(), 3);
91
92        final Map<Object, Object> newMap = new HashMap<>();
93        newMap.put("foo", 23.0);
94        newMap.put("bar", true);
95        map.putAll(newMap);
96
97        assertEquals(e.eval("obj.foo"), 23.0);
98        assertEquals(e.eval("obj.bar"), true);
99
100        // remove using map method
101        map.remove("foo");
102        assertEquals(e.eval("typeof obj.foo"), "undefined");
103
104        count = 0;
105        e.eval("var arr = [ true, 'hello' ]");
106        map = (Map<Object, Object>) e.get("arr");
107        assertFalse(map.isEmpty());
108        assertTrue(map.containsKey("length"));
109        assertTrue(map.containsValue("hello"));
110        for (final Map.Entry<?, ?> ex : map.entrySet()) {
111            final Object key = ex.getKey();
112            if (key.equals("0")) {
113                assertEquals(ex.getValue(), Boolean.TRUE);
114                count++;
115            } else if (key.equals("1")) {
116                assertEquals(ex.getValue(), "hello");
117                count++;
118            }
119        }
120        assertEquals(count, 2);
121        assertEquals(map.size(), 2);
122
123        // add element
124        map.put("2", "world");
125        assertEquals(map.get("2"), "world");
126        assertEquals(map.size(), 3);
127
128        // remove all
129        map.clear();
130        assertTrue(map.isEmpty());
131        assertEquals(e.eval("typeof arr[0]"), "undefined");
132        assertEquals(e.eval("typeof arr[1]"), "undefined");
133        assertEquals(e.eval("typeof arr[2]"), "undefined");
134    }
135
136    @Test
137    public void jsobjectTest() {
138        final ScriptEngineManager m = new ScriptEngineManager();
139        final ScriptEngine e = m.getEngineByName("nashorn");
140        try {
141            e.eval("var obj = { '1': 'world', func: function() { return this.bar; }, bar: 'hello' }");
142            final ScriptObjectMirror obj = (ScriptObjectMirror) e.get("obj");
143
144            // try basic get on existing properties
145            if (!obj.getMember("bar").equals("hello")) {
146                fail("obj.bar != 'hello'");
147            }
148
149            if (!obj.getSlot(1).equals("world")) {
150                fail("obj[1] != 'world'");
151            }
152
153            if (!obj.callMember("func", new Object[0]).equals("hello")) {
154                fail("obj.func() != 'hello'");
155            }
156
157            // try setting properties
158            obj.setMember("bar", "new-bar");
159            obj.setSlot(1, "new-element-1");
160            if (!obj.getMember("bar").equals("new-bar")) {
161                fail("obj.bar != 'new-bar'");
162            }
163
164            if (!obj.getSlot(1).equals("new-element-1")) {
165                fail("obj[1] != 'new-element-1'");
166            }
167
168            // try adding properties
169            obj.setMember("prop", "prop-value");
170            obj.setSlot(12, "element-12");
171            if (!obj.getMember("prop").equals("prop-value")) {
172                fail("obj.prop != 'prop-value'");
173            }
174
175            if (!obj.getSlot(12).equals("element-12")) {
176                fail("obj[12] != 'element-12'");
177            }
178
179            // delete properties
180            obj.removeMember("prop");
181            if ("prop-value".equals(obj.getMember("prop"))) {
182                fail("obj.prop is not deleted!");
183            }
184
185            // Simple eval tests
186            assertEquals(obj.eval("typeof Object"), "function");
187            assertEquals(obj.eval("'nashorn'.substring(3)"), "horn");
188        } catch (final Exception exp) {
189            exp.printStackTrace();
190            fail(exp.getMessage());
191        }
192    }
193
194    @Test
195    public void scriptObjectMirrorToStringTest() {
196        final ScriptEngineManager m = new ScriptEngineManager();
197        final ScriptEngine e = m.getEngineByName("nashorn");
198        try {
199            final Object obj = e.eval("new TypeError('wrong type')");
200            assertEquals(obj.toString(), "TypeError: wrong type", "toString returns wrong value");
201        } catch (final Throwable t) {
202            t.printStackTrace();
203            fail(t.getMessage());
204        }
205
206        try {
207            final Object obj = e.eval("function func() { print('hello'); }");
208            assertEquals(obj.toString(), "function func() { print('hello'); }", "toString returns wrong value");
209        } catch (final Throwable t) {
210            t.printStackTrace();
211            fail(t.getMessage());
212        }
213    }
214
215    @Test
216    public void mirrorNewObjectGlobalFunctionTest() throws ScriptException {
217        final ScriptEngineManager m = new ScriptEngineManager();
218        final ScriptEngine e = m.getEngineByName("nashorn");
219        final ScriptEngine e2 = m.getEngineByName("nashorn");
220
221        e.eval("function func() {}");
222        e2.put("foo", e.get("func"));
223        final ScriptObjectMirror e2global = (ScriptObjectMirror)e2.eval("this");
224        final Object newObj = ((ScriptObjectMirror)e2global.getMember("foo")).newObject();
225        assertTrue(newObj instanceof ScriptObjectMirror);
226    }
227
228    @Test
229    public void mirrorNewObjectInstanceFunctionTest() throws ScriptException {
230        final ScriptEngineManager m = new ScriptEngineManager();
231        final ScriptEngine e = m.getEngineByName("nashorn");
232        final ScriptEngine e2 = m.getEngineByName("nashorn");
233
234        e.eval("function func() {}");
235        e2.put("func", e.get("func"));
236        final ScriptObjectMirror e2obj = (ScriptObjectMirror)e2.eval("({ foo: func })");
237        final Object newObj = ((ScriptObjectMirror)e2obj.getMember("foo")).newObject();
238        assertTrue(newObj instanceof ScriptObjectMirror);
239    }
240
241    @Test
242    public void indexPropertiesExternalBufferTest() throws ScriptException {
243        final ScriptEngineManager m = new ScriptEngineManager();
244        final ScriptEngine e = m.getEngineByName("nashorn");
245        final ScriptObjectMirror obj = (ScriptObjectMirror)e.eval("var obj = {}; obj");
246        final ByteBuffer buf = ByteBuffer.allocate(5);
247        int i;
248        for (i = 0; i < 5; i++) {
249            buf.put(i, (byte)(i+10));
250        }
251        obj.setIndexedPropertiesToExternalArrayData(buf);
252
253        for (i = 0; i < 5; i++) {
254            assertEquals((byte)(i+10), ((Number)e.eval("obj[" + i + "]")).byteValue());
255        }
256
257        e.eval("for (i = 0; i < 5; i++) obj[i] = 0");
258        for (i = 0; i < 5; i++) {
259            assertEquals((byte)0, ((Number)e.eval("obj[" + i + "]")).byteValue());
260            assertEquals((byte)0, buf.get(i));
261        }
262    }
263
264    @Test
265    public void conversionTest() throws ScriptException {
266        final ScriptEngineManager m = new ScriptEngineManager();
267        final ScriptEngine e = m.getEngineByName("nashorn");
268        final ScriptObjectMirror arr = (ScriptObjectMirror)e.eval("[33, 45, 23]");
269        final int[] intArr = arr.to(int[].class);
270        assertEquals(intArr[0], 33);
271        assertEquals(intArr[1], 45);
272        assertEquals(intArr[2], 23);
273
274        final List<?> list = arr.to(List.class);
275        assertEquals(list.get(0), 33);
276        assertEquals(list.get(1), 45);
277        assertEquals(list.get(2), 23);
278
279        ScriptObjectMirror obj = (ScriptObjectMirror)e.eval(
280            "({ valueOf: function() { return 42 } })");
281        assertEquals(Double.valueOf(42.0), obj.to(Double.class));
282
283        obj = (ScriptObjectMirror)e.eval(
284            "({ toString: function() { return 'foo' } })");
285        assertEquals("foo", obj.to(String.class));
286    }
287
288    // @bug 8044000: Access to undefined property yields "null" instead of "undefined"
289    @Test
290    public void mapScriptObjectMirrorCallsiteTest() throws ScriptException {
291        final ScriptEngineManager m = new ScriptEngineManager();
292        final ScriptEngine engine = m.getEngineByName("nashorn");
293        final String TEST_SCRIPT = "typeof obj.foo";
294
295        final Bindings global = engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
296        engine.eval("var obj = java.util.Collections.emptyMap()");
297        // this will drive callsite "obj.foo" of TEST_SCRIPT
298        // to use "obj instanceof Map" as it's guard
299        engine.eval(TEST_SCRIPT, global);
300        // redefine 'obj' to be a script object
301        engine.eval("obj = {}");
302
303        final Bindings newGlobal = engine.createBindings();
304        // transfer 'obj' from default global to new global
305        // new global will get a ScriptObjectMirror wrapping 'obj'
306        newGlobal.put("obj", global.get("obj"));
307
308        // Every ScriptObjectMirror is a Map! If callsite "obj.foo"
309        // does not see the new 'obj' is a ScriptObjectMirror, it'll
310        // continue to use Map's get("obj.foo") instead of ScriptObjectMirror's
311        // getMember("obj.foo") - thereby getting null instead of undefined
312        assertEquals("undefined", engine.eval(TEST_SCRIPT, newGlobal));
313    }
314
315    public interface MirrorCheckExample {
316        Object test1(Object arg);
317        Object test2(Object arg);
318        boolean compare(Object o1, Object o2);
319    }
320
321    // @bug 8053910: ScriptObjectMirror causing havoc with Invocation interface
322    @Test
323    public void checkMirrorToObject() throws Exception {
324        final ScriptEngineManager engineManager = new ScriptEngineManager();
325        final ScriptEngine engine = engineManager.getEngineByName("nashorn");
326        final Invocable invocable = (Invocable)engine;
327
328        engine.eval("function test1(arg) { return { arg: arg }; }");
329        engine.eval("function test2(arg) { return arg; }");
330        engine.eval("function compare(arg1, arg2) { return arg1 == arg2; }");
331
332        final Map<String, Object> map = new HashMap<>();
333        map.put("option", true);
334
335        final MirrorCheckExample example = invocable.getInterface(MirrorCheckExample.class);
336
337        final Object value1 = invocable.invokeFunction("test1", map);
338        final Object value2 = example.test1(map);
339        final Object value3 = invocable.invokeFunction("test2", value2);
340        final Object value4 = example.test2(value2);
341
342        // check that Object type argument receives a ScriptObjectMirror
343        // when ScriptObject is passed
344        assertEquals(ScriptObjectMirror.class, value1.getClass());
345        assertEquals(ScriptObjectMirror.class, value2.getClass());
346        assertEquals(ScriptObjectMirror.class, value3.getClass());
347        assertEquals(ScriptObjectMirror.class, value4.getClass());
348        assertTrue((boolean)invocable.invokeFunction("compare", value1, value1));
349        assertTrue(example.compare(value1, value1));
350        assertTrue((boolean)invocable.invokeFunction("compare", value3, value4));
351        assertTrue(example.compare(value3, value4));
352    }
353
354    // @bug 8053910: ScriptObjectMirror causing havoc with Invocation interface
355    @Test
356    public void mirrorUnwrapInterfaceMethod() throws Exception {
357        final ScriptEngineManager engineManager = new ScriptEngineManager();
358        final ScriptEngine engine = engineManager.getEngineByName("nashorn");
359        final Invocable invocable = (Invocable)engine;
360        engine.eval("function apply(obj) { " +
361            " return obj instanceof Packages.jdk.nashorn.api.scripting.ScriptObjectMirror; " +
362            "}");
363        @SuppressWarnings("unchecked")
364        final Function<Object,Object> func = invocable.getInterface(Function.class);
365        assertFalse((boolean)func.apply(engine.eval("({ x: 2 })")));
366    }
367
368    // @bug 8055687: Wrong "this" passed to JSObject.eval call
369    @Test
370    public void checkThisForJSObjectEval() throws Exception {
371        final ScriptEngineManager engineManager = new ScriptEngineManager();
372        final ScriptEngine e = engineManager.getEngineByName("nashorn");
373        final JSObject jsobj = (JSObject)e.eval("({foo: 23, bar: 'hello' })");
374        assertEquals(((Number)jsobj.eval("this.foo")).intValue(), 23);
375        assertEquals(jsobj.eval("this.bar"), "hello");
376        assertEquals(jsobj.eval("String(this)"), "[object Object]");
377        final Object global = e.eval("this");
378        assertFalse(global.equals(jsobj.eval("this")));
379    }
380
381    @Test
382    public void topLevelAnonFuncStatement() throws Exception {
383        final ScriptEngineManager engineManager = new ScriptEngineManager();
384        final ScriptEngine e = engineManager.getEngineByName("nashorn");
385        final JSObject func = (JSObject)e.eval("function(x) { return x + ' world' }");
386        assertTrue(func.isFunction());
387        assertEquals(func.call(e.eval("this"), "hello"), "hello world");
388    }
389}
390