8081062: ListAdapter should take advantage of JSObject

Tue, 26 May 2015 16:12:23 +0200

author
attila
date
Tue, 26 May 2015 16:12:23 +0200
changeset 1378
45c33270c300
parent 1377
aa83c9841e3c
child 1379
01491258b920

8081062: ListAdapter should take advantage of JSObject
Reviewed-by: lagergren, sundar

src/jdk/nashorn/internal/runtime/JSObjectListAdapter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ListAdapter.java file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/internal/runtime/test/JDK_8081015_Test.java file | annotate | diff | comparison | revisions
     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);

mercurial