changeset 638:9a13e95cc40f

Merge
author sundar
date Tue, 15 Oct 2013 22:13:56 +0530
parents 1b0a71a9920a (current diff) b3ee112a328e (diff)
children adc5639fc4b9
files
diffstat 19 files changed, 1032 insertions(+), 225 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Tue Oct 15 22:13:56 2013 +0530
@@ -91,6 +91,7 @@
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.internal.dynalink.support.Guards;
+import jdk.internal.dynalink.support.Lookup;
 
 /**
  * Base class for dynamic methods that dispatch to a single target Java method or constructor. Handles adaptation of the
@@ -100,6 +101,9 @@
  * @version $Id: $
  */
 abstract class SingleDynamicMethod extends DynamicMethod {
+
+    private static final MethodHandle CAN_CONVERT_TO = Lookup.findOwnStatic(MethodHandles.lookup(), "canConvertTo", boolean.class, LinkerServices.class, Class.class, Object.class);
+
     SingleDynamicMethod(String name) {
         super(name);
     }
@@ -201,23 +205,69 @@
                 return createConvertingInvocation(target, linkerServices, callSiteType).asVarargsCollector(
                         callSiteLastArgType);
             }
-            if(!linkerServices.canConvert(callSiteLastArgType, varArgType)) {
-                // Call site signature guarantees the argument can definitely not be an array (i.e. it is primitive);
-                // link immediately to a vararg-packing method handle.
-                return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
+
+            // This method handle takes the single argument and packs it into a newly allocated single-element array. It
+            // will be used when the incoming argument can't be converted to the vararg array type (the "vararg packer"
+            // method).
+            final MethodHandle varArgCollectingInvocation = createConvertingInvocation(collectArguments(fixTarget,
+                    argsLen), linkerServices, callSiteType);
+
+            // Is call site type assignable from an array type (e.g. Object:int[], or Object[]:String[])
+            final boolean isAssignableFromArray = callSiteLastArgType.isAssignableFrom(varArgType);
+            // Do we have a custom conversion that can potentially convert the call site type to an array?
+            final boolean isCustomConvertible = linkerServices.canConvert(callSiteLastArgType, varArgType);
+            if(!isAssignableFromArray && !isCustomConvertible) {
+                // Call site signature guarantees the argument can definitely not be converted to an array (i.e. it is
+                // primitive), and no conversion can help with it either. Link immediately to a vararg-packing method
+                // handle.
+                return varArgCollectingInvocation;
             }
-            // Call site signature makes no guarantees that the single argument in the vararg position will be
-            // compatible across all invocations. Need to insert an appropriate guard and fall back to generic vararg
-            // method when it is not.
-            return MethodHandles.guardWithTest(Guards.isInstance(varArgType, fixParamsLen, callSiteType),
-                    createConvertingInvocation(fixTarget, linkerServices, callSiteType),
-                    createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType));
+
+            // This method handle employs language-specific conversions to convert the last argument into an array of
+            // vararg type.
+            final MethodHandle arrayConvertingInvocation = createConvertingInvocation(MethodHandles.filterArguments(
+                    fixTarget, fixParamsLen, linkerServices.getTypeConverter(callSiteLastArgType, varArgType)),
+                    linkerServices, callSiteType);
+
+            // This method handle determines whether the value can be converted to the array of vararg type using a
+            // language-specific conversion.
+            final MethodHandle canConvertArgToArray = MethodHandles.insertArguments(CAN_CONVERT_TO, 0, linkerServices,
+                    varArgType);
+
+            // This one adjusts the previous one for the location of the argument and the call site type.
+            final MethodHandle canConvertLastArgToArray = MethodHandles.dropArguments(canConvertArgToArray, 0,
+                    MethodType.genericMethodType(fixParamsLen).parameterList()).asType(callSiteType.changeReturnType(boolean.class));
+
+            // This one takes the previous ones and combines them into a method handle that converts the argument into
+            // a vararg array when it can, otherwise falls back to the vararg packer.
+            final MethodHandle convertToArrayWhenPossible = MethodHandles.guardWithTest(canConvertLastArgToArray,
+                    arrayConvertingInvocation, varArgCollectingInvocation);
+
+            if(isAssignableFromArray) {
+                return MethodHandles.guardWithTest(
+                        // Is incoming parameter already a compatible array?
+                        Guards.isInstance(varArgType, fixParamsLen, callSiteType),
+                        // Yes: just pass it to the method
+                        createConvertingInvocation(fixTarget, linkerServices, callSiteType),
+                        // No: either go through a custom conversion, or if it is not possible, go directly to the
+                        // vararg packer.
+                        isCustomConvertible ? convertToArrayWhenPossible : varArgCollectingInvocation);
+            }
+
+            // Just do the custom conversion with fallback to the vararg packer logic.
+            assert isCustomConvertible;
+            return convertToArrayWhenPossible;
         }
 
         // Remaining case: more than one vararg.
         return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
     }
 
+    @SuppressWarnings("unused")
+    private static boolean canConvertTo(final LinkerServices linkerServices, Class<?> to, Object obj) {
+        return obj == null ? false : linkerServices.canConvert(obj.getClass(), to);
+    }
+
     /**
      * Creates a method handle out of the original target that will collect the varargs for the exact component type of
      * the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs
--- a/src/jdk/internal/dynalink/support/Guards.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/internal/dynalink/support/Guards.java	Tue Oct 15 22:13:56 2013 +0530
@@ -88,6 +88,7 @@
 import java.lang.invoke.MethodType;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import jdk.internal.dynalink.DynamicLinker;
 import jdk.internal.dynalink.linker.LinkerServices;
 
 /**
@@ -115,11 +116,11 @@
     public static MethodHandle isOfClass(Class<?> clazz, MethodType type) {
         final Class<?> declaredType = type.parameterType(0);
         if(clazz == declaredType) {
-            LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type });
+            LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() });
             return constantTrue(type);
         }
         if(!declaredType.isAssignableFrom(clazz)) {
-            LOG.log(Level.WARNING, "isOfClassGuardAlwaysFalse", new Object[] { clazz.getName(), 0, type });
+            LOG.log(Level.WARNING, "isOfClassGuardAlwaysFalse", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() });
             return constantFalse(type);
         }
         return getClassBoundArgumentTest(IS_OF_CLASS, clazz, 0, type);
@@ -152,11 +153,11 @@
     public static MethodHandle isInstance(Class<?> clazz, int pos, MethodType type) {
         final Class<?> declaredType = type.parameterType(pos);
         if(clazz.isAssignableFrom(declaredType)) {
-            LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type });
+            LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() });
             return constantTrue(type);
         }
         if(!declaredType.isAssignableFrom(clazz)) {
-            LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] { clazz.getName(), pos, type });
+            LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() });
             return constantFalse(type);
         }
         return getClassBoundArgumentTest(IS_INSTANCE, clazz, pos, type);
@@ -174,11 +175,11 @@
     public static MethodHandle isArray(int pos, MethodType type) {
         final Class<?> declaredType = type.parameterType(pos);
         if(declaredType.isArray()) {
-            LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type });
+            LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() });
             return constantTrue(type);
         }
         if(!declaredType.isAssignableFrom(Object[].class)) {
-            LOG.log(Level.WARNING, "isArrayGuardAlwaysFalse", new Object[] { pos, type });
+            LOG.log(Level.WARNING, "isArrayGuardAlwaysFalse", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() });
             return constantFalse(type);
         }
         return asType(IS_ARRAY, pos, type);
--- a/src/jdk/internal/dynalink/support/messages.properties	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/internal/dynalink/support/messages.properties	Tue Oct 15 22:13:56 2013 +0530
@@ -76,11 +76,11 @@
 #      OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 #      ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-isInstanceGuardAlwaysTrue=isInstance guard for {0} in position {1} in method type {2} will always return true
-isInstanceGuardAlwaysFalse=isInstance guard for {0} in position {1} in method type {2} will always return false
+isInstanceGuardAlwaysTrue=isInstance guard for {0} in position {1} in method type {2} at {3} will always return true
+isInstanceGuardAlwaysFalse=isInstance guard for {0} in position {1} in method type {2} at {3} will always return false
 
-isOfClassGuardAlwaysTrue=isOfClass guard for {0} in position {1} in method type {2} will always return true
-isOfClassGuardAlwaysFalse=isOfClass guard for {0} in position {1} in method type {2} will always return false
+isOfClassGuardAlwaysTrue=isOfClass guard for {0} in position {1} in method type {2} at {3} will always return true
+isOfClassGuardAlwaysFalse=isOfClass guard for {0} in position {1} in method type {2} at {3} will always return false
 
-isArrayGuardAlwaysTrue=isArray guard in position {0} in method type {1} will always return true
-isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} will always return false
\ No newline at end of file
+isArrayGuardAlwaysTrue=isArray guard in position {0} in method type {1} at {2} will always return true
+isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} at {2} will always return false
\ No newline at end of file
--- a/src/jdk/nashorn/api/scripting/ScriptUtils.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java	Tue Oct 15 22:13:56 2013 +0530
@@ -25,6 +25,7 @@
 
 package jdk.nashorn.api.scripting;
 
+import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 
 /**
@@ -57,4 +58,17 @@
     public static String format(final String format, final Object[] args) {
         return Formatter.format(format, args);
     }
+
+    /**
+     * Create a wrapper function that calls {@code func} synchronized on {@code sync} or, if that is undefined,
+     * {@code self}. Used to implement "sync" function in resources/mozilla_compat.js.
+     *
+     * @param func the function to invoke
+     * @param sync the object to synchronize on
+     * @return a synchronizing wrapper function
+     */
+    public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) {
+        return func.makeSynchronizedFunction(sync);
+    }
+
 }
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Oct 15 22:13:56 2013 +0530
@@ -453,7 +453,13 @@
             public boolean enterFunctionNode(FunctionNode functionNode) {
                 // function nodes will always leave a constructed function object on stack, no need to load the symbol
                 // separately as in enterDefault()
+                lc.pop(functionNode);
                 functionNode.accept(codegen);
+                // NOTE: functionNode.accept() will produce a different FunctionNode that we discard. This incidentally
+                // doesn't cause problems as we're never touching FunctionNode again after it's visited here - codegen
+                // is the last element in the compilation pipeline, the AST it produces is not used externally. So, we
+                // re-push the original functionNode.
+                lc.push(functionNode);
                 method.convert(type);
                 return false;
             }
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Tue Oct 15 22:13:56 2013 +0530
@@ -25,6 +25,7 @@
 
 package jdk.nashorn.internal.objects;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
 import java.lang.invoke.MethodHandle;
@@ -255,6 +256,12 @@
         return makeFunction(name, methodHandle, null);
     }
 
+    @Override
+    public ScriptFunction makeSynchronizedFunction(final Object sync) {
+        final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync);
+        return makeFunction(getName(), mh);
+    }
+
     /**
      * Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we
      * can expose it to methods in this package.
--- a/src/jdk/nashorn/internal/runtime/JSType.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/runtime/JSType.java	Tue Oct 15 22:13:56 2013 +0530
@@ -31,6 +31,8 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Array;
+import java.util.Deque;
+import java.util.List;
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
@@ -110,6 +112,15 @@
     /** Combined call to toPrimitive followed by toString. */
     public static final Call TO_PRIMITIVE_TO_STRING = staticCall(myLookup, JSType.class, "toPrimitiveToString", String.class,  Object.class);
 
+    /** Method handle to convert a JS Object to a Java array. */
+    public static final Call TO_JAVA_ARRAY = staticCall(myLookup, JSType.class, "toJavaArray", Object.class, Object.class, Class.class);
+
+    /** Method handle to convert a JS Object to a Java List. */
+    public static final Call TO_JAVA_LIST = staticCall(myLookup, JSType.class, "toJavaList", List.class, Object.class);
+
+    /** Method handle to convert a JS Object to a Java deque. */
+   public static final Call TO_JAVA_DEQUE = staticCall(myLookup, JSType.class, "toJavaDeque", Deque.class, Object.class);
+
     private static final double INT32_LIMIT = 4294967296.0;
 
     /**
@@ -890,6 +901,8 @@
                 res[idx++] = itr.next();
             }
             return convertArray(res, componentType);
+        } else if(obj == null) {
+            return null;
         } else {
             throw new IllegalArgumentException("not a script object");
         }
@@ -919,6 +932,24 @@
     }
 
     /**
+     * Converts a JavaScript object to a Java List. See {@link ListAdapter} for details.
+     * @param obj the object to convert. Can be any array-like object.
+     * @return a List that is live-backed by the JavaScript object.
+     */
+    public static List<?> toJavaList(final Object obj) {
+        return ListAdapter.create(obj);
+    }
+
+    /**
+     * Converts a JavaScript object to a Java Deque. See {@link ListAdapter} for details.
+     * @param obj the object to convert. Can be any array-like object.
+     * @return a Deque that is live-backed by the JavaScript object.
+     */
+    public static Deque<?> toJavaDeque(final Object obj) {
+        return ListAdapter.create(obj);
+    }
+
+    /**
      * Check if an object is null or undefined
      *
      * @param obj object to check
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Tue Oct 15 22:13:56 2013 +0530
@@ -53,7 +53,7 @@
 public final class RecompilableScriptFunctionData extends ScriptFunctionData {
 
     /** FunctionNode with the code for this ScriptFunction */
-    private volatile FunctionNode functionNode;
+    private FunctionNode functionNode;
 
     /** Source from which FunctionNode was parsed. */
     private final Source source;
@@ -65,7 +65,7 @@
     private final PropertyMap allocatorMap;
 
     /** Code installer used for all further recompilation/specialization of this ScriptFunction */
-    private volatile CodeInstaller<ScriptEnvironment> installer;
+    private CodeInstaller<ScriptEnvironment> installer;
 
     /** Name of class where allocator function resides */
     private final String allocatorClassName;
@@ -178,7 +178,7 @@
     }
 
     @Override
-    protected void ensureCodeGenerated() {
+    protected synchronized void ensureCodeGenerated() {
          if (!code.isEmpty()) {
              return; // nothing to do, we have code, at least some.
          }
@@ -336,7 +336,7 @@
     }
 
     @Override
-    MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
+    synchronized MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
         final MethodType runtimeType = runtimeType(callSiteType, args);
         assert runtimeType.parameterCount() == callSiteType.parameterCount();
 
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Oct 15 22:13:56 2013 +0530
@@ -59,6 +59,9 @@
     /** Method handle for name getter for this ScriptFunction */
     public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
 
+    /** Method handle used for implementing sync() in mozilla_compat */
+    public static final MethodHandle INVOKE_SYNC = findOwnMH("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
+
     /** Method handle for allocate function for this ScriptFunction */
     static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
 
@@ -301,6 +304,14 @@
     public abstract void setPrototype(Object prototype);
 
     /**
+     * Create a function that invokes this function synchronized on {@code sync} or the self object
+     * of the invocation.
+     * @param sync the Object to synchronize on, or undefined
+     * @return synchronized function
+     */
+    public abstract ScriptFunction makeSynchronizedFunction(Object sync);
+
+    /**
      * Return the most appropriate invoke handle if there are specializations
      * @param type most specific method type to look for invocation with
      * @param args args for trampoline invocation
@@ -614,6 +625,15 @@
         return result;
     }
 
+    @SuppressWarnings("unused")
+    private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args)
+            throws Throwable {
+        final Object syncObj = sync == UNDEFINED ? self : sync;
+        synchronized (syncObj) {
+            return func.invoke(self, args);
+        }
+    }
+
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
         final Class<?>   own = ScriptFunction.class;
         final MethodType mt  = MH.type(rtype, types);
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Tue Oct 15 22:13:56 2013 +0530
@@ -675,7 +675,7 @@
 
     /**
      * Heuristic to figure out if the method handle has a callee argument. If it's type is either
-     * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
+     * {@code (boolean, ScriptFunction, ...)} or {@code (ScriptFunction, ...)}, then we'll assume it has
      * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
      * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
      * they also always receive a callee).
@@ -692,11 +692,11 @@
             return false;
         }
 
-        if (type.parameterType(0) == boolean.class) {
-            return length > 1 && type.parameterType(1) == ScriptFunction.class;
+        if (type.parameterType(0) == ScriptFunction.class) {
+            return true;
         }
 
-        return type.parameterType(0) == ScriptFunction.class;
+        return length > 1 && type.parameterType(0) == boolean.class && type.parameterType(1) == ScriptFunction.class;
     }
 
     /**
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Tue Oct 15 22:13:56 2013 +0530
@@ -37,6 +37,8 @@
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
+import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -131,6 +133,7 @@
 
     static final MethodHandle GETPROTO           = findOwnMH("getProto", ScriptObject.class);
     static final MethodHandle SETPROTOCHECK      = findOwnMH("setProtoCheck", void.class, Object.class);
+    static final MethodHandle MEGAMORPHIC_GET    = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class);
 
     static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
     static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
@@ -388,7 +391,7 @@
             return global.newDataDescriptor(getWithProperty(property), configurable, enumerable, writable);
         }
 
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -592,7 +595,7 @@
      * @param value value to define
      */
     protected final void defineOwnProperty(final int index, final Object value) {
-        assert ArrayIndex.isValidArrayIndex(index) : "invalid array index";
+        assert isValidArrayIndex(index) : "invalid array index";
         final long longIndex = ArrayIndex.toLongIndex(index);
         if (longIndex >= getArray().length()) {
             // make array big enough to hold..
@@ -602,9 +605,9 @@
     }
 
     private void checkIntegerKey(final String key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             final ArrayData data = getArray();
 
             if (data.has(index)) {
@@ -614,7 +617,7 @@
     }
 
     private void removeArraySlot(final String key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -717,6 +720,28 @@
     }
 
     /**
+     * Low level property API. This is similar to {@link #findProperty(String, boolean)} but returns a
+     * {@code boolean} value instead of a {@link FindProperty} object.
+     * @param key  Property key.
+     * @param deep Whether the search should look up proto chain.
+     * @return true if the property was found.
+     */
+    boolean hasProperty(final String key, final boolean deep) {
+        if (getMap().findProperty(key) != null) {
+            return true;
+        }
+
+        if (deep) {
+            final ScriptObject myProto = getProto();
+            if (myProto != null) {
+                return myProto.hasProperty(key, deep);
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * Add a new property to the object.
      * <p>
      * This a more "low level" way that doesn't involve {@link PropertyDescriptor}s
@@ -1708,8 +1733,11 @@
      */
     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+        if (request.isCallSiteUnstable()) {
+            return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
+        }
+
         final FindProperty find = findProperty(name, true);
-
         MethodHandle methodHandle;
 
         if (find == null) {
@@ -1727,10 +1755,6 @@
             throw new AssertionError(); // never invoked with any other operation
         }
 
-        if (request.isCallSiteUnstable()) {
-            return findMegaMorphicGetMethod(desc, name);
-        }
-
         final Class<?> returnType = desc.getMethodType().returnType();
         final Property property = find.getProperty();
         methodHandle = find.getGetter(returnType);
@@ -1757,11 +1781,21 @@
         return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
     }
 
-    private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
-        final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
-        final GuardedInvocation inv = findGetIndexMethod(mhType);
-
-        return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
+    private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
+        final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
+        final MethodHandle guard = getScriptObjectGuard(desc.getMethodType());
+        return new GuardedInvocation(invoker, guard);
+    }
+
+    @SuppressWarnings("unused")
+    private Object megamorphicGet(final String key, final boolean isMethod) {
+        final FindProperty find = findProperty(key, true);
+
+        if (find != null) {
+            return getObjectValue(find);
+        }
+
+        return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key);
     }
 
     /**
@@ -1810,7 +1844,7 @@
      */
     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
-        if(request.isCallSiteUnstable()) {
+        if (request.isCallSiteUnstable()) {
             return findMegaMorphicSetMethod(desc, name);
         }
 
@@ -2045,6 +2079,26 @@
         return UNDEFINED;
     }
 
+    /**
+     * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined.
+     * @param name the method name
+     * @return the bound function, or undefined
+     */
+    private Object getNoSuchMethod(final String name) {
+        final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
+
+        if (find == null) {
+            return invokeNoSuchProperty(name);
+        }
+
+        final Object value = getObjectValue(find);
+        if (! (value instanceof ScriptFunction)) {
+            return UNDEFINED;
+        }
+
+        return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name});
+    }
+
     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
         return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
     }
@@ -2308,7 +2362,7 @@
     }
 
     private int getInt(final int index, final String key) {
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        if (isValidArrayIndex(index)) {
              for (ScriptObject object = this; ; ) {
                 final FindProperty find = object.findProperty(key, false, false, this);
 
@@ -2339,7 +2393,7 @@
 
     @Override
     public int getInt(final Object key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2351,7 +2405,7 @@
 
     @Override
     public int getInt(final double key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2363,7 +2417,7 @@
 
     @Override
     public int getInt(final long key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2385,7 +2439,7 @@
     }
 
     private long getLong(final int index, final String key) {
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 final FindProperty find = object.findProperty(key, false, false, this);
 
@@ -2416,7 +2470,7 @@
 
     @Override
     public long getLong(final Object key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2428,7 +2482,7 @@
 
     @Override
     public long getLong(final double key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2440,7 +2494,7 @@
 
     @Override
     public long getLong(final long key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2462,7 +2516,7 @@
     }
 
     private double getDouble(final int index, final String key) {
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 final FindProperty find = object.findProperty(key, false, false, this);
 
@@ -2493,7 +2547,7 @@
 
     @Override
     public double getDouble(final Object key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2505,7 +2559,7 @@
 
     @Override
     public double getDouble(final double key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2517,7 +2571,7 @@
 
     @Override
     public double getDouble(final long key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2539,7 +2593,7 @@
     }
 
     private Object get(final int index, final String key) {
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 final FindProperty find = object.findProperty(key, false, false, this);
 
@@ -2570,7 +2624,7 @@
 
     @Override
     public Object get(final Object key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2582,7 +2636,7 @@
 
     @Override
     public Object get(final double key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2594,7 +2648,7 @@
 
     @Override
     public Object get(final long key) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -2710,9 +2764,9 @@
 
     @Override
     public void set(final Object key, final int value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2722,14 +2776,15 @@
             return;
         }
 
-        set(key, JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
     public void set(final Object key, final long value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2739,14 +2794,15 @@
             return;
         }
 
-        set(key, JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
     public void set(final Object key, final double value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2756,14 +2812,15 @@
             return;
         }
 
-        set(key, JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
     public void set(final Object key, final Object value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2773,17 +2830,15 @@
             return;
         }
 
-        final String       propName = JSType.toString(key);
-        final FindProperty find     = findProperty(propName, true);
-
-        setObject(find, strict, propName, value);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, value);
     }
 
     @Override
     public void set(final double key, final int value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2793,14 +2848,15 @@
             return;
         }
 
-        set(JSType.toObject(key), JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
     public void set(final double key, final long value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2810,14 +2866,15 @@
             return;
         }
 
-        set(JSType.toObject(key), JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
     public void set(final double key, final double value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2827,14 +2884,15 @@
             return;
         }
 
-        set(JSType.toObject(key), JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
     public void set(final double key, final Object value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2844,14 +2902,15 @@
             return;
         }
 
-        set(JSType.toObject(key), value, strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, value);
     }
 
     @Override
     public void set(final long key, final int value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2861,31 +2920,15 @@
             return;
         }
 
-        set(JSType.toObject(key), JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
     public void set(final long key, final long value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
-            if (getArray().has(index)) {
-                setArray(getArray().set(index, value, strict));
-            } else {
-                doesNotHave(index, value, strict);
-            }
-
-            return;
-        }
-
-        set(JSType.toObject(key), JSType.toObject(value), strict);
-    }
-
-    @Override
-    public void set(final long key, final double value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2895,14 +2938,15 @@
             return;
         }
 
-        set(JSType.toObject(key), JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
-    public void set(final long key, final Object value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+    public void set(final long key, final double value, final boolean strict) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2912,14 +2956,15 @@
             return;
         }
 
-        set(JSType.toObject(key), value, strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
-    public void set(final int key, final int value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+    public void set(final long key, final Object value, final boolean strict) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2929,14 +2974,15 @@
             return;
         }
 
-        set(JSType.toObject(key), JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, value);
     }
 
     @Override
-    public void set(final int key, final long value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+    public void set(final int key, final int value, final boolean strict) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2946,14 +2992,15 @@
             return;
         }
 
-        set(JSType.toObject(key), JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
-    public void set(final int key, final double value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+    public void set(final int key, final long value, final boolean strict) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2963,14 +3010,15 @@
             return;
         }
 
-        set(JSType.toObject(key), JSType.toObject(value), strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
     }
 
     @Override
-    public void set(final int key, final Object value, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+    public void set(final int key, final double value, final boolean strict) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
@@ -2980,14 +3028,33 @@
             return;
         }
 
-        set(JSType.toObject(key), value, strict);
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, JSType.toObject(value));
+    }
+
+    @Override
+    public void set(final int key, final Object value, final boolean strict) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
+            if (getArray().has(index)) {
+                setArray(getArray().set(index, value, strict));
+            } else {
+                doesNotHave(index, value, strict);
+            }
+
+            return;
+        }
+
+        final String propName = JSType.toString(key);
+        setObject(findProperty(propName, true), strict, propName, value);
     }
 
     @Override
     public boolean has(final Object key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             for (ScriptObject self = this; self != null; self = self.getProto()) {
                 if (self.getArray().has(index)) {
                     return true;
@@ -2995,33 +3062,14 @@
             }
         }
 
-        final FindProperty find = findProperty(JSType.toString(key), true);
-
-        return find != null;
+        return hasProperty(JSType.toString(key), true);
     }
 
     @Override
     public boolean has(final double key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
-            for (ScriptObject self = this; self != null; self = self.getProto()) {
-                if (self.getArray().has(index)) {
-                    return true;
-                }
-            }
-        }
-
-        final FindProperty find = findProperty(JSType.toString(key), true);
-
-        return find != null;
-    }
-
-    @Override
-    public boolean has(final long key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             for (ScriptObject self = this; self != null; self = self.getProto()) {
                 if (self.getArray().has(index)) {
                     return true;
@@ -3029,16 +3077,14 @@
             }
         }
 
-        final FindProperty find = findProperty(JSType.toString(key), true);
-
-        return find != null;
+        return hasProperty(JSType.toString(key), true);
     }
 
     @Override
-    public boolean has(final int key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (ArrayIndex.isValidArrayIndex(index)) {
+    public boolean has(final long key) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
             for (ScriptObject self = this; self != null; self = self.getProto()) {
                 if (self.getArray().has(index)) {
                     return true;
@@ -3046,66 +3092,47 @@
             }
         }
 
-        final FindProperty find = findProperty(JSType.toString(key), true);
-
-        return find != null;
+        return hasProperty(JSType.toString(key), true);
+    }
+
+    @Override
+    public boolean has(final int key) {
+        final int index = getArrayIndex(key);
+
+        if (isValidArrayIndex(index)) {
+            for (ScriptObject self = this; self != null; self = self.getProto()) {
+                if (self.getArray().has(index)) {
+                    return true;
+                }
+            }
+        }
+
+        return hasProperty(JSType.toString(key), true);
     }
 
     @Override
     public boolean hasOwnProperty(final Object key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (getArray().has(index)) {
-            return true;
-        }
-
-        final FindProperty find = findProperty(JSType.toString(key), false);
-
-        return find != null;
+        return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false);
     }
 
     @Override
     public boolean hasOwnProperty(final int key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (getArray().has(index)) {
-            return true;
-        }
-
-        final FindProperty find = findProperty(JSType.toString(key), false);
-
-        return find != null;
+        return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false);
     }
 
     @Override
     public boolean hasOwnProperty(final long key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (getArray().has(index)) {
-            return true;
-        }
-
-        final FindProperty find = findProperty(JSType.toString(key), false);
-
-        return find != null;
+        return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false);
     }
 
     @Override
     public boolean hasOwnProperty(final double key) {
-        final int index = ArrayIndex.getArrayIndex(key);
-
-        if (getArray().has(index)) {
-            return true;
-        }
-
-        final FindProperty find = findProperty(JSType.toString(key), false);
-
-        return find != null;
+        return getArray().has(getArrayIndex(key)) || hasProperty(JSType.toString(key), false);
     }
 
     @Override
     public boolean delete(final int key, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -3121,7 +3148,7 @@
 
     @Override
     public boolean delete(final long key, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -3137,7 +3164,7 @@
 
     @Override
     public boolean delete(final double key, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
@@ -3153,7 +3180,7 @@
 
     @Override
     public boolean delete(final Object key, final boolean strict) {
-        final int index = ArrayIndex.getArrayIndex(key);
+        final int index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
--- a/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java	Tue Oct 15 22:13:56 2013 +0530
@@ -25,9 +25,9 @@
 
 package jdk.nashorn.internal.runtime.linker;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -211,6 +211,20 @@
                 return null;
             } else if (obj instanceof Long) {
                 return (Long) obj;
+            } else if (obj instanceof Integer) {
+                return ((Integer)obj).longValue();
+            } else if (obj instanceof Double) {
+                final Double d = (Double)obj;
+                if(Double.isInfinite(d.doubleValue())) {
+                    return 0L;
+                }
+                return d.longValue();
+            } else if (obj instanceof Float) {
+                final Float f = (Float)obj;
+                if(Float.isInfinite(f.floatValue())) {
+                    return 0L;
+                }
+                return f.longValue();
             } else if (obj instanceof Number) {
                 return ((Number)obj).longValue();
             } else if (obj instanceof String || obj instanceof ConsString) {
--- a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java	Tue Oct 15 22:13:56 2013 +0530
@@ -30,6 +30,8 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Modifier;
+import java.util.Deque;
+import java.util.List;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.ConversionComparator;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -38,6 +40,8 @@
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
 import jdk.internal.dynalink.support.Guards;
+import jdk.nashorn.internal.objects.NativeArray;
+import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.Undefined;
@@ -47,6 +51,13 @@
  * includes {@link ScriptFunction} and its subclasses) as well as {@link Undefined}.
  */
 final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator {
+    private static final ClassValue<MethodHandle> ARRAY_CONVERTERS = new ClassValue<MethodHandle>() {
+        @Override
+        protected MethodHandle computeValue(Class<?> type) {
+            return createArrayConverter(type);
+        }
+    };
+
     /**
      * Returns true if {@code ScriptObject} is assignable from {@code type}, or it is {@code Undefined}.
      */
@@ -103,6 +114,12 @@
         if (mh != null) {
             return new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : IS_NASHORN_OR_UNDEFINED_TYPE);
         }
+
+        GuardedInvocation inv = getArrayConverter(sourceType, targetType);
+        if(inv != null) {
+            return inv;
+        }
+
         return getSamTypeConverter(sourceType, targetType);
     }
 
@@ -129,6 +146,41 @@
         return null;
     }
 
+    /**
+     * Returns a guarded invocation that converts from a source type that is NativeArray to a Java array or List or
+     * Deque type.
+     * @param sourceType the source type (presumably NativeArray a superclass of it)
+     * @param targetType the target type (presumably an array type, or List or Deque)
+     * @return a guarded invocation that converts from the source type to the target type. null is returned if
+     * either the source type is neither NativeArray, nor a superclass of it, or if the target type is not an array
+     * type, List, or Deque.
+     */
+    private static GuardedInvocation getArrayConverter(final Class<?> sourceType, final Class<?> targetType) {
+        final boolean isSourceTypeNativeArray = sourceType == NativeArray.class;
+        // If source type is more generic than ScriptFunction class, we'll need to use a guard
+        final boolean isSourceTypeGeneric = !isSourceTypeNativeArray && sourceType.isAssignableFrom(NativeArray.class);
+
+        if (isSourceTypeNativeArray || isSourceTypeGeneric) {
+            final MethodHandle guard = isSourceTypeGeneric ? IS_NATIVE_ARRAY : null;
+            if(targetType.isArray()) {
+                return new GuardedInvocation(ARRAY_CONVERTERS.get(targetType), guard);
+            }
+            if(targetType == List.class) {
+                return new GuardedInvocation(JSType.TO_JAVA_LIST.methodHandle(), guard);
+            }
+            if(targetType == Deque.class) {
+                return new GuardedInvocation(JSType.TO_JAVA_DEQUE.methodHandle(), guard);
+            }
+        }
+        return null;
+    }
+
+    private static MethodHandle createArrayConverter(final Class<?> type) {
+        assert type.isArray();
+        final MethodHandle converter = MH.insertArguments(JSType.TO_JAVA_ARRAY.methodHandle(), 1, type.getComponentType());
+        return MH.asType(converter, converter.type().changeReturnType(type));
+    }
+
     private static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
         return isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
                 JavaAdapterFactory.isAutoConvertibleFromFunction(clazz);
@@ -148,7 +200,26 @@
 
     @Override
     public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
+        if(sourceType == NativeArray.class) {
+            // Prefer lists, as they're less costly to create than arrays.
+            if(isList(targetType1)) {
+                if(!isList(targetType2)) {
+                    return Comparison.TYPE_1_BETTER;
+                }
+            } else if(isList(targetType2)) {
+                return Comparison.TYPE_2_BETTER;
+            }
+            // Then prefer arrays
+            if(targetType1.isArray()) {
+                if(!targetType2.isArray()) {
+                    return Comparison.TYPE_1_BETTER;
+                }
+            } else if(targetType2.isArray()) {
+                return Comparison.TYPE_2_BETTER;
+            }
+        }
         if(ScriptObject.class.isAssignableFrom(sourceType)) {
+            // Prefer interfaces
             if(targetType1.isInterface()) {
                 if(!targetType2.isInterface()) {
                     return Comparison.TYPE_1_BETTER;
@@ -160,7 +231,12 @@
         return Comparison.INDETERMINATE;
     }
 
+    private static boolean isList(Class<?> clazz) {
+        return clazz == List.class || clazz == Deque.class;
+    }
+
     private static final MethodHandle IS_SCRIPT_FUNCTION = Guards.isInstance(ScriptFunction.class, MH.type(Boolean.TYPE, Object.class));
+    private static final MethodHandle IS_NATIVE_ARRAY = Guards.isOfClass(NativeArray.class, MH.type(Boolean.TYPE, Object.class));
 
     private static final MethodHandle IS_NASHORN_OR_UNDEFINED_TYPE = findOwnMH("isNashornTypeOrUndefined",
             Boolean.TYPE, Object.class);
--- a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js	Fri Oct 11 23:31:18 2013 -0700
+++ b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js	Tue Oct 15 22:13:56 2013 +0530
@@ -98,6 +98,17 @@
 
 }
 
+// sync
+Object.defineProperty(this, "sync", {
+    configurable: true, enumerable: false, writable: true,
+    value: function(func, syncobj) {
+        if (arguments.length < 1 || arguments.length > 2 ) {
+            throw "sync(function [,object]) parameter count mismatch";
+        }
+        return Packages.jdk.nashorn.api.scripting.ScriptUtils.makeSynchronizedFunction(func, syncobj);
+    }
+});
+
 // Object.prototype.__defineGetter__
 Object.defineProperty(Object.prototype, "__defineGetter__", {
     configurable: true, enumerable: false, writable: true,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8026016.js	Tue Oct 15 22:13:56 2013 +0530
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8026016: too many relinks dominate avatar.js http benchmark
+ *
+ * @test
+ * @run
+ */
+
+function accessMegamorphic() {
+    for (var i = 0; i < 26; i++) {
+        var o = {};
+        o[String.fromCharCode(i + 97)] = 1;
+        o._;
+    }
+}
+
+function invokeMegamorphic() {
+    for (var i = 0; i < 26; i++) {
+        var o = {};
+        o[String.fromCharCode(i + 97)] = 1;
+        try {
+            o._(i);
+        } catch (e) {
+            print(e);
+        }
+    }
+}
+
+Object.prototype.__noSuchProperty__ = function() {
+    print("no such property", Array.prototype.slice.call(arguments));
+};
+
+invokeMegamorphic();
+accessMegamorphic();
+
+Object.prototype.__noSuchMethod__ = function() {
+    print("no such method", Array.prototype.slice.call(arguments));
+};
+
+invokeMegamorphic();
+accessMegamorphic();
+
+Object.prototype.__noSuchMethod__ = "nofunction";
+
+invokeMegamorphic();
+accessMegamorphic();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8026016.js.EXPECTED	Tue Oct 15 22:13:56 2013 +0530
@@ -0,0 +1,182 @@
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+TypeError: Cannot call undefined
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such method _,0
+no such method _,1
+no such method _,2
+no such method _,3
+no such method _,4
+no such method _,5
+no such method _,6
+no such method _,7
+no such method _,8
+no such method _,9
+no such method _,10
+no such method _,11
+no such method _,12
+no such method _,13
+no such method _,14
+no such method _,15
+no such method _,16
+no such method _,17
+no such method _,18
+no such method _,19
+no such method _,20
+no such method _,21
+no such method _,22
+no such method _,23
+no such method _,24
+no such method _,25
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+TypeError: Cannot call undefined
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
+no such property _
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8026367.js	Tue Oct 15 22:13:56 2013 +0530
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8026367: Add a sync keyword to mozilla_compat
+ *
+ * @test
+ * @run
+ */
+
+if (typeof sync === "undefined") {
+    load("nashorn:mozilla_compat.js");
+}
+
+var obj = {
+    count: 0,
+    // Sync called with one argument will synchronize on this-object of invocation
+    inc: sync(function(d) {
+        this.count += d;
+    }),
+    // Pass explicit object to synchronize on as second argument
+    dec: sync(function(d) {
+        this.count -= d;
+    }, obj)
+};
+
+var t1 = new java.lang.Thread(function() {
+    for (var i = 0; i < 100000; i++) obj.inc(1);
+});
+var t2 = new java.lang.Thread(function() {
+    for (var i = 0; i < 100000; i++) obj.dec(1);
+});
+
+t1.start();
+t2.start();
+t1.join();
+t2.join();
+
+if (obj.count !== 0) {
+    throw new Error("Expected count == 0, got " + obj.count);
+}
--- a/test/script/sandbox/loadcompat.js	Fri Oct 11 23:31:18 2013 -0700
+++ b/test/script/sandbox/loadcompat.js	Tue Oct 15 22:13:56 2013 +0530
@@ -48,3 +48,7 @@
 if (typeof importPackage != 'function') {
     fail("importPackage function is missing in compatibility script");
 }
+
+if (typeof sync != 'function') {
+    fail("sync function is missing in compatibility script");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/src/jdk/nashorn/api/javaaccess/ArrayConversionTest.java	Tue Oct 15 22:13:56 2013 +0530
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.api.javaaccess;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import org.testng.TestNG;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ArrayConversionTest {
+    private static ScriptEngine e = null;
+
+    public static void main(final String[] args) {
+        TestNG.main(args);
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws ScriptException {
+        e = new ScriptEngineManager().getEngineByName("nashorn");
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        e = null;
+    }
+
+    @Test
+    public void testIntArrays() throws ScriptException {
+        runTest("assertNullIntArray", "null");
+        runTest("assertEmptyIntArray", "[]");
+        runTest("assertSingle42IntArray", "[42]");
+        runTest("assertSingle42IntArray", "['42']");
+        runTest("assertIntArrayConversions", "[false, true, NaN, Infinity, -Infinity, 0.4, 0.6, null, undefined, [], {}, [1], [1, 2]]");
+    }
+
+    @Test
+    public void testIntIntArrays() throws ScriptException {
+        runTest("assertNullIntIntArray", "null");
+        runTest("assertEmptyIntIntArray", "[]");
+        runTest("assertSingleEmptyIntIntArray", "[[]]");
+        runTest("assertSingleNullIntIntArray", "[null]");
+        runTest("assertLargeIntIntArray", "[[false], [1], [2, 3], [4, 5, 6], ['7', {valueOf: function() { return 8 }}]]");
+    }
+
+    @Test
+    public void testObjectObjectArrays() throws ScriptException {
+        runTest("assertLargeObjectObjectArray", "[[false], [1], ['foo', 42.3], [{x: 17}]]");
+    }
+
+    @Test
+    public void testBooleanArrays() throws ScriptException {
+        runTest("assertBooleanArrayConversions", "[false, true, '', 'false', 0, 1, 0.4, 0.6, {}, [], [false], [true], NaN, Infinity, null, undefined]");
+    }
+
+    @Test
+    public void testArrayAmbiguity() throws ScriptException {
+        runTest("x", "'abc'");
+        runTest("x", "['foo', 'bar']");
+    }
+
+    @Test
+    public void testListArrays() throws ScriptException {
+        runTest("assertListArray", "[['foo', 'bar'], ['apple', 'orange']]");
+    }
+
+    @Test
+    public void testVarArgs() throws ScriptException {
+        // Sole NativeArray in vararg position becomes vararg array itself
+        runTest("assertVarArg_42_17", "[42, 17]");
+        // NativeArray in vararg position becomes an argument if there are more arguments
+        runTest("assertVarArg_array_17", "[42], 18");
+        // Only NativeArray is converted to vararg array, other objects (e.g. a function) aren't
+        runTest("assertVarArg_function", "function() { return 'Hello' }");
+    }
+
+    private static void runTest(final String testMethodName, final String argument) throws ScriptException {
+        e.eval("Java.type('" + ArrayConversionTest.class.getName() + "')." + testMethodName + "(" + argument + ")");
+    }
+
+    public static void assertNullIntArray(int[] array) {
+        assertNull(array);
+    }
+
+    public static void assertNullIntIntArray(int[][] array) {
+        assertNull(array);
+    }
+
+    public static void assertEmptyIntArray(int[] array) {
+        assertEquals(0, array.length);
+    }
+
+    public static void assertSingle42IntArray(int[] array) {
+        assertEquals(1, array.length);
+        assertEquals(42, array[0]);
+    }
+
+
+    public static void assertIntArrayConversions(int[] array) {
+        assertEquals(13, array.length);
+        assertEquals(0, array[0]); // false
+        assertEquals(1, array[1]); // true
+        assertEquals(0, array[2]); // NaN
+        assertEquals(0, array[3]); // Infinity
+        assertEquals(0, array[4]); // -Infinity
+        assertEquals(0, array[5]); // 0.4
+        assertEquals(0, array[6]); // 0.6 - floor, not round
+        assertEquals(0, array[7]); // null
+        assertEquals(0, array[8]); // undefined
+        assertEquals(0, array[9]); // []
+        assertEquals(0, array[10]); // {}
+        assertEquals(1, array[11]); // [1]
+        assertEquals(0, array[12]); // [1, 2]
+    }
+
+    public static void assertEmptyIntIntArray(int[][] array) {
+        assertEquals(0, array.length);
+    }
+
+    public static void assertSingleEmptyIntIntArray(int[][] array) {
+        assertEquals(1, array.length);
+        assertTrue(Arrays.equals(new int[0], array[0]));
+    }
+
+    public static void assertSingleNullIntIntArray(int[][] array) {
+        assertEquals(1, array.length);
+        assertNull(null, array[0]);
+    }
+
+    public static void assertLargeIntIntArray(int[][] array) {
+        assertEquals(5, array.length);
+        assertTrue(Arrays.equals(new int[] { 0 }, array[0]));
+        assertTrue(Arrays.equals(new int[] { 1 }, array[1]));
+        assertTrue(Arrays.equals(new int[] { 2, 3 }, array[2]));
+        assertTrue(Arrays.equals(new int[] { 4, 5, 6 }, array[3]));
+        assertTrue(Arrays.equals(new int[] { 7, 8 }, array[4]));
+    }
+
+    public static void assertLargeObjectObjectArray(Object[][] array) throws ScriptException {
+        assertEquals(4, array.length);
+        assertTrue(Arrays.equals(new Object[] { Boolean.FALSE }, array[0]));
+        assertTrue(Arrays.equals(new Object[] { 1 }, array[1]));
+        assertTrue(Arrays.equals(new Object[] { "foo", 42.3d }, array[2]));
+        assertEquals(1, array[3].length);
+        e.getBindings(ScriptContext.ENGINE_SCOPE).put("obj", array[3][0]);
+        assertEquals(17, e.eval("obj.x"));
+    }
+
+    public static void assertBooleanArrayConversions(boolean[] array) {
+        assertEquals(16, array.length);
+        assertFalse(array[0]); // false
+        assertTrue(array[1]); // true
+        assertFalse(array[2]); // ''
+        assertTrue(array[3]); // 'false' (yep, every non-empty string converts to true)
+        assertFalse(array[4]); // 0
+        assertTrue(array[5]); // 1
+        assertTrue(array[6]); // 0.4
+        assertTrue(array[7]); // 0.6
+        assertTrue(array[8]); // {}
+        assertTrue(array[9]); // []
+        assertTrue(array[10]); // [false]
+        assertTrue(array[11]); // [true]
+        assertFalse(array[12]); // NaN
+        assertTrue(array[13]); // Infinity
+        assertFalse(array[14]); // null
+        assertFalse(array[15]); // undefined
+    }
+
+    public static void assertListArray(List<?>[] array) {
+        assertEquals(2, array.length);
+        assertEquals(Arrays.asList("foo", "bar"), array[0]);
+        assertEquals(Arrays.asList("apple", "orange"), array[1]);
+    }
+
+    public static void assertVarArg_42_17(Object... args) throws ScriptException {
+        assertEquals(2, args.length);
+        assertEquals(42, ((Number)args[0]).intValue());
+        assertEquals(17, ((Number)args[1]).intValue());
+    }
+
+    public static void assertVarArg_array_17(Object... args) throws ScriptException {
+        assertEquals(2, args.length);
+        e.getBindings(ScriptContext.ENGINE_SCOPE).put("arr", args[0]);
+        assertTrue((Boolean)e.eval("arr instanceof Array && arr.length == 1 && arr[0] == 42"));
+        assertEquals(18, ((Number)args[1]).intValue());
+    }
+
+    public static void assertVarArg_function(Object... args) throws ScriptException {
+        assertEquals(1, args.length);
+        e.getBindings(ScriptContext.ENGINE_SCOPE).put("fn", args[0]);
+        assertEquals("Hello", e.eval("fn()"));
+    }
+
+
+
+    public static void x(String y) {
+        assertEquals("abc", y);
+    }
+    public static void x(String[] y) {
+        assertTrue(Arrays.equals(new String[] { "foo", "bar"}, y));
+    }
+}