# HG changeset patch # Parent 6f236ad5492a138835b382d60aecd7cd62159717 diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java Mon Nov 16 20:08:40 2015 +0100 @@ -349,82 +349,120 @@ throws Exception { final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor(); + final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory; + final LinkerServices directLinkerServices; + if (linkerServices instanceof BeansLinkerServices) { + final BeansLinkerServices bls = ((BeansLinkerServices)linkerServices); + noSuchMemberHandlerFactory = bls.noSuchMemberHandlerFactory; + directLinkerServices = bls.linkerServices; + } else { + noSuchMemberHandlerFactory = 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(noSuchMemberHandlerFactory, + request, directLinkerServices).getGuardedInvocation(); + } + return inv; } } - ComponentLinkRequest componentReq = new ComponentLinkRequest( - callSiteDescriptor, linkerServices); - - while(!componentReq.operations.isEmpty()) { - final GuardedInvocationComponent gic = getGuardedInvocationComponent(componentReq); - if(gic != null) { - return gic.getGuardedInvocation(); - } - componentReq = componentReq.popOperations(); - } - return null; + final GuardedInvocationComponent gic = getGuardedInvocationComponent( + new ComponentLinkRequest(request, directLinkerServices, + noSuchMemberHandlerFactory)); + return gic != null ? gic.getGuardedInvocation() : null; } static class ComponentLinkRequest { - final CallSiteDescriptor descriptor; + final LinkRequest linkRequest; final LinkerServices linkerServices; + final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory; final List operations; final Object name; - ComponentLinkRequest(final CallSiteDescriptor descriptor, - final LinkerServices linkerServices) { - this.descriptor = descriptor; + ComponentLinkRequest(final LinkRequest linkRequest, + final LinkerServices linkerServices, + final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory) { + this.linkRequest = linkRequest; this.linkerServices = linkerServices; - final Operation operation = descriptor.getOperation(); + this.noSuchMemberHandlerFactory = noSuchMemberHandlerFactory; + final Operation operation = linkRequest.getCallSiteDescriptor().getOperation(); this.operations = Arrays.asList( CompositeOperation.getOperations( NamedOperation.getBaseOperation(operation))); this.name = NamedOperation.getName(operation); } - private ComponentLinkRequest(final CallSiteDescriptor descriptor, + private ComponentLinkRequest(final LinkRequest linkRequest, final LinkerServices linkerServices, + final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory, final List operations, final Object name) { - this.descriptor = descriptor; + this.linkRequest = linkRequest; this.linkerServices = linkerServices; + this.noSuchMemberHandlerFactory = noSuchMemberHandlerFactory; this.operations = operations; this.name = name; } + CallSiteDescriptor getDescriptor() { + return linkRequest.getCallSiteDescriptor(); + } + ComponentLinkRequest popOperations() { - return new ComponentLinkRequest(descriptor, linkerServices, + return new ComponentLinkRequest(linkRequest, linkerServices, + noSuchMemberHandlerFactory, operations.subList(1, operations.size()), name); } } - - protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req) throws Exception { + final Operation op = req.operations.get(0); + if (op instanceof StandardOperation) { + switch((StandardOperation)op) { + case GET_PROPERTY: return getPropertyGetter(req.popOperations()); + case SET_PROPERTY: return getPropertySetter(req.popOperations()); + case GET_METHOD: return getMethodGetter(req.popOperations()); + default: + } + } + return null; + } + + GuardedInvocationComponent getNextComponent(final ComponentLinkRequest req) throws Exception { if(req.operations.isEmpty()) { + return createNoSuchMemberHandler(req.noSuchMemberHandlerFactory, + req.linkRequest, req.linkerServices); + } + final GuardedInvocationComponent gic = getGuardedInvocationComponent(req); + if (gic != null) { + return gic; + } + return getNextComponent(req.popOperations()); + } + + private GuardedInvocationComponent createNoSuchMemberHandler( + final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory, + final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { + if (noSuchMemberHandlerFactory == null) { return null; } - final Operation op = req.operations.get(0); - // Either GET_PROPERTY:name(this) or GET_PROPERTY(this, name) - if(op == StandardOperation.GET_PROPERTY) { - return getPropertyGetter(req.popOperations()); + final MethodHandle handler = noSuchMemberHandlerFactory.createNoSuchMemberHandler(linkRequest, linkerServices); + if (handler == null) { + return null; } - // Either SET_PROPERTY:name(this, value) or SET_PROPERTY(this, name, value) - if(op == StandardOperation.SET_PROPERTY) { - return getPropertySetter(req.popOperations()); - } - // Either GET_METHOD:name(this), or GET_METHOD(this, name) - if(op == StandardOperation.GET_METHOD) { - return getMethodGetter(req.popOperations()); - } - return null; + final MethodType type = linkRequest.getCallSiteDescriptor().getMethodType(); + assert handler.type().equals(type); + return getClassGuardedInvocationComponent(handler, type); } static final List pop(final List l) { @@ -518,7 +556,7 @@ } private GuardedInvocationComponent getUnnamedPropertySetter(final ComponentLinkRequest req) throws Exception { - final CallSiteDescriptor callSiteDescriptor = req.descriptor; + final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); // Must have three arguments: target object, property name, and property value. assertParameterCount(callSiteDescriptor, 3); @@ -554,7 +592,7 @@ // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V) final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType( 1)); - final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(req); + final GuardedInvocationComponent nextComponent = getNextComponent(req); final MethodHandle fallbackFolded; if(nextComponent == null) { @@ -579,9 +617,9 @@ private GuardedInvocationComponent getNamedPropertySetter(final ComponentLinkRequest req) throws Exception { // Must have two arguments: target object and property value - assertParameterCount(req.descriptor, 2); + assertParameterCount(req.getDescriptor(), 2); final GuardedInvocation gi = createGuardedDynamicMethodInvocation( - req.descriptor, req.linkerServices, req.name.toString(), + req.getDescriptor(), req.linkerServices, req.name.toString(), propertySetters); // If we have a property setter with this name, this composite operation will always stop here if(gi != null) { @@ -589,7 +627,7 @@ } // If we don't have a property setter with this name, always fall back to the next operation in the // composite (if any) - return getGuardedInvocationComponent(req); + return getNextComponent(req); } private static final Lookup privateLookup = new Lookup(MethodHandles.lookup()); @@ -614,7 +652,7 @@ // Since we can't know what kind of a getter we'll get back on different invocations, we'll just // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking // runtime might not allow coercing at that call site. - final CallSiteDescriptor callSiteDescriptor = req.descriptor; + final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class); // Must have exactly two arguments: receiver and name assertParameterCount(callSiteDescriptor, 2); @@ -639,7 +677,7 @@ // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1) final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2, type.parameterType(1)); - final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(req); + final GuardedInvocationComponent nextComponent = getNextComponent(req); final MethodHandle fallbackFolded; if(nextComponent == null) { @@ -665,14 +703,14 @@ } private GuardedInvocationComponent getNamedPropertyGetter(final ComponentLinkRequest req) throws Exception { - final CallSiteDescriptor callSiteDescriptor = req.descriptor; + final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); // Must have exactly one argument: receiver assertParameterCount(callSiteDescriptor, 1); // Fixed name final AnnotatedDynamicMethod annGetter = propertyGetters.get(req.name.toString()); if(annGetter == null) { // We have no such property, always delegate to the next component operation - return getGuardedInvocationComponent(req); + return getNextComponent(req); } final MethodHandle getter = annGetter.getInvocation(req); // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being @@ -722,19 +760,17 @@ private static MethodType getMethodGetterType(final ComponentLinkRequest req) { // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to // be visible outside of this linker, declare it to return Object. - return req.descriptor.getMethodType().changeReturnType(Object.class); + return req.getDescriptor().getMethodType().changeReturnType(Object.class); } private GuardedInvocationComponent getUnnamedMethodGetter(final ComponentLinkRequest req) throws Exception { // Must have exactly two arguments: receiver and name - assertParameterCount(req.descriptor, 2); - final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(req); + assertParameterCount(req.getDescriptor(), 2); + final GuardedInvocationComponent nextComponent = getNextComponent(req); final LinkerServices linkerServices = req.linkerServices; final MethodType type = getMethodGetterType(req); - if(nextComponent == null || !InternalTypeUtilities.areAssignable(DynamicMethod.class, - nextComponent.getGuardedInvocation().getInvocation().type().returnType())) { - // No next component operation, or it can never produce a dynamic method; just return a component - // for this operation. + if(nextComponent == null) { + // No next component operation; just return a component for this operation. return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type); } @@ -755,8 +791,11 @@ final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0, Object.class); // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get) + // Note that nextCombinedInvocation needs to have its return type changed to Object final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( - IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter); + IS_DYNAMIC_METHOD, returnMethodHandle, + nextCombinedInvocation.asType(nextCombinedInvocation.type().changeReturnType(Object.class))), + typedGetter); return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); } @@ -764,11 +803,11 @@ private GuardedInvocationComponent getNamedMethodGetter(final ComponentLinkRequest req) throws Exception { // Must have exactly one argument: receiver - assertParameterCount(req.descriptor, 1); + assertParameterCount(req.getDescriptor(), 1); final DynamicMethod method = getDynamicMethod(req.name.toString()); if(method == null) { // We have no such method, always delegate to the next component - return getGuardedInvocationComponent(req); + return getNextComponent(req); } // No delegation to the next component of the composite operation; if we have a method with that name, // we'll always return it at this point. @@ -904,7 +943,7 @@ } MethodHandle getInvocation(final ComponentLinkRequest req) { - return method.getInvocation(req.descriptor, req.linkerServices); + return method.getInvocation(req.getDescriptor(), req.linkerServices); } @SuppressWarnings("unused") diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeanLinker.java Mon Nov 16 20:08:40 2015 +0100 @@ -134,18 +134,16 @@ if(superGic != null) { return superGic; } - if(req.operations.isEmpty()) { - return null; - } - final Operation op = req.operations.get(0); - if(op == StandardOperation.GET_ELEMENT) { - return getElementGetter(req.popOperations()); - } - if(op == StandardOperation.SET_ELEMENT) { - return getElementSetter(req.popOperations()); - } - if(op == StandardOperation.GET_LENGTH) { - return getLengthGetter(req.descriptor); + if(!req.operations.isEmpty()) { + final Operation op = req.operations.get(0); + if (op instanceof StandardOperation) { + switch ((StandardOperation)op) { + case GET_ELEMENT: return getElementGetter(req.popOperations()); + case SET_ELEMENT: return getElementSetter(req.popOperations()); + case GET_LENGTH: return getLengthGetter(req.getDescriptor()); + default: + } + } } return null; } @@ -169,11 +167,11 @@ }; private GuardedInvocationComponent getElementGetter(final ComponentLinkRequest req) throws Exception { - final CallSiteDescriptor callSiteDescriptor = req.descriptor; + final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); final LinkerServices linkerServices = req.linkerServices; final MethodType callSiteType = callSiteDescriptor.getMethodType(); final Class declaredType = callSiteType.parameterType(0); - final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(req); + final GuardedInvocationComponent nextComponent = getNextComponent(req); // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're @@ -211,11 +209,13 @@ 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 + 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; } @@ -365,14 +365,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 < Array.getLength(array)) { - return true; - } - throw new ArrayIndexOutOfBoundsException("Array index out of range: " + n); + return rangeCheck(array, intIndex); + } + + private static boolean rangeCheck(final Object array, final int intIndex) { + return 0 <= intIndex && intIndex < Array.getLength(array); } @SuppressWarnings("unused") @@ -382,14 +382,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 rangeCheck(list, intIndex); + } + + private static boolean rangeCheck(final List list, final int intIndex) { + return 0 <= intIndex && intIndex < list.size(); } private static final MethodHandle SET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "set", @@ -399,7 +399,7 @@ MethodType.methodType(Object.class, Object.class, Object.class)); private GuardedInvocationComponent getElementSetter(final ComponentLinkRequest req) throws Exception { - final CallSiteDescriptor callSiteDescriptor = req.descriptor; + final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); final LinkerServices linkerServices = req.linkerServices; final MethodType callSiteType = callSiteDescriptor.getMethodType(); final Class declaredType = callSiteType.parameterType(0); @@ -441,7 +441,7 @@ // 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 = collectionType == CollectionType.MAP ? null : getGuardedInvocationComponent(req); + final GuardedInvocationComponent nextComponent = collectionType == CollectionType.MAP ? null : getNextComponent(req); if(gic == null) { return nextComponent; } @@ -450,11 +450,13 @@ 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 + 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; } diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinker.java Mon Nov 16 20:08:40 2015 +0100 @@ -164,10 +164,30 @@ } }; + private final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory; + /** * Creates a new beans linker. */ public BeansLinker() { + this(null); + } + + /** + * Creates a new beans linker with the specified factory for creating "no + * such member" handlers. + * @param noSuchMemberHandlerFactory a factory for creating handlers for + * "no such member condition. The passed function can be null for no custom + * handlers. Otherwise, it must be a function that will return a method + * handle appropriate for the call site descriptor and method type in the + * link request. Since these handlers are often conditionally linked for + * composite operations, the factory itself should not throw link-time + * exceptions but rather link a method that will throw an exception on + * invocation in case the behavior for no-such-member condition is to throw + * an exception at runtime. + */ + public BeansLinker(final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory) { + this.noSuchMemberHandlerFactory = noSuchMemberHandlerFactory; } /** @@ -178,7 +198,35 @@ * @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 (noSuchMemberHandlerFactory == null) { + return staticLinker; + } + return new NoSuchMemberHandlerBindingLinker(staticLinker, noSuchMemberHandlerFactory); + } + + private static class NoSuchMemberHandlerBindingLinker implements TypeBasedGuardingDynamicLinker { + private final TypeBasedGuardingDynamicLinker linker; + private final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory; + + NoSuchMemberHandlerBindingLinker(final TypeBasedGuardingDynamicLinker linker, final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory) { + this.linker = linker; + this.noSuchMemberHandlerFactory = noSuchMemberHandlerFactory; + } + + @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, BeansLinkerServices.get(linkerServices, noSuchMemberHandlerFactory)); + } + } + + static TypeBasedGuardingDynamicLinker getStaticLinkerForClass(final Class clazz) { return linkers.get(clazz); } @@ -234,7 +282,7 @@ * @return a set of names of all readable instance properties of a class. */ public static Set getReadableInstancePropertyNames(final Class clazz) { - final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz); + final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz); if(linker instanceof BeanLinker) { return ((BeanLinker)linker).getReadablePropertyNames(); } @@ -247,7 +295,7 @@ * @return a set of names of all writable instance properties of a class. */ public static Set getWritableInstancePropertyNames(final Class clazz) { - final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz); + final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz); if(linker instanceof BeanLinker) { return ((BeanLinker)linker).getWritablePropertyNames(); } @@ -260,7 +308,7 @@ * @return a set of names of all instance methods of a class. */ public static Set getInstanceMethodNames(final Class clazz) { - final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz); + final TypeBasedGuardingDynamicLinker linker = getStaticLinkerForClass(clazz); if(linker instanceof BeanLinker) { return ((BeanLinker)linker).getMethodNames(); } @@ -302,6 +350,6 @@ // Can't operate on null return null; } - return getLinkerForClass(receiver.getClass()).getGuardedInvocation(request, linkerServices); + return getLinkerForClass(receiver.getClass()).getGuardedInvocation(request, BeansLinkerServices.get(linkerServices, noSuchMemberHandlerFactory)); } } diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinkerServices.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/BeansLinkerServices.java Mon Nov 16 20:08:40 2015 +0100 @@ -0,0 +1,77 @@ +/* + * 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.internal.dynalink.beans; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import jdk.internal.dynalink.linker.ConversionComparator.Comparison; +import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.LinkRequest; +import jdk.internal.dynalink.linker.LinkerServices; + +final class BeansLinkerServices implements LinkerServices { + final LinkerServices linkerServices; + final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory; + + static LinkerServices get(final LinkerServices linkerServices, final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory) { + return noSuchMemberHandlerFactory == null ? linkerServices : new BeansLinkerServices(linkerServices, noSuchMemberHandlerFactory); + } + + private BeansLinkerServices(final LinkerServices linkerServices, final NoSuchMemberHandlerFactory noSuchMemberHandlerFactory) { + this.linkerServices = linkerServices; + this.noSuchMemberHandlerFactory = noSuchMemberHandlerFactory; + } + + @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); + } +} diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/NoSuchMemberHandlerFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/NoSuchMemberHandlerFactory.java Mon Nov 16 20:08:40 2015 +0100 @@ -0,0 +1,62 @@ +/* + * 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.internal.dynalink.beans; + +import java.lang.invoke.MethodHandle; +import jdk.internal.dynalink.NamedOperation; +import jdk.internal.dynalink.linker.LinkRequest; +import jdk.internal.dynalink.linker.LinkerServices; + +/** + * A factory for creating method handles for linking "no such member" condition + * in beans linker. + */ +@FunctionalInterface +public interface NoSuchMemberHandlerFactory { + /** + * Returns a method handle suitable for implementing the "no such member" + * condition for a particular link request. BeansLinker links these method + * handles into guarded invocations for various {@code GET_*} and + * {@code SET_*} {@code StandardOperation} requests. They will be linked + * both for {@link NamedOperation named} and unnamed requests. The + * implementer must ensure that the returned method handle type matches the + * link request. It is allowed to return null if the default behavior is + * sufficient. The default behavior is returning {@code null} from + * {@link BeansLinker#getGuardedInvocation(LinkRequest, LinkerServices)} for + * named operations, and returning null at runtime for unnamed operations + * when the passed variable name doesn't match anything. Note that if the + * correct behavior for a particular circumstance is to throw a + * language-specific exception then a factory should produce a method handle + * that throws the exception and should not throw an exception itself, as + * often the linkage for "no such member" is conditional. + * @param linkRequest the current link request + * @param linkerServices the current link services + * @return a method handle that can be invoked if the lookup of a property, + * element, or method results in "no such member" condition. Can be null. + * @throws Exception if something goes wrong. + */ + public MethodHandle createNoSuchMemberHandler(LinkRequest linkRequest, LinkerServices linkerServices) throws Exception; +} diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClassLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClassLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/StaticClassLinker.java Mon Nov 16 20:08:40 2015 +0100 @@ -91,6 +91,7 @@ import java.util.Set; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.NamedOperation; +import jdk.internal.dynalink.Operation; import jdk.internal.dynalink.StandardOperation; import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -162,6 +163,27 @@ } @Override + protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req) throws Exception { + final GuardedInvocationComponent superGic = super.getGuardedInvocationComponent(req); + if(superGic != null) { + return superGic; + } + if(!req.operations.isEmpty()) { + final Operation op = req.operations.get(0); + if (op instanceof StandardOperation) { + switch ((StandardOperation)op) { + case GET_ELEMENT: + case SET_ELEMENT: + // StaticClass doesn't behave as a collection + return getNextComponent(req.popOperations()); + default: + } + } + } + return null; + } + + @Override SingleDynamicMethod getConstructorMethod(final String signature) { return constructor != null? constructor.getMethodForExactParamTypes(signature) : null; } diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java Mon Nov 16 20:08:40 2015 +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.getBeansLinker().getLinkerForClass(clazz); final List properties = new ArrayList<>(propertyNames.size() + methodNames.size()); for(final String methodName: methodNames) { diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java Mon Nov 16 20:08:40 2015 +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: diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Mon Nov 16 20:08:40 2015 +0100 @@ -40,7 +40,7 @@ import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.linker.GuardedInvocation; -import jdk.internal.dynalink.linker.GuardedInvocationTransformer; +import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.MethodTypeConversionStrategy; @@ -67,6 +67,24 @@ private static final MethodHandle VOID_TO_OBJECT = MH.constant(Object.class, ScriptRuntime.UNDEFINED); + private static final BeansLinker beansLinker = new BeansLinker(Bootstrap::createNoSuchMemberHandler); + private static final GuardingDynamicLinker[] prioritizedLinkers; + private static final GuardingDynamicLinker[] fallbackLinkers; + static { + final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker(); + prioritizedLinkers = new GuardingDynamicLinker[] { + new NashornLinker(), + new NashornPrimitiveLinker(), + new NashornStaticClassLinker(), + new BoundCallableLinker(), + new JavaSuperAdapterLinker(), + new JSObjectLinker(nashornBeansLinker), + new BrowserJSObjectLinker(nashornBeansLinker), + new ReflectionCheckLinker() + }; + fallbackLinkers = new GuardingDynamicLinker[] {nashornBeansLinker, new NashornBottomLinker() }; + } + // do not create me!! private Bootstrap() { } @@ -81,31 +99,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); @@ -114,6 +115,10 @@ return factory.createLinker(); } + public static BeansLinker getBeansLinker() { + return beansLinker; + } + /** * Returns if the given object is a "callable" * @param obj object to be checked for callability @@ -493,4 +498,14 @@ } return target; } + + private static MethodHandle createNoSuchMemberHandler( + final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { + if (BrowserJSObjectLinker.canLinkTypeStatic(linkRequest.getReceiver().getClass())) { + // Don't create "no such member" handlers for the browser JS objects as they + // have their own logic. + return null; + } + return NashornBottomLinker.linkNoSuchBeanMember(linkRequest, linkerServices); + } } diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java Mon Nov 16 20:08:40 2015 +0100 @@ -48,19 +48,9 @@ * A Dynalink linker to handle web browser built-in JS (DOM etc.) objects. */ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { - private static ClassLoader extLoader; - static { - extLoader = BrowserJSObjectLinker.class.getClassLoader(); - // in case nashorn is loaded as bootstrap! - if (extLoader == null) { - extLoader = ClassLoader.getSystemClassLoader().getParent(); - } - } private static final String JSOBJECT_CLASS = "netscape.javascript.JSObject"; - // not final because this is lazily initialized - // when we hit a subclass for the first time. - private static volatile Class jsObjectClass; + private static final Class jsObjectClass = findBrowserJSObjectClass(); private final NashornBeansLinker nashornBeansLinker; BrowserJSObjectLinker(final NashornBeansLinker nashornBeansLinker) { @@ -73,22 +63,7 @@ } static boolean canLinkTypeStatic(final Class type) { - if (jsObjectClass != null && jsObjectClass.isAssignableFrom(type)) { - return true; - } - - // check if this class is a subclass of JSObject - Class clazz = type; - while (clazz != null) { - if (clazz.getClassLoader() == extLoader && - clazz.getName().equals(JSOBJECT_CLASS)) { - jsObjectClass = clazz; - return true; - } - clazz = clazz.getSuperclass(); - } - - return false; + return jsObjectClass != null && jsObjectClass.isAssignableFrom(type); } private static void checkJSObjectClass() { @@ -101,13 +76,10 @@ final CallSiteDescriptor desc = request.getCallSiteDescriptor(); checkJSObjectClass(); - GuardedInvocation inv; - if (jsObjectClass.isInstance(self)) { - inv = lookup(desc, request, linkerServices); - inv = inv.replaceMethods(linkerServices.filterInternalObjects(inv.getInvocation()), inv.getGuard()); - } else { - throw new AssertionError(); // Should never reach here. - } + assert jsObjectClass.isInstance(self); + + GuardedInvocation inv = lookup(desc, request, linkerServices); + inv = inv.replaceMethods(linkerServices.filterInternalObjects(inv.getInvocation()), inv.getGuard()); return Bootstrap.asTypeSafeReturn(inv, linkerServices, desc); } @@ -122,7 +94,7 @@ final StandardOperation op = NashornCallSiteDescriptor.getFirstStandardOperation(desc); if (op == null) { - return null; + return inv; } final String name = NashornCallSiteDescriptor.getOperand(desc); switch (op) { @@ -236,4 +208,18 @@ return MH.findVirtual(MethodHandles.publicLookup(), jsObjectClass, name, MH.type(rtype, types)); } } + + private static Class findBrowserJSObjectClass() { + ClassLoader extLoader; + extLoader = BrowserJSObjectLinker.class.getClassLoader(); + // in case nashorn is loaded as bootstrap! + if (extLoader == null) { + extLoader = ClassLoader.getSystemClassLoader().getParent(); + } + try { + return Class.forName(JSOBJECT_CLASS, false, extLoader); + } catch (final ClassNotFoundException e) { + return null; + } + } } diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java Mon Nov 16 20:08:40 2015 +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; @@ -35,7 +34,6 @@ import jdk.internal.dynalink.NamedOperation; import jdk.internal.dynalink.Operation; import jdk.internal.dynalink.StandardOperation; -import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; @@ -101,17 +99,13 @@ // Delegate to BeansLinker final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation( - BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args), + Bootstrap.getBeansLinker(), 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 +159,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); } /** diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Mon Nov 16 20:08:40 2015 +0100 @@ -85,12 +85,11 @@ } }; - private final BeansLinker beansLinker = new BeansLinker(); - @Override public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { final Object self = linkRequest.getReceiver(); final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor(); + final BeansLinker beansLinker = Bootstrap.getBeansLinker(); if (self instanceof ConsString) { // In order to treat ConsString like a java.lang.String we need a link request with a string receiver. final Object[] arguments = linkRequest.getArguments(); diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Mon Nov 16 20:08:40 2015 +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.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.NamedOperation; import jdk.internal.dynalink.Operation; +import jdk.internal.dynalink.StandardOperation; import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.linker.GuardedInvocation; 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.support.Guards; +import jdk.internal.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; @@ -59,7 +60,6 @@ * attempts to invoke arbitrary Java objects as functions or constructors. */ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeConverterFactory { - @Override public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { @@ -73,7 +73,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 +85,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 NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor)linkRequest.getCallSiteDescriptor(); final Object self = linkRequest.getReceiver(); switch (desc.getFirstOperation()) { @@ -105,35 +116,79 @@ throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self)); } throw typeError("not.a.function", desc.getFunctionErrorMessage(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 (desc.getOperand() != 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 (desc.getOperand() != 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 noSuchBeanMember throw new AssertionError("unknown call type " + desc); } } + static MethodHandle linkNoSuchBeanMember(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 lookupSupplier) throws Exception { final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType); @@ -157,8 +212,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 diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Mon Nov 16 20:08:40 2015 +0100 @@ -73,7 +73,7 @@ /** New operation {@code new Constructor(args...)} */ public static final int NEW = 7; - private static final int OPERATION_MASK = 7; + static final int OPERATION_MASK = 7; // Correspond to the operation indices above. private static final Operation[] OPERATIONS = new Operation[] { diff -r 6f236ad5492a src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Mon Nov 16 15:13:26 2015 +0100 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Mon Nov 16 20:08:40 2015 +0100 @@ -29,7 +29,6 @@ import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.NamedOperation; import jdk.internal.dynalink.StandardOperation; -import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardingDynamicLinker; @@ -53,7 +52,7 @@ * */ final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker { - private static final GuardingDynamicLinker staticClassLinker = BeansLinker.getLinkerForClass(StaticClass.class); + private static final GuardingDynamicLinker staticClassLinker = Bootstrap.getBeansLinker().getLinkerForClass(StaticClass.class); @Override public boolean canLinkType(final Class type) { diff -r 6f236ad5492a test/script/basic/JDK-8049242.js.EXPECTED --- a/test/script/basic/JDK-8049242.js.EXPECTED Mon Nov 16 15:13:26 2015 +0100 +++ b/test/script/basic/JDK-8049242.js.EXPECTED Mon Nov 16 20:08:40 2015 +0100 @@ -1,10 +1,10 @@ abc [jdk.internal.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.internal.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 diff -r 6f236ad5492a test/script/basic/JDK-8066669.js --- a/test/script/basic/JDK-8066669.js Mon Nov 16 15:13:26 2015 +0100 +++ b/test/script/basic/JDK-8066669.js Mon Nov 16 20:08:40 2015 +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); diff -r 6f236ad5492a test/script/basic/JDK-8066669.js.EXPECTED --- a/test/script/basic/JDK-8066669.js.EXPECTED Mon Nov 16 15:13:26 2015 +0100 +++ b/test/script/basic/JDK-8066669.js.EXPECTED Mon Nov 16 20:08:40 2015 +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} diff -r 6f236ad5492a test/script/basic/list.js --- a/test/script/basic/list.js Mon Nov 16 15:13:26 2015 +0100 +++ b/test/script/basic/list.js Mon Nov 16 20:08:40 2015 +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]); diff -r 6f236ad5492a test/script/basic/list.js.EXPECTED --- a/test/script/basic/list.js.EXPECTED Mon Nov 16 15:13:26 2015 +0100 +++ b/test/script/basic/list.js.EXPECTED Mon Nov 16 20:08:40 2015 +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 diff -r 6f236ad5492a test/script/basic/map.js --- a/test/script/basic/map.js Mon Nov 16 15:13:26 2015 +0100 +++ b/test/script/basic/map.js Mon Nov 16 20:08:40 2015 +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" diff -r 6f236ad5492a test/script/basic/map.js.EXPECTED --- a/test/script/basic/map.js.EXPECTED Mon Nov 16 15:13:26 2015 +0100 +++ b/test/script/basic/map.js.EXPECTED Mon Nov 16 20:08:40 2015 +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 diff -r 6f236ad5492a test/src/jdk/internal/dynalink/beans/test/CallerSensitiveTest.java --- a/test/src/jdk/internal/dynalink/beans/test/CallerSensitiveTest.java Mon Nov 16 15:13:26 2015 +0100 +++ b/test/src/jdk/internal/dynalink/beans/test/CallerSensitiveTest.java Mon Nov 16 20:08:40 2015 +0100 @@ -33,6 +33,6 @@ public class CallerSensitiveTest { @Test public void testCallerSensitive() { - BeansLinker.getLinkerForClass(ClassLoaderAware.class); + new BeansLinker().getLinkerForClass(ClassLoaderAware.class); } }