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