changeset 1598:30c3bcdb762c

8144919: Implement missing member handler for BeansLinker Reviewed-by: lagergren, mhaupt, sundar
author attila
date Thu, 14 Jan 2016 13:24:03 +0100
parents 2247904a107c
children e56208758885
files src/jdk.dynalink/share/classes/jdk/dynalink/beans/AbstractJavaLinker.java src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeansLinker.java src/jdk.dynalink/share/classes/jdk/dynalink/beans/LinkerServicesWithMissingMemberHandlerFactory.java src/jdk.dynalink/share/classes/jdk/dynalink/beans/MissingMemberHandlerFactory.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java test/script/basic/JDK-8049242.js.EXPECTED test/script/basic/JDK-8066669.js test/script/basic/JDK-8066669.js.EXPECTED test/script/basic/list.js test/script/basic/list.js.EXPECTED test/script/basic/map.js test/script/basic/map.js.EXPECTED test/src/jdk/dynalink/beans/test/BeanLinkerTest.java test/src/jdk/dynalink/beans/test/BeansLinkerTest.java test/src/jdk/dynalink/beans/test/CallerSensitiveTest.java test/src/jdk/internal/dynalink/beans/test/CallerSensitiveTest.java
diffstat 23 files changed, 714 insertions(+), 239 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AbstractJavaLinker.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeansLinker.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/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/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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Undefined.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/script/basic/JDK-8049242.js.EXPECTED	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/script/basic/JDK-8066669.js	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/script/basic/JDK-8066669.js.EXPECTED	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/script/basic/list.js	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/script/basic/list.js.EXPECTED	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/script/basic/map.js	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/script/basic/map.js.EXPECTED	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/src/jdk/dynalink/beans/test/BeanLinkerTest.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/src/jdk/dynalink/beans/test/BeansLinkerTest.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/test/src/jdk/dynalink/beans/test/CallerSensitiveTest.java	Thu Jan 14 13:22:58 2016 +0100
+++ b/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/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);
-    }
-}