8144919: Implement missing member handler for BeansLinker
Reviewed-by: lagergren, mhaupt, sundar
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AbstractJavaLinker.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AbstractJavaLinker.java Thu Jan 14 13:24:03 2016 +0100
@@ -349,31 +349,52 @@
throws Exception {
final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor();
+ final MissingMemberHandlerFactory missingMemberHandlerFactory;
+ final LinkerServices directLinkerServices;
+ if (linkerServices instanceof LinkerServicesWithMissingMemberHandlerFactory) {
+ final LinkerServicesWithMissingMemberHandlerFactory lswmmhf = ((LinkerServicesWithMissingMemberHandlerFactory)linkerServices);
+ missingMemberHandlerFactory = lswmmhf.missingMemberHandlerFactory;
+ directLinkerServices = lswmmhf.linkerServices;
+ } else {
+ missingMemberHandlerFactory = null;
+ directLinkerServices = linkerServices;
+ }
+
// Handle NamedOperation(CALL_METHOD, name) separately
final Operation operation = callSiteDescriptor.getOperation();
if (operation instanceof NamedOperation) {
final NamedOperation namedOperation = (NamedOperation)operation;
if (namedOperation.getBaseOperation() == StandardOperation.CALL_METHOD) {
- return createGuardedDynamicMethodInvocation(callSiteDescriptor,
- linkerServices, namedOperation.getName().toString(), methods);
+ final GuardedInvocation inv =
+ createGuardedDynamicMethodInvocation(callSiteDescriptor,
+ directLinkerServices, namedOperation.getName().toString(), methods);
+ if (inv == null) {
+ return createNoSuchMemberHandler(missingMemberHandlerFactory,
+ request, directLinkerServices).getGuardedInvocation();
+ }
+ return inv;
}
}
final GuardedInvocationComponent gic = getGuardedInvocationComponent(
- new ComponentLinkRequest(request, linkerServices));
+ new ComponentLinkRequest(request, directLinkerServices,
+ missingMemberHandlerFactory));
return gic != null ? gic.getGuardedInvocation() : null;
}
static final class ComponentLinkRequest {
final LinkRequest linkRequest;
final LinkerServices linkerServices;
+ final MissingMemberHandlerFactory missingMemberHandlerFactory;
final List<Operation> operations;
final Object name;
ComponentLinkRequest(final LinkRequest linkRequest,
- final LinkerServices linkerServices) {
+ final LinkerServices linkerServices,
+ final MissingMemberHandlerFactory missingMemberHandlerFactory) {
this.linkRequest = linkRequest;
this.linkerServices = linkerServices;
+ this.missingMemberHandlerFactory = missingMemberHandlerFactory;
final Operation operation = linkRequest.getCallSiteDescriptor().getOperation();
this.operations = Arrays.asList(
CompositeOperation.getOperations(
@@ -383,9 +404,11 @@
private ComponentLinkRequest(final LinkRequest linkRequest,
final LinkerServices linkerServices,
+ final MissingMemberHandlerFactory missingMemberHandlerFactory,
final List<Operation> operations, final Object name) {
this.linkRequest = linkRequest;
this.linkerServices = linkerServices;
+ this.missingMemberHandlerFactory = missingMemberHandlerFactory;
this.operations = operations;
this.name = name;
}
@@ -396,6 +419,7 @@
ComponentLinkRequest popOperations() {
return new ComponentLinkRequest(linkRequest, linkerServices,
+ missingMemberHandlerFactory,
operations.subList(1, operations.size()), name);
}
}
@@ -416,7 +440,8 @@
GuardedInvocationComponent getNextComponent(final ComponentLinkRequest req) throws Exception {
if (req.operations.isEmpty()) {
- return null;
+ return createNoSuchMemberHandler(req.missingMemberHandlerFactory,
+ req.linkRequest, req.linkerServices);
}
final GuardedInvocationComponent gic = getGuardedInvocationComponent(req);
if (gic != null) {
@@ -425,6 +450,22 @@
return getNextComponent(req.popOperations());
}
+ private GuardedInvocationComponent createNoSuchMemberHandler(
+ final MissingMemberHandlerFactory missingMemberHandlerFactory,
+ final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
+ if (missingMemberHandlerFactory == null) {
+ return null;
+ }
+ final MethodHandle handler = missingMemberHandlerFactory.createMissingMemberHandler(linkRequest, linkerServices);
+ if (handler == null) {
+ return null;
+ }
+ final MethodType type = linkRequest.getCallSiteDescriptor().getMethodType();
+ // The returned handler is allowed to differ in return type.
+ assert handler.type().changeReturnType(type.returnType()).equals(type);
+ return getClassGuardedInvocationComponent(handler, type);
+ }
+
static final <T> List<T> pop(final List<T> l) {
return l.subList(1, l.size());
}
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java Thu Jan 14 13:24:03 2016 +0100
@@ -88,6 +88,7 @@
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import jdk.dynalink.CallSiteDescriptor;
@@ -162,12 +163,27 @@
private static final MethodHandle LIST_GUARD = Guards.getInstanceOfGuard(List.class);
private static final MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class);
+ private static final MethodHandle NULL_GETTER_1;
+ private static final MethodHandle NULL_GETTER_2;
+ static {
+ final MethodHandle constantNull = MethodHandles.constant(Object.class, null);
+ NULL_GETTER_1 = dropObjectArguments(constantNull, 1);
+ NULL_GETTER_2 = dropObjectArguments(constantNull, 2);
+ }
+
+ private static MethodHandle dropObjectArguments(final MethodHandle m, final int n) {
+ return MethodHandles.dropArguments(m, 0, Collections.nCopies(n, Object.class));
+ }
+
private enum CollectionType {
ARRAY, LIST, MAP
};
private GuardedInvocationComponent getElementGetter(final ComponentLinkRequest req) throws Exception {
final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
+ final Object name = req.name;
+ final boolean isFixedKey = name != null;
+ assertParameterCount(callSiteDescriptor, isFixedKey ? 1 : 2);
final LinkerServices linkerServices = req.linkerServices;
final MethodType callSiteType = callSiteDescriptor.getMethodType();
final Class<?> declaredType = callSiteType.parameterType(0);
@@ -207,13 +223,14 @@
// Convert the key to a number if we're working with a list or array
final Object typedName;
- final Object name = req.name;
- if(collectionType != CollectionType.MAP && name != null) {
- typedName = convertKeyToInteger(name, linkerServices);
- if(typedName == null) {
- // key is not numeric, it can never succeed
+ if (collectionType != CollectionType.MAP && isFixedKey) {
+ final Integer integer = convertKeyToInteger(name, linkerServices);
+ if (integer == null || integer.intValue() < 0) {
+ // key is not a non-negative integer, it can never address an
+ // array or list element
return nextComponent;
}
+ typedName = integer;
} else {
typedName = name;
}
@@ -222,30 +239,33 @@
final Binder binder = new Binder(linkerServices, callSiteType, typedName);
final MethodHandle invocation = gi.getInvocation();
- if(nextComponent == null) {
- return gic.replaceInvocation(binder.bind(invocation));
- }
-
final MethodHandle checkGuard;
switch(collectionType) {
case LIST:
- checkGuard = convertArgToInt(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor);
+ checkGuard = convertArgToNumber(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor);
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 = linkerServices.filterInternalObjects(CONTAINS_MAP);
break;
case ARRAY:
- checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
+ checkGuard = convertArgToNumber(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
break;
default:
throw new AssertionError();
}
+
+ // If there's no next component, produce a fixed null-returning one
+ final GuardedInvocationComponent finalNextComponent;
+ if (nextComponent != null) {
+ finalNextComponent = nextComponent;
+ } else {
+ final MethodHandle nullGetterHandle = isFixedKey ? NULL_GETTER_1 : NULL_GETTER_2;
+ finalNextComponent = createGuardedInvocationComponentAsType(nullGetterHandle, callSiteType, linkerServices);
+ }
+
final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
- nextComponent.getGuardedInvocation().getInvocation());
- return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
+ finalNextComponent.getGuardedInvocation().getInvocation());
+ return finalNextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
gic.getValidatorClass(), gic.getValidationType());
}
@@ -254,6 +274,11 @@
return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation));
}
+ private static GuardedInvocationComponent createGuardedInvocationComponentAsType(
+ final MethodHandle invocation, final MethodType fromType, final LinkerServices linkerServices) {
+ return new GuardedInvocationComponent(linkerServices.asType(invocation, fromType));
+ }
+
private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
final MethodHandle invocation, final MethodHandle guard, final Class<?> validatorClass,
final ValidationType validationType, final LinkerServices linkerServices) {
@@ -307,7 +332,7 @@
return intIndex;
}
- private static MethodHandle convertArgToInt(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
+ private static MethodHandle convertArgToNumber(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
final Class<?> sourceType = desc.getMethodType().parameterType(1);
if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
return mh;
@@ -363,14 +388,10 @@
}
final Number n = (Number)index;
final int intIndex = n.intValue();
- final double doubleValue = n.doubleValue();
- if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinite trigger IOOBE
+ if (intIndex != n.doubleValue()) {
return false;
}
- if(0 <= intIndex && intIndex < Array.getLength(array)) {
- return true;
- }
- throw new ArrayIndexOutOfBoundsException("Array index out of range: " + n);
+ return 0 <= intIndex && intIndex < Array.getLength(array);
}
@SuppressWarnings("unused")
@@ -380,14 +401,14 @@
}
final Number n = (Number)index;
final int intIndex = n.intValue();
- final double doubleValue = n.doubleValue();
- if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinite trigger IOOBE
+ if (intIndex != n.doubleValue()) {
return false;
}
- if(0 <= intIndex && intIndex < list.size()) {
- return true;
- }
- throw new IndexOutOfBoundsException("Index: " + n + ", Size: " + list.size());
+ return 0 <= intIndex && intIndex < list.size();
+ }
+
+ @SuppressWarnings("unused")
+ private static void noOpSetter() {
}
private static final MethodHandle SET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "set",
@@ -396,8 +417,19 @@
private static final MethodHandle PUT_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "put",
MethodType.methodType(Object.class, Object.class, Object.class));
+ private static final MethodHandle NO_OP_SETTER_2;
+ private static final MethodHandle NO_OP_SETTER_3;
+ static {
+ final MethodHandle noOpSetter = Lookup.findOwnStatic(MethodHandles.lookup(), "noOpSetter", void.class);
+ NO_OP_SETTER_2 = dropObjectArguments(noOpSetter, 2);
+ NO_OP_SETTER_3 = dropObjectArguments(noOpSetter, 3);
+ }
+
private GuardedInvocationComponent getElementSetter(final ComponentLinkRequest req) throws Exception {
final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
+ final Object name = req.name;
+ final boolean isFixedKey = name != null;
+ assertParameterCount(callSiteDescriptor, isFixedKey ? 2 : 3);
final LinkerServices linkerServices = req.linkerServices;
final MethodType callSiteType = callSiteDescriptor.getMethodType();
final Class<?> declaredType = callSiteType.parameterType(0);
@@ -446,13 +478,14 @@
// Convert the key to a number if we're working with a list or array
final Object typedName;
- final Object name = req.name;
- if(collectionType != CollectionType.MAP && name != null) {
- typedName = convertKeyToInteger(name, linkerServices);
- if(typedName == null) {
- // key is not numeric, it can never succeed
+ if (collectionType != CollectionType.MAP && isFixedKey) {
+ final Integer integer = convertKeyToInteger(name, linkerServices);
+ if (integer == null || integer.intValue() < 0) {
+ // key is not a non-negative integer, it can never address an
+ // array or list element
return nextComponent;
}
+ typedName = integer;
} else {
typedName = name;
}
@@ -461,16 +494,27 @@
final Binder binder = new Binder(linkerServices, callSiteType, typedName);
final MethodHandle invocation = gi.getInvocation();
- if(nextComponent == null) {
+ if (collectionType == CollectionType.MAP) {
+ assert nextComponent == null;
return gic.replaceInvocation(binder.bind(invocation));
}
assert collectionType == CollectionType.LIST || collectionType == CollectionType.ARRAY;
- final MethodHandle checkGuard = convertArgToInt(collectionType == CollectionType.LIST ? RANGE_CHECK_LIST :
+ final MethodHandle checkGuard = convertArgToNumber(collectionType == CollectionType.LIST ? RANGE_CHECK_LIST :
RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
+
+ // If there's no next component, produce a no-op one.
+ final GuardedInvocationComponent finalNextComponent;
+ if (nextComponent != null) {
+ finalNextComponent = nextComponent;
+ } else {
+ final MethodHandle noOpSetterHandle = isFixedKey ? NO_OP_SETTER_2 : NO_OP_SETTER_3;
+ finalNextComponent = createGuardedInvocationComponentAsType(noOpSetterHandle, callSiteType, linkerServices);
+ }
+
final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
- nextComponent.getGuardedInvocation().getInvocation());
- return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
+ finalNextComponent.getGuardedInvocation().getInvocation());
+ return finalNextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
gic.getValidatorClass(), gic.getValidationType());
}
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeansLinker.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeansLinker.java Thu Jan 14 13:24:03 2016 +0100
@@ -146,7 +146,11 @@
* are otherwise public and link requests have call site descriptors carrying
* full-strength {@link Lookup} objects and not weakened lookups or the public
* lookup.</p>
- * <p>The class also exposes various static methods for discovery of available
+ * <p><strong>The behavior for handling missing members</strong> can be
+ * customized by passing a {@link MissingMemberHandlerFactory} to the
+ * {@link BeansLinker#BeansLinker(MissingMemberHandlerFactory) constructor}.
+ * </p>
+ * <p>The class also exposes various methods for discovery of available
* property and method names on classes and class instances, as well as access
* to per-class linkers using the {@link #getLinkerForClass(Class)}
* method.</p>
@@ -164,10 +168,27 @@
}
};
+ private final MissingMemberHandlerFactory missingMemberHandlerFactory;
+
/**
- * Creates a new beans linker.
+ * Creates a new beans linker. Equivalent to
+ * {@link BeansLinker#BeansLinker(MissingMemberHandlerFactory)} with
+ * {@code null} passed as the missing member handler factory, resulting in
+ * the default behavior for linking and evaluating missing members.
*/
public BeansLinker() {
+ this(null);
+ }
+
+ /**
+ * Creates a new beans linker with the specified factory for creating
+ * missing member handlers. The passed factory can be null if the default
+ * behavior is adequate. See {@link MissingMemberHandlerFactory} for details.
+ * @param missingMemberHandlerFactory a factory for creating handlers for
+ * operations on missing members.
+ */
+ public BeansLinker(final MissingMemberHandlerFactory missingMemberHandlerFactory) {
+ this.missingMemberHandlerFactory = missingMemberHandlerFactory;
}
/**
@@ -178,7 +199,37 @@
* @param clazz the class
* @return a bean linker for that class
*/
- public static TypeBasedGuardingDynamicLinker getLinkerForClass(final Class<?> clazz) {
+ public TypeBasedGuardingDynamicLinker getLinkerForClass(final Class<?> clazz) {
+ final TypeBasedGuardingDynamicLinker staticLinker = getStaticLinkerForClass(clazz);
+ if (missingMemberHandlerFactory == null) {
+ return staticLinker;
+ }
+ return new NoSuchMemberHandlerBindingLinker(staticLinker, missingMemberHandlerFactory);
+ }
+
+ private static class NoSuchMemberHandlerBindingLinker implements TypeBasedGuardingDynamicLinker {
+ private final TypeBasedGuardingDynamicLinker linker;
+ private final MissingMemberHandlerFactory missingMemberHandlerFactory;
+
+ NoSuchMemberHandlerBindingLinker(final TypeBasedGuardingDynamicLinker linker, final MissingMemberHandlerFactory missingMemberHandlerFactory) {
+ this.linker = linker;
+ this.missingMemberHandlerFactory = missingMemberHandlerFactory;
+ }
+
+ @Override
+ public boolean canLinkType(final Class<?> type) {
+ return linker.canLinkType(type);
+ }
+
+ @Override
+ public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
+ return linker.getGuardedInvocation(linkRequest,
+ LinkerServicesWithMissingMemberHandlerFactory.get(
+ linkerServices, missingMemberHandlerFactory));
+ }
+ }
+
+ static TypeBasedGuardingDynamicLinker getStaticLinkerForClass(final Class<?> clazz) {
return linkers.get(clazz);
}
@@ -234,7 +285,7 @@
* @return a set of names of all readable instance properties of a class.
*/
public static Set<String> getReadableInstancePropertyNames(final Class<?> clazz) {
- final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
+ final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);
if(linker instanceof BeanLinker) {
return ((BeanLinker)linker).getReadablePropertyNames();
}
@@ -247,7 +298,7 @@
* @return a set of names of all writable instance properties of a class.
*/
public static Set<String> getWritableInstancePropertyNames(final Class<?> clazz) {
- final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
+ final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);
if(linker instanceof BeanLinker) {
return ((BeanLinker)linker).getWritablePropertyNames();
}
@@ -260,7 +311,7 @@
* @return a set of names of all instance methods of a class.
*/
public static Set<String> getInstanceMethodNames(final Class<?> clazz) {
- final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
+ final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz);
if(linker instanceof BeanLinker) {
return ((BeanLinker)linker).getMethodNames();
}
@@ -302,6 +353,8 @@
// Can't operate on null
return null;
}
- return getLinkerForClass(receiver.getClass()).getGuardedInvocation(request, linkerServices);
+ return getLinkerForClass(receiver.getClass()).getGuardedInvocation(request,
+ LinkerServicesWithMissingMemberHandlerFactory.get(linkerServices,
+ missingMemberHandlerFactory));
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/LinkerServicesWithMissingMemberHandlerFactory.java Thu Jan 14 13:24:03 2016 +0100
@@ -0,0 +1,80 @@
+/*
+ * 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.dynalink.beans;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import jdk.dynalink.linker.ConversionComparator.Comparison;
+import jdk.dynalink.linker.GuardedInvocation;
+import jdk.dynalink.linker.LinkRequest;
+import jdk.dynalink.linker.LinkerServices;
+
+final class LinkerServicesWithMissingMemberHandlerFactory implements LinkerServices {
+ final LinkerServices linkerServices;
+ final MissingMemberHandlerFactory missingMemberHandlerFactory;
+
+ static LinkerServices get(final LinkerServices linkerServices, final MissingMemberHandlerFactory missingMemberHandlerFactory) {
+ if (missingMemberHandlerFactory == null) {
+ return linkerServices;
+ }
+ return new LinkerServicesWithMissingMemberHandlerFactory(linkerServices, missingMemberHandlerFactory);
+ }
+
+ private LinkerServicesWithMissingMemberHandlerFactory(final LinkerServices linkerServices, final MissingMemberHandlerFactory missingMemberHandlerFactory) {
+ this.linkerServices = linkerServices;
+ this.missingMemberHandlerFactory = missingMemberHandlerFactory;
+ }
+
+ @Override
+ public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
+ return linkerServices.asType(handle, fromType);
+ }
+
+ @Override
+ public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
+ return linkerServices.getTypeConverter(sourceType, targetType);
+ }
+
+ @Override
+ public boolean canConvert(final Class<?> from, final Class<?> to) {
+ return linkerServices.canConvert(from, to);
+ }
+
+ @Override
+ public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest) throws Exception {
+ return linkerServices.getGuardedInvocation(linkRequest);
+ }
+
+ @Override
+ public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
+ return linkerServices.compareConversion(sourceType, targetType1, targetType2);
+ }
+
+ @Override
+ public MethodHandle filterInternalObjects(final MethodHandle target) {
+ return linkerServices.filterInternalObjects(target);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/MissingMemberHandlerFactory.java Thu Jan 14 13:24:03 2016 +0100
@@ -0,0 +1,86 @@
+/*
+ * 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.dynalink.beans;
+
+import java.lang.invoke.MethodHandle;
+import jdk.dynalink.DynamicLinkerFactory;
+import jdk.dynalink.NamedOperation;
+import jdk.dynalink.NoSuchDynamicMethodException;
+import jdk.dynalink.StandardOperation;
+import jdk.dynalink.linker.LinkRequest;
+import jdk.dynalink.linker.LinkerServices;
+
+/**
+ * A factory for creating method handles for linking missing member behavior
+ * in {@link BeansLinker}. BeansLinker links these method handles into guarded
+ * invocations for link requests specifying {@code GET_*} and {@code SET_*}
+ * {@link StandardOperation}s when it is either certain or possible that the
+ * requested member (property, method, or element) is missing. They will be
+ * linked both for {@link NamedOperation named} and unnamed operations. The
+ * implementer must ensure that the parameter types of the returned method
+ * handle match the parameter types of the call site described in the link
+ * request. The return types can differ, though, to allow
+ * {@link DynamicLinkerFactory#setPrelinkTransformer(jdk.dynalink.linker.GuardedInvocationTransformer)}
+ * late return type transformations}. It is allowed to return {@code null} for a
+ * method handle if the default behavior is sufficient.
+ * <h2>Default missing member behavior</h2>
+ * When a {@link BeansLinker} is configured without a missing member handler
+ * factory, or the factory returns {@code null} for a particular handler
+ * creation invocation, the default behavior is used. The default behavior is to
+ * return {@code null} from
+ * {@link BeansLinker#getGuardedInvocation(LinkRequest, LinkerServices)} when it
+ * can be determined at link time that the linked operation will never address
+ * an existing member. This lets the {@code DynamicLinker} attempt the next
+ * linker if there is one, or ultimately fail the link request with
+ * {@link NoSuchDynamicMethodException}. For other cases (typically all unnamed
+ * member operations as well as most named operations on collection elements)
+ * {@code BeansLinker} will produce a conditional linkage that will return
+ * {@code null} when invoked at runtime with a name that does not match any
+ * member for getters and silently ignore the passed values for setters.
+ * <h2>Implementing exception-throwing behavior</h2>
+ * Note that if the language-specific behavior for an operation on a missing
+ * member is to throw an exception then the factory should produce a method
+ * handle that throws the exception when invoked, and must not throw an
+ * exception itself, as the linkage for the missing member is often conditional.
+ *
+ * @see BeansLinker#BeansLinker(MissingMemberHandlerFactory)
+ */
+@FunctionalInterface
+public interface MissingMemberHandlerFactory {
+ /**
+ * Returns a method handle suitable for implementing missing member behavior
+ * for a particular link request. See the class description for details.
+ * @param linkRequest the current link request
+ * @param linkerServices the current link services
+ * @return a method handle that can be invoked if the property, element, or
+ * method being addressed by an operation is missing. The return value can
+ * be null.
+ * @throws Exception if the operation fails for any reason. Please observe
+ * the class documentation notes for implementing exception-throwing
+ * missing member behavior.
+ */
+ public MethodHandle createMissingMemberHandler(LinkRequest linkRequest, LinkerServices linkerServices) throws Exception;
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java Thu Jan 14 13:24:03 2016 +0100
@@ -776,7 +776,7 @@
final MethodType getterType = MethodType.methodType(Object.class, clazz);
final MethodType setterType = MethodType.methodType(Object.class, clazz, Object.class);
- final GuardingDynamicLinker linker = BeansLinker.getLinkerForClass(clazz);
+ final GuardingDynamicLinker linker = Bootstrap.getBeanLinkerForClass(clazz);
final List<AccessorProperty> properties = new ArrayList<>(propertyNames.size() + methodNames.size());
for(final String methodName: methodNames) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java Thu Jan 14 13:24:03 2016 +0100
@@ -94,6 +94,9 @@
*/
public static GuardedInvocation lookup(final CallSiteDescriptor desc) {
final StandardOperation op = NashornCallSiteDescriptor.getFirstStandardOperation(desc);
+ if (op == null) {
+ return null;
+ }
switch (op) {
case CALL:
case NEW:
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Jan 14 13:24:03 2016 +0100
@@ -40,10 +40,11 @@
import jdk.dynalink.beans.BeansLinker;
import jdk.dynalink.beans.StaticClass;
import jdk.dynalink.linker.GuardedInvocation;
-import jdk.dynalink.linker.GuardedInvocationTransformer;
+import jdk.dynalink.linker.GuardingDynamicLinker;
import jdk.dynalink.linker.LinkRequest;
import jdk.dynalink.linker.LinkerServices;
import jdk.dynalink.linker.MethodTypeConversionStrategy;
+import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.dynalink.linker.support.TypeUtilities;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
@@ -67,6 +68,24 @@
private static final MethodHandle VOID_TO_OBJECT = MH.constant(Object.class, ScriptRuntime.UNDEFINED);
+ private static final BeansLinker beansLinker = new BeansLinker(Bootstrap::createMissingMemberHandler);
+ private static final GuardingDynamicLinker[] prioritizedLinkers;
+ private static final GuardingDynamicLinker[] fallbackLinkers;
+ static {
+ final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker(beansLinker);
+ prioritizedLinkers = new GuardingDynamicLinker[] {
+ new NashornLinker(),
+ new NashornPrimitiveLinker(),
+ new NashornStaticClassLinker(beansLinker),
+ new BoundCallableLinker(),
+ new JavaSuperAdapterLinker(beansLinker),
+ new JSObjectLinker(nashornBeansLinker),
+ new BrowserJSObjectLinker(nashornBeansLinker),
+ new ReflectionCheckLinker()
+ };
+ fallbackLinkers = new GuardingDynamicLinker[] {nashornBeansLinker, new NashornBottomLinker() };
+ }
+
// do not create me!!
private Bootstrap() {
}
@@ -81,31 +100,14 @@
public static DynamicLinker createDynamicLinker(final ClassLoader appLoader,
final int unstableRelinkThreshold) {
final DynamicLinkerFactory factory = new DynamicLinkerFactory();
- final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker();
- factory.setPrioritizedLinkers(
- new NashornLinker(),
- new NashornPrimitiveLinker(),
- new NashornStaticClassLinker(),
- new BoundCallableLinker(),
- new JavaSuperAdapterLinker(),
- new JSObjectLinker(nashornBeansLinker),
- new BrowserJSObjectLinker(nashornBeansLinker),
- new ReflectionCheckLinker());
- factory.setFallbackLinkers(nashornBeansLinker, new NashornBottomLinker());
+ factory.setPrioritizedLinkers(prioritizedLinkers);
+ factory.setFallbackLinkers(fallbackLinkers);
factory.setSyncOnRelink(true);
- factory.setPrelinkTransformer(new GuardedInvocationTransformer() {
- @Override
- public GuardedInvocation filter(final GuardedInvocation inv, final LinkRequest request, final LinkerServices linkerServices) {
- final CallSiteDescriptor desc = request.getCallSiteDescriptor();
- return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc).asType(linkerServices, desc.getMethodType());
- }
+ factory.setPrelinkTransformer((inv, request, linkerServices) -> {
+ final CallSiteDescriptor desc = request.getCallSiteDescriptor();
+ return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc).asType(linkerServices, desc.getMethodType());
});
- factory.setAutoConversionStrategy(new MethodTypeConversionStrategy() {
- @Override
- public MethodHandle asType(final MethodHandle target, final MethodType newType) {
- return unboxReturnType(target, newType);
- }
- });
+ factory.setAutoConversionStrategy(Bootstrap::unboxReturnType);
factory.setInternalObjectsFilter(NashornBeansLinker.createHiddenObjectFilter());
factory.setUnstableRelinkThreshold(unstableRelinkThreshold);
@@ -115,6 +117,15 @@
}
/**
+ * Returns a dynamic linker for the specific Java class using beans semantics.
+ * @param clazz the Java class
+ * @return a dynamic linker for the specific Java class using beans semantics.
+ */
+ public static TypeBasedGuardingDynamicLinker getBeanLinkerForClass(final Class<?> clazz) {
+ return beansLinker.getLinkerForClass(clazz);
+ }
+
+ /**
* Returns if the given object is a "callable"
* @param obj object to be checked for callability
* @return true if the obj is callable
@@ -475,4 +486,14 @@
}
return target;
}
+
+ private static MethodHandle createMissingMemberHandler(
+ final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
+ if (BrowserJSObjectLinker.canLinkTypeStatic(linkRequest.getReceiver().getClass())) {
+ // Don't create missing member handlers for the browser JS objects as they
+ // have their own logic.
+ return null;
+ }
+ return NashornBottomLinker.linkMissingBeanMember(linkRequest, linkerServices);
+ }
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java Thu Jan 14 13:24:03 2016 +0100
@@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime.linker;
-import static jdk.nashorn.internal.lookup.Lookup.EMPTY_GETTER;
import static jdk.nashorn.internal.runtime.linker.JavaAdapterBytecodeGenerator.SUPER_PREFIX;
import java.lang.invoke.MethodHandle;
@@ -62,6 +61,12 @@
IS_ADAPTER_OF_CLASS = lookup.findOwnStatic("isAdapterOfClass", boolean.class, Class.class, Object.class);
}
+ private final BeansLinker beansLinker;
+
+ JavaSuperAdapterLinker(final BeansLinker beansLinker) {
+ this.beansLinker = beansLinker;
+ }
+
@Override
public boolean canLinkType(final Class<?> type) {
return type == JavaSuperAdapter.class;
@@ -101,17 +106,13 @@
// Delegate to BeansLinker
final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation(
- BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args),
+ beansLinker, linkRequest.replaceArguments(newDescriptor, args),
linkerServices);
+ // Even for non-existent methods, Bootstrap's BeansLinker will link a
+ // noSuchMember handler.
+ assert guardedInv != null;
final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass);
- if(guardedInv == null) {
- // Short circuit the lookup here for non-existent methods by linking an empty getter. If we just returned
- // null instead, BeansLinker would find final methods on the JavaSuperAdapter instead: getClass() and
- // wait().
- return new GuardedInvocation(MethodHandles.dropArguments(EMPTY_GETTER, 1,type.parameterList().subList(1,
- type.parameterCount())), guard).asType(descriptor);
- }
final MethodHandle invocation = guardedInv.getInvocation();
final MethodType invType = invocation.type();
@@ -165,7 +166,7 @@
*/
@SuppressWarnings("unused")
private static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
- return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindCallable(dynamicMethod, boundThis, null);
+ return dynamicMethod == ScriptRuntime.UNDEFINED ? ScriptRuntime.UNDEFINED : Bootstrap.bindCallable(dynamicMethod, boundThis, null);
}
/**
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Thu Jan 14 13:24:03 2016 +0100
@@ -85,7 +85,11 @@
}
};
- private final BeansLinker beansLinker = new BeansLinker();
+ private final BeansLinker beansLinker;
+
+ NashornBeansLinker(final BeansLinker beansLinker) {
+ this.beansLinker = beansLinker;
+ }
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Thu Jan 14 13:24:03 2016 +0100
@@ -27,26 +27,27 @@
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED;
-import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import jdk.dynalink.CallSiteDescriptor;
import jdk.dynalink.NamedOperation;
import jdk.dynalink.Operation;
+import jdk.dynalink.StandardOperation;
import jdk.dynalink.beans.BeansLinker;
import jdk.dynalink.linker.GuardedInvocation;
import jdk.dynalink.linker.GuardingDynamicLinker;
import jdk.dynalink.linker.GuardingTypeConverterFactory;
import jdk.dynalink.linker.LinkRequest;
import jdk.dynalink.linker.LinkerServices;
-import jdk.dynalink.linker.support.Guards;
+import jdk.dynalink.linker.support.Lookup;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
@@ -73,7 +74,7 @@
// this point is a generic Java bean. Therefore, reaching here with a ScriptObject is a Nashorn bug.
assert isExpectedObject(self) : "Couldn't link " + linkRequest.getCallSiteDescriptor() + " for " + self.getClass().getName();
- return linkBean(linkRequest, linkerServices);
+ return linkBean(linkRequest);
}
private static final MethodHandle EMPTY_PROP_GETTER =
@@ -85,7 +86,18 @@
private static final MethodHandle EMPTY_ELEM_SETTER =
MH.dropArguments(EMPTY_PROP_SETTER, 0, Object.class);
- private static GuardedInvocation linkBean(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
+ private static final MethodHandle THROW_NO_SUCH_FUNCTION;
+ private static final MethodHandle THROW_STRICT_PROPERTY_SETTER;
+ private static final MethodHandle THROW_OPTIMISTIC_UNDEFINED;
+
+ static {
+ final Lookup lookup = new Lookup(MethodHandles.lookup());
+ THROW_NO_SUCH_FUNCTION = lookup.findOwnStatic("throwNoSuchFunction", Object.class, Object.class, Object.class);
+ THROW_STRICT_PROPERTY_SETTER = lookup.findOwnStatic("throwStrictPropertySetter", void.class, Object.class, Object.class);
+ THROW_OPTIMISTIC_UNDEFINED = lookup.findOwnStatic("throwOptimisticUndefined", Object.class, int.class);
+ }
+
+ private static GuardedInvocation linkBean(final LinkRequest linkRequest) throws Exception {
final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
final Object self = linkRequest.getReceiver();
switch (NashornCallSiteDescriptor.getFirstStandardOperation(desc)) {
@@ -105,35 +117,79 @@
throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
}
throw typeError("not.a.function", NashornCallSiteDescriptor.getFunctionErrorMessage(desc, self));
- case CALL_METHOD:
- throw typeError("no.such.function", getArgument(linkRequest), ScriptRuntime.safeToString(self));
- case GET_METHOD:
- // evaluate to undefined, later on Undefined will take care of throwing TypeError
- return getInvocation(MH.dropArguments(GET_UNDEFINED.get(TYPE_OBJECT_INDEX), 0, Object.class), self, linkerServices, desc);
- case GET_PROPERTY:
- case GET_ELEMENT:
- if(NashornCallSiteDescriptor.isOptimistic(desc)) {
- throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT);
- }
- if (NashornCallSiteDescriptor.getOperand(desc) != null) {
- return getInvocation(EMPTY_PROP_GETTER, self, linkerServices, desc);
- }
- return getInvocation(EMPTY_ELEM_GETTER, self, linkerServices, desc);
- case SET_PROPERTY:
- case SET_ELEMENT:
- final boolean strict = NashornCallSiteDescriptor.isStrict(desc);
- if (strict) {
- throw typeError("cant.set.property", getArgument(linkRequest), ScriptRuntime.safeToString(self));
- }
- if (NashornCallSiteDescriptor.getOperand(desc) != null) {
- return getInvocation(EMPTY_PROP_SETTER, self, linkerServices, desc);
- }
- return getInvocation(EMPTY_ELEM_SETTER, self, linkerServices, desc);
default:
+ // Everything else is supposed to have been already handled by Bootstrap.beansLinker
+ // delegating to linkNoSuchBeanMember
throw new AssertionError("unknown call type " + desc);
}
}
+ static MethodHandle linkMissingBeanMember(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
+ final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
+ final StandardOperation op = NashornCallSiteDescriptor.getFirstStandardOperation(desc);
+ if (op != null) {
+ final String operand = NashornCallSiteDescriptor.getOperand(desc);
+ switch (op) {
+ case CALL_METHOD:
+ return adaptThrower(bindOperand(THROW_NO_SUCH_FUNCTION, operand), desc);
+ case GET_METHOD:
+ case GET_PROPERTY:
+ case GET_ELEMENT: {
+ if (NashornCallSiteDescriptor.isOptimistic(desc)) {
+ return adaptThrower(MethodHandles.insertArguments(THROW_OPTIMISTIC_UNDEFINED, 0, NashornCallSiteDescriptor.getProgramPoint(desc)), desc);
+ }
+ if (NashornCallSiteDescriptor.getOperand(desc) != null) {
+ return getInvocation(EMPTY_PROP_GETTER, linkerServices, desc);
+ }
+ return getInvocation(EMPTY_ELEM_GETTER, linkerServices, desc);
+ }
+ case SET_PROPERTY:
+ case SET_ELEMENT:
+ final boolean strict = NashornCallSiteDescriptor.isStrict(desc);
+ if (strict) {
+ return adaptThrower(bindOperand(THROW_STRICT_PROPERTY_SETTER, operand), desc);
+ }
+ if (NashornCallSiteDescriptor.getOperand(desc) != null) {
+ return getInvocation(EMPTY_PROP_SETTER, linkerServices, desc);
+ }
+ return getInvocation(EMPTY_ELEM_SETTER, linkerServices, desc);
+ default:
+ }
+ }
+ throw new AssertionError("unknown call type " + desc);
+ }
+
+ private static MethodHandle bindOperand(final MethodHandle handle, final String operand) {
+ return operand == null ? handle : MethodHandles.insertArguments(handle, 1, operand);
+ }
+
+ private static MethodHandle adaptThrower(final MethodHandle handle, final CallSiteDescriptor desc) {
+ final MethodType targetType = desc.getMethodType();
+ final int paramCount = handle.type().parameterCount();
+ return MethodHandles
+ .dropArguments(handle, paramCount, targetType.parameterList().subList(paramCount, targetType.parameterCount()))
+ .asType(targetType);
+ }
+
+ @SuppressWarnings("unused")
+ private static Object throwNoSuchFunction(final Object self, final Object name) {
+ throw createTypeError(self, name, "no.such.function");
+ }
+
+ @SuppressWarnings("unused")
+ private static void throwStrictPropertySetter(final Object self, final Object name) {
+ throw createTypeError(self, name, "cant.set.property");
+ }
+
+ private static ECMAException createTypeError(final Object self, final Object name, final String msg) {
+ return typeError(msg, String.valueOf(name), ScriptRuntime.safeToString(self));
+ }
+
+ @SuppressWarnings("unused")
+ private static Object throwOptimisticUndefined(final int programPoint) {
+ throw new UnwarrantedOptimismException(UNDEFINED, programPoint, Type.OBJECT);
+ }
+
@Override
public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType, final Supplier<MethodHandles.Lookup> lookupSupplier) throws Exception {
final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType);
@@ -158,8 +214,8 @@
return null;
}
- private static GuardedInvocation getInvocation(final MethodHandle handle, final Object self, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
- return Bootstrap.asTypeSafeReturn(new GuardedInvocation(handle, Guards.getClassGuard(self.getClass())), linkerServices, desc);
+ private static MethodHandle getInvocation(final MethodHandle handle, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
+ return linkerServices.asTypeLosslessReturn(handle, desc.getMethodType());
}
// Used solely in an assertion to figure out if the object we get here is something we in fact expect. Objects
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Thu Jan 14 13:24:03 2016 +0100
@@ -53,7 +53,11 @@
* </pre>
*/
final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker {
- private static final GuardingDynamicLinker staticClassLinker = BeansLinker.getLinkerForClass(StaticClass.class);
+ private final GuardingDynamicLinker staticClassLinker;
+
+ NashornStaticClassLinker(final BeansLinker beansLinker) {
+ this.staticClassLinker = beansLinker.getLinkerForClass(StaticClass.class);
+ }
@Override
public boolean canLinkType(final Class<?> type) {
@@ -100,7 +104,7 @@
return delegate(linkerServices, request);
}
- private static GuardedInvocation delegate(final LinkerServices linkerServices, final LinkRequest request) throws Exception {
+ private GuardedInvocation delegate(final LinkerServices linkerServices, final LinkRequest request) throws Exception {
return NashornBeansLinker.getGuardedInvocation(staticClassLinker, request, linkerServices);
}
--- a/nashorn/test/script/basic/JDK-8049242.js.EXPECTED Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/script/basic/JDK-8049242.js.EXPECTED Thu Jan 14 13:24:03 2016 +0100
@@ -1,10 +1,10 @@
abc
[jdk.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)]
ava
-TypeError: null is not a function
-TypeError: null is not a function
-TypeError: null is not a function
+TypeError: Java.type("java.lang.Object")["()xxxxx"] is not a function
+TypeError: Java.type("java.lang.Object")["("] is not a function
+TypeError: Java.type("java.lang.Object")[")"] is not a function
TypeError: Constructor [jdk.dynalink.beans.SimpleDynamicMethod java.lang.String(char[],int,int)] requires "new".
-TypeError: null is not a function
-TypeError: null is not a function
+TypeError: Java.type("java.lang.Runnable")["()"] is not a function
+TypeError: Java.type("java.lang.Runnable")["(int)"] is not a function
java.lang.InstantiationException: java.io.InputStream
--- a/nashorn/test/script/basic/JDK-8066669.js Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/script/basic/JDK-8066669.js Thu Jan 14 13:24:03 2016 +0100
@@ -29,12 +29,13 @@
*/
// Make sure index access on Java objects is working as expected.
-var map = new java.util.HashMap();
+var map = new java.util.LinkedHashMap();
map["foo"] = "bar";
map[1] = 2;
map[false] = true;
map[null] = 0;
+map["a"] = null;
print(map);
@@ -49,10 +50,12 @@
print(typeof map[1], map[1]);
print(typeof map[false], map[false]);
print(typeof map[null], map[null]);
+print(typeof map["a"], map["a"]);
-print(map.foo);
-print(map.false);
-print(map.null);
+print("map.foo=" + map.foo);
+print("map.false=" + map.false);
+print("map.null=" + map.null);
+print("map.a=" + map.a);
map.foo = "baz";
print(map);
--- a/nashorn/test/script/basic/JDK-8066669.js.EXPECTED Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/script/basic/JDK-8066669.js.EXPECTED Thu Jan 14 13:24:03 2016 +0100
@@ -1,13 +1,16 @@
-{null=0, 1=2, false=true, foo=bar}
-object null
+{foo=bar, 1=2, false=true, null=0, a=null}
+string foo
number 1
boolean false
-string foo
+object null
+string a
string bar
number 2
boolean true
number 0
-bar
-null
-null
-{null=0, 1=2, false=true, foo=baz}
+object null
+map.foo=bar
+map.false=undefined
+map.null=undefined
+map.a=null
+{foo=baz, 1=2, false=true, null=0, a=null}
--- a/nashorn/test/script/basic/list.js Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/script/basic/list.js Thu Jan 14 13:24:03 2016 +0100
@@ -54,15 +54,14 @@
var size_name = "size"
print("l[size_name]()=" + l[size_name]()) // ... but existing methods can be accessed with []
-expectException(2) // Java lists don't auto-expand to accommodate new indices
-expectException(java.lang.Double.POSITIVE_INFINITY) // Dynalink will throw IOOBE
-expectException(java.lang.Double.NEGATIVE_INFINITY) // Dynalink will throw IOOBE
+// All illegal indices, even those out of bounds, return undefined
+print("l[2]=" + l[2]);
+print("l[-1]=" + l[-1]);
+print("l[2.1]=" + l[2.1]);
+print("l[-1.1]=" + l[-1.1]);
+print("l[Infinity]=" + l[Infinity]);
+print("l[-Infinity]=" + l[-Infinity]);
+print("l[NaN]=" + l[NaN]);
-function expectException(index) {
- try {
- l[index] = "x"
- print("Not caught out-of-bounds assignment for " + index)
- } catch(e) {
- print(e)
- }
-}
+l[1.1]="b"; // should be no-op
+print("l[0]=" + l[0]);
--- a/nashorn/test/script/basic/list.js.EXPECTED Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/script/basic/list.js.EXPECTED Thu Jan 14 13:24:03 2016 +0100
@@ -9,9 +9,14 @@
--for each end--
l[0]=foo
l[1]=a
-l[0.9]=null
+l[0.9]=undefined
l['blah']=undefined
l[size_name]()=2
-java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
-java.lang.IndexOutOfBoundsException: Index: Infinity, Size: 2
-java.lang.IndexOutOfBoundsException: Index: -Infinity, Size: 2
+l[2]=undefined
+l[-1]=undefined
+l[2.1]=undefined
+l[-1.1]=undefined
+l[Infinity]=undefined
+l[-Infinity]=undefined
+l[NaN]=undefined
+l[0]=foo
--- a/nashorn/test/script/basic/map.js Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/script/basic/map.js Thu Jan 14 13:24:03 2016 +0100
@@ -44,8 +44,8 @@
print("m['empty'] = " + m['empty'])
print("m[empty_key] = " + m[empty_key]) // prints "foo"
-print("m.bwah = " + m.bwah) // prints "null"
-print("m['bwah'] = " + m['bwah']) // prints "null"
+print("m.bwah = " + m.bwah) // prints "undefined"
+print("m['bwah'] = " + m['bwah']) // prints "undefined"
m.put("twonk", "ding")
print("m.twonk = " + m.twonk) // prints "ding"
--- a/nashorn/test/script/basic/map.js.EXPECTED Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/script/basic/map.js.EXPECTED Thu Jan 14 13:24:03 2016 +0100
@@ -7,8 +7,8 @@
m.empty = false
m['empty'] = foo
m[empty_key] = foo
-m.bwah = null
-m['bwah'] = null
+m.bwah = undefined
+m['bwah'] = undefined
m.twonk = ding
m['twonk'] = ding
m.size()=2
--- a/nashorn/test/src/jdk/dynalink/beans/test/BeanLinkerTest.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/src/jdk/dynalink/beans/test/BeanLinkerTest.java Thu Jan 14 13:24:03 2016 +0100
@@ -24,7 +24,17 @@
*/
package jdk.dynalink.beans.test;
+import static jdk.dynalink.StandardOperation.CALL;
+import static jdk.dynalink.StandardOperation.CALL_METHOD;
+import static jdk.dynalink.StandardOperation.GET_ELEMENT;
+import static jdk.dynalink.StandardOperation.GET_LENGTH;
+import static jdk.dynalink.StandardOperation.GET_METHOD;
+import static jdk.dynalink.StandardOperation.GET_PROPERTY;
+import static jdk.dynalink.StandardOperation.NEW;
+import static jdk.dynalink.StandardOperation.SET_ELEMENT;
+
import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.security.AccessControlException;
@@ -38,7 +48,6 @@
import jdk.dynalink.NamedOperation;
import jdk.dynalink.NoSuchDynamicMethodException;
import jdk.dynalink.Operation;
-import jdk.dynalink.StandardOperation;
import jdk.dynalink.beans.BeansLinker;
import jdk.dynalink.beans.StaticClass;
import jdk.dynalink.support.SimpleRelinkableCallSite;
@@ -72,9 +81,80 @@
return createCallSite(publicLookup, new NamedOperation(op, name), mt);
}
+ private static final MethodHandle throwArrayIndexOutOfBounds = findThrower("throwArrayIndexOutOfBounds");
+ private static final MethodHandle throwIndexOutOfBounds = findThrower("throwIndexOutOfBounds");
+
+ private static final MethodHandle findThrower(final String name) {
+ try {
+ return MethodHandles.lookup().findStatic(BeanLinkerTest.class, name,
+ MethodType.methodType(Object.class, Object.class, Object.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ Assert.fail("Unexpected exception", e);
+ return null;
+ }
+ }
+
+ private static Object throwArrayIndexOutOfBounds(final Object receiver, final Object index) {
+ throw new ArrayIndexOutOfBoundsException(String.valueOf(index));
+ }
+
+ private static Object throwIndexOutOfBounds(final Object receiver, final Object index) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+
@BeforeTest
public void initLinker() {
final DynamicLinkerFactory factory = new DynamicLinkerFactory();
+ factory.setFallbackLinkers(new BeansLinker((req, services) -> {
+ // This is a MissingMemberHandlerFactory that creates a missing
+ // member handler for element getters and setters that throw an
+ // ArrayIndexOutOfBoundsException when applied to an array and an
+ // IndexOutOfBoundsException when applied to a list.
+
+ final CallSiteDescriptor desc = req.getCallSiteDescriptor();
+ final Operation op = desc.getOperation();
+ final Operation baseOp = NamedOperation.getBaseOperation(op);
+ if (baseOp != GET_ELEMENT && baseOp != SET_ELEMENT) {
+ // We only handle GET_ELEMENT and SET_ELEMENT.
+ return null;
+ }
+
+ final Object receiver = req.getReceiver();
+ Assert.assertNotNull(receiver);
+
+ final Class<?> clazz = receiver.getClass();
+ final MethodHandle throwerHandle;
+ if (clazz.isArray()) {
+ throwerHandle = throwArrayIndexOutOfBounds;
+ } else if (List.class.isAssignableFrom(clazz)) {
+ throwerHandle = throwIndexOutOfBounds;
+ } else {
+ Assert.fail("Unexpected receiver type " + clazz.getName());
+ return null;
+ }
+
+ final Object name = NamedOperation.getName(op);
+ final MethodHandle nameBoundHandle;
+ if (name == null) {
+ nameBoundHandle = throwerHandle;
+ } else {
+ // If the operation is for a fixed index, bind it
+ nameBoundHandle = MethodHandles.insertArguments(throwerHandle, 1, name);
+ }
+
+ final MethodType callSiteType = desc.getMethodType();
+ final MethodHandle arityMatchedHandle;
+ if (baseOp == SET_ELEMENT) {
+ // Drop "value" parameter for a setter
+ final int handleArity = nameBoundHandle.type().parameterCount();
+ arityMatchedHandle = MethodHandles.dropArguments(nameBoundHandle,
+ handleArity, callSiteType.parameterType(handleArity));
+ } else {
+ arityMatchedHandle = nameBoundHandle;
+ }
+
+ return arityMatchedHandle.asType(callSiteType);
+ }));
this.linker = factory.createLinker();
}
@@ -86,7 +166,7 @@
@Test(dataProvider = "flags")
public void getPropertyTest(final boolean publicLookup) throws Throwable {
final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_PROPERTY, mt);
+ final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt);
Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class);
Assert.assertEquals(cs.getTarget().invoke(new Date(), "class"), Date.class);
}
@@ -94,14 +174,14 @@
@Test(dataProvider = "flags")
public void getPropertyNegativeTest(final boolean publicLookup) throws Throwable {
final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_PROPERTY, mt);
+ final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt);
Assert.assertNull(cs.getTarget().invoke(new Object(), "DOES_NOT_EXIST"));
}
@Test(dataProvider = "flags")
public void getPropertyTest2(final boolean publicLookup) throws Throwable {
final MethodType mt = MethodType.methodType(Object.class, Object.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_PROPERTY, "class", mt);
+ final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "class", mt);
Assert.assertEquals(cs.getTarget().invoke(new Object()), Object.class);
Assert.assertEquals(cs.getTarget().invoke(new Date()), Date.class);
}
@@ -109,12 +189,12 @@
@Test(dataProvider = "flags")
public void getPropertyNegativeTest2(final boolean publicLookup) throws Throwable {
final MethodType mt = MethodType.methodType(Object.class, Object.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_PROPERTY, "DOES_NOT_EXIST", mt);
+ final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "DOES_NOT_EXIST", mt);
try {
cs.getTarget().invoke(new Object());
throw new RuntimeException("Expected NoSuchDynamicMethodException");
- } catch (Throwable th) {
+ } catch (final Throwable th) {
Assert.assertTrue(th instanceof NoSuchDynamicMethodException);
}
}
@@ -122,7 +202,7 @@
@Test(dataProvider = "flags")
public void getLengthPropertyTest(final boolean publicLookup) throws Throwable {
final MethodType mt = MethodType.methodType(int.class, Object.class, String.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_PROPERTY, mt);
+ final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt);
Assert.assertEquals((int) cs.getTarget().invoke(new int[10], "length"), 10);
Assert.assertEquals((int) cs.getTarget().invoke(new String[33], "length"), 33);
@@ -131,7 +211,7 @@
@Test(dataProvider = "flags")
public void getlengthTest(final boolean publicLookup) throws Throwable {
final MethodType mt = MethodType.methodType(int.class, Object.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_LENGTH, mt);
+ final CallSite cs = createCallSite(publicLookup, GET_LENGTH, mt);
final int[] arr = {23, 42};
Assert.assertEquals((int) cs.getTarget().invoke((Object) arr), 2);
@@ -151,21 +231,21 @@
@Test(dataProvider = "flags")
public void getElementTest(final boolean publicLookup) throws Throwable {
final MethodType mt = MethodType.methodType(int.class, Object.class, int.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_ELEMENT, mt);
+ final CallSite cs = createCallSite(publicLookup, GET_ELEMENT, mt);
final int[] arr = {23, 42};
Assert.assertEquals((int) cs.getTarget().invoke(arr, 0), 23);
Assert.assertEquals((int) cs.getTarget().invoke(arr, 1), 42);
try {
- int x = (int) cs.getTarget().invoke(arr, -1);
+ final int x = (int) cs.getTarget().invoke(arr, -1);
throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
- } catch (ArrayIndexOutOfBoundsException ex) {
+ } catch (final ArrayIndexOutOfBoundsException ex) {
}
try {
- int x = (int) cs.getTarget().invoke(arr, arr.length);
+ final int x = (int) cs.getTarget().invoke(arr, arr.length);
throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
- } catch (ArrayIndexOutOfBoundsException ex) {
+ } catch (final ArrayIndexOutOfBoundsException ex) {
}
final List<Integer> list = new ArrayList<>();
@@ -176,22 +256,22 @@
Assert.assertEquals((int) cs.getTarget().invoke(list, 1), (int) list.get(1));
Assert.assertEquals((int) cs.getTarget().invoke(list, 2), (int) list.get(2));
try {
- int x = (int) cs.getTarget().invoke(list, -1);
+ final int x = (int) cs.getTarget().invoke(list, -1);
throw new RuntimeException("expected IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException ex) {
+ } catch (final IndexOutOfBoundsException ex) {
}
try {
- int x = (int) cs.getTarget().invoke(list, list.size());
+ final int x = (int) cs.getTarget().invoke(list, list.size());
throw new RuntimeException("expected IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException ex) {
+ } catch (final IndexOutOfBoundsException ex) {
}
}
@Test(dataProvider = "flags")
public void setElementTest(final boolean publicLookup) throws Throwable {
final MethodType mt = MethodType.methodType(void.class, Object.class, int.class, int.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.SET_ELEMENT, mt);
+ final CallSite cs = createCallSite(publicLookup, SET_ELEMENT, mt);
final int[] arr = {23, 42};
cs.getTarget().invoke(arr, 0, 0);
@@ -202,13 +282,13 @@
try {
cs.getTarget().invoke(arr, -1, 12);
throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
- } catch (ArrayIndexOutOfBoundsException ex) {
+ } catch (final ArrayIndexOutOfBoundsException ex) {
}
try {
cs.getTarget().invoke(arr, arr.length, 20);
throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
- } catch (ArrayIndexOutOfBoundsException ex) {
+ } catch (final ArrayIndexOutOfBoundsException ex) {
}
final List<Integer> list = new ArrayList<>();
@@ -223,25 +303,25 @@
try {
cs.getTarget().invoke(list, -1, 343);
throw new RuntimeException("expected IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException ex) {
+ } catch (final IndexOutOfBoundsException ex) {
}
try {
cs.getTarget().invoke(list, list.size(), 43543);
throw new RuntimeException("expected IndexOutOfBoundsException");
- } catch (IndexOutOfBoundsException ex) {
+ } catch (final IndexOutOfBoundsException ex) {
}
}
@Test(dataProvider = "flags")
public void newObjectTest(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(Object.class, Object.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.NEW, mt);
+ final CallSite cs = createCallSite(publicLookup, NEW, mt);
Object obj = null;
try {
obj = cs.getTarget().invoke(StaticClass.forClass(Date.class));
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
@@ -251,12 +331,12 @@
@Test(dataProvider = "flags")
public void staticPropertyTest(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(Object.class, Class.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_PROPERTY, "static", mt);
+ final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "static", mt);
Object obj = null;
try {
obj = cs.getTarget().invoke(Object.class);
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
@@ -265,7 +345,7 @@
try {
obj = cs.getTarget().invoke(Date.class);
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
@@ -274,7 +354,7 @@
try {
obj = cs.getTarget().invoke(Object[].class);
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
@@ -285,14 +365,14 @@
@Test(dataProvider = "flags")
public void instanceMethodCallTest(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(Object.class, Object.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_METHOD, "getClass", mt);
+ final CallSite cs = createCallSite(publicLookup, GET_METHOD, "getClass", mt);
final MethodType mt2 = MethodType.methodType(Class.class, Object.class, Object.class);
- final CallSite cs2 = createCallSite(publicLookup, StandardOperation.CALL, mt2);
+ final CallSite cs2 = createCallSite(publicLookup, CALL, mt2);
Object method = null;
try {
method = cs.getTarget().invoke(new Date());
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
@@ -301,7 +381,7 @@
Class clz = null;
try {
clz = (Class) cs2.getTarget().invoke(method, new Date());
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
@@ -311,11 +391,11 @@
@Test(dataProvider = "flags")
public void instanceMethodCallTest2(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(Class.class, Object.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.CALL_METHOD, "getClass", mt);
+ final CallSite cs = createCallSite(publicLookup, CALL_METHOD, "getClass", mt);
Class clz = null;
try {
clz = (Class) cs.getTarget().invoke(new Date());
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
@@ -325,14 +405,14 @@
@Test(dataProvider = "flags")
public void staticMethodCallTest(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(Object.class, StaticClass.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.GET_METHOD, "getProperty", mt);
+ final CallSite cs = createCallSite(publicLookup, GET_METHOD, "getProperty", mt);
final MethodType mt2 = MethodType.methodType(String.class, Object.class, Object.class, String.class);
- final CallSite cs2 = createCallSite(publicLookup, StandardOperation.CALL, mt2);
+ final CallSite cs2 = createCallSite(publicLookup, CALL, mt2);
Object method = null;
try {
method = cs.getTarget().invoke(StaticClass.forClass(System.class));
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
@@ -342,7 +422,7 @@
String str = null;
try {
str = (String) cs2.getTarget().invoke(method, null, "os.name");
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
Assert.assertEquals(str, System.getProperty("os.name"));
@@ -351,12 +431,12 @@
@Test(dataProvider = "flags")
public void staticMethodCallTest2(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(String.class, Object.class, String.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.CALL_METHOD, "getProperty", mt);
+ final CallSite cs = createCallSite(publicLookup, CALL_METHOD, "getProperty", mt);
String str = null;
try {
str = (String) cs.getTarget().invoke(StaticClass.forClass(System.class), "os.name");
- } catch (Throwable th) {
+ } catch (final Throwable th) {
throw new RuntimeException(th);
}
Assert.assertEquals(str, System.getProperty("os.name"));
@@ -366,12 +446,12 @@
@Test(dataProvider = "flags")
public void systemGetenvTest(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(Object.class, Object.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.CALL_METHOD, "getenv", mt);
+ final CallSite cs = createCallSite(publicLookup, CALL_METHOD, "getenv", mt);
try {
cs.getTarget().invoke(StaticClass.forClass(System.class));
throw new RuntimeException("should not reach here in any case!");
- } catch (Throwable th) {
+ } catch (final Throwable th) {
Assert.assertTrue(th instanceof SecurityException);
}
}
@@ -380,12 +460,12 @@
@Test(dataProvider = "flags")
public void systemGetPropertyTest(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(String.class, Object.class, String.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.CALL_METHOD, "getProperty", mt);
+ final CallSite cs = createCallSite(publicLookup, CALL_METHOD, "getProperty", mt);
try {
cs.getTarget().invoke(StaticClass.forClass(System.class), "java.home");
throw new RuntimeException("should not reach here in any case!");
- } catch (Throwable th) {
+ } catch (final Throwable th) {
Assert.assertTrue(th instanceof SecurityException);
}
}
@@ -394,12 +474,12 @@
@Test(dataProvider = "flags")
public void systemLoadLibraryTest(final boolean publicLookup) {
final MethodType mt = MethodType.methodType(void.class, Object.class, String.class);
- final CallSite cs = createCallSite(publicLookup, StandardOperation.CALL_METHOD, "loadLibrary", mt);
+ final CallSite cs = createCallSite(publicLookup, CALL_METHOD, "loadLibrary", mt);
try {
cs.getTarget().invoke(StaticClass.forClass(System.class), "foo");
throw new RuntimeException("should not reach here in any case!");
- } catch (Throwable th) {
+ } catch (final Throwable th) {
if (publicLookup) {
Assert.assertTrue(th instanceof IllegalAccessError);
} else {
--- a/nashorn/test/src/jdk/dynalink/beans/test/BeansLinkerTest.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/src/jdk/dynalink/beans/test/BeansLinkerTest.java Thu Jan 14 13:24:03 2016 +0100
@@ -33,6 +33,7 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -44,6 +45,7 @@
import jdk.dynalink.CompositeOperation;
import jdk.dynalink.DynamicLinkerFactory;
import jdk.dynalink.NamedOperation;
+import jdk.dynalink.NoSuchDynamicMethodException;
import jdk.dynalink.Operation;
import jdk.dynalink.StandardOperation;
import jdk.dynalink.support.SimpleRelinkableCallSite;
@@ -207,6 +209,30 @@
Assert.assertEquals("element2", map.get("name"));
}
+ @Test
+ public static void testMissingMembersAtLinkTime() {
+ testPermutations(GETTER_PERMUTATIONS, (op) -> expectNoSuchDynamicMethodException(()-> call(named("foo", op), new Object())));
+ testPermutations(SETTER_PERMUTATIONS, (op) -> expectNoSuchDynamicMethodException(()-> call(named("foo", op), new Object(), "newValue")));
+ }
+
+ @Test
+ public static void testMissingMembersAtRunTime() {
+ call(GET_ELEMENT, new ArrayList<>(), "foo");
+ Stream.of(new HashMap(), new ArrayList(), new Object[0]).forEach((receiver) -> {
+ testPermutations(GETTER_PERMUTATIONS, (op) -> { System.err.println(op + " " + receiver.getClass().getName()); Assert.assertNull(call(op, receiver, "foo"));});
+ // No assertion for the setter; we just expect it to silently succeed
+ testPermutations(SETTER_PERMUTATIONS, (op) -> call(op, receiver, "foo", "newValue"));
+ });
+ }
+
+ private static void expectNoSuchDynamicMethodException(final Runnable r) {
+ try {
+ r.run();
+ Assert.fail("Should've thrown NoSuchDynamicMethodException");
+ } catch(final NoSuchDynamicMethodException e) {
+ }
+ }
+
private static Operation[] GETTER_PERMUTATIONS = new Operation[] {
GET_PROPERTY,
GET_METHOD,
@@ -240,6 +266,10 @@
testPermutationsWithFilter(ops, (op)->regex.matcher(op.toString()).matches(), expectedCount, test);
}
+ private static void testPermutations(final Operation[] ops, final Consumer<Operation> test) {
+ testPermutationsWithFilter(ops, (op)->true, ops.length, test);
+ }
+
private static void testPermutationsWithFilter(final Operation[] ops, final Predicate<Operation> filter, final int expectedCount, final Consumer<Operation> test) {
final int[] counter = new int[1];
Stream.of(ops).filter(filter).forEach((op)-> { counter[0]++; test.accept(op); });
--- a/nashorn/test/src/jdk/dynalink/beans/test/CallerSensitiveTest.java Thu Jan 14 13:22:58 2016 +0100
+++ b/nashorn/test/src/jdk/dynalink/beans/test/CallerSensitiveTest.java Thu Jan 14 13:24:03 2016 +0100
@@ -33,6 +33,6 @@
public class CallerSensitiveTest {
@Test
public void testCallerSensitive() {
- BeansLinker.getLinkerForClass(ClassLoaderAware.class);
+ new BeansLinker().getLinkerForClass(ClassLoaderAware.class);
}
}
--- a/nashorn/test/src/jdk/internal/dynalink/beans/test/CallerSensitiveTest.java Thu Jan 14 13:22:58 2016 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2014, 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.internal.dynalink.beans.test;
-
-import jdk.dynalink.beans.BeansLinker;
-import jdk.nashorn.test.models.ClassLoaderAware;
-import org.testng.annotations.Test;
-
-@SuppressWarnings("javadoc")
-public class CallerSensitiveTest {
- @Test
- public void testCallerSensitive() {
- BeansLinker.getLinkerForClass(ClassLoaderAware.class);
- }
-}