PluggableJSObjectTest.java revision 1445:8535274223d7
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.IntBuffer; 34import java.util.Collection; 35import java.util.HashMap; 36import java.util.LinkedHashMap; 37import java.util.Set; 38import javax.script.Invocable; 39import javax.script.ScriptEngine; 40import javax.script.ScriptEngineManager; 41import jdk.nashorn.api.scripting.AbstractJSObject; 42import jdk.nashorn.api.scripting.ScriptObjectMirror; 43import org.testng.annotations.Test; 44 45/** 46 * Tests for pluggable external impls. of jdk.nashorn.api.scripting.JSObject. 47 * 48 * JDK-8024615: Refactor ScriptObjectMirror and JSObject to support external 49 * JSObject implementations. 50 */ 51@SuppressWarnings("javadoc") 52public class PluggableJSObjectTest { 53 public static class MapWrapperObject extends AbstractJSObject { 54 private final HashMap<String, Object> map = new LinkedHashMap<>(); 55 56 public HashMap<String, Object> getMap() { 57 return map; 58 } 59 60 @Override 61 public Object getMember(final String name) { 62 return map.get(name); 63 } 64 65 @Override 66 public void setMember(final String name, final Object value) { 67 map.put(name, value); 68 } 69 70 @Override 71 public boolean hasMember(final String name) { 72 return map.containsKey(name); 73 } 74 75 @Override 76 public void removeMember(final String name) { 77 map.remove(name); 78 } 79 80 @Override 81 public Set<String> keySet() { 82 return map.keySet(); 83 } 84 85 @Override 86 public Collection<Object> values() { 87 return map.values(); 88 } 89 } 90 91 @Test 92 // Named property access on a JSObject 93 public void namedAccessTest() { 94 final ScriptEngineManager m = new ScriptEngineManager(); 95 final ScriptEngine e = m.getEngineByName("nashorn"); 96 try { 97 final MapWrapperObject obj = new MapWrapperObject(); 98 e.put("obj", obj); 99 obj.getMap().put("foo", "bar"); 100 101 // property-like access on MapWrapperObject objects 102 assertEquals(e.eval("obj.foo"), "bar"); 103 e.eval("obj.foo = 'hello'"); 104 assertEquals(e.eval("'foo' in obj"), Boolean.TRUE); 105 assertEquals(e.eval("obj.foo"), "hello"); 106 assertEquals(obj.getMap().get("foo"), "hello"); 107 e.eval("delete obj.foo"); 108 assertFalse(obj.getMap().containsKey("foo")); 109 assertEquals(e.eval("'foo' in obj"), Boolean.FALSE); 110 } catch (final Exception exp) { 111 exp.printStackTrace(); 112 fail(exp.getMessage()); 113 } 114 } 115 116 // @bug 8062030: Nashorn bug retrieving array property after key string concatenation 117 @Test 118 // ConsString attribute access on a JSObject 119 public void consStringTest() { 120 final ScriptEngineManager m = new ScriptEngineManager(); 121 final ScriptEngine e = m.getEngineByName("nashorn"); 122 try { 123 final MapWrapperObject obj = new MapWrapperObject(); 124 e.put("obj", obj); 125 e.put("f", "f"); 126 e.eval("obj[f + 'oo'] = 'bar';"); 127 128 assertEquals(obj.getMap().get("foo"), "bar"); 129 assertEquals(e.eval("obj[f + 'oo']"), "bar"); 130 assertEquals(e.eval("obj['foo']"), "bar"); 131 assertEquals(e.eval("f + 'oo' in obj"), Boolean.TRUE); 132 assertEquals(e.eval("'foo' in obj"), Boolean.TRUE); 133 e.eval("delete obj[f + 'oo']"); 134 assertFalse(obj.getMap().containsKey("foo")); 135 assertEquals(e.eval("obj[f + 'oo']"), null); 136 assertEquals(e.eval("obj['foo']"), null); 137 assertEquals(e.eval("f + 'oo' in obj"), Boolean.FALSE); 138 assertEquals(e.eval("'foo' in obj"), Boolean.FALSE); 139 } catch (final Exception exp) { 140 exp.printStackTrace(); 141 fail(exp.getMessage()); 142 } 143 } 144 145 public static class BufferObject extends AbstractJSObject { 146 private final IntBuffer buf; 147 148 public BufferObject(final int size) { 149 buf = IntBuffer.allocate(size); 150 } 151 152 public IntBuffer getBuffer() { 153 return buf; 154 } 155 156 @Override 157 public Object getMember(final String name) { 158 return name.equals("length")? buf.capacity() : null; 159 } 160 161 @Override 162 public boolean hasSlot(final int i) { 163 return i > -1 && i < buf.capacity(); 164 } 165 166 @Override 167 public Object getSlot(final int i) { 168 return buf.get(i); 169 } 170 171 @Override 172 public void setSlot(final int i, final Object value) { 173 buf.put(i, ((Number)value).intValue()); 174 } 175 176 @Override 177 public boolean isArray() { 178 return true; 179 } 180 } 181 182 @Test 183 // array-like indexed access for a JSObject 184 public void indexedAccessTest() { 185 final ScriptEngineManager m = new ScriptEngineManager(); 186 final ScriptEngine e = m.getEngineByName("nashorn"); 187 try { 188 final BufferObject buf = new BufferObject(2); 189 e.put("buf", buf); 190 191 // array-like access on BufferObject objects 192 assertEquals(e.eval("buf.length"), buf.getBuffer().capacity()); 193 e.eval("buf[0] = 23"); 194 assertEquals(buf.getBuffer().get(0), 23); 195 assertEquals(e.eval("buf[0]"), 23); 196 assertEquals(e.eval("buf[1]"), 0); 197 buf.getBuffer().put(1, 42); 198 assertEquals(e.eval("buf[1]"), 42); 199 assertEquals(e.eval("Array.isArray(buf)"), Boolean.TRUE); 200 } catch (final Exception exp) { 201 exp.printStackTrace(); 202 fail(exp.getMessage()); 203 } 204 } 205 206 public static class Adder extends AbstractJSObject { 207 @Override 208 public Object call(final Object thiz, final Object... args) { 209 double res = 0.0; 210 for (final Object arg : args) { 211 res += ((Number)arg).doubleValue(); 212 } 213 return res; 214 } 215 216 @Override 217 public boolean isFunction() { 218 return true; 219 } 220 } 221 222 @Test 223 // a callable JSObject 224 public void callableJSObjectTest() { 225 final ScriptEngineManager m = new ScriptEngineManager(); 226 final ScriptEngine e = m.getEngineByName("nashorn"); 227 try { 228 e.put("sum", new Adder()); 229 // check callability of Adder objects 230 assertEquals(e.eval("typeof sum"), "function"); 231 assertEquals(((Number)e.eval("sum(1, 2, 3, 4, 5)")).intValue(), 15); 232 } catch (final Exception exp) { 233 exp.printStackTrace(); 234 fail(exp.getMessage()); 235 } 236 } 237 238 public static class Factory extends AbstractJSObject { 239 @SuppressWarnings("unused") 240 @Override 241 public Object newObject(final Object... args) { 242 return new HashMap<Object, Object>(); 243 } 244 245 @Override 246 public boolean isFunction() { 247 return true; 248 } 249 } 250 251 @Test 252 // a factory JSObject 253 public void factoryJSObjectTest() { 254 final ScriptEngineManager m = new ScriptEngineManager(); 255 final ScriptEngine e = m.getEngineByName("nashorn"); 256 try { 257 e.put("Factory", new Factory()); 258 259 // check new on Factory 260 assertEquals(e.eval("typeof Factory"), "function"); 261 assertEquals(e.eval("typeof new Factory()"), "object"); 262 assertEquals(e.eval("(new Factory()) instanceof java.util.Map"), Boolean.TRUE); 263 } catch (final Exception exp) { 264 exp.printStackTrace(); 265 fail(exp.getMessage()); 266 } 267 } 268 269 @Test 270 // iteration tests 271 public void iteratingJSObjectTest() { 272 final ScriptEngineManager m = new ScriptEngineManager(); 273 final ScriptEngine e = m.getEngineByName("nashorn"); 274 try { 275 final MapWrapperObject obj = new MapWrapperObject(); 276 obj.setMember("foo", "hello"); 277 obj.setMember("bar", "world"); 278 e.put("obj", obj); 279 280 // check for..in 281 Object val = e.eval("var str = ''; for (i in obj) str += i; str"); 282 assertEquals(val.toString(), "foobar"); 283 284 // check for..each..in 285 val = e.eval("var str = ''; for each (i in obj) str += i; str"); 286 assertEquals(val.toString(), "helloworld"); 287 } catch (final Exception exp) { 288 exp.printStackTrace(); 289 fail(exp.getMessage()); 290 } 291 } 292 293 // @bug 8137258: JSObjectLinker and BrowserJSObjectLinker should not expose internal JS objects 294 @Test 295 public void hidingInternalObjectsForJSObjectTest() throws Exception { 296 final ScriptEngineManager engineManager = new ScriptEngineManager(); 297 final ScriptEngine e = engineManager.getEngineByName("nashorn"); 298 299 final String code = "function func(obj) { obj.foo = [5, 5]; obj.bar = {} }"; 300 e.eval(code); 301 302 // call the exposed function but pass user defined JSObject impl as argument 303 ((Invocable)e).invokeFunction("func", new AbstractJSObject() { 304 @Override 305 public void setMember(final String name, final Object value) { 306 // make sure that wrapped objects are passed (and not internal impl. objects) 307 assertTrue(value.getClass() == ScriptObjectMirror.class); 308 } 309 }); 310 } 311} 312