changeset 742:66b98808fdff

8032681: Issues with Nashorn Reviewed-by: ahgross, jlaskey, sundar
author attila
date Thu, 30 Jan 2014 20:13:27 +0100
parents 6895d234836c
children 832f89ff25d9 22420177ec9d 17c5f77518b1
files src/jdk/internal/dynalink/linker/GuardedTypeConversion.java src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java src/jdk/internal/dynalink/support/LinkerServicesImpl.java src/jdk/internal/dynalink/support/TypeConverterFactory.java src/jdk/nashorn/api/scripting/NashornScriptEngine.java src/jdk/nashorn/internal/objects/NativeJava.java src/jdk/nashorn/internal/objects/NativeJavaImporter.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/NativeJavaPackage.java src/jdk/nashorn/internal/runtime/ScriptFunction.java src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java src/jdk/nashorn/internal/runtime/linker/NashornLinker.java src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java src/jdk/nashorn/internal/runtime/resources/Messages.properties test/script/basic/JDK-8014647.js test/script/basic/JDK-8014647.js.EXPECTED test/script/basic/javaclassoverrides.js test/script/basic/javaclassoverrides.js.EXPECTED test/script/sandbox/javaextend.js test/script/sandbox/javaextend.js.EXPECTED test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java
diffstat 30 files changed, 729 insertions(+), 246 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java	Thu Jan 30 20:13:27 2014 +0100
@@ -0,0 +1,102 @@
+/*
+ * 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;
+    }
+}
--- a/src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java	Thu Jan 30 20:13:27 2014 +0100
@@ -96,19 +96,19 @@
  */
 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;
 }
--- a/src/jdk/internal/dynalink/support/LinkerServicesImpl.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/internal/dynalink/support/LinkerServicesImpl.java	Thu Jan 30 20:13:27 2014 +0100
@@ -98,6 +98,9 @@
  */
 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;
 
@@ -135,6 +138,26 @@
 
     @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();
     }
 }
--- a/src/jdk/internal/dynalink/support/TypeConverterFactory.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/internal/dynalink/support/TypeConverterFactory.java	Thu Jan 30 20:13:27 2014 +0100
@@ -94,6 +94,7 @@
 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;
 
@@ -134,8 +135,8 @@
                 @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;
                         }
                     }
@@ -145,6 +146,24 @@
         }
     };
 
+    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
@@ -253,7 +272,7 @@
      * @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();
     }
 
     /**
@@ -294,9 +313,21 @@
         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);
     }
 
     /**
@@ -309,22 +340,44 @@
      * @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;
+        }
+    }
 }
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Thu Jan 30 20:13:27 2014 +0100
@@ -32,6 +32,7 @@
 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;
@@ -104,7 +105,7 @@
     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";
@@ -355,7 +356,8 @@
                 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);
--- a/src/jdk/nashorn/internal/objects/NativeJava.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/objects/NativeJava.java	Thu Jan 30 20:13:27 2014 +0100
@@ -28,6 +28,7 @@
 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;
@@ -463,12 +464,14 @@
      * </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
@@ -486,15 +489,18 @@
      * 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
@@ -541,7 +547,18 @@
         } 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);
     }
 
     /**
--- a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java	Thu Jan 30 20:13:27 2014 +0100
@@ -33,6 +33,7 @@
 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;
@@ -161,8 +162,9 @@
             } 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
                 }
--- a/src/jdk/nashorn/internal/runtime/Context.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Thu Jan 30 20:13:27 2014 +0100
@@ -37,7 +37,6 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Modifier;
-import java.util.concurrent.atomic.AtomicLong;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.security.AccessControlContext;
@@ -48,7 +47,7 @@
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
 import java.util.Map;
-
+import java.util.concurrent.atomic.AtomicLong;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
@@ -652,6 +651,19 @@
     }
 
     /**
+     * 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
--- a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Thu Jan 30 20:13:27 2014 +0100
@@ -85,6 +85,8 @@
      */
     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;
     }
 
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Thu Jan 30 20:13:27 2014 +0100
@@ -26,14 +26,13 @@
 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;
@@ -524,7 +523,11 @@
             }
         } 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);
--- a/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java	Thu Jan 30 20:13:27 2014 +0100
@@ -47,7 +47,8 @@
         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, "");
--- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Thu Jan 30 20:13:27 2014 +0100
@@ -32,6 +32,7 @@
 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;
@@ -79,7 +80,7 @@
     }
 
     @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;
@@ -90,7 +91,7 @@
             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);
     }
 
 
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java	Thu Jan 30 20:13:27 2014 +0100
@@ -59,6 +59,7 @@
 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;
@@ -66,21 +67,23 @@
 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
@@ -89,16 +92,17 @@
  * </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>
@@ -111,16 +115,20 @@
  * 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);
@@ -171,7 +179,6 @@
     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$";
@@ -199,6 +206,7 @@
     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;
 
@@ -207,8 +215,8 @@
      * @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,
@@ -230,8 +238,7 @@
         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);
@@ -244,17 +251,16 @@
         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() {
@@ -305,11 +311,9 @@
     }
 
     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();
         }
     }
 
@@ -337,7 +341,7 @@
                 } 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);
@@ -351,15 +355,15 @@
             mv.aconst(mi.getName());
             mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
             mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR, false);
-            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);
     }
@@ -390,21 +394,21 @@
             // 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);
         }
     }
 
@@ -430,7 +434,7 @@
     }
 
     /**
-     * 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
@@ -498,7 +502,7 @@
                 mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
                 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor, false);
             }
-            mv.putfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+            mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
         }
 
         // Assign "this.global = Context.getGlobal()"
@@ -536,8 +540,7 @@
     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));
@@ -567,25 +570,20 @@
             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() {
@@ -624,23 +622,19 @@
                 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())) {
@@ -654,25 +648,17 @@
             emitSuperCall(mv, method.getDeclaringClass(), 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
@@ -685,38 +671,39 @@
         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;
@@ -835,7 +822,7 @@
         endMethod(mv);
     }
 
-    private void emitSuperCall(final InstructionAdapter mv, final Class owner, final String name, final String methodDesc) {
+    private void emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) {
         mv.visitVarInsn(ALOAD, 0);
         int nextParam = 1;
         final Type methodType = Type.getMethodType(methodDesc);
@@ -853,6 +840,42 @@
         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) {
@@ -873,16 +896,32 @@
      * 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);
@@ -890,7 +929,7 @@
                         if (Modifier.isAbstract(m)) {
                             abstractMethodNames.add(mi.getName());
                         }
-                        mi.setIsCanonical(usedFieldNames, classOverride);
+                        mi.setIsCanonical(this);
                     }
                 }
             }
@@ -911,7 +950,7 @@
         }
     }
 
-    private void gatherMethods(final List<Class<?>> classes) {
+    private void gatherMethods(final List<Class<?>> classes) throws AdaptationException {
         for(final Class<?> c: classes) {
             gatherMethods(c);
         }
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java	Thu Jan 30 20:13:27 2014 +0100
@@ -27,10 +27,6 @@
 
 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;
@@ -45,11 +41,10 @@
  */
 @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('/', '.');
@@ -57,23 +52,18 @@
     }
 
     /**
-     * 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
                 }
@@ -88,7 +78,7 @@
     // 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();
 
@@ -112,21 +102,10 @@
             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);
-    }
 }
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java	Thu Jan 30 20:13:27 2014 +0100
@@ -29,17 +29,23 @@
 
 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;
@@ -70,6 +76,8 @@
 
 @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",
@@ -99,11 +107,18 @@
      * @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) {
@@ -114,7 +129,23 @@
                 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();
+            }
+        });
     }
 
     /**
@@ -129,10 +160,10 @@
      * @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);
     }
@@ -220,10 +251,10 @@
         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;
 
@@ -231,11 +262,8 @@
             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;
         }
 
@@ -245,22 +273,42 @@
 
         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);
             }
@@ -285,4 +333,12 @@
             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);
+    }
 }
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java	Thu Jan 30 20:13:27 2014 +0100
@@ -25,10 +25,28 @@
 
 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;
@@ -40,6 +58,7 @@
  */
 public final class JavaAdapterServices {
     private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>();
+    private static final MethodHandle NO_PERMISSIONS_INVOKER = createNoPermissionsInvoker();
 
     private JavaAdapterServices() {
     }
@@ -55,7 +74,7 @@
      */
     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);
     }
 
     /**
@@ -83,7 +102,7 @@
 
         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 {
@@ -103,11 +122,67 @@
         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);
+        }
     }
 }
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java	Thu Jan 30 20:13:27 2014 +0100
@@ -25,19 +25,20 @@
 
 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;
@@ -134,9 +135,9 @@
     }
 
     @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);
     }
 
     /**
--- a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java	Thu Jan 30 20:13:27 2014 +0100
@@ -29,7 +29,10 @@
 
 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;
@@ -37,16 +40,17 @@
 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;
@@ -100,9 +104,16 @@
     }
 
     @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;
     }
 
     /**
@@ -126,12 +137,7 @@
             return arrayConverter;
         }
 
-        final GuardedInvocation mirrorConverter = getMirrorConverter(sourceType, targetType);
-        if(mirrorConverter != null) {
-            return mirrorConverter;
-        }
-
-        return getSamTypeConverter(sourceType, targetType);
+        return getMirrorConverter(sourceType, targetType);
     }
 
     /**
@@ -150,13 +156,23 @@
         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.
--- a/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java	Thu Jan 30 20:13:27 2014 +0100
@@ -31,6 +31,7 @@
 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;
@@ -75,13 +76,13 @@
      * @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);
     }
 
     /**
--- a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java	Thu Jan 30 20:13:27 2014 +0100
@@ -76,7 +76,8 @@
             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.
--- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Thu Jan 30 19:28:40 2014 +0530
+++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Thu Jan 30 20:13:27 2014 +0100
@@ -130,6 +130,7 @@
 type.error.extend.ERROR_NO_ACCESSIBLE_CONSTRUCTOR=Can not extend class {0} as it has no public or protected constructors.
 type.error.extend.ERROR_MULTIPLE_SUPERCLASSES=Can not extend multiple classes {0}. At most one of the specified types can be a class, the rest must all be interfaces.
 type.error.extend.ERROR_NO_COMMON_LOADER=Can not find a common class loader for ScriptObject and {0}.
+type.error.extend.ERROR_FINAL_FINALIZER=Can not extend class because {0} has a final finalize method.
 type.error.no.constructor.matches.args=Can not construct {0} with the passed arguments; they do not match any of its constructor signatures.
 type.error.no.method.matches.args=Can not invoke method {0} with the passed arguments; they do not match any of its method signatures.
 type.error.method.not.constructor=Java method {0} can't be used as a constructor.
--- a/test/script/basic/JDK-8014647.js	Thu Jan 30 19:28:40 2014 +0530
+++ b/test/script/basic/JDK-8014647.js	Thu Jan 30 20:13:27 2014 +0100
@@ -32,9 +32,10 @@
 var RunnableImpl2 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 2!") })
 var r1 = new RunnableImpl1()
 var r2 = new RunnableImpl2()
-var r3 = new RunnableImpl2(function() { print("I'm runnable 3!") })
+var RunnableImpl3 = Java.extend(RunnableImpl2);
+var r3 = new RunnableImpl3({ run: function() { print("I'm runnable 3!") }})
 r1.run()
 r2.run()
 r3.run()
-print("r1.class === r2.class: " + (r1.class === r2.class))
-print("r2.class === r3.class: " + (r2.class === r3.class))
+print("r1.class !== r2.class: " + (r1.class !== r2.class))
+print("r2.class !== r3.class: " + (r2.class !== r3.class))
--- a/test/script/basic/JDK-8014647.js.EXPECTED	Thu Jan 30 19:28:40 2014 +0530
+++ b/test/script/basic/JDK-8014647.js.EXPECTED	Thu Jan 30 20:13:27 2014 +0100
@@ -1,5 +1,5 @@
 I'm runnable 1!
 I'm runnable 2!
 I'm runnable 3!
-r1.class === r2.class: false
-r2.class === r3.class: true
+r1.class !== r2.class: true
+r2.class !== r3.class: true
--- a/test/script/basic/javaclassoverrides.js	Thu Jan 30 19:28:40 2014 +0530
+++ b/test/script/basic/javaclassoverrides.js	Thu Jan 30 20:13:27 2014 +0100
@@ -46,7 +46,8 @@
 var r1 = new R1
 var r2 = new R2
 // Create one with an instance-override too
-var r3 = new R2(function() { print("r3.run() invoked") })
+var R3 = Java.extend(R2)
+var r3 = new R3({ run: function() { print("r3.run() invoked") }})
 
 // Run 'em - we're passing them through a Thread to make sure they indeed
 // are full-blown Runnables
@@ -60,9 +61,9 @@
 runInThread(r3)
 
 // Two class-override classes differ
-print("r1.class != r2.class: " + (r1.class != r2.class))
-// However, adding instance-overrides doesn't change the class
-print("r2.class == r3.class: " + (r2.class == r3.class))
+print("r1.class !== r2.class: " + (r1.class !== r2.class))
+// instance-override class also differs
+print("r2.class !== r3.class: " + (r2.class !== r3.class))
 
 function checkAbstract(r) {
     try {
@@ -77,10 +78,10 @@
 // overrides nor instance overrides are present
 var RAbstract = Java.extend(java.lang.Runnable, {})
 checkAbstract(new RAbstract()) // class override (empty)
-checkAbstract(new RAbstract() {}) // class+instance override (empty)
+checkAbstract(new (Java.extend(RAbstract))() {}) // class+instance override (empty)
 
 // Check we delegate to superclass if neither class
 // overrides nor instance overrides are present
 var ExtendsList = Java.extend(java.util.ArrayList, {})
 print("(new ExtendsList).size() = " + (new ExtendsList).size())
-print("(new ExtendsList(){}).size() = " + (new ExtendsList(){}).size())
\ No newline at end of file
+print("(new (Java.extend(ExtendsList)){}).size() = " + (new (Java.extend(ExtendsList)){}).size())
--- a/test/script/basic/javaclassoverrides.js.EXPECTED	Thu Jan 30 19:28:40 2014 +0530
+++ b/test/script/basic/javaclassoverrides.js.EXPECTED	Thu Jan 30 20:13:27 2014 +0100
@@ -1,9 +1,9 @@
 R1.run() invoked
 R2.run() invoked
 r3.run() invoked
-r1.class != r2.class: true
-r2.class == r3.class: true
+r1.class !== r2.class: true
+r2.class !== r3.class: true
 Got exception: java.lang.UnsupportedOperationException
 Got exception: java.lang.UnsupportedOperationException
 (new ExtendsList).size() = 0
-(new ExtendsList(){}).size() = 0
+(new (Java.extend(ExtendsList)){}).size() = 0
--- a/test/script/sandbox/javaextend.js	Thu Jan 30 19:28:40 2014 +0530
+++ b/test/script/sandbox/javaextend.js	Thu Jan 30 20:13:27 2014 +0100
@@ -51,6 +51,21 @@
     print(e)
 }
 
+// Can't extend a class with explicit non-overridable finalizer
+try {
+    Java.extend(model("ClassWithFinalFinalizer"))
+} catch(e) {
+    print(e)
+}
+
+// Can't extend a class with inherited non-overridable finalizer
+try {
+    Java.extend(model("ClassWithInheritedFinalFinalizer"))
+} catch(e) {
+    print(e)
+}
+
+
 // Can't extend two classes
 try {
     Java.extend(java.lang.Thread,java.lang.Number)
--- a/test/script/sandbox/javaextend.js.EXPECTED	Thu Jan 30 19:28:40 2014 +0530
+++ b/test/script/sandbox/javaextend.js.EXPECTED	Thu Jan 30 20:13:27 2014 +0100
@@ -1,6 +1,8 @@
 TypeError: Can not extend final class jdk.nashorn.test.models.FinalClass.
 TypeError: Can not extend class jdk.nashorn.test.models.NoAccessibleConstructorClass as it has no public or protected constructors.
 TypeError: Can not extend/implement non-public class/interface jdk.nashorn.test.models.NonPublicClass.
+TypeError: Can not extend class because jdk.nashorn.test.models.ClassWithFinalFinalizer has a final finalize method.
+TypeError: Can not extend class because jdk.nashorn.test.models.ClassWithFinalFinalizer has a final finalize method.
 TypeError: Can not extend multiple classes java.lang.Number and java.lang.Thread. At most one of the specified types can be a class, the rest must all be interfaces.
 abcdabcd
 run-object
--- a/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java	Thu Jan 30 19:28:40 2014 +0530
+++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java	Thu Jan 30 20:13:27 2014 +0100
@@ -33,8 +33,8 @@
 import java.util.Objects;
 import javax.script.Invocable;
 import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
 import javax.script.ScriptException;
-import javax.script.ScriptEngineManager;
 import org.testng.annotations.Test;
 
 /**
@@ -130,6 +130,23 @@
         }
     }
 
+
+    @Test
+    public void securitySystemExitFromFinalizerThread() throws ScriptException {
+        if (System.getSecurityManager() == null) {
+            // pass vacuously
+            return;
+        }
+
+        final ScriptEngineManager m = new ScriptEngineManager();
+        final ScriptEngine e = m.getEngineByName("nashorn");
+        e.eval("var o = Java.extend(Java.type('javax.imageio.spi.ServiceRegistry'), { deregisterAll: this.exit.bind(null, 1234)});\n" +
+                "new o(new java.util.ArrayList().iterator())");
+        System.gc();
+        System.runFinalization();
+        // NOTE: this test just exits the VM if it fails.
+    }
+
     @Test
     public void securitySystemLoadLibrary() {
         if (System.getSecurityManager() == null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java	Thu Jan 30 20:13:27 2014 +0100
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package jdk.nashorn.test.models;
+
+public class ClassWithFinalFinalizer {
+    protected final void finalize() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java	Thu Jan 30 20:13:27 2014 +0100
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package jdk.nashorn.test.models;
+
+public class ClassWithInheritedFinalFinalizer extends ClassWithFinalFinalizer {
+}