8072596: Arrays.asList results in ClassCastException with a JS array
authorattila
Thu, 12 Feb 2015 16:43:33 +0100
changeset 28881 0008daeef352
parent 28880 4958f80c2d24
child 28882 db20db37be55
8072596: Arrays.asList results in ClassCastException with a JS array Reviewed-by: lagergren, sundar
nashorn/samples/javashell.js
nashorn/samples/shell.js
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedMethod.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkerServices.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/MethodHandleTransformer.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/DefaultInternalObjectFilter.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkerServicesImpl.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java
nashorn/test/script/basic/JDK-8072596.js
nashorn/test/src/jdk/nashorn/test/models/Jdk8072596TestSubject.java
--- a/nashorn/samples/javashell.js	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/samples/javashell.js	Thu Feb 12 16:43:33 2015 +0100
@@ -40,7 +40,6 @@
 var Arrays = Java.type("java.util.Arrays");
 var BufferedReader = Java.type("java.io.BufferedReader");
 var FileWriter = Java.type("java.io.FileWriter");
-var List = Java.type("java.util.List");
 var LocalDateTime = Java.type("java.time.LocalDateTime");
 var InputStreamReader = Java.type("java.io.InputStreamReader");
 var PrintWriter = Java.type("java.io.PrintWriter");
@@ -122,7 +121,7 @@
 // execute code command
 function exec(args) {
     // build child process and start it!
-    new ProcessBuilder(Java.to(args.split(' '), List))
+    new ProcessBuilder(Arrays.asList(args.split(' ')))
          .inheritIO()
          .start()
          .waitFor();
--- a/nashorn/samples/shell.js	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/samples/shell.js	Thu Feb 12 16:43:33 2015 +0100
@@ -42,7 +42,6 @@
     var Arrays = Java.type("java.util.Arrays");
     var BufferedReader = Java.type("java.io.BufferedReader");
     var InputStreamReader = Java.type("java.io.InputStreamReader");
-    var List = Java.type("java.util.List");
     var ProcessBuilder = Java.type("java.lang.ProcessBuilder");
     var System = Java.type("java.lang.System");
 
@@ -67,7 +66,7 @@
                     }
                 } else {
                     // build child process and start it!
-                    new ProcessBuilder(Java.to(args, List))
+                    new ProcessBuilder(Arrays.asList(args))
                         .inheritIO()
                         .start()
                         .waitFor();
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java	Thu Feb 12 16:43:33 2015 +0100
@@ -97,6 +97,8 @@
 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
 import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.linker.MethodHandleTransformer;
 import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
 import jdk.internal.dynalink.support.AutoDiscovery;
 import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
@@ -132,6 +134,7 @@
     private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
     private GuardedInvocationFilter prelinkFilter;
     private MethodTypeConversionStrategy autoConversionStrategy;
+    private MethodHandleTransformer internalObjectsFilter;
 
     /**
      * Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
@@ -284,6 +287,15 @@
     }
 
     /**
+     * Sets a method handle transformer that is supposed to act as the implementation of this linker factory's linkers'
+     * services {@link LinkerServices#filterInternalObjects(java.lang.invoke.MethodHandle)} method.
+     * @param internalObjectsFilter a method handle transformer filtering out internal objects, or null.
+     */
+    public void setInternalObjectsFilter(final MethodHandleTransformer internalObjectsFilter) {
+        this.internalObjectsFilter = internalObjectsFilter;
+    }
+
+    /**
      * Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
      * the pre-link filter.
      *
@@ -350,8 +362,8 @@
         }
 
         return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters,
-                autoConversionStrategy), composite), prelinkFilter, runtimeContextArgCount, syncOnRelink,
-                unstableRelinkThreshold);
+                autoConversionStrategy), composite, internalObjectsFilter), prelinkFilter, runtimeContextArgCount,
+                syncOnRelink, unstableRelinkThreshold);
     }
 
     private static ClassLoader getThreadContextClassLoader() {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Thu Feb 12 16:43:33 2015 +0100
@@ -570,7 +570,7 @@
     private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
             MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
     private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
-            "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class));
+            "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class, LinkerServices.class));
     private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
 
     private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
@@ -593,7 +593,7 @@
                 final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
                         AnnotatedDynamicMethod.class));
                 final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
-                        GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
+                        GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup(), linkerServices);
                 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
                         callSiteBoundMethodGetter);
                 // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
@@ -873,8 +873,8 @@
         }
 
         @SuppressWarnings("unused")
-        MethodHandle getTarget(final MethodHandles.Lookup lookup) {
-            final MethodHandle inv = method.getTarget(lookup);
+        MethodHandle getTarget(final MethodHandles.Lookup lookup, final LinkerServices linkerServices) {
+            final MethodHandle inv = linkerServices.filterInternalObjects(method.getTarget(lookup));
             assert inv != null;
             return inv;
         }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java	Thu Feb 12 16:43:33 2015 +0100
@@ -165,6 +165,10 @@
     private static MethodHandle LIST_GUARD = Guards.getInstanceOfGuard(List.class);
     private static MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class);
 
+    private enum CollectionType {
+        ARRAY, LIST, MAP
+    };
+
     private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor,
             final LinkerServices linkerServices, final List<String> operations) throws Exception {
         final MethodType callSiteType = callSiteDescriptor.getMethodType();
@@ -178,27 +182,27 @@
         // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
         // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
         final GuardedInvocationComponent gic;
-        final boolean isMap;
+        final CollectionType collectionType;
         if(declaredType.isArray()) {
-            gic = new GuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType));
-            isMap = false;
+            gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType), linkerServices);
+            collectionType = CollectionType.ARRAY;
         } else if(List.class.isAssignableFrom(declaredType)) {
-            gic = new GuardedInvocationComponent(GET_LIST_ELEMENT);
-            isMap = false;
+            gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, linkerServices);
+            collectionType = CollectionType.LIST;
         } else if(Map.class.isAssignableFrom(declaredType)) {
-            gic = new GuardedInvocationComponent(GET_MAP_ELEMENT);
-            isMap = true;
+            gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, linkerServices);
+            collectionType = CollectionType.MAP;
         } else if(clazz.isArray()) {
-            gic = getClassGuardedInvocationComponent(MethodHandles.arrayElementGetter(clazz), callSiteType);
-            isMap = false;
+            gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(MethodHandles.arrayElementGetter(clazz)), callSiteType);
+            collectionType = CollectionType.ARRAY;
         } else if(List.class.isAssignableFrom(clazz)) {
-            gic = new GuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class,
-                    ValidationType.INSTANCE_OF);
-            isMap = false;
+            gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF,
+                    linkerServices);
+            collectionType = CollectionType.LIST;
         } else if(Map.class.isAssignableFrom(clazz)) {
-            gic = new GuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class,
-                    ValidationType.INSTANCE_OF);
-            isMap = true;
+            gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, ValidationType.INSTANCE_OF,
+                    linkerServices);
+            collectionType = CollectionType.MAP;
         } else {
             // Can't retrieve elements for objects that are neither arrays, nor list, nor maps.
             return nextComponent;
@@ -208,7 +212,7 @@
         final String fixedKey = getFixedKey(callSiteDescriptor);
         // Convert the key to a number if we're working with a list or array
         final Object typedFixedKey;
-        if(!isMap && fixedKey != null) {
+        if(collectionType != CollectionType.MAP && fixedKey != null) {
             typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
             if(typedFixedKey == null) {
                 // key is not numeric, it can never succeed
@@ -227,15 +231,21 @@
         }
 
         final MethodHandle checkGuard;
-        if(invocation == GET_LIST_ELEMENT) {
+        switch(collectionType) {
+        case LIST:
             checkGuard = convertArgToInt(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor);
-        } else if(invocation == GET_MAP_ELEMENT) {
+            break;
+        case MAP:
             // TODO: A more complex solution could be devised for maps, one where we do a get() first, and fold it
             // into a GWT that tests if it returned null, and if it did, do another GWT with containsKey()
             // that returns constant null (on true), or falls back to next component (on false)
-            checkGuard = CONTAINS_MAP;
-        } else {
+            checkGuard = linkerServices.filterInternalObjects(CONTAINS_MAP);
+            break;
+        case ARRAY:
             checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
+            break;
+        default:
+            throw new AssertionError();
         }
         final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
                 nextComponent.getGuardedInvocation().getInvocation());
@@ -243,6 +253,18 @@
                 gic.getValidatorClass(), gic.getValidationType());
     }
 
+    private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
+            final MethodHandle invocation, final LinkerServices linkerServices) {
+        return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation));
+    }
+
+    private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
+            final MethodHandle invocation, final MethodHandle guard, final Class<?> validatorClass,
+            final ValidationType validationType, final LinkerServices linkerServices) {
+        return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation), guard,
+                validatorClass, validationType);
+    }
+
     private static String getFixedKey(final CallSiteDescriptor callSiteDescriptor) {
         return callSiteDescriptor.getNameTokenCount() == 2 ? null : callSiteDescriptor.getNameToken(
                 CallSiteDescriptor.NAME_OPERAND);
@@ -381,37 +403,38 @@
         // dealing with an array, or a list or map, but hey...
         // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
         // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
-        final boolean isMap;
+        final CollectionType collectionType;
         if(declaredType.isArray()) {
-            gic = new GuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType));
-            isMap = false;
+            gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType), linkerServices);
+            collectionType = CollectionType.ARRAY;
         } else if(List.class.isAssignableFrom(declaredType)) {
-            gic = new GuardedInvocationComponent(SET_LIST_ELEMENT);
-            isMap = false;
+            gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, linkerServices);
+            collectionType = CollectionType.LIST;
         } else if(Map.class.isAssignableFrom(declaredType)) {
-            gic = new GuardedInvocationComponent(PUT_MAP_ELEMENT);
-            isMap = true;
+            gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, linkerServices);
+            collectionType = CollectionType.MAP;
         } else if(clazz.isArray()) {
-            gic = getClassGuardedInvocationComponent(MethodHandles.arrayElementSetter(clazz), callSiteType);
-            isMap = false;
+            gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(
+                    MethodHandles.arrayElementSetter(clazz)), callSiteType);
+            collectionType = CollectionType.ARRAY;
         } else if(List.class.isAssignableFrom(clazz)) {
-            gic = new GuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class,
-                    ValidationType.INSTANCE_OF);
-            isMap = false;
+            gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF,
+                    linkerServices);
+            collectionType = CollectionType.LIST;
         } else if(Map.class.isAssignableFrom(clazz)) {
-            gic = new GuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class,
-                    ValidationType.INSTANCE_OF);
-            isMap = true;
+            gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType),
+                    Map.class, ValidationType.INSTANCE_OF, linkerServices);
+            collectionType = CollectionType.MAP;
         } else {
             // Can't set elements for objects that are neither arrays, nor list, nor maps.
             gic = null;
-            isMap = false;
+            collectionType = null;
         }
 
         // In contrast to, say, getElementGetter, we only compute the nextComponent if the target object is not a map,
         // as maps will always succeed in setting the element and will never need to fall back to the next component
         // operation.
-        final GuardedInvocationComponent nextComponent = isMap ? null : getGuardedInvocationComponent(
+        final GuardedInvocationComponent nextComponent = collectionType == CollectionType.MAP ? null : getGuardedInvocationComponent(
                 callSiteDescriptor, linkerServices, operations);
         if(gic == null) {
             return nextComponent;
@@ -421,7 +444,7 @@
         final String fixedKey = getFixedKey(callSiteDescriptor);
         // Convert the key to a number if we're working with a list or array
         final Object typedFixedKey;
-        if(!isMap && fixedKey != null) {
+        if(collectionType != CollectionType.MAP && fixedKey != null) {
             typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
             if(typedFixedKey == null) {
                 // key is not numeric, it can never succeed
@@ -439,7 +462,8 @@
             return gic.replaceInvocation(binder.bind(invocation));
         }
 
-        final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST :
+        assert collectionType == CollectionType.LIST || collectionType == CollectionType.ARRAY;
+        final MethodHandle checkGuard = convertArgToInt(collectionType == CollectionType.LIST ? RANGE_CHECK_LIST :
             RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
         final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
                 nextComponent.getGuardedInvocation().getInvocation());
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedMethod.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedMethod.java	Thu Feb 12 16:43:33 2015 +0100
@@ -139,7 +139,8 @@
         final MethodHandle bound = SELECT_METHOD.bindTo(this);
         final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
                 callSiteType.changeReturnType(MethodHandle.class));
-        invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(this.callSiteType), collecting);
+        invoker = linkerServices.asTypeLosslessReturn(MethodHandles.foldArguments(
+                MethodHandles.exactInvoker(this.callSiteType), collecting), callSiteType);
     }
 
     MethodHandle getInvoker() {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Thu Feb 12 16:43:33 2015 +0100
@@ -165,10 +165,11 @@
      * @return the adapted method handle.
      */
     static MethodHandle getInvocation(final MethodHandle target, final MethodType callSiteType, final LinkerServices linkerServices) {
-        final MethodType methodType = target.type();
+        final MethodHandle filteredTarget = linkerServices.filterInternalObjects(target);
+        final MethodType methodType = filteredTarget.type();
         final int paramsLen = methodType.parameterCount();
         final boolean varArgs = target.isVarargsCollector();
-        final MethodHandle fixTarget = varArgs ? target.asFixedArity() : target;
+        final MethodHandle fixTarget = varArgs ? filteredTarget.asFixedArity() : filteredTarget;
         final int fixParamsLen = varArgs ? paramsLen - 1 : paramsLen;
         final int argsLen = callSiteType.parameterCount();
         if(argsLen < fixParamsLen) {
@@ -204,7 +205,7 @@
             if(varArgType.isAssignableFrom(callSiteLastArgType)) {
                 // Call site signature guarantees we'll always be passed a single compatible array; just link directly
                 // to the method, introducing necessary conversions. Also, preserve it being a variable arity method.
-                return createConvertingInvocation(target, linkerServices, callSiteType).asVarargsCollector(
+                return createConvertingInvocation(filteredTarget, linkerServices, callSiteType).asVarargsCollector(
                         callSiteLastArgType);
             }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkerServices.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/LinkerServices.java	Thu Feb 12 16:43:33 2015 +0100
@@ -181,6 +181,15 @@
     public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2);
 
     /**
+     * Modifies the method handle so that any parameters that can receive potentially internal language runtime objects
+     * will have a filter added on them to prevent them from escaping, potentially by wrapping them.
+     * It can also potentially add an unwrapping filter to the return value.
+     * @param target the target method handle
+     * @return a method handle with parameters and/or return type potentially filtered for wrapping and unwrapping.
+     */
+    public MethodHandle filterInternalObjects(final MethodHandle target);
+
+    /**
      * If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default
      * implementation. Since we can't do that, we extract common default implementations into this static class.
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/MethodHandleTransformer.java	Thu Feb 12 16:43:33 2015 +0100
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+   Copyright 2009-2015 Attila Szegedi
+
+   Licensed under both the Apache License, Version 2.0 (the "Apache License")
+   and the BSD License (the "BSD License"), with licensee being free to
+   choose either of the two at their discretion.
+
+   You may not use this file except in compliance with either the Apache
+   License or the BSD License.
+
+   If you choose to use this file in compliance with the Apache License, the
+   following notice applies to you:
+
+       You may obtain a copy of the Apache License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+       implied. See the License for the specific language governing
+       permissions and limitations under the License.
+
+   If you choose to use this file in compliance with the BSD License, the
+   following notice applies to you:
+
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in the
+         documentation and/or other materials provided with the distribution.
+       * Neither the name of the copyright holder nor the names of
+         contributors may be used to endorse or promote products derived from
+         this software without specific prior written permission.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.linker;
+
+import java.lang.invoke.MethodHandle;
+
+/**
+ * A generic interface describing operations that transform method handles.
+ */
+public interface MethodHandleTransformer {
+    /**
+     * Transforms a method handle.
+     * @param target the method handle being transformed.
+     * @return transformed method handle.
+     */
+    public MethodHandle transform(final MethodHandle target);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/DefaultInternalObjectFilter.java	Thu Feb 12 16:43:33 2015 +0100
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+   Copyright 2009-2015 Attila Szegedi
+
+   Licensed under both the Apache License, Version 2.0 (the "Apache License")
+   and the BSD License (the "BSD License"), with licensee being free to
+   choose either of the two at their discretion.
+
+   You may not use this file except in compliance with either the Apache
+   License or the BSD License.
+
+   If you choose to use this file in compliance with the Apache License, the
+   following notice applies to you:
+
+       You may obtain a copy of the Apache License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+       implied. See the License for the specific language governing
+       permissions and limitations under the License.
+
+   If you choose to use this file in compliance with the BSD License, the
+   following notice applies to you:
+
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in the
+         documentation and/or other materials provided with the distribution.
+       * Neither the name of the copyright holder nor the names of
+         contributors may be used to endorse or promote products derived from
+         this software without specific prior written permission.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.support;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import jdk.internal.dynalink.DynamicLinkerFactory;
+import jdk.internal.dynalink.linker.MethodHandleTransformer;
+
+/**
+ * Default implementation for a {@link DynamicLinkerFactory#setInternalObjectsFilter(MethodHandleTransformer)}.
+ * Given a method handle of {@code Object(Object)} type for filtering parameter and another one of the same type for
+ * filtering return values, applies them to passed method handles where their parameter types and/or return value types
+ * are declared to be {@link Object}.
+ */
+public class DefaultInternalObjectFilter implements MethodHandleTransformer {
+    private static final MethodHandle FILTER_VARARGS = new Lookup(MethodHandles.lookup()).findStatic(
+            DefaultInternalObjectFilter.class, "filterVarArgs", MethodType.methodType(Object[].class, MethodHandle.class, Object[].class));
+
+    private final MethodHandle parameterFilter;
+    private final MethodHandle returnFilter;
+    private final MethodHandle varArgFilter;
+
+    /**
+     * Creates a new filter.
+     * @param parameterFilter the filter for method parameters. Must be of type {@code Object(Object)}, or null.
+     * @param returnFilter the filter for return values. Must be of type {@code Object(Object)}, or null.
+     * @throws IllegalArgumentException if one or both filters are not of the expected type.
+     */
+    public DefaultInternalObjectFilter(final MethodHandle parameterFilter, final MethodHandle returnFilter) {
+        this.parameterFilter = checkHandle(parameterFilter, "parameterFilter");
+        this.returnFilter = checkHandle(returnFilter, "returnFilter");
+        this.varArgFilter = parameterFilter == null ? null : FILTER_VARARGS.bindTo(parameterFilter);
+    }
+
+    @Override
+    public MethodHandle transform(final MethodHandle target) {
+        assert target != null;
+        MethodHandle[] filters = null;
+        final MethodType type = target.type();
+        final boolean isVarArg = target.isVarargsCollector();
+        final int paramCount = type.parameterCount();
+        final MethodHandle paramsFiltered;
+        // Filter parameters
+        if (parameterFilter != null) {
+            int firstFilter = -1;
+            // Ignore receiver, start from argument 1
+            for(int i = 1; i < paramCount; ++i) {
+                final Class<?> paramType = type.parameterType(i);
+                final boolean filterVarArg = isVarArg && i == paramCount - 1 && paramType == Object[].class;
+                if (filterVarArg || paramType == Object.class) {
+                    if (filters == null) {
+                        firstFilter = i;
+                        filters = new MethodHandle[paramCount - firstFilter];
+                    }
+                    filters[i - firstFilter] = filterVarArg ? varArgFilter : parameterFilter;
+                }
+            }
+            paramsFiltered = filters != null ? MethodHandles.filterArguments(target, firstFilter, filters) : target;
+        } else {
+            paramsFiltered = target;
+        }
+        // Filter return value if needed
+        final MethodHandle returnFiltered = returnFilter != null && type.returnType() == Object.class ? MethodHandles.filterReturnValue(paramsFiltered, returnFilter) : paramsFiltered;
+        // Preserve varargs collector state
+        return isVarArg && !returnFiltered.isVarargsCollector() ? returnFiltered.asVarargsCollector(type.parameterType(paramCount - 1)) : returnFiltered;
+
+    }
+
+    private static MethodHandle checkHandle(final MethodHandle handle, final String handleKind) {
+        if (handle != null) {
+            final MethodType objectObjectType = MethodType.methodType(Object.class, Object.class);
+            if (!handle.type().equals(objectObjectType)) {
+                throw new IllegalArgumentException("Method type for " + handleKind + " must be " + objectObjectType);
+            }
+        }
+        return handle;
+    }
+
+    @SuppressWarnings("unused")
+    private static Object[] filterVarArgs(final MethodHandle parameterFilter, final Object[] args) throws Throwable {
+        Object[] newArgs = null;
+        for(int i = 0; i < args.length; ++i) {
+            final Object arg = args[i];
+            final Object newArg = parameterFilter.invokeExact(arg);
+            if (arg != newArg) {
+                if (newArgs == null) {
+                    newArgs = args.clone();
+                }
+                newArgs[i] = newArg;
+            }
+        }
+        return newArgs == null ? args : newArgs;
+    }
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkerServicesImpl.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/LinkerServicesImpl.java	Thu Feb 12 16:43:33 2015 +0100
@@ -90,6 +90,7 @@
 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.linker.MethodHandleTransformer;
 
 /**
  * Default implementation of the {@link LinkerServices} interface.
@@ -103,17 +104,21 @@
 
     private final TypeConverterFactory typeConverterFactory;
     private final GuardingDynamicLinker topLevelLinker;
+    private final MethodHandleTransformer internalObjectsFilter;
 
     /**
      * Creates a new linker services object.
      *
      * @param typeConverterFactory the type converter factory exposed by the services.
      * @param topLevelLinker the top level linker used by the services.
+     * @param internalObjectsFilter a method handle transformer that is supposed to act as the implementation of this
+     * services' {@link #filterInternalObjects(java.lang.invoke.MethodHandle)} method.
      */
     public LinkerServicesImpl(final TypeConverterFactory typeConverterFactory,
-            final GuardingDynamicLinker topLevelLinker) {
+            final GuardingDynamicLinker topLevelLinker, final MethodHandleTransformer internalObjectsFilter) {
         this.typeConverterFactory = typeConverterFactory;
         this.topLevelLinker = topLevelLinker;
+        this.internalObjectsFilter = internalObjectsFilter;
     }
 
     @Override
@@ -152,6 +157,11 @@
         }
     }
 
+    @Override
+    public MethodHandle filterInternalObjects(final MethodHandle target) {
+        return internalObjectsFilter != null ? internalObjectsFilter.transform(target) : target;
+    }
+
     /**
      * Returns the currently processed link request, or null if the method is invoked outside of the linking process.
      * @return the currently processed link request, or null.
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Thu Feb 12 16:43:33 2015 +0100
@@ -119,6 +119,7 @@
                 return unboxReturnType(target, newType);
             }
         });
+        factory.setInternalObjectsFilter(NashornBeansLinker.createHiddenObjectFilter());
         final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
         if (relinkThreshold > -1) {
             factory.setUnstableRelinkThreshold(relinkThreshold);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java	Tue Feb 10 13:10:07 2015 +0300
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java	Thu Feb 12 16:43:33 2015 +0100
@@ -40,10 +40,11 @@
 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.linker.MethodHandleTransformer;
+import jdk.internal.dynalink.support.DefaultInternalObjectFilter;
 import jdk.internal.dynalink.support.Guards;
 import jdk.internal.dynalink.support.Lookup;
 import jdk.nashorn.api.scripting.ScriptUtils;
-import jdk.nashorn.internal.objects.NativeArray;
 import jdk.nashorn.internal.runtime.ConsString;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ScriptObject;
@@ -52,10 +53,14 @@
 
 /**
  * This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified
- * {@code asType} method that will ensure that we never pass internal engine objects that should not be externally
- * observable (currently ConsString and ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add
- * this functionality as custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when
- * the target method handle parameter signature is {@code Object}.
+ * {@code compareConversion} method that favors conversion of {@link ConsString} to either {@link String} or
+ * {@link CharSequence}. It also provides a {@link #createHiddenObjectFilter()} method for use with bootstrap that will
+ * ensure that we never pass internal engine objects that should not be externally observable (currently ConsString and
+ * ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add this functionality as
+ * custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when
+ * the target method handle parameter signature is {@code Object}. This linker also makes sure that primitive
+ * {@link String} operations can be invoked on a {@link ConsString}, and allows invocation of objects implementing
+ * the {@link FunctionalInterface} attribute.
  */
 public class NashornBeansLinker implements GuardingDynamicLinker {
     // System property to control whether to wrap ScriptObject->ScriptObjectMirror for
@@ -63,16 +68,12 @@
     private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
 
     private static final MethodHandle EXPORT_ARGUMENT;
-    private static final MethodHandle EXPORT_NATIVE_ARRAY;
-    private static final MethodHandle EXPORT_SCRIPT_OBJECT;
     private static final MethodHandle IMPORT_RESULT;
     private static final MethodHandle FILTER_CONSSTRING;
 
     static {
         final Lookup lookup  = new Lookup(MethodHandles.lookup());
         EXPORT_ARGUMENT      = lookup.findOwnStatic("exportArgument", Object.class, Object.class);
-        EXPORT_NATIVE_ARRAY  = lookup.findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
-        EXPORT_SCRIPT_OBJECT = lookup.findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
         IMPORT_RESULT        = lookup.findOwnStatic("importResult", Object.class, Object.class);
         FILTER_CONSSTRING    = lookup.findOwnStatic("consStringFilter", Object.class, Object.class);
     }
@@ -115,9 +116,10 @@
                 }
                 return new GuardedInvocation(
                         // drop 'thiz' passed from the script.
-                        MH.dropArguments(desc.getLookup().unreflect(m), 1, callType.parameterType(1)),
-                        Guards.getInstanceOfGuard(m.getDeclaringClass())).asTypeSafeReturn(
-                                new NashornBeansLinkerServices(linkerServices), callType);
+                        MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1,
+                                callType.parameterType(1)), Guards.getInstanceOfGuard(
+                                        m.getDeclaringClass())).asTypeSafeReturn(
+                                                new NashornBeansLinkerServices(linkerServices), callType);
             }
         }
         return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
@@ -141,21 +143,6 @@
         return exportArgument(arg, MIRROR_ALWAYS);
     }
 
-    @SuppressWarnings("unused")
-    private static Object exportNativeArray(final NativeArray arg) {
-        return exportArgument(arg, MIRROR_ALWAYS);
-    }
-
-    @SuppressWarnings("unused")
-    private static Object exportScriptObject(final ScriptObject arg) {
-        return exportArgument(arg, MIRROR_ALWAYS);
-    }
-
-    @SuppressWarnings("unused")
-    private static Object exportScriptArray(final NativeArray arg) {
-        return exportArgument(arg, MIRROR_ALWAYS);
-    }
-
     static Object exportArgument(final Object arg, final boolean mirrorAlways) {
         if (arg instanceof ConsString) {
             return arg.toString();
@@ -208,6 +195,10 @@
         return FUNCTIONAL_IFACE_METHOD.get(clazz);
     }
 
+    static MethodHandleTransformer createHiddenObjectFilter() {
+        return new DefaultInternalObjectFilter(EXPORT_ARGUMENT, MIRROR_ALWAYS ? IMPORT_RESULT : null);
+    }
+
     private static class NashornBeansLinkerServices implements LinkerServices {
         private final LinkerServices linkerServices;
 
@@ -217,50 +208,7 @@
 
         @Override
         public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
-            final MethodType handleType = handle.type();
-            final int paramCount = handleType.parameterCount();
-            assert fromType.parameterCount() == handleType.parameterCount();
-
-            MethodType newFromType = fromType;
-            MethodHandle[] filters = null;
-            for(int i = 0; i < paramCount; ++i) {
-                final MethodHandle filter = argConversionFilter(handleType.parameterType(i), fromType.parameterType(i));
-                if (filter != null) {
-                    if (filters == null) {
-                        filters = new MethodHandle[paramCount];
-                    }
-                    // "erase" specific type with Object type or else we'll get filter mismatch
-                    newFromType = newFromType.changeParameterType(i, Object.class);
-                    filters[i] = filter;
-                }
-            }
-
-            final MethodHandle typed = linkerServices.asType(handle, newFromType);
-            MethodHandle result = filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed;
-            // Filter Object typed return value for possible ScriptObjectMirror. We convert
-            // ScriptObjectMirror as ScriptObject (if it is mirror from current global).
-            if (MIRROR_ALWAYS && areBothObjects(handleType.returnType(), fromType.returnType())) {
-                result = MethodHandles.filterReturnValue(result, IMPORT_RESULT);
-            }
-
-            return result;
-        }
-
-        private static MethodHandle argConversionFilter(final Class<?> handleType, final Class<?> fromType) {
-            if (handleType == Object.class) {
-                if (fromType == Object.class) {
-                    return EXPORT_ARGUMENT;
-                } else if (fromType == NativeArray.class) {
-                    return EXPORT_NATIVE_ARRAY;
-                } else if (fromType == ScriptObject.class) {
-                    return EXPORT_SCRIPT_OBJECT;
-                }
-            }
-            return null;
-        }
-
-        private static boolean areBothObjects(final Class<?> handleType, final Class<?> fromType) {
-            return handleType == Object.class && fromType == Object.class;
+            return linkerServices.asType(handle, fromType);
         }
 
         @Override
@@ -296,5 +244,10 @@
             }
             return linkerServices.compareConversion(sourceType, targetType1, targetType2);
         }
+
+        @Override
+        public MethodHandle filterInternalObjects(MethodHandle target) {
+            return linkerServices.filterInternalObjects(target);
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8072596.js	Thu Feb 12 16:43:33 2015 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015 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-8072596: Arrays.asList results in ClassCastException with a JS array
+ *
+ * @test
+ * @run
+ */
+var arr = java.util.Arrays.asList("hello world".split(' '));
+// We split it into a list of two elements: [hello, world]
+Assert.assertTrue(arr instanceof java.util.List);
+Assert.assertEquals(arr.length, 2);
+Assert.assertEquals(arr[0], "hello");
+Assert.assertEquals(arr[1], "world");
+
+var Jdk8072596TestSubject = Java.type("jdk.nashorn.test.models.Jdk8072596TestSubject");
+var testSubject = new Jdk8072596TestSubject({bar: 0});
+testSubject.test1(true, {foo: 1}, {bar: 2});
+testSubject.test2(true, {foo: 1}, {bar: 2}, {baz: 3}, {bing: 4});
+var h = "h";
+var ello = "ello";
+testSubject.test3(true, {foo: 5}, /* ConsString, why not */ h + ello, [6, 7], 8);
+Jdk8072596TestSubject.test4({foo: 9});
+
+// Test wrapping setters arguments and unwrapping getters return values on list.
+var list = new java.util.ArrayList();
+list.add(null);
+var obj0 = {valueOf: function() { return 0; }};
+var obj1 = {foo: 10};
+list[obj0] = obj1;
+testSubject.testListHasWrappedObject(list);
+// NOTE: can't use Assert.assertSame(obj1, list[obj0]), as the arguments would end up being wrapped...
+Assert.assertTrue(obj1 === list[obj0]);
+
+// Test wrapping setters arguments and unwrapping getters return values on array.
+var arr2 = new (Java.type("java.lang.Object[]"))(1);
+var obj2 = {bar: 11};
+arr2[obj0] = obj2;
+testSubject.testArrayHasWrappedObject(arr2);
+Assert.assertTrue(obj2 === arr2[obj0]);
+
+// Test wrapping setters index and arguments and getters index, and unwrapping getters return values on map.
+// Since ScriptObjectMirror.equals() uses underlying ScriptObject identity, using them as map keys works.
+var map = new java.util.HashMap();
+var obj3 = {bar: 12};
+map[obj0] = obj3;
+testSubject.testMapHasWrappedObject(map, obj0);
+Assert.assertTrue(obj3 === map[obj0]);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/test/models/Jdk8072596TestSubject.java	Thu Feb 12 16:43:33 2015 +0100
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2015, 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.test.models;
+
+import java.util.List;
+import java.util.Map;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import org.testng.Assert;
+
+public class Jdk8072596TestSubject {
+
+    public Jdk8072596TestSubject(final Object x) {
+        Assert.assertTrue(x instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)x).get("bar"), 0);
+    }
+
+    // Test having to wrap some arguments but not others
+    public void test1(final String x, final Object y, final ScriptObject w) {
+        Assert.assertEquals(x, "true");
+
+        Assert.assertTrue(y instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)y).get("foo"), 1);
+
+        Assert.assertEquals(w.get("bar"), 2);
+    }
+
+    // Test having to wrap some arguments but not others, and a vararg array
+    public void test2(String x, final Object y, final ScriptObject w, final Object... z) {
+        test1(x, y, w);
+
+        Assert.assertEquals(z.length, 2);
+
+        Assert.assertTrue(z[0] instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)z[0]).get("baz"), 3);
+
+        Assert.assertTrue(z[1] instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)z[1]).get("bing"), 4);
+    }
+
+    // Test mixed (wrappable and non-wrappable) elements in a vararg array
+    public void test3(final Object... z) {
+        Assert.assertEquals(z.length, 5);
+
+        Assert.assertEquals(z[0], true);
+
+        Assert.assertTrue(z[1] instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)z[1]).get("foo"), 5);
+
+        Assert.assertEquals(z[2], "hello");
+
+        Assert.assertTrue(z[3] instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)z[3]).getSlot(0), 6);
+        Assert.assertEquals(((ScriptObjectMirror)z[3]).getSlot(1), 7);
+
+        Assert.assertEquals(z[4], 8);
+    }
+
+    // test wrapping the first argument of a static method
+    public static void test4(final Object x) {
+        Assert.assertTrue(x instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)x).get("foo"), 9);
+    }
+
+    public void testListHasWrappedObject(final List<?> l) {
+        Assert.assertEquals(l.size(), 1);
+        Assert.assertTrue(l.get(0) instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)l.get(0)).get("foo"), 10);
+    }
+
+    public void testArrayHasWrappedObject(final Object[] a) {
+        Assert.assertEquals(a.length, 1);
+        Assert.assertTrue(a[0] instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)a[0]).get("bar"), 11);
+    }
+
+    public void testMapHasWrappedObject(final Map<?, ?> m, final Object key) {
+        Assert.assertEquals(m.size(), 1);
+        Assert.assertTrue(key instanceof ScriptObjectMirror);
+        Assert.assertTrue(m.get(key) instanceof ScriptObjectMirror);
+        Assert.assertEquals(((ScriptObjectMirror)m.get(key)).get("bar"), 12);
+    }
+}