8011544: Allow subclassing Java classes from script without creating instances
authorattila
Thu, 04 Apr 2013 15:53:26 +0200
changeset 16777 207a993adb9a
parent 16773 ed65c8a9ae7c
child 16778 55e38b535002
8011544: Allow subclassing Java classes from script without creating instances Reviewed-by: jlaskey, sundar
nashorn/src/jdk/nashorn/internal/objects/NativeJava.java
nashorn/src/jdk/nashorn/internal/runtime/linker/AdaptationException.java
nashorn/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java
nashorn/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterGeneratorBase.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java
nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties
nashorn/test/script/basic/javaclassoverrides.js
nashorn/test/script/basic/javaclassoverrides.js.EXPECTED
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java	Thu Apr 04 13:54:51 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java	Thu Apr 04 15:53:26 2013 +0200
@@ -394,22 +394,56 @@
      * </pre>
      * We can see several important concepts in the above example:
      * <ul>
-     * <li>Every Java class will have exactly one extender subclass in Nashorn - repeated invocations of {@code extend}
-     * for the same type 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 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>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>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
+     * argument to their constructor. Example:
+     * <pre>
+     * var Runnable = Java.type("java.lang.Runnable")
+     * var R1 = Java.extend(Runnable, {
+     *     run: function() {
+     *         print("R1.run() invoked!")
+     *     }
+     * })
+     * var r1 = new R1
+     * var t = new java.lang.Thread(r1)
+     * t.start()
+     * 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:
+     * <pre>
+     * var r2 = new R1(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.
+     * </li>
      * </ul>
      * @param self not used
      * @param types the original types. The caller must pass at least one Java type object of class {@link StaticClass}
      * representing either a public interface or a non-final public class with at least one public or protected
      * constructor. If more than one type is specified, at most one can be a class and the rest have to be interfaces.
-     * Invoking the method twice with exactly the same types in the same order will return the same adapter
-     * class, any reordering of types or even addition or removal of redundant types (i.e. interfaces that other types
-     * in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of
-     * interfaces) will result in a different adapter class, even though those adapter classes are functionally
-     * identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists.
+     * Invoking the method twice with exactly the same types in the same order - in absence of class-level overrides -
+     * will return the same adapter class, any reordering of types or even addition or removal of redundant types (i.e.
+     * interfaces that other types in the list already implement/extend, or {@code java.lang.Object} in a list of types
+     * consisting purely of interfaces) will result in a different adapter class, even though those adapter classes are
+     * functionally identical; we deliberately don't want to incur the additional processing cost of canonicalizing type
+     * lists. As a special case, the last argument can be a {@code ScriptObject} instead of a type. In this case, a
+     * separate adapter class is generated - new one for each invocation - that will use the passed script object as its
+     * implementation for all instances. Instances of such adapter classes can then be created without passing another
+     * script object in the constructor, as the class has a class-level behavior defined by the script object. However,
+     * you can still pass a script object (or if it's a SAM type, a function) to the constructor to provide further
+     * instance-level overrides.
+     *
      * @return a new {@link StaticClass} that represents the adapter for the original types.
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
@@ -417,14 +451,27 @@
         if(types == null || types.length == 0) {
             throw typeError("extend.expects.at.least.one.argument");
         }
-        final Class<?>[] stypes = new Class<?>[types.length];
+        final int l = types.length;
+        final int typesLen;
+        final ScriptObject classOverrides;
+        if(types[l - 1] instanceof ScriptObject) {
+            classOverrides = (ScriptObject)types[l - 1];
+            typesLen = l - 1;
+            if(typesLen == 0) {
+                throw typeError("extend.expects.at.least.one.type.argument");
+            }
+        } else {
+            classOverrides = null;
+            typesLen = l;
+        }
+        final Class<?>[] stypes = new Class<?>[typesLen];
         try {
-            for(int i = 0; i < types.length; ++i) {
+            for(int i = 0; i < typesLen; ++i) {
                 stypes[i] = ((StaticClass)types[i]).getRepresentedClass();
             }
         } catch(final ClassCastException e) {
             throw typeError("extend.expects.java.types");
         }
-        return JavaAdapterFactory.getAdapterClassFor(stypes);
+        return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/AdaptationException.java	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,39 @@
+/*
+ * 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.internal.runtime.linker;
+
+@SuppressWarnings("serial")
+class AdaptationException extends Exception {
+    private final AdaptationResult adaptationResult;
+
+    AdaptationException(final AdaptationResult.Outcome outcome, final String classList) {
+        this.adaptationResult = new AdaptationResult(outcome, classList);
+    }
+
+    AdaptationResult getAdaptationResult() {
+        return adaptationResult;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,74 @@
+/*
+ * 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.internal.runtime.linker;
+
+import jdk.nashorn.internal.runtime.ECMAErrors;
+import jdk.nashorn.internal.runtime.ECMAException;
+
+/**
+ * A result of generating an adapter for a class. A tuple of an outcome and - in case of an error outcome - a list of
+ * classes that caused the error.
+ */
+class AdaptationResult {
+    /**
+     * Contains various outcomes for attempting to generate an adapter class. These are stored in AdapterInfo instances.
+     * We have a successful outcome (adapter class was generated) and four possible error outcomes: superclass is final,
+     * superclass is not public, superclass has no public or protected constructor, more than one superclass was
+     * specified. We don't throw exceptions when we try to generate the adapter, but rather just record these error
+     * conditions as they are still useful as partial outcomes, as Nashorn's linker can still successfully check whether
+     * the class can be autoconverted from a script function even when it is not possible to generate an adapter for it.
+     */
+    enum Outcome {
+        SUCCESS,
+        ERROR_FINAL_CLASS,
+        ERROR_NON_PUBLIC_CLASS,
+        ERROR_NO_ACCESSIBLE_CONSTRUCTOR,
+        ERROR_MULTIPLE_SUPERCLASSES,
+        ERROR_NO_COMMON_LOADER
+    }
+
+    static final AdaptationResult SUCCESSFUL_RESULT = new AdaptationResult(Outcome.SUCCESS, "");
+
+    private final Outcome outcome;
+    private final String classList;
+
+    AdaptationResult(final Outcome outcome, final String classList) {
+        this.outcome = outcome;
+        this.classList = classList;
+    }
+
+    Outcome getOutcome() {
+        return outcome;
+    }
+
+    String getClassList() {
+        return classList;
+    }
+
+    ECMAException typeError() {
+        return ECMAErrors.typeError("extend." + outcome, classList);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,188 @@
+/*
+ * 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.internal.runtime.linker;
+
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A tuple of a class loader and a single class representative of the classes that can be loaded through it. Its
+ * equals/hashCode is defined in terms of the identity of the class loader. The rationale for this class is that it
+ * couples a class loader with a random representative class coming from that loader - this representative class is then
+ * used to determine if one loader can see the other loader's classes.
+ */
+final class ClassAndLoader {
+    private final Class<?> representativeClass;
+    // Don't access this directly; most of the time, use getRetrievedLoader(), or if you know what you're doing,
+    // getLoader().
+    private ClassLoader loader;
+    // We have mild affinity against eagerly retrieving the loader, as we need to do it in a privileged block. For
+    // the most basic case of looking up an already-generated adapter info for a single type, we avoid it.
+    private boolean loaderRetrieved;
+
+    ClassAndLoader(final Class<?> representativeClass, final boolean retrieveLoader) {
+        this.representativeClass = representativeClass;
+        if(retrieveLoader) {
+            retrieveLoader();
+        }
+    }
+
+    Class<?> getRepresentativeClass() {
+        return representativeClass;
+    }
+
+    boolean canSee(ClassAndLoader other) {
+        try {
+            final Class<?> otherClass = other.getRepresentativeClass();
+            return Class.forName(otherClass.getName(), false, getLoader()) == otherClass;
+        } catch (final ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+    ClassLoader getLoader() {
+        if(!loaderRetrieved) {
+            retrieveLoader();
+        }
+        return getRetrievedLoader();
+    }
+
+    ClassLoader getRetrievedLoader() {
+        assert loaderRetrieved;
+        return loader;
+    }
+
+    private void retrieveLoader() {
+        loader = representativeClass.getClassLoader();
+        loaderRetrieved = true;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader();
+    }
+
+    @Override
+    public int hashCode() {
+        return System.identityHashCode(getRetrievedLoader());
+    }
+
+    /**
+     * Given a list of types that define the superclass/interfaces for an adapter class, returns a single type from the
+     * list that will be used to attach the adapter to its ClassValue. The first type in the array that is defined in a
+     * class loader that can also see all other types is returned. If there is no such loader, an exception is thrown.
+     * @param types the input types
+     * @return the first type from the array that is defined in a class loader that can also see all other types.
+     */
+    static ClassAndLoader getDefiningClassAndLoader(final Class<?>[] types) {
+        // Short circuit the cheap case
+        if(types.length == 1) {
+            return new ClassAndLoader(types[0], false);
+        }
+
+        return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() {
+            @Override
+            public ClassAndLoader run() {
+                return getDefiningClassAndLoaderPrivileged(types);
+            }
+        });
+    }
+
+    static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) {
+        final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types);
+
+        final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
+        if(maximumVisibilityLoaders.size() == 1) {
+            // Fortunate case - single maximally specific class loader; return its representative class.
+            return it.next();
+        }
+
+        // Ambiguity; throw an error.
+        assert maximumVisibilityLoaders.size() > 1; // basically, can't be zero
+        final StringBuilder b = new StringBuilder();
+        b.append(it.next().getRepresentativeClass().getCanonicalName());
+        while(it.hasNext()) {
+            b.append(", ").append(it.next().getRepresentativeClass().getCanonicalName());
+        }
+        throw typeError("extend.ambiguous.defining.class", b.toString());
+    }
+
+    /**
+     * Given an array of types, return a subset of their class loaders that are maximal according to the
+     * "can see other loaders' classes" relation, which is presumed to be a partial ordering.
+     * @param types types
+     * @return a collection of maximum visibility class loaders. It is guaranteed to have at least one element.
+     */
+    private static Collection<ClassAndLoader> getMaximumVisibilityLoaders(final Class<?>[] types) {
+        final List<ClassAndLoader> maximumVisibilityLoaders = new LinkedList<>();
+        outer:  for(final ClassAndLoader maxCandidate: getClassLoadersForTypes(types)) {
+            final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
+            while(it.hasNext()) {
+                final ClassAndLoader existingMax = it.next();
+                final boolean candidateSeesExisting = maxCandidate.canSee(existingMax);
+                final boolean exitingSeesCandidate = existingMax.canSee(maxCandidate);
+                if(candidateSeesExisting) {
+                    if(!exitingSeesCandidate) {
+                        // The candidate sees the the existing maximum, so drop the existing one as it's no longer maximal.
+                        it.remove();
+                    }
+                    // NOTE: there's also the anomalous case where both loaders see each other. Not sure what to do
+                    // about that one, as two distinct class loaders both seeing each other's classes is weird and
+                    // violates the assumption that the relation "sees others' classes" is a partial ordering. We'll
+                    // just not do anything, and treat them as incomparable; hopefully some later class loader that
+                    // comes along can eliminate both of them, if it can not, we'll end up with ambiguity anyway and
+                    // throw an error at the end.
+                } else if(exitingSeesCandidate) {
+                    // Existing sees the candidate, so drop the candidate.
+                    continue outer;
+                }
+            }
+            // If we get here, no existing maximum visibility loader could see the candidate, so the candidate is a new
+            // maximum.
+            maximumVisibilityLoaders.add(maxCandidate);
+        }
+        return maximumVisibilityLoaders;
+    }
+
+    private static Collection<ClassAndLoader> getClassLoadersForTypes(final Class<?>[] types) {
+        final Map<ClassAndLoader, ClassAndLoader> classesAndLoaders = new LinkedHashMap<>();
+        for(final Class<?> c: types) {
+            final ClassAndLoader cl = new ClassAndLoader(c, true);
+            if(!classesAndLoaders.containsKey(cl)) {
+                classesAndLoaders.put(cl, cl);
+            }
+        }
+        return classesAndLoaders.keySet();
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,884 @@
+/*
+ * 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.internal.runtime.linker;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
+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.ACC_VARARGS;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
+import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
+import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
+import static jdk.internal.org.objectweb.asm.Opcodes.POP;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+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;
+
+/**
+ * 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
+ * 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.
+ * {@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
+ * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be
+ * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too.
+ * </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 {@code null} as its "this".
+ * </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.
+ * </li>
+ * </ul>
+ * </p><p>
+ * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect
+ * to coerce the JavaScript function return value to the expected Java return type.
+ * </p><p>
+ * Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be
+ * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The
+ * reason we are passing the additional argument at the end of the argument list instead at the front is that the
+ * 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.
+ */
+final class JavaAdapterBytecodeGenerator extends JavaAdapterGeneratorBase {
+    private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class);
+    private static final Type STRING_TYPE = Type.getType(String.class);
+    private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
+    private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
+    private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
+            OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
+    private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
+            SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
+    private static final String GET_CLASS_INITIALIZER_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
+    private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class);
+    private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
+    private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class);
+
+    private static final String SERVICES_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterServices.class);
+    private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName();
+    private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class);
+    private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName();
+    private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName();
+
+    private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor();
+    private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
+    private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Class.class));
+
+    // Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because
+    // it's a java.* package.
+    private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/internal/javaadapters/";
+    // Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package.
+    private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter";
+    private static final String JAVA_PACKAGE_PREFIX = "java/";
+    private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 238; //255 - 17; 17 is the maximum possible length for the global setter inner class suffix
+
+    private static final String CLASS_INIT = "<clinit>";
+    private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal";
+
+    /**
+     * Collection of methods we never override: Object.clone(), Object.finalize().
+     */
+    private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods();
+
+    private static final Random random = new SecureRandom();
+
+    // This is the superclass for our generated adapter.
+    private final Class<?> superClass;
+    // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class
+    // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the
+    // Nashorn classes.
+    private final ClassLoader commonLoader;
+    // Is this a generator for the version of the class that can have overrides on the class level?
+    private final boolean classOverride;
+    // Binary name of the superClass
+    private final String superClassName;
+    // Binary name of the generated class.
+    private final String generatedClassName;
+    // Binary name of the PrivilegedAction inner class that is used to
+    private final String globalSetterClassName;
+    private final Set<String> usedFieldNames = new HashSet<>();
+    private final Set<String> abstractMethodNames = new HashSet<>();
+    private final String samName;
+    private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
+    private final Set<MethodInfo> methodInfos = new HashSet<>();
+    private boolean autoConvertibleFromFunction = false;
+
+    private final ClassWriter cw;
+
+    /**
+     * Creates a generator for the bytecode for the adapter for the specified superclass and interfaces.
+     * @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.
+     * @throws AdaptationException if the adapter can not be generated for some reason.
+     */
+    JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces,
+            final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException {
+        assert superClass != null && !superClass.isInterface();
+        assert interfaces != null;
+
+        this.superClass = superClass;
+        this.classOverride = classOverride;
+        this.commonLoader = commonLoader;
+        cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
+            @Override
+            protected String getCommonSuperClass(final String type1, final String type2) {
+                // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class
+                // loader to find the common superclass of two types when needed.
+                return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2);
+            }
+        };
+        superClassName = Type.getInternalName(superClass);
+        generatedClassName = getGeneratedClassName(superClass, interfaces);
+
+        // Randomize the name of the privileged global setter, to make it non-feasible to find.
+        final long l;
+        synchronized(random) {
+            l = random.nextLong();
+        }
+
+        // NOTE: they way this class name is calculated affects the value of MAX_GENERATED_TYPE_NAME_LENGTH constant. If
+        // you change the calculation of globalSetterClassName, adjust the constant too.
+        globalSetterClassName = generatedClassName.concat("$" + Long.toHexString(l & Long.MAX_VALUE));
+        cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
+
+        generateGlobalFields();
+
+        gatherMethods(superClass);
+        gatherMethods(interfaces);
+        samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null;
+        generateHandleFields();
+        if(classOverride) {
+            generateClassInit();
+        }
+        generateConstructors();
+        generateMethods();
+        // }
+        cw.visitEnd();
+    }
+
+    private void generateGlobalFields() {
+        cw.visitField(ACC_PRIVATE | ACC_FINAL, 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() {
+        return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray(), globalSetterClassName);
+    }
+
+    boolean isAutoConvertibleFromFunction() {
+        return autoConvertibleFromFunction;
+    }
+
+    private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) {
+        // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're
+        // just implementing interfaces or extending Object), then the first implemented interface or Object.
+        final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType;
+        final Package pkg = namingType.getPackage();
+        final String namingTypeName = Type.getInternalName(namingType);
+        final StringBuilder buf = new StringBuilder();
+        if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) {
+            // Can't define new classes in java.* packages
+            buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName);
+        } else {
+            buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX);
+        }
+        final Iterator<Class<?>> it = interfaces.iterator();
+        if(superType == Object.class && it.hasNext()) {
+            it.next(); // Skip first interface, it was used to primarily name the adapter
+        }
+        // Append interface names to the adapter name
+        while(it.hasNext()) {
+            buf.append("$$").append(it.next().getSimpleName());
+        }
+        return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length()));
+    }
+
+    /**
+     * Given a list of class objects, return an array with their binary names. Used to generate the array of interface
+     * names to implement.
+     * @param classes the classes
+     * @return an array of names
+     */
+    private static String[] getInternalTypeNames(final List<Class<?>> classes) {
+        final int interfaceCount = classes.size();
+        final String[] interfaceNames = new String[interfaceCount];
+        for(int i = 0; i < interfaceCount; ++i) {
+            interfaceNames[i] = Type.getInternalName(classes.get(i));
+        }
+        return interfaceNames;
+    }
+
+    private void generateHandleFields() {
+        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();
+            }
+        }
+    }
+
+    private void generateClassInit() {
+        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT,
+                Type.getMethodDescriptor(Type.VOID_TYPE), null, null));
+
+        mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR);
+        // Assign MethodHandle fields through invoking getHandle()
+        for (final MethodInfo mi : methodInfos) {
+            mv.dup();
+            mv.aconst(mi.getName());
+            mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
+            mv.iconst(mi.method.isVarArgs() ? 1 : 0);
+            mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR);
+            mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+        }
+
+        // Assign "staticGlobal = Context.getGlobal()"
+        invokeGetGlobalWithNullCheck(mv);
+        mv.putstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+
+        endInitMethod(mv);
+    }
+
+    private static void invokeGetGlobalWithNullCheck(final InstructionAdapter mv) {
+        invokeGetGlobal(mv);
+        mv.dup();
+        mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR); // check against null Context
+        mv.pop();
+    }
+
+    private void generateConstructors() throws AdaptationException {
+        boolean gotCtor = false;
+        for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) {
+            final int modifier = ctor.getModifiers();
+            if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
+                generateConstructors(ctor);
+                gotCtor = true;
+            }
+        }
+        if(!gotCtor) {
+            throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName());
+        }
+    }
+
+    private void generateConstructors(final Constructor<?> ctor) {
+        if(classOverride) {
+            // 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);
+        }
+
+        // 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 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);
+        }
+    }
+
+    private void generateDelegatingConstructor(final Constructor<?> ctor) {
+        final Type originalCtorType = Type.getType(ctor);
+        final Type[] argTypes = originalCtorType.getArgumentTypes();
+
+        // All constructors must be public, even if in the superclass they were protected.
+        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
+                Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null));
+
+        mv.visitCode();
+        // Invoke super constructor with the same arguments.
+        mv.visitVarInsn(ALOAD, 0);
+        int offset = 1; // First arg is at position 1, after this.
+        for (Type argType: argTypes) {
+            mv.load(offset, argType);
+            offset += argType.getSize();
+        }
+        mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
+
+        endInitMethod(mv);
+    }
+
+    /**
+     * Generates a constructor for the 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
+     * function itself, if that's what's passed). There is one method handle field in the adapter class for every method
+     * that can be implemented or overridden; the name of every field is same as the name of the method, with a number
+     * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke
+     * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType,
+     * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity
+     * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}.
+     * The constructor that takes a script function will only initialize the methods with the same name as the single
+     * abstract method. The constructor will also store the Nashorn global that was current at the constructor
+     * invocation time in a field named "global". The generated constructor will be public, regardless of whether the
+     * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the
+     * supertype constructor was.
+     * @param ctor the supertype constructor that is serving as the base for the generated constructor.
+     * @param fromFunction true if we're generating a constructor that initializes SAM types from a single
+     * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a
+     * ScriptObject passed to it.
+     */
+    private void generateOverridingConstructor(final Constructor<?> ctor, final boolean fromFunction) {
+        final Type originalCtorType = Type.getType(ctor);
+        final Type[] originalArgTypes = originalCtorType.getArgumentTypes();
+        final int argLen = originalArgTypes.length;
+        final Type[] newArgTypes = new Type[argLen + 1];
+
+        // Insert ScriptFunction|Object as the last argument to the constructor
+        final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE;
+        newArgTypes[argLen] = extraArgumentType;
+        System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
+
+        // All constructors must be public, even if in the superclass they were protected.
+        // Existing super constructor <init>(this, args...) triggers generating <init>(this, scriptObj, args...).
+        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
+                Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
+
+        mv.visitCode();
+        // First, invoke super constructor with original arguments. If the form of the constructor we're generating is
+        // <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...).
+        mv.visitVarInsn(ALOAD, 0);
+        final Class<?>[] argTypes = ctor.getParameterTypes();
+        int offset = 1; // First arg is at position 1, after this.
+        for (int i = 0; i < argLen; ++i) {
+            final Type argType = Type.getType(argTypes[i]);
+            mv.load(offset, argType);
+            offset += argType.getSize();
+        }
+        mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
+
+        // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method.
+        final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR;
+
+        // Assign MethodHandle fields through invoking getHandle()
+        for (final MethodInfo mi : methodInfos) {
+            mv.visitVarInsn(ALOAD, 0);
+            if (fromFunction && !mi.getName().equals(samName)) {
+                // Constructors initializing from a ScriptFunction only initialize methods with the SAM name.
+                // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This
+                // is a deliberate design choice. All other method handles are initialized to null.
+                mv.visitInsn(ACONST_NULL);
+            } else {
+                mv.visitVarInsn(ALOAD, offset);
+                if(!fromFunction) {
+                    mv.aconst(mi.getName());
+                }
+                mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
+                mv.iconst(mi.method.isVarArgs() ? 1 : 0);
+                mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor);
+            }
+            mv.putfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+        }
+
+        // Assign "this.global = Context.getGlobal()"
+        mv.visitVarInsn(ALOAD, 0);
+        invokeGetGlobalWithNullCheck(mv);
+        mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+
+        endInitMethod(mv);
+    }
+
+    private static void endInitMethod(final InstructionAdapter mv) {
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+    }
+
+    private static void invokeGetGlobal(final InstructionAdapter mv) {
+        mv.invokestatic(CONTEXT_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR);
+    }
+
+    private void invokeSetGlobal(final InstructionAdapter mv) {
+        mv.invokestatic(globalSetterClassName, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
+    }
+
+    /**
+     * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the
+     * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the
+     * method handle serving as the implementation of this method in adapter instances.
+     *
+     */
+    private static class MethodInfo {
+        private final Method method;
+        private final MethodType type;
+        private String methodHandleInstanceFieldName;
+        private String methodHandleClassFieldName;
+
+        private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException {
+            this(clazz.getDeclaredMethod(name, argTypes));
+        }
+
+        private MethodInfo(final Method method) {
+            this.method = method;
+            this.type   = MH.type(method.getReturnType(), method.getParameterTypes());
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            return obj instanceof MethodInfo && equals((MethodInfo)obj);
+        }
+
+        private boolean equals(final MethodInfo other) {
+            // Only method name and type are used for comparison; method handle field name is not.
+            return getName().equals(other.getName()) && type.equals(other.type);
+        }
+
+        String getName() {
+            return method.getName();
+        }
+
+        @Override
+        public int hashCode() {
+            return getName().hashCode() ^ type.hashCode();
+        }
+
+        void setIsCanonical(final Set<String> usedFieldNames, boolean classOverride) {
+            methodHandleInstanceFieldName = nextName(usedFieldNames);
+            if(classOverride) {
+                methodHandleClassFieldName = nextName(usedFieldNames);
+            }
+        }
+
+        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 void generateMethods() {
+        for(final MethodInfo mi: methodInfos) {
+            generateMethod(mi);
+        }
+    }
+
+    /**
+     * Generates a method in the adapter class that adapts a method from the original class. The generated methods will
+     * inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation
+     * for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an
+     * {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is
+     * invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the
+     * current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter
+     * instance, the creating global is set to be the current global. In this case, the previously current global is
+     * restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared
+     * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime
+     * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of
+     * the method; this is guaranteed by the way constructors of the adapter class obtain them using
+     * {@link #getHandle(Object, String, MethodType, boolean)}.
+     * @param mi the method info describing the method to be generated.
+     */
+    private void generateMethod(final MethodInfo mi) {
+        final Method method = mi.method;
+        final int mod = method.getModifiers();
+        final int access = ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
+        final Class<?>[] exceptions = method.getExceptionTypes();
+        final String[] exceptionNames = new String[exceptions.length];
+        for (int i = 0; i < exceptions.length; ++i) {
+            exceptionNames[i] = Type.getInternalName(exceptions[i]);
+        }
+        final MethodType type = mi.type;
+        final String methodDesc = type.toMethodDescriptorString();
+        final String name = mi.getName();
+
+        final Type asmType = Type.getMethodType(methodDesc);
+        final Type[] asmArgTypes = asmType.getArgumentTypes();
+
+        // Determine the first index for a local variable
+        int nextLocalVar = 1; // this
+        for(final Type t: asmArgTypes) {
+            nextLocalVar += t.getSize();
+        }
+
+        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(access, name, methodDesc, null,
+                exceptionNames));
+        mv.visitCode();
+
+        final Label instanceHandleDefined = new Label();
+        final Label classHandleDefined = 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);
+
+        if(classOverride) {
+            // See if we have the static handle
+            mv.getstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+            // stack: [classHandle]
+            jumpIfNonNullKeepOperand(mv, classHandleDefined);
+        }
+
+        // No handle is available, fall back to default behavior
+        if(Modifier.isAbstract(mod)) {
+            // If the super method is abstract, throw an exception
+            mv.anew(UNSUPPORTED_OPERATION_TYPE);
+            mv.dup();
+            mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR);
+            mv.athrow();
+        } else {
+            // If the super method is not abstract, delegate to it.
+            mv.visitVarInsn(ALOAD, 0);
+            int nextParam = 1;
+            for(final Type t: asmArgTypes) {
+                mv.load(nextParam, t);
+                nextParam += t.getSize();
+            }
+            mv.invokespecial(superClassName, name, methodDesc);
+            mv.areturn(asmReturnType);
+        }
+
+        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);
+
+        final int currentGlobalVar  = nextLocalVar++;
+        final int globalsDifferVar  = nextLocalVar++;
+
+        mv.dup();
+        // stack: [creatingGlobal, creatingGlobal, someHandle]
+
+        // 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]
+        // if(definingGlobal == currentGlobal) {
+        final Label globalsDiffer = new Label();
+        mv.ifacmpne(globalsDiffer);
+        // stack: [someGlobal, someHandle]
+        //     globalsDiffer = false
+        mv.pop();
+        // stack: [someHandle]
+        mv.iconst(0); // false
+        // stack: [false, someHandle]
+        final Label invokeHandle = new Label();
+        mv.goTo(invokeHandle);
+        mv.visitLabel(globalsDiffer);
+        // } else {
+        //     Context.setGlobal(definingGlobal);
+        // stack: [someGlobal, someHandle]
+        invokeSetGlobal(mv);
+        // stack: [someHandle]
+        //     globalsDiffer = true
+        mv.iconst(1);
+        // stack: [true, someHandle]
+
+        mv.visitLabel(invokeHandle);
+        mv.visitVarInsn(ISTORE, globalsDifferVar);
+        // stack: [someHandle]
+
+        // Load all parameters back on stack for dynamic invocation.
+        int varOffset = 1;
+        for (final Type t : asmArgTypes) {
+            mv.load(varOffset, t);
+            varOffset += t.getSize();
+        }
+
+        // Invoke the target method handle
+        final Label tryBlockStart = new Label();
+        mv.visitLabel(tryBlockStart);
+        mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString());
+        final Label tryBlockEnd = new Label();
+        mv.visitLabel(tryBlockEnd);
+        emitFinally(mv, currentGlobalVar, globalsDifferVar);
+        mv.areturn(asmReturnType);
+
+        // If Throwable is not declared, we need an adapter from Throwable to RuntimeException
+        final boolean throwableDeclared = isThrowableDeclared(exceptions);
+        final Label throwableHandler;
+        if (!throwableDeclared) {
+            // Add "throw new RuntimeException(Throwable)" handler for Throwable
+            throwableHandler = new Label();
+            mv.visitLabel(throwableHandler);
+            mv.anew(RUNTIME_EXCEPTION_TYPE);
+            mv.dupX1();
+            mv.swap();
+            mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE));
+            // Fall through to rethrow handler
+        } else {
+            throwableHandler = null;
+        }
+        final Label rethrowHandler = new Label();
+        mv.visitLabel(rethrowHandler);
+        // Rethrow handler for RuntimeException, Error, and all declared exception types
+        emitFinally(mv, currentGlobalVar, globalsDifferVar);
+        mv.athrow();
+        final Label methodEnd = new Label();
+        mv.visitLabel(methodEnd);
+
+        mv.visitLocalVariable("currentGlobal", SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, setupGlobal, methodEnd, currentGlobalVar);
+        mv.visitLocalVariable("globalsDiffer", Type.INT_TYPE.getDescriptor(), null, setupGlobal, methodEnd, globalsDifferVar);
+
+        if(throwableDeclared) {
+            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME);
+            assert throwableHandler == null;
+        } else {
+            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
+            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME);
+            for(final String excName: exceptionNames) {
+                mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName);
+            }
+            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
+        }
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+    }
+
+    /**
+     * Emits code for jumping to a label if the top stack operand is not null. The operand is kept on the stack if it
+     * is not null (so is available to code at the jump address) and is popped if it is null.
+     * @param mv the instruction adapter being used to emit code
+     * @param label the label to jump to
+     */
+    private static void jumpIfNonNullKeepOperand(final InstructionAdapter mv, final Label label) {
+        mv.visitInsn(DUP);
+        mv.visitJumpInsn(IFNONNULL, label);
+        mv.visitInsn(POP);
+    }
+
+    /**
+     * Emit code to restore the previous Nashorn Context when needed.
+     * @param mv the instruction adapter
+     * @param currentGlobalVar index of the local variable holding the reference to the current global at method
+     * entry.
+     * @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored.
+     */
+    private void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) {
+        // Emit code to restore the previous Nashorn global if needed
+        mv.visitVarInsn(ILOAD, globalsDifferVar);
+        final Label skip = new Label();
+        mv.ifeq(skip);
+        mv.visitVarInsn(ALOAD, currentGlobalVar);
+        invokeSetGlobal(mv);
+        mv.visitLabel(skip);
+    }
+
+    private static boolean isThrowableDeclared(final Class<?>[] exceptions) {
+        for (final Class<?> exception : exceptions) {
+            if (exception == Throwable.class) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gathers methods that can be implemented or overridden from the specified type into this factory's
+     * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from
+     * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its
+     * superclass and the interfaces it implements, and add further methods that were not directly declared on the
+     * class.
+     * @param type the type defining the methods.
+     */
+    private void gatherMethods(final Class<?> type) {
+        if (Modifier.isPublic(type.getModifiers())) {
+            final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
+
+            for (final Method typeMethod: typeMethods) {
+                final int m = typeMethod.getModifiers();
+                if (Modifier.isStatic(m)) {
+                    continue;
+                }
+                if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
+                    final MethodInfo mi = new MethodInfo(typeMethod);
+                    if (Modifier.isFinal(m)) {
+                        finalMethods.add(mi);
+                    } else if (!finalMethods.contains(mi) && methodInfos.add(mi)) {
+                        if (Modifier.isAbstract(m)) {
+                            abstractMethodNames.add(mi.getName());
+                        }
+                        mi.setIsCanonical(usedFieldNames, classOverride);
+                    }
+                }
+            }
+        }
+        // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done.
+        // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to
+        // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a
+        // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and
+        // getMethods() does provide those declared in a superinterface.
+        if (!type.isInterface()) {
+            final Class<?> superType = type.getSuperclass();
+            if (superType != null) {
+                gatherMethods(superType);
+            }
+            for (final Class<?> itf: type.getInterfaces()) {
+                gatherMethods(itf);
+            }
+        }
+    }
+
+    private void gatherMethods(final List<Class<?>> classes) {
+        for(final Class<?> c: classes) {
+            gatherMethods(c);
+        }
+    }
+
+    /**
+     * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters,
+     * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and
+     * {@code Object.clone()}.
+     * @return a collection of method infos representing those methods that we never override in adapter classes.
+     */
+    private static Collection<MethodInfo> getExcludedMethods() {
+        return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() {
+            @Override
+            public Collection<MethodInfo> run() {
+                try {
+                    return Arrays.asList(
+                            new MethodInfo(Object.class, "finalize"),
+                            new MethodInfo(Object.class, "clone"));
+                } catch (final NoSuchMethodException e) {
+                    throw new AssertionError(e);
+                }
+            }
+        });
+    }
+
+    private String getCommonSuperClass(final String type1, final String type2) {
+        try {
+            final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader);
+            final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader);
+            if (c1.isAssignableFrom(c2)) {
+                return type1;
+            }
+            if (c2.isAssignableFrom(c1)) {
+                return type2;
+            }
+            if (c1.isInterface() || c2.isInterface()) {
+                return OBJECT_TYPE_NAME;
+            }
+            return assignableSuperClass(c1, c2).getName().replace('.', '/');
+        } catch(final ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) {
+        final Class<?> superClass = c1.getSuperclass();
+        return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,225 @@
+/*
+ * 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.internal.runtime.linker;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
+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.ACONST_NULL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+
+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;
+import jdk.internal.dynalink.beans.StaticClass;
+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.ScriptObject;
+
+/**
+ * This class encapsulates the bytecode of the adapter class and can be used to load it into the JVM as an actual Class.
+ * It can be invoked repeatedly to create multiple adapter classes from the same bytecode; adapter classes that have
+ * class-level overrides must be re-created for every set of such overrides. Note that while this class is named
+ * "class loader", it does not, in fact, extend {@code ClassLoader}, but rather uses them internally. Instances of this
+ * class are normally created by {@link JavaAdapterBytecodeGenerator}.
+ */
+class JavaAdapterClassLoader extends JavaAdapterGeneratorBase {
+    private static final Type PRIVILEGED_ACTION_TYPE = Type.getType(PrivilegedAction.class);
+
+    private static final String PRIVILEGED_ACTION_TYPE_NAME = PRIVILEGED_ACTION_TYPE.getInternalName();
+    private static final String PRIVILEGED_RUN_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE);
+
+    private static final ProtectionDomain GENERATED_PROTECTION_DOMAIN = createGeneratedProtectionDomain();
+
+    private final String className;
+    private final byte[] classBytes;
+    private final String globalSetterClassName;
+
+    JavaAdapterClassLoader(String className, byte[] classBytes, String globalSetterClassName) {
+        this.className = className.replace('/', '.');
+        this.classBytes = classBytes;
+        this.globalSetterClassName = globalSetterClassName.replace('/', '.');
+    }
+
+    /**
+     * Loads the generated adapter class into the JVM.
+     * @param parentLoader the parent class loader for the generated class loader
+     * @return the generated adapter class
+     */
+    StaticClass generateClass(final ClassLoader parentLoader) {
+        return AccessController.doPrivileged(new PrivilegedAction<StaticClass>() {
+            @Override
+            public StaticClass run() {
+                try {
+                    return StaticClass.forClass(Class.forName(className, true, createClassLoader(parentLoader)));
+                } catch (final ClassNotFoundException e) {
+                    throw new AssertionError(e); // cannot happen
+                }
+            }
+        });
+    }
+
+    private static class AdapterLoader extends SecureClassLoader {
+        AdapterLoader(ClassLoader parent) {
+            super(parent);
+        }
+    }
+
+    static boolean isAdapterClass(Class<?> clazz) {
+        return clazz.getClassLoader() instanceof AdapterLoader;
+    }
+
+    // Note that the adapter class is created in the protection domain of the class/interface being
+    // extended/implemented, and only the privileged global setter action class is generated in the protection domain
+    // of Nashorn itself. Also note that the creation and loading of the global setter is deferred until it is
+    // required by JVM linker, which will only happen on first invocation of any of the adapted method. We could defer
+    // 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) {
+        return new AdapterLoader(parentLoader) {
+            private final ClassLoader myLoader = getClass().getClassLoader();
+            private final ProtectionDomain myProtectionDomain = getClass().getProtectionDomain();
+
+            @Override
+            public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+                try {
+                    return super.loadClass(name, resolve);
+                } catch (final SecurityException se) {
+                    // we may be implementing an interface or extending a class that was
+                    // loaded by a loader that prevents package.access. If so, it'd throw
+                    // SecurityException for nashorn's classes!. For adapter's to work, we
+                    // should be able to refer to nashorn classes.
+                    if (name.startsWith("jdk.nashorn.internal.")) {
+                        return myLoader.loadClass(name);
+                    }
+                    throw se;
+                }
+            }
+
+            @Override
+            protected Class<?> findClass(final String name) throws ClassNotFoundException {
+                if(name.equals(className)) {
+                    return defineClass(name, classBytes, 0, classBytes.length, GENERATED_PROTECTION_DOMAIN);
+                } else if(name.equals(globalSetterClassName)) {
+                    final byte[] bytes = generatePrivilegedActionClassBytes(globalSetterClassName.replace('.', '/'));
+                    return defineClass(name, bytes, 0, bytes.length, myProtectionDomain);
+                } else {
+                    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);
+    }
+
+    /**
+     * Generates a PrivilegedAction implementation class for invoking {@link Context#setGlobal(ScriptObject)} from the
+     * adapter class.
+     */
+    private static byte[] generatePrivilegedActionClassBytes(final String className) {
+        final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+        // class GlobalSetter implements PrivilegedAction {
+        w.visit(Opcodes.V1_7, ACC_SUPER | ACC_FINAL, className, null, OBJECT_TYPE_NAME, new String[] {
+                PRIVILEGED_ACTION_TYPE_NAME
+        });
+
+        // private final ScriptObject global;
+        w.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
+
+        // private GlobalSetter(ScriptObject global) {
+        InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PRIVATE, INIT,
+                SET_GLOBAL_METHOD_DESCRIPTOR, null, new String[0]));
+        mv.visitCode();
+        // super();
+        mv.visitVarInsn(ALOAD, 0);
+        mv.invokespecial(OBJECT_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR);
+        // this.global = global;
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitVarInsn(ALOAD, 1);
+        mv.putfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+
+        mv.visitInsn(RETURN);
+        mv.visitEnd();
+        mv.visitMaxs(0, 0);
+
+        // public Object run() {
+        mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC, "run", PRIVILEGED_RUN_METHOD_DESCRIPTOR, null,
+                new String[0]));
+        mv.visitCode();
+        // Context.setGlobal(this.global);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.getfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+        mv.invokestatic(CONTEXT_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
+        // return null;
+        mv.visitInsn(ACONST_NULL);
+        mv.visitInsn(ARETURN);
+
+        mv.visitEnd();
+        mv.visitMaxs(0, 0);
+
+        // static void setGlobal(ScriptObject global) {
+        mv = new InstructionAdapter(w.visitMethod(ACC_STATIC, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, null,
+                new String[0]));
+        mv.visitCode();
+        // new GlobalSetter(ScriptObject global)
+        mv.anew(Type.getType("L" + className + ";"));
+        mv.dup();
+        mv.visitVarInsn(ALOAD, 0);
+        mv.invokespecial(className, INIT, SET_GLOBAL_METHOD_DESCRIPTOR);
+        // AccessController.doPrivileged(...)
+        mv.invokestatic(Type.getInternalName(AccessController.class), "doPrivileged", Type.getMethodDescriptor(
+                OBJECT_TYPE, PRIVILEGED_ACTION_TYPE));
+        mv.pop();
+        mv.visitInsn(RETURN);
+
+        mv.visitEnd();
+        mv.visitMaxs(0, 0);
+
+        return w.toByteArray();
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java	Thu Apr 04 13:54:51 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java	Thu Apr 04 15:53:26 2013 +0200
@@ -25,110 +25,39 @@
 
 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_PRIVATE;
-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.ACC_VARARGS;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
-import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
-import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
-import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
-import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
-import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL;
-import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
-import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
-import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
-import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 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.PrivilegedExceptionAction;
-import java.security.ProtectionDomain;
-import java.security.SecureClassLoader;
-import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
-import java.util.Set;
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.internal.dynalink.support.LinkRequestImpl;
-import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.internal.org.objectweb.asm.Label;
-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.objects.NativeJava;
-import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.ECMAErrors;
 import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
-import jdk.nashorn.internal.runtime.Undefined;
 
 /**
  * <p>A factory class that generates adapter classes. Adapter classes allow implementation of Java interfaces and
  * extending of Java classes from JavaScript. For every combination of a superclass to extend and interfaces to
  * implement (collectively: "original types"), exactly one adapter class is generated that extends the specified
- * superclass and implements the specified interfaces.
+ * superclass and implements the specified interfaces. (But see the discussion of class-based overrides for exceptions.)
  * </p><p>
  * The adapter class is generated in a new secure class loader that inherits Nashorn's protection domain, and has either
  * one of the original types' class loader or the Nashorn's class loader as its parent - the parent class loader
  * is chosen so that all the original types and the Nashorn core classes are visible from it (as the adapter will have
  * constant pool references to ScriptObject and ScriptFunction classes). In case none of the candidate class loaders has
- * visibility of all the required types, an error is thrown.
- * </p><p>
- * For every protected or public constructor in the extended class, the adapter class will have one or two public
- * constructors (visibility of protected constructors in the extended class is promoted to public). In every case, for
- * every original constructor, a new constructor taking a trailing ScriptObject argument preceded by original
- * constructor arguments is present 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
- * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be
- * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too.
- * </p><p>
- * 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 {@code null} as its "this".
- * </p><p>
- * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect
- * to coerce the JavaScript function return value to the expected Java return type.
- * </p><p>
- * Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be
- * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The
- * reason we are passing the additional argument at the end of the argument list instead at the front is that the
- * 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>.
+ * visibility of all the required types, an error is thrown. The class uses {@link JavaAdapterBytecodeGenerator} to
+ * generate the adapter class itself; see its documentation for details about the generated class.
  * </p><p>
  * You normally don't use this class directly, but rather either create adapters from script using
  * {@link NativeJava#extend(Object, Object...)}, using the {@code new} operator on abstract classes and interfaces (see
@@ -138,72 +67,6 @@
  */
 
 public final class JavaAdapterFactory {
-    private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class);
-    private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class);
-    private static final Type OBJECT_TYPE = Type.getType(Object.class);
-    private static final Type STRING_TYPE = Type.getType(String.class);
-    private static final Type CONTEXT_TYPE = Type.getType(Context.class);
-    private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
-    private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
-    private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
-            OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
-    private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
-            SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
-    private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class);
-    private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
-    private static final Type PRIVILEGED_ACTION_TYPE = Type.getType(PrivilegedAction.class);
-    private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class);
-
-    private static final String THIS_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterFactory.class);
-    private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName();
-    private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class);
-    private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName();
-    private static final String CONTEXT_TYPE_NAME = CONTEXT_TYPE.getInternalName();
-    private static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName();
-    private static final String PRIVILEGED_ACTION_TYPE_NAME = PRIVILEGED_ACTION_TYPE.getInternalName();
-    private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName();
-
-    private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor();
-    private static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor();
-    private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
-    private static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, SCRIPT_OBJECT_TYPE);
-    private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Class.class));
-    private static final String PRIVILEGED_RUN_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE);
-
-    // Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because
-    // it's a java.* package.
-    private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/internal/javaadapters/";
-    // Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package.
-    private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter";
-    private static final String JAVA_PACKAGE_PREFIX = "java/";
-    private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 238; //255 - 17; 17 is the maximum possible length for the global setter inner class suffix
-
-    private static final String INIT = "<init>";
-    private static final String VOID_NOARG = Type.getMethodDescriptor(Type.VOID_TYPE);
-    private static final String GLOBAL_FIELD_NAME = "global";
-
-    /**
-     * Contains various outcomes for attempting to generate an adapter class. These are stored in AdapterInfo instances.
-     * We have a successful outcome (adapter class was generated) and four possible error outcomes: superclass is final,
-     * superclass is not public, superclass has no public or protected constructor, more than one superclass was
-     * specified. We don't throw exceptions when we try to generate the adapter, but rather just record these error
-     * conditions as they are still useful as partial outcomes, as Nashorn's linker can still successfully check whether
-     * the class can be autoconverted from a script function even when it is not possible to generate an adapter for it.
-     */
-    private enum AdaptationOutcome {
-        SUCCESS,
-        ERROR_FINAL_CLASS,
-        ERROR_NON_PUBLIC_CLASS,
-        ERROR_NO_ACCESSIBLE_CONSTRUCTOR,
-        ERROR_MULTIPLE_SUPERCLASSES,
-        ERROR_NO_COMMON_LOADER
-    }
-
-    /**
-     * Collection of methods we never override: Object.clone(), Object.finalize().
-     */
-    private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods();
-
     /**
      * A mapping from an original Class object to AdapterInfo representing the adapter for the class it represents.
      */
@@ -214,127 +77,6 @@
         }
     };
 
-    private static final Random random = new SecureRandom();
-    private static final ProtectionDomain GENERATED_PROTECTION_DOMAIN = createGeneratedProtectionDomain();
-
-    // This is the superclass for our generated adapter.
-    private final Class<?> superClass;
-    // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class
-    // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the
-    // Nashorn classes.
-    private final ClassLoader commonLoader;
-
-    // Binary name of the superClass
-    private final String superClassName;
-    // Binary name of the generated class.
-    private final String generatedClassName;
-    // Binary name of the PrivilegedAction inner class that is used to
-    private final String globalSetterClassName;
-    private final Set<String> usedFieldNames = new HashSet<>();
-    private final Set<String> abstractMethodNames = new HashSet<>();
-    private final String samName;
-    private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
-    private final Set<MethodInfo> methodInfos = new HashSet<>();
-    private boolean autoConvertibleFromFunction = false;
-
-    private final ClassWriter cw;
-
-    /**
-     * Creates a factory that will produce the adapter type for the specified original type.
-     * @param originalType the type for which this factory will generate the adapter type.
-     * @param definingClassAndLoader the class in whose ClassValue we'll store the generated adapter, and its class loader.
-     * @throws AdaptationException if the adapter can not be generated for some reason.
-     */
-    private JavaAdapterFactory(final Class<?> superType, final List<Class<?>> interfaces, final ClassAndLoader definingClassAndLoader) throws AdaptationException {
-        assert superType != null && !superType.isInterface();
-        assert interfaces != null;
-        assert definingClassAndLoader != null;
-
-        this.superClass = superType;
-        this.commonLoader = findCommonLoader(definingClassAndLoader);
-        cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
-            @Override
-            protected String getCommonSuperClass(final String type1, final String type2) {
-                // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class
-                // loader to find the common superclass of two types when needed.
-                return JavaAdapterFactory.this.getCommonSuperClass(type1, type2);
-            }
-        };
-        superClassName = Type.getInternalName(superType);
-        generatedClassName = getGeneratedClassName(superType, interfaces);
-
-        // Randomize the name of the privileged global setter, to make it non-feasible to find.
-        final long l;
-        synchronized(random) {
-            l = random.nextLong();
-        }
-        // NOTE: they way this class name is calculated affects the value of MAX_GENERATED_TYPE_NAME_LENGTH constant. If
-        // you change the calculation of globalSetterClassName, adjust the constant too.
-        globalSetterClassName = generatedClassName.concat("$" + Long.toHexString(l & Long.MAX_VALUE));
-        cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
-        cw.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
-        usedFieldNames.add(GLOBAL_FIELD_NAME);
-
-        gatherMethods(superType);
-        gatherMethods(interfaces);
-        samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null;
-        generateFields();
-        generateConstructors();
-        generateMethods();
-        // }
-        cw.visitEnd();
-    }
-
-    private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) {
-        // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're
-        // just implementing interfaces or extending Object), then the first implemented interface or Object.
-        final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType;
-        final Package pkg = namingType.getPackage();
-        final String namingTypeName = Type.getInternalName(namingType);
-        final StringBuilder buf = new StringBuilder();
-        if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) {
-            // Can't define new classes in java.* packages
-            buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName);
-        } else {
-            buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX);
-        }
-        final Iterator<Class<?>> it = interfaces.iterator();
-        if(superType == Object.class && it.hasNext()) {
-            it.next(); // Skip first interface, it was used to primarily name the adapter
-        }
-        // Append interface names to the adapter name
-        while(it.hasNext()) {
-            buf.append("$$").append(it.next().getSimpleName());
-        }
-        return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length()));
-    }
-
-    /**
-     * Given a list of class objects, return an array with their binary names. Used to generate the array of interface
-     * names to implement.
-     * @param classes the classes
-     * @return an array of names
-     */
-    private static String[] getInternalTypeNames(final List<Class<?>> classes) {
-        final int interfaceCount = classes.size();
-        final String[] interfaceNames = new String[interfaceCount];
-        for(int i = 0; i < interfaceCount; ++i) {
-            interfaceNames[i] = Type.getInternalName(classes.get(i));
-        }
-        return interfaceNames;
-    }
-
-    /**
-     * Utility method used by few other places in the code. Tests if the class has the abstract modifier and is not an
-     * array class. For some reason, array classes have the abstract modifier set in HotSpot JVM, and we don't want to
-     * treat array classes as abstract.
-     * @param clazz the inspected class
-     * @return true if the class is abstract and is not an array type.
-     */
-    static boolean isAbstractClass(final Class<?> clazz) {
-        return Modifier.isAbstract(clazz.getModifiers()) && !clazz.isArray();
-    }
-
     /**
      * Returns an adapter class for the specified original types. The adapter class extends/implements the original
      * class/interfaces.
@@ -346,27 +88,68 @@
      * in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of
      * interfaces) will result in a different adapter class, even though those adapter classes are functionally
      * identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists.
+     * @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.
      * @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) {
+    public static StaticClass getAdapterClassFor(final Class<?>[] types, ScriptObject classOverrides) {
         assert types != null && types.length > 0;
-        final AdapterInfo adapterInfo = getAdapterInfo(types);
+        return getAdapterInfo(types).getAdapterClassFor(classOverrides);
+    }
 
-        final StaticClass clazz = adapterInfo.adapterClass;
-        if (clazz != null) {
-            return clazz;
-        }
-        adapterInfo.adaptationOutcome.typeError();
+    /**
+     * Returns a method handle representing a constructor that takes a single argument of the source type (which,
+     * really, should be one of {@link ScriptObject}, {@link ScriptFunction}, or {@link Object}, and returns an instance
+     * of the adapter for the target type. Used to implement the function autoconverters as well as the Nashorn's
+     * JSR-223 script engine's {@code getInterface()} method.
+     * @param sourceType the source type; should be either {@link ScriptObject}, {@link ScriptFunction}, or
+     * {@link Object}. In case of {@code Object}, it will return a method handle that dispatches to either the script
+     * object or function constructor at invocation based on the actual argument.
+     * @param targetType the target type, for which adapter instances will be created
+     * @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);
+        return AccessController.doPrivileged(new PrivilegedExceptionAction<MethodHandle>() {
+            @Override
+            public MethodHandle run() throws Exception {
+                return  MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(NashornCallSiteDescriptor.get(
+                    "dyn:new", MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false,
+                    adapterClass, null)).getInvocation(), adapterClass);
+            }
+        });
+    }
 
-        throw new AssertionError();
+    /**
+     * Tells if the given Class is an adapter or support class
+     * @param clazz Class object
+     * @return true if the Class given is adapter or support class
+     */
+    public static boolean isAdapterClass(Class<?> clazz) {
+        return JavaAdapterClassLoader.isAdapterClass(clazz);
+    }
+
+    /**
+     * Returns whether an instance of the specified class/interface can be generated from a ScriptFunction. Returns true
+     * iff: the adapter for the class/interface can be created, it is abstract (this includes interfaces), it has at
+     * least one abstract method, all the abstract methods share the same name, and it has a public or protected default
+     * constructor. Note that invoking this class will most likely result in the adapter class being defined in the JVM
+     * if it hasn't been already.
+     * @param clazz the inspected class
+     * @return true iff an instance of the specified class/interface can be generated from a ScriptFunction.
+     */
+    static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
+        return getAdapterInfo(new Class<?>[] { clazz }).autoConvertibleFromFunction;
     }
 
     private static AdapterInfo getAdapterInfo(final Class<?>[] types) {
-        final ClassAndLoader definingClassAndLoader = getDefiningClassAndLoader(types);
+        final ClassAndLoader definingClassAndLoader = ClassAndLoader.getDefiningClassAndLoader(types);
 
-        final Map<List<Class<?>>, AdapterInfo> adapterInfoMap = ADAPTER_INFO_MAPS.get(definingClassAndLoader.clazz);
+        final Map<List<Class<?>>, AdapterInfo> adapterInfoMap = ADAPTER_INFO_MAPS.get(definingClassAndLoader.getRepresentativeClass());
         final List<Class<?>> typeList = types.length == 1 ? getSingletonClassList(types[0]) : Arrays.asList(types.clone());
         AdapterInfo adapterInfo;
         synchronized(adapterInfoMap) {
@@ -385,740 +168,6 @@
     }
 
     /**
-     * Returns whether an instance of the specified class/interface can be generated from a ScriptFunction. Returns true
-     * iff: the adapter for the class/interface can be created, it is abstract (this includes interfaces), it has at
-     * least one abstract method, all the abstract methods share the same name, and it has a public or protected default
-     * constructor. Note that invoking this class will most likely result in the adapter class being defined in the JVM
-     * if it hasn't been already.
-     * @param clazz the inspected class
-     * @return true iff an instance of the specified class/interface can be generated from a ScriptFunction.
-     */
-    static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
-        return getAdapterInfo(new Class<?>[] { clazz }).autoConvertibleFromFunction;
-    }
-
-    /**
-     * Returns a method handle representing a constructor that takes a single argument of the source type (which,
-     * really, should be one of {@link ScriptObject}, {@link ScriptFunction}, or {@link Object}, and returns an instance
-     * of the adapter for the target type. Used to implement the function autoconverters as well as the Nashorn's
-     * JSR-223 script engine's {@code getInterface()} method.
-     * @param sourceType the source type; should be either {@link ScriptObject}, {@link ScriptFunction}, or
-     * {@link Object}. In case of {@code Object}, it will return a method handle that dispatches to either the script
-     * object or function constructor at invocation based on the actual argument.
-     * @param targetType the target type, for which adapter instances will be created
-     * @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 });
-        return AccessController.doPrivileged(new PrivilegedExceptionAction<MethodHandle>() {
-            @Override
-            public MethodHandle run() throws Exception {
-                return  MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(NashornCallSiteDescriptor.get(
-                    "dyn:new", MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false,
-                    adapterClass, null)).getInvocation(), adapterClass);
-            }
-        });
-    }
-
-    /**
-     * Finishes the bytecode generation for the adapter class that was started in the constructor, and loads the
-     * bytecode as a new class into the JVM.
-     * @return the generated adapter class
-     */
-    private Class<?> generateClass() {
-        final String binaryName = generatedClassName.replace('/', '.');
-        try {
-            return Class.forName(binaryName, true, createClassLoader(commonLoader, binaryName, cw.toByteArray(),
-                    globalSetterClassName.replace('/', '.')));
-        } catch (final ClassNotFoundException e) {
-            throw new AssertionError(e); // cannot happen
-        }
-    }
-
-    /**
-     * Tells if the given Class is an adapter or support class
-     * @param clazz Class object
-     * @return true if the Class given is adapter or support class
-     */
-    public static boolean isAdapterClass(Class<?> clazz) {
-        return clazz.getClassLoader() instanceof AdapterLoader;
-    }
-
-    private static class AdapterLoader extends SecureClassLoader {
-        AdapterLoader(ClassLoader parent) {
-            super(parent);
-        }
-    }
-
-    // Creation of class loader is in a separate static method so that it doesn't retain a reference to the factory
-    // instance. Note that the adapter class is created in the protection domain of the class/interface being
-    // extended/implemented, and only the privileged global setter action class is generated in the protection domain
-    // of Nashorn itself. Also note that the creation and loading of the global setter is deferred until it is
-    // required by JVM linker, which will only happen on first invocation of any of the adapted method. We could defer
-    // 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 static ClassLoader createClassLoader(final ClassLoader parentLoader, final String className,
-            final byte[] classBytes, final String privilegedActionClassName) {
-        return new AdapterLoader(parentLoader) {
-            private final ClassLoader myLoader = getClass().getClassLoader();
-            private final ProtectionDomain myProtectionDomain = getClass().getProtectionDomain();
-
-            @Override
-            public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
-                try {
-                    return super.loadClass(name, resolve);
-                } catch (final SecurityException se) {
-                    // we may be implementing an interface or extending a class that was
-                    // loaded by a loader that prevents package.access. If so, it'd throw
-                    // SecurityException for nashorn's classes!. For adapter's to work, we
-                    // should be able to refer to nashorn classes.
-                    if (name.startsWith("jdk.nashorn.internal.")) {
-                        return myLoader.loadClass(name);
-                    }
-                    throw se;
-                }
-            }
-
-            @Override
-            protected Class<?> findClass(final String name) throws ClassNotFoundException {
-                if(name.equals(className)) {
-                    final byte[] bytes = classBytes;
-                    return defineClass(name, bytes, 0, bytes.length, GENERATED_PROTECTION_DOMAIN);
-                } else if(name.equals(privilegedActionClassName)) {
-                    final byte[] bytes = generatePrivilegedActionClassBytes(privilegedActionClassName.replace('.', '/'));
-                    return defineClass(name, bytes, 0, bytes.length, myProtectionDomain);
-                } else {
-                    throw new ClassNotFoundException(name);
-                }
-            }
-        };
-    }
-
-    /**
-     * Generates a PrivilegedAction implementation class for invoking {@link Context#setGlobal(ScriptObject)} from the
-     * adapter class.
-     */
-    private static byte[] generatePrivilegedActionClassBytes(final String className) {
-        final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
-        // class GlobalSetter implements PrivilegedAction {
-        w.visit(Opcodes.V1_7, ACC_SUPER | ACC_FINAL, className, null, OBJECT_TYPE_NAME, new String[] {
-                PRIVILEGED_ACTION_TYPE_NAME
-        });
-
-        // private final ScriptObject global;
-        w.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
-
-        // private GlobalSetter(ScriptObject global) {
-        InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PRIVATE, INIT,
-                SET_GLOBAL_METHOD_DESCRIPTOR, null, new String[0]));
-        mv.visitCode();
-        // super();
-        mv.visitVarInsn(ALOAD, 0);
-        mv.invokespecial(OBJECT_TYPE_NAME, INIT, VOID_NOARG);
-        // this.global = global;
-        mv.visitVarInsn(ALOAD, 0);
-        mv.visitVarInsn(ALOAD, 1);
-        mv.putfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
-        mv.visitInsn(RETURN);
-        mv.visitEnd();
-        mv.visitMaxs(0, 0);
-
-        // public Object run() {
-        mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC, "run", PRIVILEGED_RUN_METHOD_DESCRIPTOR, null,
-                new String[0]));
-        mv.visitCode();
-        // Context.setGlobal(this.global);
-        mv.visitVarInsn(ALOAD, 0);
-        mv.getfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-        mv.invokestatic(CONTEXT_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
-        // return null;
-        mv.visitInsn(ACONST_NULL);
-        mv.visitInsn(ARETURN);
-
-        mv.visitEnd();
-        mv.visitMaxs(0, 0);
-
-        // static void setGlobal(ScriptObject global) {
-        mv = new InstructionAdapter(w.visitMethod(ACC_STATIC, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, null,
-                new String[0]));
-        mv.visitCode();
-        // new GlobalSetter(ScriptObject global)
-        mv.anew(Type.getType("L" + className + ";"));
-        mv.dup();
-        mv.visitVarInsn(ALOAD, 0);
-        mv.invokespecial(className, INIT, SET_GLOBAL_METHOD_DESCRIPTOR);
-        // AccessController.doPrivileged(...)
-        mv.invokestatic(Type.getInternalName(AccessController.class), "doPrivileged", Type.getMethodDescriptor(
-                OBJECT_TYPE, PRIVILEGED_ACTION_TYPE));
-        mv.pop();
-        mv.visitInsn(RETURN);
-
-        mv.visitEnd();
-        mv.visitMaxs(0, 0);
-
-        return w.toByteArray();
-    }
-
-    private void generateFields() {
-        for (final MethodInfo mi: methodInfos) {
-            cw.visitField(ACC_PRIVATE | ACC_FINAL, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
-        }
-    }
-
-    private void generateConstructors() throws AdaptationException {
-        boolean gotCtor = false;
-        for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) {
-            final int modifier = ctor.getModifiers();
-            if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
-                generateConstructor(ctor);
-                gotCtor = true;
-            }
-        }
-        if(!gotCtor) {
-            throw new AdaptationException(AdaptationOutcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName());
-        }
-    }
-
-    boolean isAutoConvertibleFromFunction() {
-        return autoConvertibleFromFunction;
-    }
-
-    private void generateConstructor(final Constructor<?> ctor) {
-        // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
-        // beginning of its parameter list.
-        generateConstructor(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 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.
-            generateConstructor(ctor, true);
-        }
-    }
-
-    /**
-     * Generates a constructor for the 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
-     * function itself, if that's what's passed). There is one method handle field in the adapter class for every method
-     * that can be implemented or overridden; the name of every field is same as the name of the method, with a number
-     * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke
-     * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType,
-     * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity
-     * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}.
-     * The constructor that takes a script function will only initialize the methods with the same name as the single
-     * abstract method. The constructor will also store the Nashorn global that was current at the constructor
-     * invocation time in a field named "global". The generated constructor will be public, regardless of whether the
-     * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the
-     * supertype constructor was.
-     * @param ctor the supertype constructor that is serving as the base for the generated constructor.
-     * @param fromFunction true if we're generating a constructor that initializes SAM types from a single
-     * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a
-     * ScriptObject passed to it.
-     */
-    private void generateConstructor(final Constructor<?> ctor, final boolean fromFunction) {
-        final Type originalCtorType = Type.getType(ctor);
-        final Type[] originalArgTypes = originalCtorType.getArgumentTypes();
-        final int argLen = originalArgTypes.length;
-        final Type[] newArgTypes = new Type[argLen + 1];
-
-        // Insert ScriptFunction|Object as the last argument to the constructor
-        final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE;
-        newArgTypes[argLen] = extraArgumentType;
-        System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
-
-        // All constructors must be public, even if in the superclass they were protected.
-        // Existing super constructor <init>(this, args...) triggers generating <init>(this, scriptObj, args...).
-        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
-                Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
-
-        mv.visitCode();
-        // First, invoke super constructor with original arguments. If the form of the constructor we're generating is
-        // <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...).
-        mv.visitVarInsn(ALOAD, 0);
-        final Class<?>[] argTypes = ctor.getParameterTypes();
-        int offset = 1; // First arg is at position 1, after this.
-        for (int i = 0; i < argLen; ++i) {
-            final Type argType = Type.getType(argTypes[i]);
-            mv.load(offset, argType);
-            offset += argType.getSize();
-        }
-        mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
-
-        // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method.
-        final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR;
-
-        // Assign MethodHandle fields through invoking getHandle()
-        for (final MethodInfo mi : methodInfos) {
-            mv.visitVarInsn(ALOAD, 0);
-            if (fromFunction && !mi.getName().equals(samName)) {
-                // Constructors initializing from a ScriptFunction only initialize methods with the SAM name.
-                // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This
-                // is a deliberate design choice. All other method handles are initialized to null.
-                mv.visitInsn(ACONST_NULL);
-            } else {
-                mv.visitVarInsn(ALOAD, offset);
-                if(!fromFunction) {
-                    mv.aconst(mi.getName());
-                }
-                mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
-                mv.iconst(mi.method.isVarArgs() ? 1 : 0);
-                mv.invokestatic(THIS_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor);
-            }
-            mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
-        }
-
-        // Assign "this.global = Context.getGlobal()"
-        mv.visitVarInsn(ALOAD, 0);
-        invokeGetGlobal(mv);
-        mv.dup();
-        mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR); // check against null Context
-        mv.pop();
-        mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
-        // Wrap up
-        mv.visitInsn(RETURN);
-        mv.visitMaxs(0, 0);
-        mv.visitEnd();
-    }
-
-    private static void invokeGetGlobal(final InstructionAdapter mv) {
-        mv.invokestatic(CONTEXT_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR);
-    }
-
-    private void invokeSetGlobal(final InstructionAdapter mv) {
-        mv.invokestatic(globalSetterClassName, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
-    }
-
-    /**
-     * Given a JS script function, binds it to null JS "this", and adapts its parameter types, return types, and arity
-     * to the specified type and arity. This method is public mainly for implementation reasons, so the adapter classes
-     * can invoke it from their constructors that take a ScriptFunction in its first argument to obtain the method
-     * handles for their abstract method implementations.
-     * @param fn the script function
-     * @param type the method type it has to conform to
-     * @param varArg if the Java method for which the function is being adapted is a variable arity method
-     * @return the appropriately adapted method handle for invoking the script function.
-     */
-    public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type, final boolean varArg) {
-        // JS "this" will be null for SAMs
-        return adaptHandle(fn.getBoundInvokeHandle(null), type, varArg);
-    }
-
-    /**
-     * Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and
-     * adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly
-     * for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object
-     * in its first argument to obtain the method handles for their method implementations.
-     * @param obj the script obj
-     * @param name the name of the property that contains the function
-     * @param type the method type it has to conform to
-     * @param varArg if the Java method for which the function is being adapted is a variable arity method
-     * @return the appropriately adapted method handle for invoking the script function, or null if the value of the
-     * property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly
-     * define it but just inherits it through prototype.
-     */
-    public static MethodHandle getHandle(final Object obj, final String name, final MethodType type, final boolean varArg) {
-        if (! (obj instanceof ScriptObject)) {
-            throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
-        }
-
-        final ScriptObject sobj = (ScriptObject)obj;
-        // Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified
-        if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) {
-            return null;
-        }
-
-        final Object fnObj = sobj.get(name);
-        if (fnObj instanceof ScriptFunction) {
-            return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type, varArg);
-        } else if(fnObj == null || fnObj instanceof Undefined) {
-            return null;
-        } else {
-            throw typeError("not.a.function", name);
-        }
-    }
-
-    private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type, final boolean varArg) {
-        return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, varArg), type);
-    }
-
-    /**
-     * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the
-     * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the
-     * method handle serving as the implementation of this method in adapter instances.
-     *
-     */
-    private static class MethodInfo {
-        private final Method method;
-        private final MethodType type;
-        private String methodHandleFieldName;
-
-        private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException {
-            this(clazz.getDeclaredMethod(name, argTypes));
-        }
-
-        private MethodInfo(final Method method) {
-            this.method = method;
-            this.type   = MH.type(method.getReturnType(), method.getParameterTypes());
-        }
-
-        @Override
-        public boolean equals(final Object obj) {
-            return obj instanceof MethodInfo && equals((MethodInfo)obj);
-        }
-
-        private boolean equals(final MethodInfo other) {
-            // Only method name and type are used for comparison; method handle field name is not.
-            return getName().equals(other.getName()) && type.equals(other.type);
-        }
-
-        String getName() {
-            return method.getName();
-        }
-
-        @Override
-        public int hashCode() {
-            return getName().hashCode() ^ type.hashCode();
-        }
-
-        void setIsCanonical(final Set<String> usedFieldNames) {
-            int i = 0;
-            String fieldName = getName();
-            while(!usedFieldNames.add(fieldName)) {
-                fieldName = getName() + (i++);
-            }
-            methodHandleFieldName = fieldName;
-        }
-    }
-
-    private void generateMethods() {
-        for(final MethodInfo mi: methodInfos) {
-            generateMethod(mi);
-        }
-    }
-
-    /**
-     * Generates a method in the adapter class that adapts a method from the original class. The generated methods will
-     * inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation
-     * for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an
-     * {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is
-     * invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the
-     * current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter
-     * instance, the creating global is set to be the current global. In this case, the previously current global is
-     * restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared
-     * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime
-     * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of
-     * the method; this is guaranteed by the way constructors of the adapter class obtain them using
-     * {@link #getHandle(Object, String, MethodType, boolean)}.
-     * @param mi the method info describing the method to be generated.
-     */
-    private void generateMethod(final MethodInfo mi) {
-        final Method method = mi.method;
-        final int mod = method.getModifiers();
-        final int access = ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
-        final Class<?>[] exceptions = method.getExceptionTypes();
-        final String[] exceptionNames = new String[exceptions.length];
-        for (int i = 0; i < exceptions.length; ++i) {
-            exceptionNames[i] = Type.getInternalName(exceptions[i]);
-        }
-        final MethodType type = mi.type;
-        final String methodDesc = type.toMethodDescriptorString();
-        final String name = mi.getName();
-
-        final Type asmType = Type.getMethodType(methodDesc);
-        final Type[] asmArgTypes = asmType.getArgumentTypes();
-
-        // Determine the first index for a local variable
-        int nextLocalVar = 1; // this
-        for(final Type t: asmArgTypes) {
-            nextLocalVar += t.getSize();
-        }
-
-        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(access, name, methodDesc, null,
-                exceptionNames));
-        mv.visitCode();
-
-        final Label methodHandleNotNull = new Label();
-        final Label methodEnd = new Label();
-
-        final Type returnType = Type.getType(type.returnType());
-
-        // Get the method handle
-        mv.visitVarInsn(ALOAD, 0);
-        mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
-        mv.visitInsn(DUP); // It'll remain on the stack all the way until the invocation
-        // Check if the method handle is null
-        mv.visitJumpInsn(IFNONNULL, methodHandleNotNull);
-        if(Modifier.isAbstract(mod)) {
-            // If it's null, and the method is abstract, throw an exception
-            mv.anew(UNSUPPORTED_OPERATION_TYPE);
-            mv.dup();
-            mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG);
-            mv.athrow();
-        } else {
-            // If it's null, and the method is not abstract, delegate to super method.
-            mv.visitVarInsn(ALOAD, 0);
-            int nextParam = 1;
-            for(final Type t: asmArgTypes) {
-                mv.load(nextParam, t);
-                nextParam += t.getSize();
-            }
-            mv.invokespecial(superClassName, name, methodDesc);
-            mv.areturn(returnType);
-        }
-
-        mv.visitLabel(methodHandleNotNull);
-        final int currentGlobalVar = nextLocalVar++;
-        final int globalsDifferVar = nextLocalVar++;
-
-        // Emit code for switching to the creating global
-        // ScriptObject currentGlobal = Context.getGlobal();
-        invokeGetGlobal(mv);
-        mv.dup();
-        mv.visitVarInsn(ASTORE, currentGlobalVar);
-        // if(this.global == currentGlobal) {
-        loadGlobalOnStack(mv);
-        final Label globalsDiffer = new Label();
-        mv.ifacmpne(globalsDiffer);
-        //     globalsDiffer = false
-        mv.iconst(0); // false
-        final Label proceed = new Label();
-        mv.goTo(proceed);
-        mv.visitLabel(globalsDiffer);
-        // } else {
-        //     Context.setGlobal(this.global);
-        loadGlobalOnStack(mv);
-        invokeSetGlobal(mv);
-        //     globalsDiffer = true
-        mv.iconst(1);
-
-        mv.visitLabel(proceed);
-        mv.visitVarInsn(ISTORE, globalsDifferVar);
-
-        // Load all parameters back on stack for dynamic invocation.
-        int varOffset = 1;
-        for (final Type t : asmArgTypes) {
-            mv.load(varOffset, t);
-            varOffset += t.getSize();
-        }
-
-        // Invoke the target method handle
-        final Label tryBlockStart = new Label();
-        mv.visitLabel(tryBlockStart);
-        mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString());
-        final Label tryBlockEnd = new Label();
-        mv.visitLabel(tryBlockEnd);
-        emitFinally(mv, currentGlobalVar, globalsDifferVar);
-        mv.areturn(returnType);
-
-        // If Throwable is not declared, we need an adapter from Throwable to RuntimeException
-        final boolean throwableDeclared = isThrowableDeclared(exceptions);
-        final Label throwableHandler;
-        if (!throwableDeclared) {
-            // Add "throw new RuntimeException(Throwable)" handler for Throwable
-            throwableHandler = new Label();
-            mv.visitLabel(throwableHandler);
-            mv.anew(RUNTIME_EXCEPTION_TYPE);
-            mv.dupX1();
-            mv.swap();
-            mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE));
-            // Fall through to rethrow handler
-        } else {
-            throwableHandler = null;
-        }
-        final Label rethrowHandler = new Label();
-        mv.visitLabel(rethrowHandler);
-        // Rethrow handler for RuntimeException, Error, and all declared exception types
-        emitFinally(mv, currentGlobalVar, globalsDifferVar);
-        mv.athrow();
-        mv.visitLabel(methodEnd);
-
-        mv.visitLocalVariable("currentGlobal", SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, methodHandleNotNull, methodEnd, currentGlobalVar);
-        mv.visitLocalVariable("globalsDiffer", Type.INT_TYPE.getDescriptor(), null, methodHandleNotNull, methodEnd, globalsDifferVar);
-
-        if(throwableDeclared) {
-            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME);
-            assert throwableHandler == null;
-        } else {
-            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
-            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME);
-            for(final String excName: exceptionNames) {
-                mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName);
-            }
-            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
-        }
-        mv.visitMaxs(0, 0);
-        mv.visitEnd();
-    }
-
-    /**
-     * Emit code to restore the previous Nashorn Context when needed.
-     * @param mv the instruction adapter
-     * @param currentGlobalVar index of the local variable holding the reference to the current global at method
-     * entry.
-     * @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored.
-     */
-    private void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) {
-        // Emit code to restore the previous Nashorn global if needed
-        mv.visitVarInsn(ILOAD, globalsDifferVar);
-        final Label skip = new Label();
-        mv.ifeq(skip);
-        mv.visitVarInsn(ALOAD, currentGlobalVar);
-        invokeSetGlobal(mv);
-        mv.visitLabel(skip);
-    }
-
-    private void loadGlobalOnStack(final InstructionAdapter mv) {
-        mv.visitVarInsn(ALOAD, 0);
-        mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-    }
-
-    private static boolean isThrowableDeclared(final Class<?>[] exceptions) {
-        for (final Class<?> exception : exceptions) {
-            if (exception == Throwable.class) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Gathers methods that can be implemented or overridden from the specified type into this factory's
-     * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from
-     * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its
-     * superclass and the interfaces it implements, and add further methods that were not directly declared on the
-     * class.
-     * @param type the type defining the methods.
-     */
-    private void gatherMethods(final Class<?> type) {
-        if (Modifier.isPublic(type.getModifiers())) {
-            final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
-
-            for (final Method typeMethod: typeMethods) {
-                final int m = typeMethod.getModifiers();
-                if (Modifier.isStatic(m)) {
-                    continue;
-                }
-                if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
-                    final MethodInfo mi = new MethodInfo(typeMethod);
-                    if (Modifier.isFinal(m)) {
-                        finalMethods.add(mi);
-                    } else if (!finalMethods.contains(mi) && methodInfos.add(mi)) {
-                        if (Modifier.isAbstract(m)) {
-                            abstractMethodNames.add(mi.getName());
-                        }
-                        mi.setIsCanonical(usedFieldNames);
-                    }
-                }
-            }
-        }
-        // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done.
-        // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to
-        // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a
-        // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and
-        // getMethods() does provide those declared in a superinterface.
-        if (!type.isInterface()) {
-            final Class<?> superType = type.getSuperclass();
-            if (superType != null) {
-                gatherMethods(superType);
-            }
-            for (final Class<?> itf: type.getInterfaces()) {
-                gatherMethods(itf);
-            }
-        }
-    }
-
-    private void gatherMethods(final List<Class<?>> classes) {
-        for(final Class<?> c: classes) {
-            gatherMethods(c);
-        }
-    }
-
-    /**
-     * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters,
-     * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and
-     * {@code Object.clone()}.
-     * @return a collection of method infos representing those methods that we never override in adapter classes.
-     */
-    private static Collection<MethodInfo> getExcludedMethods() {
-        return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() {
-            @Override
-            public Collection<MethodInfo> run() {
-                try {
-                    return Arrays.asList(
-                            new MethodInfo(Object.class, "finalize"),
-                            new MethodInfo(Object.class, "clone"));
-                } catch (final NoSuchMethodException e) {
-                    throw new AssertionError(e);
-                }
-            }
-        });
-    }
-
-    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);
-    }
-
-    private static class AdapterInfo {
-        final StaticClass adapterClass;
-        final boolean autoConvertibleFromFunction;
-        final AnnotatedAdaptationOutcome adaptationOutcome;
-
-        AdapterInfo(final StaticClass adapterClass, final boolean autoConvertibleFromFunction) {
-            this.adapterClass = adapterClass;
-            this.autoConvertibleFromFunction = autoConvertibleFromFunction;
-            this.adaptationOutcome = AnnotatedAdaptationOutcome.SUCCESS;
-        }
-
-        AdapterInfo(final AdaptationOutcome outcome, final String classList) {
-            this(new AnnotatedAdaptationOutcome(outcome, classList));
-        }
-
-        AdapterInfo(final AnnotatedAdaptationOutcome adaptationOutcome) {
-            this.adapterClass = null;
-            this.autoConvertibleFromFunction = false;
-            this.adaptationOutcome = adaptationOutcome;
-        }
-    }
-
-    /**
-     * An adaptation outcome accompanied with a name of a class (or a list of multiple class names) that are the reason
-     * an adapter could not be generated.
-     */
-    private static class AnnotatedAdaptationOutcome {
-        static final AnnotatedAdaptationOutcome SUCCESS = new AnnotatedAdaptationOutcome(AdaptationOutcome.SUCCESS, "");
-
-        private final AdaptationOutcome adaptationOutcome;
-        private final String classList;
-
-        AnnotatedAdaptationOutcome(final AdaptationOutcome adaptationOutcome, final String classList) {
-            this.adaptationOutcome = adaptationOutcome;
-            this.classList = classList;
-        }
-
-        void typeError() {
-            assert adaptationOutcome != AdaptationOutcome.SUCCESS;
-            throw ECMAErrors.typeError("extend." + adaptationOutcome, classList);
-        }
-    }
-
-    /**
      * For a given class, create its adapter class and associated info.
      * @param type the class for which the adapter is created
      * @return the adapter info for the class.
@@ -1130,17 +179,17 @@
             final int mod = t.getModifiers();
             if(!t.isInterface()) {
                 if(superClass != null) {
-                    return new AdapterInfo(AdaptationOutcome.ERROR_MULTIPLE_SUPERCLASSES, t.getCanonicalName() + " and " + superClass.getCanonicalName());
+                    return new AdapterInfo(AdaptationResult.Outcome.ERROR_MULTIPLE_SUPERCLASSES, t.getCanonicalName() + " and " + superClass.getCanonicalName());
                 }
                 if (Modifier.isFinal(mod)) {
-                    return new AdapterInfo(AdaptationOutcome.ERROR_FINAL_CLASS, t.getCanonicalName());
+                    return new AdapterInfo(AdaptationResult.Outcome.ERROR_FINAL_CLASS, t.getCanonicalName());
                 }
                 superClass = t;
             } else {
                 interfaces.add(t);
             }
             if(!Modifier.isPublic(mod)) {
-                return new AdapterInfo(AdaptationOutcome.ERROR_NON_PUBLIC_CLASS, t.getCanonicalName());
+                return new AdapterInfo(AdaptationResult.Outcome.ERROR_NON_PUBLIC_CLASS, t.getCanonicalName());
             }
         }
         final Class<?> effectiveSuperClass = superClass == null ? Object.class : superClass;
@@ -1148,211 +197,78 @@
             @Override
             public AdapterInfo run() {
                 try {
-                    final JavaAdapterFactory factory = new JavaAdapterFactory(effectiveSuperClass, interfaces, definingClassAndLoader);
-                    return new AdapterInfo(StaticClass.forClass(factory.generateClass()),
-                            factory.isAutoConvertibleFromFunction());
+                    return new AdapterInfo(effectiveSuperClass, interfaces, definingClassAndLoader);
                 } catch (final AdaptationException e) {
-                    return new AdapterInfo(e.outcome);
+                    return new AdapterInfo(e.getAdaptationResult());
                 }
             }
         });
     }
 
-    @SuppressWarnings("serial")
-    private static class AdaptationException extends Exception {
-        private final AnnotatedAdaptationOutcome outcome;
-        AdaptationException(final AdaptationOutcome outcome, final String classList) {
-            this.outcome = new AnnotatedAdaptationOutcome(outcome, classList);
-        }
-    }
-
-    private String getCommonSuperClass(final String type1, final String type2) {
-        try {
-            final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader);
-            final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader);
-            if (c1.isAssignableFrom(c2)) {
-                return type1;
-            }
-            if (c2.isAssignableFrom(c1)) {
-                return type2;
-            }
-            if (c1.isInterface() || c2.isInterface()) {
-                return "java/lang/Object";
-            }
-            return assignableSuperClass(c1, c2).getName().replace('.', '/');
-        } catch(final ClassNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) {
-        final Class<?> superClass = c1.getSuperclass();
-        return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2);
-    }
+    private static class AdapterInfo {
+        private static final ClassAndLoader SCRIPT_OBJECT_LOADER = new ClassAndLoader(ScriptObject.class, true);
 
-    /**
-     * Choose between the passed class loader and the class loader that defines the ScriptObject class, based on which
-     * of the two can see the classes in both.
-     * @param classAndLoader the loader and a representative class from it that will be used to add the generated
-     * adapter to its ADAPTER_INFO_MAPS.
-     * @return the class loader that sees both the specified class and Nashorn classes.
-     * @throws IllegalStateException if no such class loader is found.
-     */
-    private static ClassLoader findCommonLoader(final ClassAndLoader classAndLoader) throws AdaptationException {
-        final ClassLoader loader = classAndLoader.getLoader();
-        if (canSeeClass(loader, ScriptObject.class)) {
-            return loader;
-        }
-
-        final ClassLoader nashornLoader = ScriptObject.class.getClassLoader();
-        if(canSeeClass(nashornLoader, classAndLoader.clazz)) {
-            return nashornLoader;
-        }
+        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;
+        final boolean autoConvertibleFromFunction;
+        final AdaptationResult adaptationResult;
 
-        throw new AdaptationException(AdaptationOutcome.ERROR_NO_COMMON_LOADER, classAndLoader.clazz.getCanonicalName());
-    }
-
-    private static boolean canSeeClass(final ClassLoader cl, final Class<?> clazz) {
-        try {
-            return Class.forName(clazz.getName(), false, cl) == clazz;
-        } catch (final ClassNotFoundException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Given a list of types that define the superclass/interfaces for an adapter class, returns a single type from the
-     * list that will be used to attach the adapter to its ClassValue. The first type in the array that is defined in a
-     * class loader that can also see all other types is returned. If there is no such loader, an exception is thrown.
-     * @param types the input types
-     * @return the first type from the array that is defined in a class loader that can also see all other types.
-     */
-    private static ClassAndLoader getDefiningClassAndLoader(final Class<?>[] types) {
-        // Short circuit the cheap case
-        if(types.length == 1) {
-            return new ClassAndLoader(types[0], false);
+        AdapterInfo(Class<?> superClass, List<Class<?>> interfaces, ClassAndLoader definingLoader) throws AdaptationException {
+            this.commonLoader = findCommonLoader(definingLoader);
+            final JavaAdapterBytecodeGenerator gen = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, false);
+            this.autoConvertibleFromFunction = gen.isAutoConvertibleFromFunction();
+            this.instanceAdapterClass = gen.createAdapterClassLoader().generateClass(commonLoader);
+            this.adapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader();
+            this.adaptationResult = AdaptationResult.SUCCESSFUL_RESULT;
         }
 
-        return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() {
-            @Override
-            public ClassAndLoader run() {
-                return getDefiningClassAndLoaderPrivileged(types);
-            }
-        });
-    }
+        AdapterInfo(final AdaptationResult.Outcome outcome, final String classList) {
+            this(new AdaptationResult(outcome, classList));
+        }
 
-    private static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) {
-        final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types);
-
-        final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
-        if(maximumVisibilityLoaders.size() == 1) {
-            // Fortunate case - single maximally specific class loader; return its representative class.
-            return it.next();
+        AdapterInfo(final AdaptationResult adaptationResult) {
+            this.commonLoader = null;
+            this.adapterGenerator = null;
+            this.instanceAdapterClass = null;
+            this.autoConvertibleFromFunction = false;
+            this.adaptationResult = adaptationResult;
         }
 
-        // Ambiguity; throw an error.
-        assert maximumVisibilityLoaders.size() > 1; // basically, can't be zero
-        final StringBuilder b = new StringBuilder();
-        b.append(it.next().clazz.getCanonicalName());
-        while(it.hasNext()) {
-            b.append(", ").append(it.next().clazz.getCanonicalName());
-        }
-        throw typeError("extend.ambiguous.defining.class", b.toString());
-    }
-
-    /**
-     * Given an array of types, return a subset of their class loaders that are maximal according to the
-     * "can see other loaders' classes" relation, which is presumed to be a partial ordering.
-     * @param types types
-     * @return a collection of maximum visibility class loaders. It is guaranteed to have at least one element.
-     */
-    private static Collection<ClassAndLoader> getMaximumVisibilityLoaders(final Class<?>[] types) {
-        final List<ClassAndLoader> maximumVisibilityLoaders = new LinkedList<>();
-        outer:  for(final ClassAndLoader maxCandidate: getClassLoadersForTypes(types)) {
-            final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
-            while(it.hasNext()) {
-                final ClassAndLoader existingMax = it.next();
-                final boolean candidateSeesExisting = canSeeClass(maxCandidate.getRetrievedLoader(), existingMax.clazz);
-                final boolean exitingSeesCandidate = canSeeClass(existingMax.getRetrievedLoader(), maxCandidate.clazz);
-                if(candidateSeesExisting) {
-                    if(!exitingSeesCandidate) {
-                        // The candidate sees the the existing maximum, so drop the existing one as it's no longer maximal.
-                        it.remove();
-                    }
-                    // NOTE: there's also the anomalous case where both loaders see each other. Not sure what to do
-                    // about that one, as two distinct class loaders both seeing each other's classes is weird and
-                    // violates the assumption that the relation "sees others' classes" is a partial ordering. We'll
-                    // just not do anything, and treat them as incomparable; hopefully some later class loader that
-                    // comes along can eliminate both of them, if it can not, we'll end up with ambiguity anyway and
-                    // throw an error at the end.
-                } else if(exitingSeesCandidate) {
-                    // Existing sees the candidate, so drop the candidate.
-                    continue outer;
-                }
+        StaticClass getAdapterClassFor(ScriptObject classOverrides) {
+            if(adaptationResult.getOutcome() != AdaptationResult.Outcome.SUCCESS) {
+                throw adaptationResult.typeError();
             }
-            // If we get here, no existing maximum visibility loader could see the candidate, so the candidate is a new
-            // maximum.
-            maximumVisibilityLoaders.add(maxCandidate);
-        }
-        return maximumVisibilityLoaders;
-    }
-
-    private static Collection<ClassAndLoader> getClassLoadersForTypes(final Class<?>[] types) {
-        final Map<ClassAndLoader, ClassAndLoader> classesAndLoaders = new LinkedHashMap<>();
-        for(final Class<?> c: types) {
-            final ClassAndLoader cl = new ClassAndLoader(c, true);
-            if(!classesAndLoaders.containsKey(cl)) {
-                classesAndLoaders.put(cl, cl);
+            if(classOverrides == null) {
+                return instanceAdapterClass;
             }
-        }
-        return classesAndLoaders.keySet();
-    }
-
-    /**
-     * A tuple of a class loader and a single class representative of the classes that can be loaded through it. Its
-     * equals/hashCode is defined in terms of the identity of the class loader.
-     */
-    private static final class ClassAndLoader {
-        private final Class<?> clazz;
-        // Don't access this directly; most of the time, use getRetrievedLoader(), or if you know what you're doing,
-        // getLoader().
-        private ClassLoader loader;
-        // We have mild affinity against eagerly retrieving the loader, as we need to do it in a privileged block. For
-        // the most basic case of looking up an already-generated adapter info for a single type, we avoid it.
-        private boolean loaderRetrieved;
-
-        ClassAndLoader(final Class<?> clazz, final boolean retrieveLoader) {
-            this.clazz = clazz;
-            if(retrieveLoader) {
-                retrieveLoader();
+            JavaAdapterServices.setClassOverrides(classOverrides);
+            try {
+                return adapterGenerator.generateClass(commonLoader);
+            } finally {
+                JavaAdapterServices.setClassOverrides(null);
             }
         }
 
-        ClassLoader getLoader() {
-            if(!loaderRetrieved) {
-                retrieveLoader();
+        /**
+         * Choose between the passed class loader and the class loader that defines the ScriptObject class, based on which
+         * of the two can see the classes in both.
+         * @param classAndLoader the loader and a representative class from it that will be used to add the generated
+         * adapter to its ADAPTER_INFO_MAPS.
+         * @return the class loader that sees both the specified class and Nashorn classes.
+         * @throws IllegalStateException if no such class loader is found.
+         */
+        private static ClassLoader findCommonLoader(final ClassAndLoader classAndLoader) throws AdaptationException {
+            if(classAndLoader.canSee(SCRIPT_OBJECT_LOADER)) {
+                return classAndLoader.getLoader();
             }
-            return getRetrievedLoader();
-        }
-
-        ClassLoader getRetrievedLoader() {
-            assert loaderRetrieved;
-            return loader;
-        }
+            if (SCRIPT_OBJECT_LOADER.canSee(classAndLoader)) {
+                return SCRIPT_OBJECT_LOADER.getLoader();
+            }
 
-        private void retrieveLoader() {
-            loader = clazz.getClassLoader();
-            loaderRetrieved = true;
-        }
-
-        @Override
-        public boolean equals(final Object obj) {
-            return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader();
-        }
-
-        @Override
-        public int hashCode() {
-            return System.identityHashCode(getRetrievedLoader());
+            throw new AdaptationException(AdaptationResult.Outcome.ERROR_NO_COMMON_LOADER, classAndLoader.getRepresentativeClass().getCanonicalName());
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterGeneratorBase.java	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,55 @@
+/*
+ * 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.internal.runtime.linker;
+
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ScriptObject;
+
+/**
+ * Base class for both {@link JavaAdapterBytecodeGenerator} and {@link JavaAdapterClassLoader}, containing those
+ * bytecode types, type names and method descriptor that are used by both.
+ */
+abstract class JavaAdapterGeneratorBase {
+    static final Type CONTEXT_TYPE       = Type.getType(Context.class);
+    static final Type OBJECT_TYPE        = Type.getType(Object.class);
+    static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class);
+
+    static final String CONTEXT_TYPE_NAME = CONTEXT_TYPE.getInternalName();
+    static final String OBJECT_TYPE_NAME  = OBJECT_TYPE.getInternalName();
+
+    static final String INIT = "<init>";
+
+    static final String GLOBAL_FIELD_NAME = "global";
+
+    static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor();
+
+    static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, SCRIPT_OBJECT_TYPE);
+    static final String VOID_NOARG_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE);
+
+    protected JavaAdapterGeneratorBase() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,114 @@
+/*
+ * 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.internal.runtime.linker;
+
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.Undefined;
+
+/**
+ * Provides static utility services to generated Java adapter classes.
+ */
+public class JavaAdapterServices {
+    private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>();
+
+    private JavaAdapterServices() {
+    }
+
+    /**
+     * Given a JS script function, binds it to null JS "this", and adapts its parameter types, return types, and arity
+     * to the specified type and arity. This method is public mainly for implementation reasons, so the adapter classes
+     * can invoke it from their constructors that take a ScriptFunction in its first argument to obtain the method
+     * handles for their abstract method implementations.
+     * @param fn the script function
+     * @param type the method type it has to conform to
+     * @param varArg if the Java method for which the function is being adapted is a variable arity method
+     * @return the appropriately adapted method handle for invoking the script function.
+     */
+    public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type, final boolean varArg) {
+        // JS "this" will be null for SAMs
+        return adaptHandle(fn.getBoundInvokeHandle(null), type, varArg);
+    }
+
+    /**
+     * Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and
+     * adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly
+     * for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object
+     * in its first argument to obtain the method handles for their method implementations.
+     * @param obj the script obj
+     * @param name the name of the property that contains the function
+     * @param type the method type it has to conform to
+     * @param varArg if the Java method for which the function is being adapted is a variable arity method
+     * @return the appropriately adapted method handle for invoking the script function, or null if the value of the
+     * property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly
+     * define it but just inherits it through prototype.
+     */
+    public static MethodHandle getHandle(final Object obj, final String name, final MethodType type, final boolean varArg) {
+        if (! (obj instanceof ScriptObject)) {
+            throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
+        }
+
+        final ScriptObject sobj = (ScriptObject)obj;
+        // Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified
+        if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) {
+            return null;
+        }
+
+        final Object fnObj = sobj.get(name);
+        if (fnObj instanceof ScriptFunction) {
+            return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type, varArg);
+        } else if(fnObj == null || fnObj instanceof Undefined) {
+            return null;
+        } else {
+            throw typeError("not.a.function", name);
+        }
+    }
+
+    /**
+     * Returns a thread-local JS object used to define methods for the adapter class being initialized on the current
+     * thread. This method is public solely for implementation reasons, so the adapter classes can invoke it from their
+     * static initializers.
+     * @return the thread-local JS object used to define methods for the class being initialized.
+     */
+    public static ScriptObject getClassOverrides() {
+        final ScriptObject overrides = classOverrides.get();
+        assert overrides != null;
+        return overrides;
+    }
+
+    static void setClassOverrides(ScriptObject overrides) {
+        classOverrides.set(overrides);
+    }
+
+    private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type, final boolean varArg) {
+        return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, varArg), type);
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java	Thu Apr 04 13:54:51 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java	Thu Apr 04 15:53:26 2013 +0200
@@ -29,6 +29,7 @@
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Modifier;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.ConversionComparator;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -131,10 +132,22 @@
     }
 
     private static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
-        return JavaAdapterFactory.isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
+        return isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
                 JavaAdapterFactory.isAutoConvertibleFromFunction(clazz);
     }
 
+    /**
+     * Utility method used by few other places in the code. Tests if the class has the abstract modifier and is not an
+     * array class. For some reason, array classes have the abstract modifier set in HotSpot JVM, and we don't want to
+     * treat array classes as abstract.
+     * @param clazz the inspected class
+     * @return true if the class is abstract and is not an array type.
+     */
+    static boolean isAbstractClass(final Class<?> clazz) {
+        return Modifier.isAbstract(clazz.getModifiers()) && !clazz.isArray();
+    }
+
+
     @Override
     public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
         if(ScriptObject.class.isAssignableFrom(sourceType)) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java	Thu Apr 04 13:54:51 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java	Thu Apr 04 15:53:26 2013 +0200
@@ -68,10 +68,10 @@
         if ("new".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
             final Class<?> receiverClass = ((StaticClass) self).getRepresentedClass();
             // Is the class abstract? (This includes interfaces.)
-            if (JavaAdapterFactory.isAbstractClass(receiverClass)) {
+            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 });
+                args[0] = JavaAdapterFactory.getAdapterClassFor(new Class<?>[] { receiverClass }, null);
                 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/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Thu Apr 04 13:54:51 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Thu Apr 04 15:53:26 2013 +0200
@@ -113,6 +113,7 @@
 type.error.cant.convert.to.java.number=Cannot convert object of type {0} to a Java argument of number type
 type.error.cant.convert.to.javascript.array=Can only convert Java arrays and lists to JavaScript arrays. Can't convert object of type {0}.
 type.error.extend.expects.at.least.one.argument=Java.extend needs at least one argument.
+type.error.extend.expects.at.least.one.type.argument=Java.extend needs at least one type argument.
 type.error.extend.expects.java.types=Java.extend needs Java types as its arguments.
 type.error.extend.ambiguous.defining.class=There is no class loader that can see all of {0} at once.
 type.error.extend.ERROR_FINAL_CLASS=Can not extend final class {0}.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/javaclassoverrides.js	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * Check behavior of class-level overrides.
+ *
+ * @test
+ * @run
+ */
+
+
+// Make two classes with class overrides
+
+var R1 = Java.extend(java.lang.Runnable, {
+    run: function() {
+        print("R1.run() invoked")
+    }
+})
+
+var R2 = Java.extend(java.lang.Runnable, {
+    run: function() {
+        print("R2.run() invoked")
+    }
+})
+
+var r1 = new R1
+var r2 = new R2
+// Create one with an instance-override too
+var r3 = new R2(function() { print("r3.run() invoked") })
+
+// Run 'em - we're passing them through a Thread to make sure they indeed
+// are full-blown Runnables
+function runInThread(r) {
+    var t = new java.lang.Thread(r)
+    t.start()
+    t.join()
+}
+runInThread(r1)
+runInThread(r2)
+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))
+
+function checkAbstract(r) {
+    try {
+        r.run()
+        print("Expected to fail!")
+    } catch(e) {
+        print("Got exception: " + e)
+    }
+}
+
+// Check we're hitting UnsupportedOperationException if neither class
+// 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)
+
+// 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/javaclassoverrides.js.EXPECTED	Thu Apr 04 15:53:26 2013 +0200
@@ -0,0 +1,9 @@
+R1.run() invoked
+R2.run() invoked
+r3.run() invoked
+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