8072596: Arrays.asList results in ClassCastException with a JS array
Reviewed-by: lagergren, sundar
--- 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);
+ }
+}