diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/ListAdapter.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/ListAdapter.java | 177 |
1 files changed, 67 insertions, 110 deletions
diff --git a/src/jdk/nashorn/internal/runtime/ListAdapter.java b/src/jdk/nashorn/internal/runtime/ListAdapter.java index 25179a59..4d0e169a 100644 --- a/src/jdk/nashorn/internal/runtime/ListAdapter.java +++ b/src/jdk/nashorn/internal/runtime/ListAdapter.java @@ -25,17 +25,19 @@ package jdk.nashorn.internal.runtime; +import java.lang.invoke.MethodHandle; import java.util.AbstractList; import java.util.Deque; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.RandomAccess; import java.util.concurrent.Callable; import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.api.scripting.ScriptObjectMirror; +import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.linker.Bootstrap; -import jdk.nashorn.internal.runtime.linker.InvokeByName; /** * An adapter that can wrap any ECMAScript Array-like object (that adheres to the array rules for the property @@ -50,82 +52,43 @@ import jdk.nashorn.internal.runtime.linker.InvokeByName; * operations respectively, while {@link #addLast(Object)} and {@link #removeLast()} will translate to {@code push} and * {@code pop}. */ -public abstract class ListAdapter extends AbstractList<Object> implements RandomAccess, Deque<Object> { - // These add to the back and front of the list - private static final Object PUSH = new Object(); - private static InvokeByName getPUSH() { - return Context.getGlobal().getInvokeByName(PUSH, - new Callable<InvokeByName>() { - @Override - public InvokeByName call() { - return new InvokeByName("push", Object.class, void.class, Object.class); - } - }); - } +public class ListAdapter extends AbstractList<Object> implements RandomAccess, Deque<Object> { + // Invoker creator for methods that add to the start or end of the list: PUSH and UNSHIFT. Takes fn, this, and value, returns void. + private static final Callable<MethodHandle> ADD_INVOKER_CREATOR = invokerCreator(void.class, Object.class, JSObject.class, Object.class); + // PUSH adds to the start of the list + private static final Object PUSH = new Object(); + // UNSHIFT adds to the end of the list private static final Object UNSHIFT = new Object(); - private static InvokeByName getUNSHIFT() { - return Context.getGlobal().getInvokeByName(UNSHIFT, - new Callable<InvokeByName>() { - @Override - public InvokeByName call() { - return new InvokeByName("unshift", Object.class, void.class, Object.class); - } - }); - } - // These remove from the back and front of the list - private static final Object POP = new Object(); - private static InvokeByName getPOP() { - return Context.getGlobal().getInvokeByName(POP, - new Callable<InvokeByName>() { - @Override - public InvokeByName call() { - return new InvokeByName("pop", Object.class, Object.class); - } - }); - } + // Invoker creator for methods that remove from the tail or head of the list: POP and SHIFT. Takes fn, this, returns Object. + private static final Callable<MethodHandle> REMOVE_INVOKER_CREATOR = invokerCreator(Object.class, Object.class, JSObject.class); + // POP removes from the start of the list + private static final Object POP = new Object(); + // SHIFT removes from the end of the list private static final Object SHIFT = new Object(); - private static InvokeByName getSHIFT() { - return Context.getGlobal().getInvokeByName(SHIFT, - new Callable<InvokeByName>() { - @Override - public InvokeByName call() { - return new InvokeByName("shift", Object.class, Object.class); - } - }); - } - // These insert and remove in the middle of the list + // SPLICE can be used to add a value in the middle of the list. private static final Object SPLICE_ADD = new Object(); - private static InvokeByName getSPLICE_ADD() { - return Context.getGlobal().getInvokeByName(SPLICE_ADD, - new Callable<InvokeByName>() { - @Override - public InvokeByName call() { - return new InvokeByName("splice", Object.class, void.class, int.class, int.class, Object.class); - } - }); - } + private static final Callable<MethodHandle> SPLICE_ADD_INVOKER_CREATOR = invokerCreator(void.class, Object.class, JSObject.class, int.class, int.class, Object.class); + // SPLICE can also be used to remove values from the middle of the list. private static final Object SPLICE_REMOVE = new Object(); - private static InvokeByName getSPLICE_REMOVE() { - return Context.getGlobal().getInvokeByName(SPLICE_REMOVE, - new Callable<InvokeByName>() { - @Override - public InvokeByName call() { - return new InvokeByName("splice", Object.class, void.class, int.class, int.class); - } - }); - } + private static final Callable<MethodHandle> SPLICE_REMOVE_INVOKER_CREATOR = invokerCreator(void.class, Object.class, JSObject.class, int.class, int.class); /** wrapped object */ - protected final Object obj; + final JSObject obj; + private final Global global; // allow subclasses only in this package - ListAdapter(final Object obj) { + ListAdapter(final JSObject obj, final Global global) { + if (global == null) { + throw new IllegalStateException(ECMAErrors.getMessage("list.adapter.null.global")); + } + this.obj = obj; + this.global = global; } /** @@ -135,14 +98,17 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random * @return A ListAdapter wrapper object */ public static ListAdapter create(final Object obj) { + final Global global = Context.getGlobal(); + return new ListAdapter(getJSObject(obj, global), global); + } + + private static JSObject getJSObject(final Object obj, final Global global) { if (obj instanceof ScriptObject) { - final Object mirror = ScriptObjectMirror.wrap(obj, Context.getGlobal()); - return new JSObjectListAdapter((JSObject)mirror); + return (JSObject)ScriptObjectMirror.wrap(obj, global); } else if (obj instanceof JSObject) { - return new JSObjectListAdapter((JSObject)obj); - } else { - throw new IllegalArgumentException("ScriptObject or JSObject expected"); + return (JSObject)obj; } + throw new IllegalArgumentException("ScriptObject or JSObject expected"); } @Override @@ -151,28 +117,18 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random return getAt(index); } - /** - * Get object at an index - * @param index index in list - * @return object - */ - protected abstract Object getAt(final int index); + private Object getAt(final int index) { + return obj.getSlot(index); + } @Override public Object set(final int index, final Object element) { checkRange(index); final Object prevValue = getAt(index); - setAt(index, element); + obj.setSlot(index, element); return prevValue; } - /** - * Set object at an index - * @param index index in list - * @param element element - */ - protected abstract void setAt(final int index, final Object element); - private void checkRange(final int index) { if(index < 0 || index >= size()) { throw invalidIndex(index); @@ -180,6 +136,11 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } @Override + public int size() { + return JSType.toInt32(obj.getMember("length")); + } + + @Override public final void push(final Object e) { addFirst(e); } @@ -193,10 +154,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random @Override public final void addFirst(final Object e) { try { - final InvokeByName unshiftInvoker = getUNSHIFT(); - final Object fn = unshiftInvoker.getGetter().invokeExact(obj); - checkFunction(fn, unshiftInvoker); - unshiftInvoker.getInvoker().invokeExact(fn, obj, e); + getDynamicInvoker(UNSHIFT, ADD_INVOKER_CREATOR).invokeExact(getFunction("unshift"), obj, e); } catch(RuntimeException | Error ex) { throw ex; } catch(final Throwable t) { @@ -207,10 +165,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random @Override public final void addLast(final Object e) { try { - final InvokeByName pushInvoker = getPUSH(); - final Object fn = pushInvoker.getGetter().invokeExact(obj); - checkFunction(fn, pushInvoker); - pushInvoker.getInvoker().invokeExact(fn, obj, e); + getDynamicInvoker(PUSH, ADD_INVOKER_CREATOR).invokeExact(getFunction("push"), obj, e); } catch(RuntimeException | Error ex) { throw ex; } catch(final Throwable t) { @@ -228,10 +183,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random } else { final int size = size(); if(index < size) { - final InvokeByName spliceAddInvoker = getSPLICE_ADD(); - final Object fn = spliceAddInvoker.getGetter().invokeExact(obj); - checkFunction(fn, spliceAddInvoker); - spliceAddInvoker.getInvoker().invokeExact(fn, obj, index, 0, e); + getDynamicInvoker(SPLICE_ADD, SPLICE_ADD_INVOKER_CREATOR).invokeExact(obj.getMember("splice"), obj, index, 0, e); } else if(index == size) { addLast(e); } else { @@ -244,10 +196,12 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random throw new RuntimeException(t); } } - private static void checkFunction(final Object fn, final InvokeByName invoke) { + private Object getFunction(final String name) { + final Object fn = obj.getMember(name); if(!(Bootstrap.isCallable(fn))) { - throw new UnsupportedOperationException("The script object doesn't have a function named " + invoke.getName()); + throw new UnsupportedOperationException("The script object doesn't have a function named " + name); } + return fn; } private static IndexOutOfBoundsException invalidIndex(final int index) { @@ -321,10 +275,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random private Object invokeShift() { try { - final InvokeByName shiftInvoker = getSHIFT(); - final Object fn = shiftInvoker.getGetter().invokeExact(obj); - checkFunction(fn, shiftInvoker); - return shiftInvoker.getInvoker().invokeExact(fn, obj); + return getDynamicInvoker(SHIFT, REMOVE_INVOKER_CREATOR).invokeExact(getFunction("shift"), obj); } catch(RuntimeException | Error ex) { throw ex; } catch(final Throwable t) { @@ -334,10 +285,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random private Object invokePop() { try { - final InvokeByName popInvoker = getPOP(); - final Object fn = popInvoker.getGetter().invokeExact(obj); - checkFunction(fn, popInvoker); - return popInvoker.getInvoker().invokeExact(fn, obj); + return getDynamicInvoker(POP, REMOVE_INVOKER_CREATOR).invokeExact(getFunction("pop"), obj); } catch(RuntimeException | Error ex) { throw ex; } catch(final Throwable t) { @@ -352,10 +300,7 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random private void invokeSpliceRemove(final int fromIndex, final int count) { try { - final InvokeByName spliceRemoveInvoker = getSPLICE_REMOVE(); - final Object fn = spliceRemoveInvoker.getGetter().invokeExact(obj); - checkFunction(fn, spliceRemoveInvoker); - spliceRemoveInvoker.getInvoker().invokeExact(fn, obj, fromIndex, count); + getDynamicInvoker(SPLICE_REMOVE, SPLICE_REMOVE_INVOKER_CREATOR).invokeExact(getFunction("splice"), obj, fromIndex, count); } catch(RuntimeException | Error ex) { throw ex; } catch(final Throwable t) { @@ -443,12 +388,24 @@ public abstract class ListAdapter extends AbstractList<Object> implements Random private static boolean removeOccurrence(final Object o, final Iterator<Object> it) { while(it.hasNext()) { - final Object e = it.next(); - if(o == null ? e == null : o.equals(e)) { + if(Objects.equals(o, it.next())) { it.remove(); return true; } } return false; } + + private static Callable<MethodHandle> invokerCreator(final Class<?> rtype, final Class<?>... ptypes) { + return new Callable<MethodHandle>() { + @Override + public MethodHandle call() { + return Bootstrap.createDynamicInvoker("dyn:call", rtype, ptypes); + } + }; + } + + private MethodHandle getDynamicInvoker(final Object key, final Callable<MethodHandle> creator) { + return global.getDynamicInvoker(key, creator); + } } |