Tue, 26 May 2015 16:12:23 +0200
8081062: ListAdapter should take advantage of JSObject
Reviewed-by: lagergren, sundar
1.1 --- a/src/jdk/nashorn/internal/runtime/JSObjectListAdapter.java Wed May 27 16:52:49 2015 +0530 1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 1.3 @@ -1,56 +0,0 @@ 1.4 -/* 1.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 1.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 - * 1.8 - * This code is free software; you can redistribute it and/or modify it 1.9 - * under the terms of the GNU General Public License version 2 only, as 1.10 - * published by the Free Software Foundation. Oracle designates this 1.11 - * particular file as subject to the "Classpath" exception as provided 1.12 - * by Oracle in the LICENSE file that accompanied this code. 1.13 - * 1.14 - * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 - * version 2 for more details (a copy is included in the LICENSE file that 1.18 - * accompanied this code). 1.19 - * 1.20 - * You should have received a copy of the GNU General Public License version 1.21 - * 2 along with this work; if not, write to the Free Software Foundation, 1.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 - * 1.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 - * or visit www.oracle.com if you need additional information or have any 1.26 - * questions. 1.27 - */ 1.28 - 1.29 -package jdk.nashorn.internal.runtime; 1.30 - 1.31 -import jdk.nashorn.api.scripting.JSObject; 1.32 - 1.33 -/** 1.34 - * A ListAdapter that can wraps a JSObject. 1.35 - */ 1.36 -public final class JSObjectListAdapter extends ListAdapter { 1.37 - /** 1.38 - * Creates a new list wrapper for the specified JSObject. 1.39 - * @param obj JSOcript the object to wrap 1.40 - */ 1.41 - public JSObjectListAdapter(final JSObject obj) { 1.42 - super(obj); 1.43 - } 1.44 - 1.45 - @Override 1.46 - public int size() { 1.47 - return JSType.toInt32(((JSObject)obj).getMember("length")); 1.48 - } 1.49 - 1.50 - @Override 1.51 - protected Object getAt(final int index) { 1.52 - return ((JSObject)obj).getSlot(index); 1.53 - } 1.54 - 1.55 - @Override 1.56 - protected void setAt(final int index, final Object element) { 1.57 - ((JSObject)obj).setSlot(index, element); 1.58 - } 1.59 -}
2.1 --- a/src/jdk/nashorn/internal/runtime/ListAdapter.java Wed May 27 16:52:49 2015 +0530 2.2 +++ b/src/jdk/nashorn/internal/runtime/ListAdapter.java Tue May 26 16:12:23 2015 +0200 2.3 @@ -25,17 +25,18 @@ 2.4 2.5 package jdk.nashorn.internal.runtime; 2.6 2.7 +import java.lang.invoke.MethodHandle; 2.8 import java.util.AbstractList; 2.9 import java.util.Deque; 2.10 import java.util.Iterator; 2.11 import java.util.ListIterator; 2.12 import java.util.NoSuchElementException; 2.13 +import java.util.Objects; 2.14 import java.util.RandomAccess; 2.15 import java.util.concurrent.Callable; 2.16 import jdk.nashorn.api.scripting.JSObject; 2.17 import jdk.nashorn.api.scripting.ScriptObjectMirror; 2.18 import jdk.nashorn.internal.runtime.linker.Bootstrap; 2.19 -import jdk.nashorn.internal.runtime.linker.InvokeByName; 2.20 2.21 /** 2.22 * An adapter that can wrap any ECMAScript Array-like object (that adheres to the array rules for the property 2.23 @@ -50,81 +51,56 @@ 2.24 * operations respectively, while {@link #addLast(Object)} and {@link #removeLast()} will translate to {@code push} and 2.25 * {@code pop}. 2.26 */ 2.27 -public abstract class ListAdapter extends AbstractList<Object> implements RandomAccess, Deque<Object> { 2.28 - // These add to the back and front of the list 2.29 - private static final Object PUSH = new Object(); 2.30 - private static InvokeByName getPUSH() { 2.31 - return Context.getGlobal().getInvokeByName(PUSH, 2.32 - new Callable<InvokeByName>() { 2.33 - @Override 2.34 - public InvokeByName call() { 2.35 - return new InvokeByName("push", Object.class, void.class, Object.class); 2.36 - } 2.37 - }); 2.38 +public final class ListAdapter extends AbstractList<Object> implements RandomAccess, Deque<Object> { 2.39 + // Invoker creator for methods that add to the start or end of the list: PUSH and UNSHIFT. Takes fn, this, and value, returns void. 2.40 + private static final Callable<MethodHandle> ADD_INVOKER_CREATOR = invokerCreator(void.class, Object.class, JSObject.class, Object.class); 2.41 + 2.42 + // PUSH adds to the end of the list 2.43 + private static final Object PUSH = new Object(); 2.44 + private static MethodHandle getPushInvoker() { 2.45 + return getDynamicInvoker(PUSH, ADD_INVOKER_CREATOR); 2.46 } 2.47 2.48 + // UNSHIFT adds to the start of the list 2.49 private static final Object UNSHIFT = new Object(); 2.50 - private static InvokeByName getUNSHIFT() { 2.51 - return Context.getGlobal().getInvokeByName(UNSHIFT, 2.52 - new Callable<InvokeByName>() { 2.53 - @Override 2.54 - public InvokeByName call() { 2.55 - return new InvokeByName("unshift", Object.class, void.class, Object.class); 2.56 - } 2.57 - }); 2.58 + private static MethodHandle getUnshiftInvoker() { 2.59 + return getDynamicInvoker(UNSHIFT, ADD_INVOKER_CREATOR); 2.60 } 2.61 2.62 - // These remove from the back and front of the list 2.63 + // Invoker creator for methods that remove from the tail or head of the list: POP and SHIFT. Takes fn, this, returns Object. 2.64 + private static final Callable<MethodHandle> REMOVE_INVOKER_CREATOR = invokerCreator(Object.class, Object.class, JSObject.class); 2.65 + 2.66 + // POP removes from the to the end of the list 2.67 private static final Object POP = new Object(); 2.68 - private static InvokeByName getPOP() { 2.69 - return Context.getGlobal().getInvokeByName(POP, 2.70 - new Callable<InvokeByName>() { 2.71 - @Override 2.72 - public InvokeByName call() { 2.73 - return new InvokeByName("pop", Object.class, Object.class); 2.74 - } 2.75 - }); 2.76 + private static MethodHandle getPopInvoker() { 2.77 + return getDynamicInvoker(POP, REMOVE_INVOKER_CREATOR); 2.78 } 2.79 2.80 + // SHIFT removes from the to the start of the list 2.81 private static final Object SHIFT = new Object(); 2.82 - private static InvokeByName getSHIFT() { 2.83 - return Context.getGlobal().getInvokeByName(SHIFT, 2.84 - new Callable<InvokeByName>() { 2.85 - @Override 2.86 - public InvokeByName call() { 2.87 - return new InvokeByName("shift", Object.class, Object.class); 2.88 - } 2.89 - }); 2.90 + private static MethodHandle getShiftInvoker() { 2.91 + return getDynamicInvoker(SHIFT, REMOVE_INVOKER_CREATOR); 2.92 } 2.93 2.94 - // These insert and remove in the middle of the list 2.95 + // SPLICE can be used to add a value in the middle of the list. 2.96 private static final Object SPLICE_ADD = new Object(); 2.97 - private static InvokeByName getSPLICE_ADD() { 2.98 - return Context.getGlobal().getInvokeByName(SPLICE_ADD, 2.99 - new Callable<InvokeByName>() { 2.100 - @Override 2.101 - public InvokeByName call() { 2.102 - return new InvokeByName("splice", Object.class, void.class, int.class, int.class, Object.class); 2.103 - } 2.104 - }); 2.105 + private static final Callable<MethodHandle> SPLICE_ADD_INVOKER_CREATOR = invokerCreator(void.class, Object.class, JSObject.class, int.class, int.class, Object.class); 2.106 + private static MethodHandle getSpliceAddInvoker() { 2.107 + return getDynamicInvoker(SPLICE_ADD, SPLICE_ADD_INVOKER_CREATOR); 2.108 } 2.109 2.110 + // SPLICE can also be used to remove values from the middle of the list. 2.111 private static final Object SPLICE_REMOVE = new Object(); 2.112 - private static InvokeByName getSPLICE_REMOVE() { 2.113 - return Context.getGlobal().getInvokeByName(SPLICE_REMOVE, 2.114 - new Callable<InvokeByName>() { 2.115 - @Override 2.116 - public InvokeByName call() { 2.117 - return new InvokeByName("splice", Object.class, void.class, int.class, int.class); 2.118 - } 2.119 - }); 2.120 + private static final Callable<MethodHandle> SPLICE_REMOVE_INVOKER_CREATOR = invokerCreator(void.class, Object.class, JSObject.class, int.class, int.class); 2.121 + private static MethodHandle getSpliceRemoveInvoker() { 2.122 + return getDynamicInvoker(SPLICE_REMOVE, SPLICE_REMOVE_INVOKER_CREATOR); 2.123 } 2.124 2.125 /** wrapped object */ 2.126 - protected final Object obj; 2.127 + protected final JSObject obj; 2.128 2.129 // allow subclasses only in this package 2.130 - ListAdapter(final Object obj) { 2.131 + ListAdapter(final JSObject obj) { 2.132 this.obj = obj; 2.133 } 2.134 2.135 @@ -135,14 +111,16 @@ 2.136 * @return A ListAdapter wrapper object 2.137 */ 2.138 public static ListAdapter create(final Object obj) { 2.139 + return new ListAdapter(getJSObject(obj)); 2.140 + } 2.141 + 2.142 + private static JSObject getJSObject(final Object obj) { 2.143 if (obj instanceof ScriptObject) { 2.144 - final Object mirror = ScriptObjectMirror.wrap(obj, Context.getGlobal()); 2.145 - return new JSObjectListAdapter((JSObject)mirror); 2.146 + return (JSObject)ScriptObjectMirror.wrap(obj, Context.getGlobal()); 2.147 } else if (obj instanceof JSObject) { 2.148 - return new JSObjectListAdapter((JSObject)obj); 2.149 - } else { 2.150 - throw new IllegalArgumentException("ScriptObject or JSObject expected"); 2.151 + return (JSObject)obj; 2.152 } 2.153 + throw new IllegalArgumentException("ScriptObject or JSObject expected"); 2.154 } 2.155 2.156 @Override 2.157 @@ -151,28 +129,18 @@ 2.158 return getAt(index); 2.159 } 2.160 2.161 - /** 2.162 - * Get object at an index 2.163 - * @param index index in list 2.164 - * @return object 2.165 - */ 2.166 - protected abstract Object getAt(final int index); 2.167 + private Object getAt(final int index) { 2.168 + return obj.getSlot(index); 2.169 + } 2.170 2.171 @Override 2.172 public Object set(final int index, final Object element) { 2.173 checkRange(index); 2.174 final Object prevValue = getAt(index); 2.175 - setAt(index, element); 2.176 + obj.setSlot(index, element); 2.177 return prevValue; 2.178 } 2.179 2.180 - /** 2.181 - * Set object at an index 2.182 - * @param index index in list 2.183 - * @param element element 2.184 - */ 2.185 - protected abstract void setAt(final int index, final Object element); 2.186 - 2.187 private void checkRange(final int index) { 2.188 if(index < 0 || index >= size()) { 2.189 throw invalidIndex(index); 2.190 @@ -180,6 +148,11 @@ 2.191 } 2.192 2.193 @Override 2.194 + public int size() { 2.195 + return JSType.toInt32(obj.getMember("length")); 2.196 + } 2.197 + 2.198 + @Override 2.199 public final void push(final Object e) { 2.200 addFirst(e); 2.201 } 2.202 @@ -193,10 +166,7 @@ 2.203 @Override 2.204 public final void addFirst(final Object e) { 2.205 try { 2.206 - final InvokeByName unshiftInvoker = getUNSHIFT(); 2.207 - final Object fn = unshiftInvoker.getGetter().invokeExact(obj); 2.208 - checkFunction(fn, unshiftInvoker); 2.209 - unshiftInvoker.getInvoker().invokeExact(fn, obj, e); 2.210 + getUnshiftInvoker().invokeExact(getFunction("unshift"), obj, e); 2.211 } catch(RuntimeException | Error ex) { 2.212 throw ex; 2.213 } catch(final Throwable t) { 2.214 @@ -207,10 +177,7 @@ 2.215 @Override 2.216 public final void addLast(final Object e) { 2.217 try { 2.218 - final InvokeByName pushInvoker = getPUSH(); 2.219 - final Object fn = pushInvoker.getGetter().invokeExact(obj); 2.220 - checkFunction(fn, pushInvoker); 2.221 - pushInvoker.getInvoker().invokeExact(fn, obj, e); 2.222 + getPushInvoker().invokeExact(getFunction("push"), obj, e); 2.223 } catch(RuntimeException | Error ex) { 2.224 throw ex; 2.225 } catch(final Throwable t) { 2.226 @@ -228,10 +195,7 @@ 2.227 } else { 2.228 final int size = size(); 2.229 if(index < size) { 2.230 - final InvokeByName spliceAddInvoker = getSPLICE_ADD(); 2.231 - final Object fn = spliceAddInvoker.getGetter().invokeExact(obj); 2.232 - checkFunction(fn, spliceAddInvoker); 2.233 - spliceAddInvoker.getInvoker().invokeExact(fn, obj, index, 0, e); 2.234 + getSpliceAddInvoker().invokeExact(obj.getMember("splice"), obj, index, 0, e); 2.235 } else if(index == size) { 2.236 addLast(e); 2.237 } else { 2.238 @@ -244,10 +208,12 @@ 2.239 throw new RuntimeException(t); 2.240 } 2.241 } 2.242 - private static void checkFunction(final Object fn, final InvokeByName invoke) { 2.243 + private Object getFunction(final String name) { 2.244 + final Object fn = obj.getMember(name); 2.245 if(!(Bootstrap.isCallable(fn))) { 2.246 - throw new UnsupportedOperationException("The script object doesn't have a function named " + invoke.getName()); 2.247 + throw new UnsupportedOperationException("The script object doesn't have a function named " + name); 2.248 } 2.249 + return fn; 2.250 } 2.251 2.252 private static IndexOutOfBoundsException invalidIndex(final int index) { 2.253 @@ -321,10 +287,7 @@ 2.254 2.255 private Object invokeShift() { 2.256 try { 2.257 - final InvokeByName shiftInvoker = getSHIFT(); 2.258 - final Object fn = shiftInvoker.getGetter().invokeExact(obj); 2.259 - checkFunction(fn, shiftInvoker); 2.260 - return shiftInvoker.getInvoker().invokeExact(fn, obj); 2.261 + return getShiftInvoker().invokeExact(getFunction("shift"), obj); 2.262 } catch(RuntimeException | Error ex) { 2.263 throw ex; 2.264 } catch(final Throwable t) { 2.265 @@ -334,10 +297,7 @@ 2.266 2.267 private Object invokePop() { 2.268 try { 2.269 - final InvokeByName popInvoker = getPOP(); 2.270 - final Object fn = popInvoker.getGetter().invokeExact(obj); 2.271 - checkFunction(fn, popInvoker); 2.272 - return popInvoker.getInvoker().invokeExact(fn, obj); 2.273 + return getPopInvoker().invokeExact(getFunction("pop"), obj); 2.274 } catch(RuntimeException | Error ex) { 2.275 throw ex; 2.276 } catch(final Throwable t) { 2.277 @@ -352,10 +312,7 @@ 2.278 2.279 private void invokeSpliceRemove(final int fromIndex, final int count) { 2.280 try { 2.281 - final InvokeByName spliceRemoveInvoker = getSPLICE_REMOVE(); 2.282 - final Object fn = spliceRemoveInvoker.getGetter().invokeExact(obj); 2.283 - checkFunction(fn, spliceRemoveInvoker); 2.284 - spliceRemoveInvoker.getInvoker().invokeExact(fn, obj, fromIndex, count); 2.285 + getSpliceRemoveInvoker().invokeExact(getFunction("splice"), obj, fromIndex, count); 2.286 } catch(RuntimeException | Error ex) { 2.287 throw ex; 2.288 } catch(final Throwable t) { 2.289 @@ -443,12 +400,24 @@ 2.290 2.291 private static boolean removeOccurrence(final Object o, final Iterator<Object> it) { 2.292 while(it.hasNext()) { 2.293 - final Object e = it.next(); 2.294 - if(o == null ? e == null : o.equals(e)) { 2.295 + if(Objects.equals(o, it.next())) { 2.296 it.remove(); 2.297 return true; 2.298 } 2.299 } 2.300 return false; 2.301 } 2.302 + 2.303 + private static Callable<MethodHandle> invokerCreator(final Class<?> rtype, final Class<?>... ptypes) { 2.304 + return new Callable<MethodHandle>() { 2.305 + @Override 2.306 + public MethodHandle call() { 2.307 + return Bootstrap.createDynamicInvoker("dyn:call", rtype, ptypes); 2.308 + } 2.309 + }; 2.310 + } 2.311 + 2.312 + private static MethodHandle getDynamicInvoker(final Object key, final Callable<MethodHandle> creator) { 2.313 + return Context.getGlobal().getDynamicInvoker(key, creator); 2.314 + } 2.315 }
3.1 --- a/test/src/jdk/nashorn/internal/runtime/test/JDK_8081015_Test.java Wed May 27 16:52:49 2015 +0530 3.2 +++ b/test/src/jdk/nashorn/internal/runtime/test/JDK_8081015_Test.java Tue May 26 16:12:23 2015 +0200 3.3 @@ -28,19 +28,19 @@ 3.4 import static org.testng.Assert.assertEquals; 3.5 import static org.testng.Assert.assertNull; 3.6 3.7 -import jdk.nashorn.test.models.JDK_8081015_TestModel; 3.8 - 3.9 import java.util.Collection; 3.10 import java.util.Queue; 3.11 import javax.script.ScriptEngine; 3.12 import javax.script.ScriptException; 3.13 import jdk.nashorn.api.scripting.NashornScriptEngineFactory; 3.14 +import jdk.nashorn.test.models.JDK_8081015_TestModel; 3.15 import org.testng.annotations.Test; 3.16 3.17 /** 3.18 * @bug 8081015 3.19 * @summary Test that native arrays get converted to {@link Queue} and {@link Collection}. 3.20 */ 3.21 +@SuppressWarnings("javadoc") 3.22 public class JDK_8081015_Test { 3.23 @Test 3.24 public void testConvertToCollection() throws ScriptException { 3.25 @@ -62,7 +62,7 @@ 3.26 test("receiveQueue"); 3.27 } 3.28 3.29 - private void test(final String methodName) throws ScriptException { 3.30 + private static void test(final String methodName) throws ScriptException { 3.31 final ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); 3.32 final JDK_8081015_TestModel model = new JDK_8081015_TestModel(); 3.33 engine.put("test", model);