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