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