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