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 26 package jdk.nashorn.api.scripting; 27 28 import static org.testng.Assert.assertEquals; 29 import static org.testng.Assert.assertFalse; 30 import static org.testng.Assert.assertTrue; 31 import static org.testng.Assert.fail; 32 33 import java.nio.ByteBuffer; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.function.Function; 38 import javax.script.Bindings; 39 import javax.script.Invocable; 40 import javax.script.ScriptContext; 41 import javax.script.ScriptEngine; 42 import javax.script.ScriptEngineManager; 43 import javax.script.ScriptException; 44 import org.testng.annotations.Test; 45 46 /** 47 * Tests to check jdk.nashorn.api.scripting.ScriptObjectMirror API. 48 */ 49 public class ScriptObjectMirrorTest { 50 51 @SuppressWarnings("unchecked") 52 @Test 53 public void reflectionTest() throws ScriptException { 54 final ScriptEngineManager m = new ScriptEngineManager(); 55 final ScriptEngine e = m.getEngineByName("nashorn"); 56 57 e.eval("var obj = { x: 344, y: 'nashorn' }"); 58 59 int count = 0; 60 Map<Object, Object> map = (Map<Object, Object>) e.get("obj"); 61 assertFalse(map.isEmpty()); 62 assertTrue(map.keySet().contains("x")); 63 assertTrue(map.containsKey("x")); 64 assertTrue(map.values().contains("nashorn")); 65 assertTrue(map.containsValue("nashorn")); 66 for (final Map.Entry<?, ?> ex : map.entrySet()) { 67 final Object key = ex.getKey(); 68 if (key.equals("x")) { 69 assertTrue(344 == ((Number) ex.getValue()).doubleValue()); 70 count++; 71 } else if (key.equals("y")) { 72 assertEquals(ex.getValue(), "nashorn"); 73 count++; 74 } 75 } 76 assertEquals(2, count); 77 assertEquals(2, map.size()); 78 79 // add property 80 map.put("z", "hello"); 81 assertEquals(e.eval("obj.z"), "hello"); 82 assertEquals(map.get("z"), "hello"); 83 assertTrue(map.keySet().contains("z")); 84 assertTrue(map.containsKey("z")); 85 assertTrue(map.values().contains("hello")); 86 assertTrue(map.containsValue("hello")); 87 assertEquals(map.size(), 3); 88 89 final Map<Object, Object> newMap = new HashMap<>(); 90 newMap.put("foo", 23.0); 91 newMap.put("bar", true); 92 map.putAll(newMap); 93 94 assertEquals(e.eval("obj.foo"), 23.0); 95 assertEquals(e.eval("obj.bar"), true); 96 97 // remove using map method 98 map.remove("foo"); 99 assertEquals(e.eval("typeof obj.foo"), "undefined"); 100 101 count = 0; 102 e.eval("var arr = [ true, 'hello' ]"); 103 map = (Map<Object, Object>) e.get("arr"); 104 assertFalse(map.isEmpty()); 105 assertTrue(map.containsKey("length")); 106 assertTrue(map.containsValue("hello")); 107 for (final Map.Entry<?, ?> ex : map.entrySet()) { 108 final Object key = ex.getKey(); 109 if (key.equals("0")) { 110 assertEquals(ex.getValue(), Boolean.TRUE); 111 count++; 112 } else if (key.equals("1")) { 113 assertEquals(ex.getValue(), "hello"); 114 count++; 115 } 116 } 117 assertEquals(count, 2); 118 assertEquals(map.size(), 2); 119 120 // add element 121 map.put("2", "world"); 122 assertEquals(map.get("2"), "world"); 123 assertEquals(map.size(), 3); 124 125 // remove all 126 map.clear(); 127 assertTrue(map.isEmpty()); 128 assertEquals(e.eval("typeof arr[0]"), "undefined"); 129 assertEquals(e.eval("typeof arr[1]"), "undefined"); 130 assertEquals(e.eval("typeof arr[2]"), "undefined"); 131 } 132 133 @Test 134 public void jsobjectTest() { 135 final ScriptEngineManager m = new ScriptEngineManager(); 136 final ScriptEngine e = m.getEngineByName("nashorn"); 137 try { 138 e.eval("var obj = { '1': 'world', func: function() { return this.bar; }, bar: 'hello' }"); 139 final ScriptObjectMirror obj = (ScriptObjectMirror) e.get("obj"); 140 141 // try basic get on existing properties 142 if (!obj.getMember("bar").equals("hello")) { 143 fail("obj.bar != 'hello'"); 144 } 145 146 if (!obj.getSlot(1).equals("world")) { 147 fail("obj[1] != 'world'"); 148 } 149 150 if (!obj.callMember("func", new Object[0]).equals("hello")) { 151 fail("obj.func() != 'hello'"); 152 } 153 154 // try setting properties 155 obj.setMember("bar", "new-bar"); 156 obj.setSlot(1, "new-element-1"); 157 if (!obj.getMember("bar").equals("new-bar")) { 158 fail("obj.bar != 'new-bar'"); 159 } 160 161 if (!obj.getSlot(1).equals("new-element-1")) { 162 fail("obj[1] != 'new-element-1'"); 163 } 164 165 // try adding properties 166 obj.setMember("prop", "prop-value"); 167 obj.setSlot(12, "element-12"); 168 if (!obj.getMember("prop").equals("prop-value")) { 169 fail("obj.prop != 'prop-value'"); 170 } 171 172 if (!obj.getSlot(12).equals("element-12")) { 173 fail("obj[12] != 'element-12'"); 174 } 175 176 // delete properties 177 obj.removeMember("prop"); 178 if ("prop-value".equals(obj.getMember("prop"))) { 179 fail("obj.prop is not deleted!"); 180 } 181 182 // Simple eval tests 183 assertEquals(obj.eval("typeof Object"), "function"); 184 assertEquals(obj.eval("'nashorn'.substring(3)"), "horn"); 185 } catch (final Exception exp) { 186 exp.printStackTrace(); 187 fail(exp.getMessage()); 188 } 189 } 190 191 @Test 192 public void scriptObjectMirrorToStringTest() { 193 final ScriptEngineManager m = new ScriptEngineManager(); 194 final ScriptEngine e = m.getEngineByName("nashorn"); 195 try { 196 final Object obj = e.eval("new TypeError('wrong type')"); 197 assertEquals(obj.toString(), "TypeError: wrong type", "toString returns wrong value"); 198 } catch (final Throwable t) { 199 t.printStackTrace(); 200 fail(t.getMessage()); 201 } 202 203 try { 204 final Object obj = e.eval("function func() { print('hello'); }"); 205 assertEquals(obj.toString(), "function func() { print('hello'); }", "toString returns wrong value"); 206 } catch (final Throwable t) { 207 t.printStackTrace(); 208 fail(t.getMessage()); 209 } 210 } 211 212 @Test 213 public void mirrorNewObjectGlobalFunctionTest() throws ScriptException { 214 final ScriptEngineManager m = new ScriptEngineManager(); 215 final ScriptEngine e = m.getEngineByName("nashorn"); 216 final ScriptEngine e2 = m.getEngineByName("nashorn"); 217 218 e.eval("function func() {}"); 219 e2.put("foo", e.get("func")); 220 final ScriptObjectMirror e2global = (ScriptObjectMirror)e2.eval("this"); 221 final Object newObj = ((ScriptObjectMirror)e2global.getMember("foo")).newObject(); 222 assertTrue(newObj instanceof ScriptObjectMirror); 223 } 224 225 @Test 226 public void mirrorNewObjectInstanceFunctionTest() throws ScriptException { 227 final ScriptEngineManager m = new ScriptEngineManager(); 228 final ScriptEngine e = m.getEngineByName("nashorn"); 229 final ScriptEngine e2 = m.getEngineByName("nashorn"); 230 231 e.eval("function func() {}"); 232 e2.put("func", e.get("func")); 233 final ScriptObjectMirror e2obj = (ScriptObjectMirror)e2.eval("({ foo: func })"); 234 final Object newObj = ((ScriptObjectMirror)e2obj.getMember("foo")).newObject(); 235 assertTrue(newObj instanceof ScriptObjectMirror); 236 } 237 238 @Test 239 public void indexPropertiesExternalBufferTest() throws ScriptException { 240 final ScriptEngineManager m = new ScriptEngineManager(); 241 final ScriptEngine e = m.getEngineByName("nashorn"); 242 final ScriptObjectMirror obj = (ScriptObjectMirror)e.eval("var obj = {}; obj"); 243 final ByteBuffer buf = ByteBuffer.allocate(5); 244 int i; 245 for (i = 0; i < 5; i++) { 246 buf.put(i, (byte)(i+10)); 247 } 248 obj.setIndexedPropertiesToExternalArrayData(buf); 249 250 for (i = 0; i < 5; i++) { 251 assertEquals((byte)(i+10), ((Number)e.eval("obj[" + i + "]")).byteValue()); 252 } 253 254 e.eval("for (i = 0; i < 5; i++) obj[i] = 0"); 255 for (i = 0; i < 5; i++) { 256 assertEquals((byte)0, ((Number)e.eval("obj[" + i + "]")).byteValue()); 257 assertEquals((byte)0, buf.get(i)); 258 } 259 } 260 261 @Test 262 public void conversionTest() throws ScriptException { 263 final ScriptEngineManager m = new ScriptEngineManager(); 264 final ScriptEngine e = m.getEngineByName("nashorn"); 265 final ScriptObjectMirror arr = (ScriptObjectMirror)e.eval("[33, 45, 23]"); 266 final int[] intArr = arr.to(int[].class); 267 assertEquals(intArr[0], 33); 268 assertEquals(intArr[1], 45); 269 assertEquals(intArr[2], 23); 270 271 final List<?> list = arr.to(List.class); 272 assertEquals(list.get(0), 33); 273 assertEquals(list.get(1), 45); 274 assertEquals(list.get(2), 23); 275 276 ScriptObjectMirror obj = (ScriptObjectMirror)e.eval( 277 "({ valueOf: function() { return 42 } })"); 278 assertEquals(Double.valueOf(42.0), obj.to(Double.class)); 279 280 obj = (ScriptObjectMirror)e.eval( 281 "({ toString: function() { return 'foo' } })"); 282 assertEquals("foo", obj.to(String.class)); 283 } 284 285 // @bug 8044000: Access to undefined property yields "null" instead of "undefined" 286 @Test 287 public void mapScriptObjectMirrorCallsiteTest() throws ScriptException { 288 final ScriptEngineManager m = new ScriptEngineManager(); 289 final ScriptEngine engine = m.getEngineByName("nashorn"); 290 final String TEST_SCRIPT = "typeof obj.foo"; 291 292 final Bindings global = engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE); 293 engine.eval("var obj = java.util.Collections.emptyMap()"); 294 // this will drive callsite "obj.foo" of TEST_SCRIPT 295 // to use "obj instanceof Map" as it's guard 296 engine.eval(TEST_SCRIPT, global); 297 // redefine 'obj' to be a script object 298 engine.eval("obj = {}"); 299 300 final Bindings newGlobal = engine.createBindings(); 301 // transfer 'obj' from default global to new global 302 // new global will get a ScriptObjectMirror wrapping 'obj' 303 newGlobal.put("obj", global.get("obj")); 304 305 // Every ScriptObjectMirror is a Map! If callsite "obj.foo" 306 // does not see the new 'obj' is a ScriptObjectMirror, it'll 307 // continue to use Map's get("obj.foo") instead of ScriptObjectMirror's 308 // getMember("obj.foo") - thereby getting null instead of undefined 309 assertEquals("undefined", engine.eval(TEST_SCRIPT, newGlobal)); 310 } 311 312 public interface MirrorCheckExample { 313 Object test1(Object arg); 314 Object test2(Object arg); 315 boolean compare(Object o1, Object o2); 316 } 317 318 // @bug 8053910: ScriptObjectMirror causing havoc with Invocation interface 319 @Test 320 public void checkMirrorToObject() throws Exception { 321 final ScriptEngineManager engineManager = new ScriptEngineManager(); 322 final ScriptEngine engine = engineManager.getEngineByName("nashorn"); 323 final Invocable invocable = (Invocable)engine; 324 325 engine.eval("function test1(arg) { return { arg: arg }; }"); 326 engine.eval("function test2(arg) { return arg; }"); 327 engine.eval("function compare(arg1, arg2) { return arg1 == arg2; }"); 328 329 final Map<String, Object> map = new HashMap<>(); 330 map.put("option", true); 331 332 final MirrorCheckExample example = invocable.getInterface(MirrorCheckExample.class); 333 334 final Object value1 = invocable.invokeFunction("test1", map); 335 final Object value2 = example.test1(map); 336 final Object value3 = invocable.invokeFunction("test2", value2); 337 final Object value4 = example.test2(value2); 338 339 // check that Object type argument receives a ScriptObjectMirror 340 // when ScriptObject is passed 341 assertEquals(ScriptObjectMirror.class, value1.getClass()); 342 assertEquals(ScriptObjectMirror.class, value2.getClass()); 343 assertEquals(ScriptObjectMirror.class, value3.getClass()); 344 assertEquals(ScriptObjectMirror.class, value4.getClass()); 345 assertTrue((boolean)invocable.invokeFunction("compare", value1, value1)); 346 assertTrue((boolean)example.compare(value1, value1)); 347 assertTrue((boolean)invocable.invokeFunction("compare", value3, value4)); 348 assertTrue((boolean)example.compare(value3, value4)); 349 } 350 351 // @bug 8053910: ScriptObjectMirror causing havoc with Invocation interface 352 @Test 353 @SuppressWarnings("unchecked") 354 public void mirrorUnwrapInterfaceMethod() throws Exception { 355 final ScriptEngineManager engineManager = new ScriptEngineManager(); 356 final ScriptEngine engine = engineManager.getEngineByName("nashorn"); 357 final Invocable invocable = (Invocable)engine; 358 engine.eval("function apply(obj) { " + 359 " return obj instanceof Packages.jdk.nashorn.api.scripting.ScriptObjectMirror; " + 360 "}"); 361 final Function<Object,Object> func = invocable.getInterface(Function.class); 362 assertFalse((boolean)func.apply(engine.eval("({ x: 2 })"))); 363 } 364 }