8072596: Arrays.asList results in ClassCastException with a JS array
Reviewed-by: lagergren, sundar
This commit is contained in:
parent
d1c194480f
commit
8d084dc298
@ -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 @@ EOF
|
||||
// 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();
|
||||
|
@ -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();
|
||||
|
@ -97,6 +97,8 @@ import jdk.internal.dynalink.beans.BeansLinker;
|
||||
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 @@ public class DynamicLinkerFactory {
|
||||
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
|
||||
@ -283,6 +286,15 @@ public class DynamicLinkerFactory {
|
||||
this.autoConversionStrategy = autoConversionStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public class DynamicLinkerFactory {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -570,7 +570,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
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 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
@ -165,6 +165,10 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
// 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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
}
|
||||
|
||||
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
// 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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
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 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
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());
|
||||
|
@ -139,7 +139,8 @@ class OverloadedMethod {
|
||||
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() {
|
||||
|
@ -165,10 +165,11 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
* @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 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -180,6 +180,15 @@ public interface LinkerServices {
|
||||
*/
|
||||
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.
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -90,6 +90,7 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
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 @@ public class LinkerServicesImpl implements LinkerServices {
|
||||
|
||||
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 @@ public class LinkerServicesImpl implements LinkerServices {
|
||||
}
|
||||
}
|
||||
|
||||
@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.
|
||||
|
@ -119,6 +119,7 @@ public final class Bootstrap {
|
||||
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);
|
||||
|
@ -40,10 +40,11 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
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 @@ import jdk.nashorn.internal.runtime.options.Options;
|
||||
|
||||
/**
|
||||
* 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 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
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 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
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 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
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 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
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 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
|
||||
@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 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
return linkerServices.compareConversion(sourceType, targetType1, targetType2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle filterInternalObjects(MethodHandle target) {
|
||||
return linkerServices.filterInternalObjects(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
69
nashorn/test/script/basic/JDK-8072596.js
Normal file
69
nashorn/test/script/basic/JDK-8072596.js
Normal file
@ -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]);
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user