Commits

Anonymous committed 73cbad0 Merge

Merge

Comments (0)

Files changed (37)

src/jdk/internal/dynalink/linker/GuardedTypeConversion.java

+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+   Copyright 2009-2013 Attila Szegedi
+
+   Licensed under both the Apache License, Version 2.0 (the "Apache License")
+   and the BSD License (the "BSD License"), with licensee being free to
+   choose either of the two at their discretion.
+
+   You may not use this file except in compliance with either the Apache
+   License or the BSD License.
+
+   If you choose to use this file in compliance with the Apache License, the
+   following notice applies to you:
+
+       You may obtain a copy of the Apache License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+       implied. See the License for the specific language governing
+       permissions and limitations under the License.
+
+   If you choose to use this file in compliance with the BSD License, the
+   following notice applies to you:
+
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in the
+         documentation and/or other materials provided with the distribution.
+       * Neither the name of the copyright holder nor the names of
+         contributors may be used to endorse or promote products derived from
+         this software without specific prior written permission.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.linker;
+
+public class GuardedTypeConversion {
+    private final GuardedInvocation conversionInvocation;
+    private final boolean cacheable;
+
+    public GuardedTypeConversion(final GuardedInvocation conversionInvocation, final boolean cacheable) {
+        this.conversionInvocation = conversionInvocation;
+        this.cacheable = cacheable;
+    }
+
+    public GuardedInvocation getConversionInvocation() {
+        return conversionInvocation;
+    }
+
+    public boolean isCacheable() {
+        return cacheable;
+    }
+}

src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java

  */
 public interface GuardingTypeConverterFactory {
     /**
-     * Returns a guarded invocation that receives an Object of the specified source type and returns an Object converted
-     * to the specified target type. The type of the invocation is targetType(sourceType), while the type of the guard
-     * is boolean(sourceType). Note that this will never be invoked for type conversions allowed by the JLS 5.3 "Method
-     * Invocation Conversion", see {@link TypeUtilities#isMethodInvocationConvertible(Class, Class)} for details. An
-     * implementation can assume it is never requested to produce a converter for these conversions.
+     * Returns a guarded type conversion that receives an Object of the specified source type and returns an Object
+     * converted to the specified target type. The type of the invocation is targetType(sourceType), while the type of
+     * the guard is boolean(sourceType). Note that this will never be invoked for type conversions allowed by the JLS
+     * 5.3 "Method Invocation Conversion", see {@link TypeUtilities#isMethodInvocationConvertible(Class, Class)} for
+     * details. An implementation can assume it is never requested to produce a converter for these conversions.
      *
      * @param sourceType source type
      * @param targetType the target type.
-     * @return a guarded invocation that can take an object (if it passes guard) and returns another object that is its
-     * representation coerced into the target type. In case the factory is certain it is unable to handle a conversion,
-     * it can return null. In case the factory is certain that it can always handle the conversion, it can return an
-     * unconditional invocation (one whose guard is null).
+     * @return a guarded type conversion that contains a guarded invocation that can take an object (if it passes guard)
+     * and return another object that is its representation coerced into the target type. In case the factory is certain
+     * it is unable to handle a conversion, it can return null. In case the factory is certain that it can always handle
+     * the conversion, it can return an unconditional invocation (one whose guard is null).
      * @throws Exception if there was an error during creation of the converter
      */
-    public GuardedInvocation convertToType(Class<?> sourceType, Class<?> targetType) throws Exception;
+    public GuardedTypeConversion convertToType(Class<?> sourceType, Class<?> targetType) throws Exception;
 }

src/jdk/internal/dynalink/support/LinkerServicesImpl.java

  */
 public class LinkerServicesImpl implements LinkerServices {
 
+    private static final RuntimePermission GET_CURRENT_LINK_REQUEST = new RuntimePermission("dynalink.getCurrentLinkRequest");
+    private static final ThreadLocal<LinkRequest> threadLinkRequest = new ThreadLocal<>();
+
     private final TypeConverterFactory typeConverterFactory;
     private final GuardingDynamicLinker topLevelLinker;
 
 
     @Override
     public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest) throws Exception {
-        return topLevelLinker.getGuardedInvocation(linkRequest, this);
+        final LinkRequest prevLinkRequest = threadLinkRequest.get();
+        threadLinkRequest.set(linkRequest);
+        try {
+            return topLevelLinker.getGuardedInvocation(linkRequest, this);
+        } finally {
+            threadLinkRequest.set(prevLinkRequest);
+        }
+    }
+
+    /**
+     * Returns the currently processed link request, or null if the method is invoked outside of the linking process.
+     * @return the currently processed link request, or null.
+     * @throws SecurityException if the calling code doesn't have the {@code "dynalink.getCurrentLinkRequest"} runtime
+     * permission.
+     */
+    public static LinkRequest getCurrentLinkRequest() {
+        SecurityManager sm = System.getSecurityManager();
+        if(sm != null) {
+            sm.checkPermission(GET_CURRENT_LINK_REQUEST);
+        }
+        return threadLinkRequest.get();
     }
 }

src/jdk/internal/dynalink/support/TypeConverterFactory.java

 import jdk.internal.dynalink.linker.ConversionComparator;
 import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
 import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.GuardedTypeConversion;
 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
 import jdk.internal.dynalink.linker.LinkerServices;
 
                 @Override
                 protected MethodHandle computeValue(Class<?> targetType) {
                     if(!canAutoConvert(sourceType, targetType)) {
-                        final MethodHandle converter = getTypeConverterNull(sourceType, targetType);
-                        if(converter != null) {
+                        final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType);
+                        if(converter != IDENTITY_CONVERSION) {
                             return converter;
                         }
                     }
         }
     };
 
+    private final ClassValue<ClassMap<Boolean>> canConvert = new ClassValue<ClassMap<Boolean>>() {
+        @Override
+        protected ClassMap<Boolean> computeValue(final Class<?> sourceType) {
+            return new ClassMap<Boolean>(getClassLoader(sourceType)) {
+                @Override
+                protected Boolean computeValue(Class<?> targetType) {
+                    try {
+                        return getTypeConverterNull(sourceType, targetType) != null;
+                    } catch (RuntimeException e) {
+                        throw e;
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            };
+        }
+    };
+
     private static final ClassLoader getClassLoader(final Class<?> clazz) {
         return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
             @Override
      * @return true if there can be a conversion, false if there can not.
      */
     public boolean canConvert(final Class<?> from, final Class<?> to) {
-        return canAutoConvert(from, to) || getTypeConverterNull(from, to) != null;
+        return canAutoConvert(from, to) || canConvert.get(from).get(to).booleanValue();
     }
 
     /**
         return TypeUtilities.isMethodInvocationConvertible(fromType, toType);
     }
 
+    /*private*/ MethodHandle getCacheableTypeConverterNull(Class<?> sourceType, Class<?> targetType) {
+        final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType);
+        return converter == IDENTITY_CONVERSION ? null : converter;
+    }
+
     /*private*/ MethodHandle getTypeConverterNull(Class<?> sourceType, Class<?> targetType) {
-        final MethodHandle converter = converterMap.get(sourceType).get(targetType);
-        return converter == IDENTITY_CONVERSION ? null : converter;
+        try {
+            return getCacheableTypeConverterNull(sourceType, targetType);
+        } catch(NotCacheableConverter e) {
+            return e.converter;
+        }
+    }
+
+    /*private*/ MethodHandle getCacheableTypeConverter(Class<?> sourceType, Class<?> targetType) {
+        return converterMap.get(sourceType).get(targetType);
     }
 
     /**
      * @return a method handle performing the conversion.
      */
     public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType) {
-        return converterIdentityMap.get(sourceType).get(targetType);
+        try {
+            return converterIdentityMap.get(sourceType).get(targetType);
+        } catch(NotCacheableConverter e) {
+            return e.converter;
+        }
     }
 
     /*private*/ MethodHandle createConverter(Class<?> sourceType, Class<?> targetType) throws Exception {
         final MethodType type = MethodType.methodType(targetType, sourceType);
         final MethodHandle identity = IDENTITY_CONVERSION.asType(type);
         MethodHandle last = identity;
+        boolean cacheable = true;
         for(int i = factories.length; i-- > 0;) {
-            final GuardedInvocation next = factories[i].convertToType(sourceType, targetType);
+            final GuardedTypeConversion next = factories[i].convertToType(sourceType, targetType);
             if(next != null) {
-                next.assertType(type);
-                last = next.compose(last);
+                cacheable = cacheable && next.isCacheable();
+                final GuardedInvocation conversionInvocation = next.getConversionInvocation();
+                conversionInvocation.assertType(type);
+                last = conversionInvocation.compose(last);
             }
         }
-        return last == identity ? IDENTITY_CONVERSION : last;
+        if(last == identity) {
+            return IDENTITY_CONVERSION;
+        }
+        if(cacheable) {
+            return last;
+        }
+        throw new NotCacheableConverter(last);
     }
 
     /*private*/ static final MethodHandle IDENTITY_CONVERSION = MethodHandles.identity(Object.class);
+
+    private static class NotCacheableConverter extends RuntimeException {
+        final MethodHandle converter;
+
+        NotCacheableConverter(final MethodHandle converter) {
+            super("", null, false, false);
+            this.converter = converter;
+        }
+    }
 }

src/jdk/nashorn/api/scripting/NashornScriptEngine.java

 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.net.URL;
     private volatile Property         contextProperty;
 
     // default options passed to Nashorn Options object
-    private static final String[] DEFAULT_OPTIONS = new String[] { "-scripting", "-doe" };
+    private static final String[] DEFAULT_OPTIONS = new String[] { "-doe" };
 
     // Nashorn script engine error message management
     private static final String MESSAGES_RESOURCE = "jdk.nashorn.api.scripting.resources.Messages";
                 if (! isInterfaceImplemented(clazz, realSelf)) {
                     return null;
                 }
-                return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz).invoke(realSelf));
+                return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz,
+                        MethodHandles.publicLookup()).invoke(realSelf));
             } finally {
                 if (globalChanged) {
                     Context.setGlobal(oldGlobal);

src/jdk/nashorn/internal/objects/NativeJava.java

 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
+import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Array;
 import java.util.Collection;
 import java.util.Deque;
      * </pre>
      * We can see several important concepts in the above example:
      * <ul>
-     * <li>Every specified list of Java types will have exactly one extender subclass in Nashorn - repeated invocations
-     * of {@code extend} for the same list of types will yield the same extender type. It's a generic adapter that
-     * delegates to whatever JavaScript functions its implementation object has on a per-instance basis.</li>
+     * <li>Every specified list of Java types will have one extender subclass in Nashorn per caller protection domain -
+     * repeated invocations of {@code extend} for the same list of types for scripts same protection domain will yield
+     * the same extender type. It's a generic adapter that delegates to whatever JavaScript functions its implementation
+     * object has on a per-instance basis.</li>
      * <li>If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter
      * must be prepared to deal with all overloads.</li>
-     * <li>You can't invoke {@code super.*()} from adapters for now.</li>
+     * <li>To invoke super methods from adapters, call them on the adapter instance prefixing them with {@code super$},
+     * or use the special {@link #_super(Object, Object) super-adapter}.</li>
      * <li>It is also possible to specify an ordinary JavaScript object as the last argument to {@code extend}. In that
      * case, it is treated as a class-level override. {@code extend} will return an extender class where all instances
      * will have the methods implemented by functions on that object, just as if that object were passed as the last
      * t.join()
      * </pre>
      * As you can see, you don't have to pass any object when you create a new instance of {@code R1} as its
-     * {@code run()} function was defined already when extending the class. Of course, you can still provide
-     * instance-level overrides on these objects. The order of precedence is instance-level method, class-level method,
-     * superclass method, or {@code UnsupportedOperationException} if the superclass method is abstract. If we continue
-     * our previous example:
+     * {@code run()} function was defined already when extending the class. If you also want to add instance-level
+     * overrides on these objects, you will have to repeatedly use {@code extend()} to subclass the class-level adapter.
+     * For such adapters, the order of precedence is instance-level method, class-level method, superclass method, or
+     * {@code UnsupportedOperationException} if the superclass method is abstract. If we continue our previous example:
      * <pre>
-     * var r2 = new R1(function() { print("r2.run() invoked!") })
+     * var R2 = Java.extend(R1);
+     * var r2 = new R2(function() { print("r2.run() invoked!") })
      * r2.run()
      * </pre>
      * We'll see it'll print {@code "r2.run() invoked!"}, thus overriding on instance-level the class-level behavior.
+     * Note that you must use {@code Java.extend} to explicitly create an instance-override adapter class from a
+     * class-override adapter class, as the class-override adapter class is no longer abstract.
      * </li>
      * </ul>
      * @param self not used
         } catch(final ClassCastException e) {
             throw typeError("extend.expects.java.types");
         }
-        return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides);
+        // Note that while the public API documentation claims self is not used, we actually use it.
+        // ScriptFunction.findCallMethod will bind the lookup object into it, and we can then use that lookup when
+        // requesting the adapter class. Note that if Java.extend is invoked with no lookup object, it'll pass the
+        // public lookup which'll result in generation of a no-permissions adapter. A typical situation this can happen
+        // is when the extend function is bound.
+        final MethodHandles.Lookup lookup;
+        if(self instanceof MethodHandles.Lookup) {
+            lookup = (MethodHandles.Lookup)self;
+        } else {
+            lookup = MethodHandles.publicLookup();
+        }
+        return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides, lookup);
     }
 
     /**

src/jdk/nashorn/internal/objects/NativeJavaImporter.java

 import jdk.nashorn.internal.objects.annotations.Constructor;
 import jdk.nashorn.internal.objects.annotations.Function;
 import jdk.nashorn.internal.objects.annotations.ScriptClass;
+import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.NativeJavaPackage;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.ScriptObject;
             } else if (obj instanceof NativeJavaPackage) {
                 final String pkgName  = ((NativeJavaPackage)obj).getName();
                 final String fullName = pkgName.isEmpty() ? name : (pkgName + "." + name);
+                final Context context = Global.instance().getContext();
                 try {
-                    return StaticClass.forClass(Class.forName(fullName));
+                    return StaticClass.forClass(context.findClass(fullName));
                 } catch (final ClassNotFoundException e) {
                     // IGNORE
                 }

src/jdk/nashorn/internal/objects/NativeObject.java

             targetObj.addBoundProperties(source, props);
         } else if (source instanceof StaticClass) {
             final Class<?> clazz = ((StaticClass)source).getRepresentedClass();
+            Bootstrap.checkReflectionAccess(clazz, true);
             bindBeanProperties(targetObj, source, BeansLinker.getReadableStaticPropertyNames(clazz),
                     BeansLinker.getWritableStaticPropertyNames(clazz), BeansLinker.getStaticMethodNames(clazz));
         } else {
             final Class<?> clazz = source.getClass();
+            Bootstrap.checkReflectionAccess(clazz, false);
             bindBeanProperties(targetObj, source, BeansLinker.getReadableInstancePropertyNames(clazz),
                     BeansLinker.getWritableInstancePropertyNames(clazz), BeansLinker.getInstanceMethodNames(clazz));
         }
         propertyNames.addAll(writablePropertyNames);
 
         final Class<?> clazz = source.getClass();
-        Bootstrap.checkReflectionAccess(clazz);
 
         final MethodType getterType = MethodType.methodType(Object.class, clazz);
         final MethodType setterType = MethodType.methodType(Object.class, clazz, Object.class);

src/jdk/nashorn/internal/runtime/Context.java

     }
 
     /**
+     * Checks that the given package name can be accessed from no permissions context.
+     *
+     * @param pkgName package name
+     * @throw SecurityException if not accessible
+     */
+    public static void checkPackageAccess(final String pkgName) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            checkPackageAccess(sm, pkgName.endsWith(".")? pkgName : pkgName + ".");
+        }
+    }
+
+    /**
      * Checks that the given package can be accessed from no permissions context.
      *
      * @param sm current security manager instance

src/jdk/nashorn/internal/runtime/NativeJavaPackage.java

      */
     public NativeJavaPackage(final String name, final ScriptObject proto) {
         super(proto, null);
+        // defense-in-path, check here for sensitive packages
+        Context.checkPackageAccess(name);
         this.name = name;
     }
 

src/jdk/nashorn/internal/runtime/ScriptFunction.java

 package jdk.nashorn.internal.runtime;
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
-
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
             }
         } else {
             final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1), request.getArguments());
-            if (scopeCall) {
+            if (data.isBuiltin() && "extend".equals(data.getName())) {
+                // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
+                // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
+                boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, Object.class, Object.class);
+            } else if (scopeCall) {
                 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
                 // (this, args...) => (args...)
                 boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);

src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java

         ERROR_NON_PUBLIC_CLASS,
         ERROR_NO_ACCESSIBLE_CONSTRUCTOR,
         ERROR_MULTIPLE_SUPERCLASSES,
-        ERROR_NO_COMMON_LOADER
+        ERROR_NO_COMMON_LOADER,
+        ERROR_FINAL_FINALIZER
     }
 
     static final AdaptationResult SUCCESSFUL_RESULT = new AdaptationResult(Outcome.SUCCESS, "");

src/jdk/nashorn/internal/runtime/linker/Bootstrap.java

      * {@code java.lang.invoke} package, as well a {@link Class} and any subclass of {@link ClassLoader}) and there is
      * a security manager in the system, then it checks the {@code nashorn.JavaReflection} {@code RuntimePermission}.
      * @param clazz the class being tested
+     * @param isStatic is access checked for static members (or instance members)
      */
-    public static void checkReflectionAccess(Class<?> clazz) {
-        ReflectionCheckLinker.checkReflectionAccess(clazz);
+    public static void checkReflectionAccess(Class<?> clazz, boolean isStatic) {
+        ReflectionCheckLinker.checkReflectionAccess(clazz, isStatic);
     }
 
     /**

src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java

 import java.util.Map;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.GuardedTypeConversion;
 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.linker.LinkerServices;
     }
 
     @Override
-    public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
+    public GuardedTypeConversion convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
         final boolean sourceIsAlwaysJSObject = JSObject.class.isAssignableFrom(sourceType);
         if(!sourceIsAlwaysJSObject && !sourceType.isAssignableFrom(JSObject.class)) {
             return null;
             return null;
         }
 
-        return new GuardedInvocation(converter, sourceIsAlwaysJSObject ? null : IS_JSOBJECT_GUARD).asType(MethodType.methodType(targetType, sourceType));
+        return new GuardedTypeConversion(new GuardedInvocation(converter, sourceIsAlwaysJSObject ? null : IS_JSOBJECT_GUARD).asType(MethodType.methodType(targetType, sourceType)), true);
     }
 
 

src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java

 import java.util.List;
 import java.util.Set;
 import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Handle;
 import jdk.internal.org.objectweb.asm.Label;
 import jdk.internal.org.objectweb.asm.Opcodes;
 import jdk.internal.org.objectweb.asm.Type;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome;
 import sun.reflect.CallerSensitive;
 
 /**
  * Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}.
  * </p><p>
- * For every protected or public constructor in the extended class, the adapter class will have between one to three
+ * For every protected or public constructor in the extended class, the adapter class will have either one or two
  * public constructors (visibility of protected constructors in the extended class is promoted to public).
- * <ul>
- * <li>In every case, a constructor taking a trailing ScriptObject argument preceded by original constructor arguments
- * is always created on the adapter class. When such a constructor is invoked, the passed ScriptObject's member
- * functions are used to implement and/or override methods on the original class, dispatched by name. A single
- * JavaScript function will act as the implementation for all overloaded methods of the same name. When methods on an
- * adapter instance are invoked, the functions are invoked having the ScriptObject passed in the instance constructor as
- * their "this". Subsequent changes to the ScriptObject (reassignment or removal of its functions) are not reflected in
- * the adapter instance; the method implementations are bound to functions at constructor invocation time.
+ * <li>
+ * <li>For adapter classes with instance-level overrides, a constructor taking a trailing ScriptObject argument preceded
+ * by original constructor arguments is always created on the adapter class. When such a constructor is invoked, the
+ * passed ScriptObject's member functions are used to implement and/or override methods on the original class,
+ * dispatched by name. A single JavaScript function will act as the implementation for all overloaded methods of the
+ * same name. When methods on an adapter instance are invoked, the functions are invoked having the ScriptObject passed
+ * in the instance constructor as their "this". Subsequent changes to the ScriptObject (reassignment or removal of its
+ * functions) are not reflected in the adapter instance; the method implementations are bound to functions at
+ * constructor invocation time.
  * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The
  * only restriction is that since every JavaScript object already has a {@code toString} function through the
  * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a
  * </li>
  * <li>
  * If the original types collectively have only one abstract method, or have several of them, but all share the
- * same name, an additional constructor is provided for every original constructor; this one takes a ScriptFunction as
- * its last argument preceded by original constructor arguments. This constructor will use the passed function as the
- * implementation for all abstract methods. For consistency, any concrete methods sharing the single abstract method
- * name will also be overridden by the function. When methods on the adapter instance are invoked, the ScriptFunction is
- * invoked with global or UNDEFINED as its "this" depending whether the function is non-strict or not.
+ * same name, an additional constructor for instance-level override adapter is provided for every original constructor;
+ * this one takes a ScriptFunction as its last argument preceded by original constructor arguments. This constructor
+ * will use the passed function as the implementation for all abstract methods. For consistency, any concrete methods
+ * sharing the single abstract method name will also be overridden by the function. When methods on the adapter instance
+ * are invoked, the ScriptFunction is invoked with UNDEFINED or Global as its "this" depending whether the function is
+ * strict or not.
  * </li>
  * <li>
  * If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass
- * constructors are also created. These constructors simply delegate to the superclass constructor. They are used to
- * create instances of the adapter class with no instance-level overrides.
+ * constructors are created. These constructors simply delegate to the superclass constructor. They are simply used to
+ * create instances of the adapter class, with no instance-level overrides, as they don't have them.
  * </li>
  * </ul>
  * </p><p>
  * source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses
  * to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>.
  * </p><p>
- * It is possible to create two different classes: those that can have both class-level and instance-level overrides,
- * and those that can only have instance-level overrides. When
- * {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked with non-null {@code classOverrides}
- * parameter, an adapter class is created that can have class-level overrides, and the passed script object will be used
- * as the implementations for its methods, just as in the above case of the constructor taking a script object. Note
- * that in the case of class-level overrides, a new adapter class is created on every invocation, and the implementation
- * object is bound to the class, not to any instance. All created instances will share these functions. Of course, when
- * instances of such a class are being created, they can still take another object (or possibly a function) in their
- * constructor's trailing position and thus provide further instance-specific overrides. The order of invocation is
- * always instance-specified method, then a class-specified method, and finally the superclass method.
+ * It is possible to create two different adapter classes: those that can have class-level overrides, and those that can
+ * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked
+ * with non-null {@code classOverrides} parameter, an adapter class is created that can have class-level overrides, and
+ * the passed script object will be used as the implementations for its methods, just as in the above case of the
+ * constructor taking a script object. Note that in the case of class-level overrides, a new adapter class is created on
+ * every invocation, and the implementation object is bound to the class, not to any instance. All created instances
+ * will share these functions. If it is required to have both class-level overrides and instance-level overrides, the
+ * class-level override adapter class should be subclassed with an instance-override adapter. Since adapters delegate to
+ * super class when an overriding method handle is not specified, this will behave as expected. It is not possible to
+ * have both class-level and instance-level overrides in the same class for security reasons: adapter classes are
+ * defined with a protection domain of their creator code, and an adapter class that has both class and instance level
+ * overrides would need to have two potentially different protection domains: one for class-based behavior and one for
+ * instance-based behavior; since Java classes can only belong to a single protection domain, this could not be
+ * implemented securely.
  */
 final class JavaAdapterBytecodeGenerator {
     static final Type CONTEXT_TYPE       = Type.getType(Context.class);
     private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255;
 
     private static final String CLASS_INIT = "<clinit>";
-    private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal";
 
     // Method name prefix for invoking super-methods
     static final String SUPER_PREFIX = "super$";
     private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
     private final Set<MethodInfo> methodInfos = new HashSet<>();
     private boolean autoConvertibleFromFunction = false;
+    private boolean hasExplicitFinalizer = false;
 
     private final ClassWriter cw;
 
      * @param superClass the superclass the adapter will extend.
      * @param interfaces the interfaces the adapter will implement.
      * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes.
-     * @param classOverride true to generate the bytecode for the adapter that has both class-level and instance-level
-     * overrides, false to generate the bytecode for the adapter that only has instance-level overrides.
+     * @param classOverride true to generate the bytecode for the adapter that has class-level overrides, false to
+     * generate the bytecode for the adapter that has instance-level overrides.
      * @throws AdaptationException if the adapter can not be generated for some reason.
      */
     JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces,
         superClassName = Type.getInternalName(superClass);
         generatedClassName = getGeneratedClassName(superClass, interfaces);
 
-        cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
-
+        cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
         generateGlobalFields();
 
         gatherMethods(superClass);
         generateConstructors();
         generateMethods();
         generateSuperMethods();
+        if (hasExplicitFinalizer) {
+            generateFinalizerMethods();
+        }
         // }
         cw.visitEnd();
     }
 
     private void generateGlobalFields() {
-        cw.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
+        cw.visitField(ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0), GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
         usedFieldNames.add(GLOBAL_FIELD_NAME);
-        if(classOverride) {
-            cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
-            usedFieldNames.add(STATIC_GLOBAL_FIELD_NAME);
-        }
     }
 
     JavaAdapterClassLoader createAdapterClassLoader() {
     }
 
     private void generateHandleFields() {
+        final int flags = ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0);
         for (final MethodInfo mi: methodInfos) {
-            cw.visitField(ACC_PRIVATE | ACC_FINAL, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
-            if(classOverride) {
-                cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
-            }
+            cw.visitField(flags, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
         }
     }
 
                 } else {
                     mv.visitInsn(ACONST_NULL);
                 }
-                mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+                mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
             }
             initGlobal = new Label();
             mv.goTo(initGlobal);
             mv.aconst(mi.getName());
             mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
             mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR);
-            mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+            mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
         }
 
         if(initGlobal != null) {
             mv.visitLabel(initGlobal);
         }
-        // Assign "staticGlobal = Context.getGlobal()"
+        // Assign "global = Context.getGlobal()"
         invokeGetGlobalWithNullCheck(mv);
-        mv.putstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+        mv.putstatic(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
 
         endInitMethod(mv);
     }
             // Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want
             // to create instances without further per-instance overrides.
             generateDelegatingConstructor(ctor);
-        }
+        } else {
+            // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
+            // beginning of its parameter list.
+            generateOverridingConstructor(ctor, false);
 
-        // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
-        // beginning of its parameter list.
-        generateOverridingConstructor(ctor, false);
-
-        if (samName != null) {
-            if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) {
-                // If the original type only has a single abstract method name, as well as a default ctor, then it can
-                // be automatically converted from JS function.
-                autoConvertibleFromFunction = true;
+            if (samName != null) {
+                if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) {
+                    // If the original type only has a single abstract method name, as well as a default ctor, then it can
+                    // be automatically converted from JS function.
+                    autoConvertibleFromFunction = true;
+                }
+                // If all our abstract methods have a single name, generate an additional constructor, one that takes a
+                // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
+                generateOverridingConstructor(ctor, true);
             }
-            // If all our abstract methods have a single name, generate an additional constructor, one that takes a
-            // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
-            generateOverridingConstructor(ctor, true);
         }
     }
 
     }
 
     /**
-     * Generates a constructor for the adapter class. This constructor will take the same arguments as the supertype
+     * Generates a constructor for the instance adapter class. This constructor will take the same arguments as the supertype
      * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of
      * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize
      * all the method handle fields of the adapter instance with functions from the script object (or the script
                 mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
                 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor);
             }
-            mv.putfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+            mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
         }
 
         // Assign "this.global = Context.getGlobal()"
     private static class MethodInfo {
         private final Method method;
         private final MethodType type;
-        private String methodHandleInstanceFieldName;
-        private String methodHandleClassFieldName;
+        private String methodHandleFieldName;
 
         private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException {
             this(clazz.getDeclaredMethod(name, argTypes));
             return getName().hashCode() ^ type.hashCode();
         }
 
-        void setIsCanonical(final Set<String> usedFieldNames, boolean classOverride) {
-            methodHandleInstanceFieldName = nextName(usedFieldNames);
-            if(classOverride) {
-                methodHandleClassFieldName = nextName(usedFieldNames);
-            }
+        void setIsCanonical(final JavaAdapterBytecodeGenerator self) {
+            methodHandleFieldName = self.nextName(getName());
         }
+    }
 
-        String nextName(final Set<String> usedFieldNames) {
-            int i = 0;
-            final String name = getName();
-            String nextName = name;
-            while (!usedFieldNames.add(nextName)) {
-                final String ordinal = String.valueOf(i++);
-                final int maxNameLen = 255 - ordinal.length();
-                nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal);
-            }
-            return nextName;
+    private String nextName(final String name) {
+        int i = 0;
+        String nextName = name;
+        while (!usedFieldNames.add(nextName)) {
+            final String ordinal = String.valueOf(i++);
+            final int maxNameLen = 255 - ordinal.length();
+            nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal);
         }
-
+        return nextName;
     }
 
     private void generateMethods() {
                 methodDesc, null, exceptionNames));
         mv.visitCode();
 
-        final Label instanceHandleDefined = new Label();
-        final Label classHandleDefined = new Label();
+        final Label handleDefined = new Label();
 
         final Type asmReturnType = Type.getType(type.returnType());
 
-        // See if we have instance handle defined
-        mv.visitVarInsn(ALOAD, 0);
-        mv.getfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
-        // stack: [instanceHandle]
-        jumpIfNonNullKeepOperand(mv, instanceHandleDefined);
-
+        // See if we have overriding method handle defined
         if(classOverride) {
-            // See if we have the static handle
-            mv.getstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
-            // stack: [classHandle]
-            jumpIfNonNullKeepOperand(mv, classHandleDefined);
+            mv.getstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+        } else {
+            mv.visitVarInsn(ALOAD, 0);
+            mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
         }
+        // stack: [handle]
+        jumpIfNonNullKeepOperand(mv, handleDefined);
 
         // No handle is available, fall back to default behavior
         if(Modifier.isAbstract(method.getModifiers())) {
             emitSuperCall(mv, name, methodDesc);
         }
 
+        mv.visitLabel(handleDefined);
+        // Load the creatingGlobal object
+        if(classOverride) {
+            // If class handle is defined, load the static defining global
+            mv.getstatic(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+        } else {
+            mv.visitVarInsn(ALOAD, 0);
+            mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+        }
+        // stack: [creatingGlobal, handle]
         final Label setupGlobal = new Label();
-
-        if(classOverride) {
-            mv.visitLabel(classHandleDefined);
-            // If class handle is defined, load the static defining global
-            mv.getstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-            // stack: [creatingGlobal := classGlobal, classHandle]
-            mv.goTo(setupGlobal);
-        }
-
-        mv.visitLabel(instanceHandleDefined);
-        // If instance handle is defined, load the instance defining global
-        mv.visitVarInsn(ALOAD, 0);
-        mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-        // stack: [creatingGlobal := instanceGlobal, instanceHandle]
-
-        // fallthrough to setupGlobal
-
-        // stack: [creatingGlobal, someHandle]
         mv.visitLabel(setupGlobal);
 
         // Determine the first index for a local variable
         final int globalsDifferVar  = nextLocalVar++;
 
         mv.dup();
-        // stack: [creatingGlobal, creatingGlobal, someHandle]
+        // stack: [creatingGlobal, creatingGlobal, handle]
 
         // Emit code for switching to the creating global
         // ScriptObject currentGlobal = Context.getGlobal();
         invokeGetGlobal(mv);
         mv.dup();
+
         mv.visitVarInsn(ASTORE, currentGlobalVar);
-        // stack: [currentGlobal, creatingGlobal, creatingGlobal, someHandle]
+        // stack: [currentGlobal, creatingGlobal, creatingGlobal, handle]
         // if(definingGlobal == currentGlobal) {
         final Label globalsDiffer = new Label();
         mv.ifacmpne(globalsDiffer);
-        // stack: [someGlobal, someHandle]
+        // stack: [creatingGlobal, handle]
         //     globalsDiffer = false
         mv.pop();
-        // stack: [someHandle]
+        // stack: [handle]
         mv.iconst(0); // false
-        // stack: [false, someHandle]
+        // stack: [false, handle]
         final Label invokeHandle = new Label();
         mv.goTo(invokeHandle);
         mv.visitLabel(globalsDiffer);
         // } else {
         //     Context.setGlobal(definingGlobal);
-        // stack: [someGlobal, someHandle]
+        // stack: [creatingGlobal, handle]
         invokeSetGlobal(mv);
-        // stack: [someHandle]
+        // stack: [handle]
         //     globalsDiffer = true
         mv.iconst(1);
-        // stack: [true, someHandle]
+        // stack: [true, handle]
 
         mv.visitLabel(invokeHandle);
         mv.visitVarInsn(ISTORE, globalsDifferVar);
-        // stack: [someHandle]
+        // stack: [handle]
 
         // Load all parameters back on stack for dynamic invocation.
         int varOffset = 1;
         mv.areturn(methodType.getReturnType());
     }
 
+    private void generateFinalizerMethods() {
+        final String finalizerDelegateName = nextName("access$");
+        generateFinalizerDelegate(finalizerDelegateName);
+        generateFinalizerOverride(finalizerDelegateName);
+    }
+
+    private void generateFinalizerDelegate(final String finalizerDelegateName) {
+        // Generate a delegate that will be invoked from the no-permission trampoline. Note it can be private, as we'll
+        // refer to it with a MethodHandle constant pool entry in the overridden finalize() method (see
+        // generateFinalizerOverride()).
+        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PRIVATE | ACC_STATIC,
+                finalizerDelegateName, Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE), null, null));
+
+        // Simply invoke super.finalize()
+        mv.visitVarInsn(ALOAD, 0);
+        mv.checkcast(Type.getType(generatedClassName));
+        mv.invokespecial(superClassName, "finalize", Type.getMethodDescriptor(Type.VOID_TYPE), false);
+
+        mv.visitInsn(RETURN);
+        endMethod(mv);
+    }
+
+    private void generateFinalizerOverride(final String finalizerDelegateName) {
+        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, "finalize",
+                VOID_NOARG_METHOD_DESCRIPTOR, null, null));
+        // Overridden finalizer will take a MethodHandle to the finalizer delegating method, ...
+        mv.aconst(new Handle(Opcodes.H_INVOKESTATIC, generatedClassName, finalizerDelegateName,
+                Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE)));
+        mv.visitVarInsn(ALOAD, 0);
+        // ...and invoke it through JavaAdapterServices.invokeNoPermissions
+        mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "invokeNoPermissions",
+                Type.getMethodDescriptor(METHOD_HANDLE_TYPE, OBJECT_TYPE), false);
+        mv.visitInsn(RETURN);
+        endMethod(mv);
+    }
+
     private static String[] getExceptionNames(final Class<?>[] exceptions) {
         final String[] exceptionNames = new String[exceptions.length];
         for (int i = 0; i < exceptions.length; ++i) {
      * class.
      * @param type the type defining the methods.
      */
-    private void gatherMethods(final Class<?> type) {
+    private void gatherMethods(final Class<?> type) throws AdaptationException {
         if (Modifier.isPublic(type.getModifiers())) {
             final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
 
             for (final Method typeMethod: typeMethods) {
+                final String name = typeMethod.getName();
+                if(name.startsWith(SUPER_PREFIX)) {
+                    continue;
+                }
                 final int m = typeMethod.getModifiers();
                 if (Modifier.isStatic(m)) {
                     continue;
                 }
                 if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
+                    // Is it a "finalize()"?
+                    if(name.equals("finalize") && typeMethod.getParameterCount() == 0) {
+                        if(type != Object.class) {
+                            hasExplicitFinalizer = true;
+                            if(Modifier.isFinal(m)) {
+                                // Must be able to override an explicit finalizer
+                                throw new AdaptationException(Outcome.ERROR_FINAL_FINALIZER, type.getCanonicalName());
+                            }
+                        }
+                        continue;
+                    }
+
                     final MethodInfo mi = new MethodInfo(typeMethod);
                     if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) {
                         finalMethods.add(mi);
                         if (Modifier.isAbstract(m)) {
                             abstractMethodNames.add(mi.getName());
                         }
-                        mi.setIsCanonical(usedFieldNames, classOverride);
+                        mi.setIsCanonical(this);
                     }
                 }
             }
         }
     }
 
-    private void gatherMethods(final List<Class<?>> classes) {
+    private void gatherMethods(final List<Class<?>> classes) throws AdaptationException {
         for(final Class<?> c: classes) {
             gatherMethods(c);
         }

src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java

 
 import java.security.AccessControlContext;
 import java.security.AccessController;
-import java.security.AllPermission;
-import java.security.CodeSigner;
-import java.security.CodeSource;
-import java.security.Permissions;
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
 import java.security.SecureClassLoader;
  */
 @SuppressWarnings("javadoc")
 final class JavaAdapterClassLoader {
-    private static final ProtectionDomain GENERATED_PROTECTION_DOMAIN = createGeneratedProtectionDomain();
     private static final AccessControlContext CREATE_LOADER_ACC_CTXT = ClassAndLoader.createPermAccCtxt("createClassLoader");
 
     private final String className;
-    private volatile byte[] classBytes;
+    private final byte[] classBytes;
 
     JavaAdapterClassLoader(String className, byte[] classBytes) {
         this.className = className.replace('/', '.');
     }
 
     /**
-     * clear classBytes after loading class.
-     */
-    void clearClassBytes() {
-       this.classBytes = null;
-    }
-
-    /**
      * Loads the generated adapter class into the JVM.
      * @param parentLoader the parent class loader for the generated class loader
+     * @param protectionDomain the protection domain for the generated class
      * @return the generated adapter class
      */
-    StaticClass generateClass(final ClassLoader parentLoader) {
+    StaticClass generateClass(final ClassLoader parentLoader, final ProtectionDomain protectionDomain) {
+        assert protectionDomain != null;
         return AccessController.doPrivileged(new PrivilegedAction<StaticClass>() {
             @Override
             public StaticClass run() {
                 try {
-                    return StaticClass.forClass(Class.forName(className, true, createClassLoader(parentLoader)));
+                    return StaticClass.forClass(Class.forName(className, true, createClassLoader(parentLoader, protectionDomain)));
                 } catch (final ClassNotFoundException e) {
                     throw new AssertionError(e); // cannot happen
                 }
     // it even more by separating its invocation into a separate static method on the adapter class, but then someone
     // with ability to introspect on the class and use setAccessible(true) on it could invoke the method. It's a
     // security tradeoff...
-    private ClassLoader createClassLoader(final ClassLoader parentLoader) {
+    private ClassLoader createClassLoader(final ClassLoader parentLoader, final ProtectionDomain protectionDomain) {
         return new SecureClassLoader(parentLoader) {
             private final ClassLoader myLoader = getClass().getClassLoader();
 
             protected Class<?> findClass(final String name) throws ClassNotFoundException {
                 if(name.equals(className)) {
                     assert classBytes != null : "what? already cleared .class bytes!!";
-                    return defineClass(name, classBytes, 0, classBytes.length, GENERATED_PROTECTION_DOMAIN);
+                    return defineClass(name, classBytes, 0, classBytes.length, protectionDomain);
                 }
                 throw new ClassNotFoundException(name);
             }
         };
     }
-
-    private static ProtectionDomain createGeneratedProtectionDomain() {
-        // Generated classes need to have AllPermission. Since we require the "createClassLoader" RuntimePermission, we
-        // can create a class loader that'll load new classes with any permissions. Our generated classes are just
-        // delegating adapters, so having AllPermission can't cause anything wrong; the effective set of permissions for
-        // the executing script functions will still be limited by the permissions of the caller and the permissions of
-        // the script.
-        final Permissions permissions = new Permissions();
-        permissions.add(new AllPermission());
-        return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions);
-    }
 }

src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java

 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Modifier;
 import java.security.AccessControlContext;
 import java.security.AccessController;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.Permissions;
 import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.internal.dynalink.support.LinkRequestImpl;
 import jdk.nashorn.internal.objects.NativeJava;
 
 @SuppressWarnings("javadoc")
 public final class JavaAdapterFactory {
+    private static final ProtectionDomain MINIMAL_PERMISSION_DOMAIN = createMinimalPermissionDomain();
+
     // context with permissions needs for AdapterInfo creation
     private static final AccessControlContext CREATE_ADAPTER_INFO_ACC_CTXT =
         ClassAndLoader.createPermAccCtxt("createClassLoader", "getClassLoader",
      * @param classOverrides a JavaScript object with functions serving as the class-level overrides and
      * implementations. These overrides are defined for all instances of the class, and can be further overridden on a
      * per-instance basis by passing additional objects in the constructor.
+     * @param lookup the lookup object identifying the caller class. The generated adapter class will have the
+     * protection domain of the caller class iff the lookup object is full-strength, otherwise it will be completely
+     * unprivileged.
      * @return an adapter class. See this class' documentation for details on the generated adapter class.
      * @throws ECMAException with a TypeError if the adapter class can not be generated because the original class is
      * final, non-public, or has no public or protected constructors.
      */
-    public static StaticClass getAdapterClassFor(final Class<?>[] types, ScriptObject classOverrides) {
+    public static StaticClass getAdapterClassFor(final Class<?>[] types, ScriptObject classOverrides, final MethodHandles.Lookup lookup) {
+        return getAdapterClassFor(types, classOverrides, getProtectionDomain(lookup));
+    }
+
+    private static StaticClass getAdapterClassFor(final Class<?>[] types, ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
         assert types != null && types.length > 0;
         final SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             for (Class<?> type : types) {
                 // check for restricted package access
                 Context.checkPackageAccess(type);
+                // check for classes, interfaces in reflection
+                ReflectionCheckLinker.checkReflectionAccess(type, true);
             }
         }
-        return getAdapterInfo(types).getAdapterClassFor(classOverrides);
+        return getAdapterInfo(types).getAdapterClass(classOverrides, protectionDomain);
+    }
+
+    private static ProtectionDomain getProtectionDomain(final MethodHandles.Lookup lookup) {
+        if((lookup.lookupModes() & Lookup.PRIVATE) == 0) {
+            return MINIMAL_PERMISSION_DOMAIN;
+        }
+        return getProtectionDomain(lookup.lookupClass());
+    }
+
+    private static ProtectionDomain getProtectionDomain(final Class<?> clazz) {
+        return AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() {
+            @Override
+            public ProtectionDomain run() {
+                return clazz.getProtectionDomain();
+            }
+        });
     }
 
     /**
      * @return the constructor method handle.
      * @throws Exception if anything goes wrong
      */
-    public static MethodHandle getConstructor(final Class<?> sourceType, final Class<?> targetType) throws Exception {
-        final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null);
+    public static MethodHandle getConstructor(final Class<?> sourceType, final Class<?> targetType, final MethodHandles.Lookup lookup) throws Exception {
+        final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null, lookup);
         return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(
-                NashornCallSiteDescriptor.get(MethodHandles.publicLookup(),  "dyn:new",
+                NashornCallSiteDescriptor.get(lookup, "dyn:new",
                         MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false,
                         adapterClass, null)).getInvocation(), adapterClass);
     }
         private static final ClassAndLoader SCRIPT_OBJECT_LOADER = new ClassAndLoader(ScriptObject.class, true);
 
         private final ClassLoader commonLoader;
-        private final JavaAdapterClassLoader adapterGenerator;
-        // Cacheable adapter class that is shared by all adapter instances that don't have class overrides, only
-        // instance overrides.
-        final StaticClass instanceAdapterClass;
+        // TODO: soft reference the JavaAdapterClassLoader objects. They can be recreated when needed.
+        private final JavaAdapterClassLoader classAdapterGenerator;
+        private final JavaAdapterClassLoader instanceAdapterGenerator;
+        private final Map<CodeSource, StaticClass> instanceAdapters = new ConcurrentHashMap<>();
         final boolean autoConvertibleFromFunction;
         final AdaptationResult adaptationResult;
 
             this.commonLoader = findCommonLoader(definingLoader);
             final JavaAdapterBytecodeGenerator gen = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, false);
             this.autoConvertibleFromFunction = gen.isAutoConvertibleFromFunction();
-            final JavaAdapterClassLoader jacl = gen.createAdapterClassLoader();
-            this.instanceAdapterClass = jacl.generateClass(commonLoader);
-            // loaded Class - no need to keep class bytes around
-            jacl.clearClassBytes();
-            this.adapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader();
+            instanceAdapterGenerator = gen.createAdapterClassLoader();
+            this.classAdapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader();
             this.adaptationResult = AdaptationResult.SUCCESSFUL_RESULT;
         }
 
 
         AdapterInfo(final AdaptationResult adaptationResult) {
             this.commonLoader = null;
-            this.adapterGenerator = null;
-            this.instanceAdapterClass = null;
+            this.classAdapterGenerator = null;
+            this.instanceAdapterGenerator = null;
             this.autoConvertibleFromFunction = false;
             this.adaptationResult = adaptationResult;
         }
 
-        StaticClass getAdapterClassFor(ScriptObject classOverrides) {
+        StaticClass getAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
             if(adaptationResult.getOutcome() != AdaptationResult.Outcome.SUCCESS) {
                 throw adaptationResult.typeError();
             }
-            if(classOverrides == null) {
+            return classOverrides == null ? getInstanceAdapterClass(protectionDomain) :
+                getClassAdapterClass(classOverrides, protectionDomain);
+        }
+
+        private StaticClass getInstanceAdapterClass(final ProtectionDomain protectionDomain) {
+            CodeSource codeSource = protectionDomain.getCodeSource();
+            if(codeSource == null) {
+                codeSource = MINIMAL_PERMISSION_DOMAIN.getCodeSource();
+            }
+            StaticClass instanceAdapterClass = instanceAdapters.get(codeSource);
+            if(instanceAdapterClass != null) {
                 return instanceAdapterClass;
             }
+            // Any "unknown source" code source will default to no permission domain.
+            final ProtectionDomain effectiveDomain = codeSource.equals(MINIMAL_PERMISSION_DOMAIN.getCodeSource()) ?
+                    MINIMAL_PERMISSION_DOMAIN : protectionDomain;
+
+            instanceAdapterClass = instanceAdapterGenerator.generateClass(commonLoader, effectiveDomain);
+            final StaticClass existing = instanceAdapters.putIfAbsent(codeSource, instanceAdapterClass);
+            return existing == null ? instanceAdapterClass : existing;
+        }
+
+        private StaticClass getClassAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) {
             JavaAdapterServices.setClassOverrides(classOverrides);
             try {
-                return adapterGenerator.generateClass(commonLoader);
+                return classAdapterGenerator.generateClass(commonLoader, protectionDomain);
             } finally {
                 JavaAdapterServices.setClassOverrides(null);
             }
             throw new AdaptationException(AdaptationResult.Outcome.ERROR_NO_COMMON_LOADER, classAndLoader.getRepresentativeClass().getCanonicalName());
         }
     }
+
+    private static ProtectionDomain createMinimalPermissionDomain() {
+        // Generated classes need to have at least the permission to access Nashorn runtime and runtime.linker packages.
+        final Permissions permissions = new Permissions();
+        permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime"));
+        permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime.linker"));
+        return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions);
+    }
 }

src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java

 
 package jdk.nashorn.internal.runtime.linker;
 
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
+import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.security.AccessController;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.Permissions;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.SecureClassLoader;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
  */
 public final class JavaAdapterServices {
     private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>();
+    private static final MethodHandle NO_PERMISSIONS_INVOKER = createNoPermissionsInvoker();
 
     private JavaAdapterServices() {
     }
      */
     public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type) {
         // JS "this" will be global object or undefined depending on if 'fn' is strict or not
-        return adaptHandle(fn.getBoundInvokeHandle(fn.isStrict()? ScriptRuntime.UNDEFINED : Context.getGlobal()), type);
+        return bindAndAdaptHandle(fn, fn.isStrict()? ScriptRuntime.UNDEFINED : Context.getGlobal(), type);
     }
 
     /**
 
         final Object fnObj = sobj.get(name);
         if (fnObj instanceof ScriptFunction) {
-            return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type);
+            return bindAndAdaptHandle((ScriptFunction)fnObj, sobj, type);
         } else if(fnObj == null || fnObj instanceof Undefined) {
             return null;
         } else {
         return overrides;
     }
 
+    /**
+     * Takes a method handle and an argument to it, and invokes the method handle passing it the argument. Basically
+     * equivalent to {@code method.invokeExact(arg)}, except that the method handle will be invoked in a protection
+     * domain with absolutely no permissions.
+     * @param method the method handle to invoke. The handle must have the exact type of {@code void(Object)}.
+     * @param arg the argument to pass to the handle.
+     * @throws Throwable if anything goes wrong.
+     */
+    public static void invokeNoPermissions(final MethodHandle method, final Object arg) throws Throwable {
+        NO_PERMISSIONS_INVOKER.invokeExact(method, arg);
+    }
+
     static void setClassOverrides(ScriptObject overrides) {
         classOverrides.set(overrides);
     }
 
-    private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type) {
-        return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, false), type);
+    private static MethodHandle bindAndAdaptHandle(final ScriptFunction fn, final Object self, final MethodType type) {
+        return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(fn.getBoundInvokeHandle(self), type, false), type);
+    }
+
+    private static MethodHandle createNoPermissionsInvoker() {
+        final String className = "NoPermissionsInvoker";
+
+        final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+        cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, className, null, "java/lang/Object", null);
+        final Type objectType = Type.getType(Object.class);
+        final Type methodHandleType = Type.getType(MethodHandle.class);
+        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invoke",
+                Type.getMethodDescriptor(Type.VOID_TYPE, methodHandleType, objectType), null, null));
+        mv.visitCode();
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitVarInsn(ALOAD, 1);
+        mv.invokevirtual(methodHandleType.getInternalName(), "invokeExact", Type.getMethodDescriptor(
+                Type.VOID_TYPE, objectType), false);
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+        cw.visitEnd();
+        final byte[] bytes = cw.toByteArray();
+
+        final ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+            @Override
+            public ClassLoader run() {
+                return new SecureClassLoader(null) {
+                    @Override
+                    protected Class<?> findClass(String name) throws ClassNotFoundException {
+                        if(name.equals(className)) {
+                            return defineClass(name, bytes, 0, bytes.length, new ProtectionDomain(
+                                    new CodeSource(null, (CodeSigner[])null), new Permissions()));
+                        }
+                        throw new ClassNotFoundException(name);
+                    }
+                };
+            }
+        });
+
+        try {
+            return MethodHandles.lookup().findStatic(Class.forName(className, true, loader), "invoke",
+                    MethodType.methodType(void.class, MethodHandle.class, Object.class));
+        } catch(ReflectiveOperationException e) {
+            throw new AssertionError(e.getMessage(), e);
+        }
     }
 }

src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java

 
 package jdk.nashorn.internal.runtime.linker;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.HashMap;
 import java.util.Map;
-import java.util.HashMap;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.beans.BeansLinker;
 import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.GuardedTypeConversion;
 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
 import jdk.internal.dynalink.linker.LinkRequest;
     }
 
     @Override
-    public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
+    public GuardedTypeConversion convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
         final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType);
-        return gi == null ? null : gi.asType(MH.type(targetType, sourceType));
+        return gi == null ? null : new GuardedTypeConversion(gi.asType(MH.type(targetType, sourceType)), true);
     }
 
     /**

src/jdk/nashorn/internal/runtime/linker/NashornLinker.java

 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Deque;
 import java.util.List;
 import java.util.Map;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.ConversionComparator;
 import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.GuardedTypeConversion;
 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
 import jdk.internal.dynalink.support.Guards;
+import jdk.internal.dynalink.support.LinkerServicesImpl;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
 import jdk.nashorn.api.scripting.ScriptUtils;
 import jdk.nashorn.internal.objects.NativeArray;
-import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
     }
 
     @Override
-    public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
-        final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType);
-        return gi == null ? null : gi.asType(MH.type(targetType, sourceType));
+    public GuardedTypeConversion convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
+        GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType);
+        if(gi != null) {
+            return new GuardedTypeConversion(gi.asType(MH.type(targetType, sourceType)), true);
+        }
+        gi = getSamTypeConverter(sourceType, targetType);
+        if(gi != null) {
+            return new GuardedTypeConversion(gi.asType(MH.type(targetType, sourceType)), false);
+        }
+        return null;
     }
 
     /**
             return arrayConverter;
         }
 
-        final GuardedInvocation mirrorConverter = getMirrorConverter(sourceType, targetType);
-        if(mirrorConverter != null) {
-            return mirrorConverter;
-        }
-
-        return getSamTypeConverter(sourceType, targetType);
+        return getMirrorConverter(sourceType, targetType);
     }
 
     /**
         final boolean isSourceTypeGeneric = sourceType.isAssignableFrom(ScriptFunction.class);
 
         if ((isSourceTypeGeneric || ScriptFunction.class.isAssignableFrom(sourceType)) && isAutoConvertibleFromFunction(targetType)) {
-            final MethodHandle ctor = JavaAdapterFactory.getConstructor(ScriptFunction.class, targetType);
+            final MethodHandle ctor = JavaAdapterFactory.getConstructor(ScriptFunction.class, targetType, getCurrentLookup());
             assert ctor != null; // if isAutoConvertibleFromFunction() returned true, then ctor must exist.
             return new GuardedInvocation(ctor, isSourceTypeGeneric ? IS_SCRIPT_FUNCTION : null);
         }
         return null;
     }
 
+    private static Lookup getCurrentLookup() {
+        final LinkRequest currentRequest = AccessController.doPrivileged(new PrivilegedAction<LinkRequest>() {
+            @Override
+            public LinkRequest run() {
+                return LinkerServicesImpl.getCurrentLinkRequest();
+            }
+        });
+        return currentRequest == null ? MethodHandles.publicLookup() : currentRequest.getCallSiteDescriptor().getLookup();
+    }
+
     /**
      * Returns a guarded invocation that converts from a source type that is NativeArray to a Java array or List or
      * Deque type.

src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java

 import java.lang.invoke.MethodHandles;
 import jdk.internal.dynalink.linker.ConversionComparator;
 import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.GuardedTypeConversion;
 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.linker.LinkerServices;
      * @return a conditional converter from source to target type
      */
     @Override
-    public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) {
+    public GuardedTypeConversion convertToType(final Class<?> sourceType, final Class<?> targetType) {
         final MethodHandle mh = JavaArgumentConverters.getConverter(targetType);
         if (mh == null) {
             return null;
         }
 
-        return new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : GUARD_PRIMITIVE).asType(mh.type().changeParameterType(0, sourceType));
+        return new GuardedTypeConversion(new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : GUARD_PRIMITIVE).asType(mh.type().changeParameterType(0, sourceType)), true);
     }
 
     /**

src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java

             return null;
         }
         final Class<?> receiverClass = ((StaticClass) self).getRepresentedClass();
-        Bootstrap.checkReflectionAccess(receiverClass);
+        Bootstrap.checkReflectionAccess(receiverClass, true);
         final CallSiteDescriptor desc = request.getCallSiteDescriptor();
         // We intercept "new" on StaticClass instances to provide additional capabilities
         if ("new".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
             if (NashornLinker.isAbstractClass(receiverClass)) {
                 // Change this link request into a link request on the adapter class.
                 final Object[] args = request.getArguments();
-                args[0] = JavaAdapterFactory.getAdapterClassFor(new Class<?>[] { receiverClass }, null);
+                args[0] = JavaAdapterFactory.getAdapterClassFor(new Class<?>[] { receiverClass }, null,
+                        linkRequest.getCallSiteDescriptor().getLookup());
                 final LinkRequest adapterRequest = request.replaceArguments(request.getCallSiteDescriptor(), args);
                 final GuardedInvocation gi = checkNullConstructor(delegate(linkerServices, adapterRequest), receiverClass);
                 // Finally, modify the guard to test for the original abstract class.

src/jdk/nashorn/internal/runtime/linker/ReflectionCheckLinker.java

 package jdk.nashorn.internal.runtime.linker;
 
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
         if (type == Class.class || ClassLoader.class.isAssignableFrom(type)) {
             return true;
         }
+
         final String name = type.getName();
         return name.startsWith("java.lang.reflect.") || name.startsWith("java.lang.invoke.");
     }
         return null;
     }
 
-    static void checkReflectionAccess(Class<?> clazz) {
+    private static boolean isReflectiveCheckNeeded(final Class<?> type, final boolean isStatic) {
+         // special handling for Proxy subclasses
+         if (Proxy.class.isAssignableFrom(type)) {
+            if (Proxy.isProxyClass(type)) {
+                // real Proxy class - filter only static access
+                return isStatic;
+            }
+
+            // fake Proxy subclass - filter it always!
+            return true;
+        }
+
+        // check for any other reflective Class
+        return isReflectionClass(type);
+    }
+
+    static void checkReflectionAccess(final Class<?> clazz, final boolean isStatic) {
         final SecurityManager sm = System.getSecurityManager();
-        if (sm != null && isReflectionClass(clazz)) {
+        if (sm != null && isReflectiveCheckNeeded(clazz, isStatic)) {
             checkReflectionPermission(sm);
         }
     }
                 if(CallSiteDescriptorFactory.tokenizeOperators(desc).contains("getProp")) {
                     if ("static".equals(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND))) {
                         if (Context.isAccessibleClass((Class<?>)self) && !isReflectionClass((Class<?>)self)) {
+
                             // If "getProp:static" passes access checks, allow access.