diff --git a/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java b/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java
index 31ec590daed..70103228212 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java
@@ -145,7 +145,7 @@ public class NativeJava {
* this usage though, you can't use non-default constructors; the type must be either an interface, or must have a
* protected or public no-arg constructor.
*
- * You can also subclass non-abstract classes; for that you will need to use the {@link #extend(Object, Object)}
+ * You can also subclass non-abstract classes; for that you will need to use the {@link #extend(Object, Object...)}
* method.
*
Accessing static members
* Examples:
@@ -371,15 +371,29 @@ public class NativeJava {
* must be prepared to deal with all overloads.
* You can't invoke {@code super.*()} from adapters for now.
* @param self not used
- * @param type the original type. Must be a 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.
- * @return a new {@link StaticClass} that represents the adapter for the original type.
+ * @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.
+ * @return a new {@link StaticClass} that represents the adapter for the original types.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
- public static Object extend(final Object self, final Object type) {
- if(!(type instanceof StaticClass)) {
- typeError(Global.instance(), "extend.expects.java.type");
+ public static Object extend(final Object self, final Object... types) {
+ if(types == null || types.length == 0) {
+ typeError(Global.instance(), "extend.expects.at.least.one.argument");
}
- return JavaAdapterFactory.getAdapterClassFor((StaticClass)type);
+ final Class>[] stypes = new Class>[types.length];
+ try {
+ for(int i = 0; i < types.length; ++i) {
+ stypes[i] = ((StaticClass)types[i]).getRepresentedClass();
+ }
+ } catch(final ClassCastException e) {
+ typeError(Global.instance(), "extend.expects.java.types");
+ }
+ return JavaAdapterFactory.getAdapterClassFor(stypes);
}
}
diff --git a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
index f6001dbf23d..c42b19c6c00 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
@@ -57,9 +57,17 @@ import java.security.PrivilegedAction;
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.org.objectweb.asm.ClassWriter;
@@ -69,6 +77,7 @@ 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;
@@ -78,43 +87,38 @@ import org.dynalang.dynalink.support.LinkRequestImpl;
/**
* A factory class that generates adapter classes. Adapter classes allow implementation of Java interfaces and
- * extending of Java classes from JavaScript. For every original Class object, exactly one adapter Class is generated
- * that either extends the original class or - if the original Class represents an interface - extends Object and
- * implements the interface represented by the original Class.
+ * 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.
*
* The adapter class is generated in a new secure class loader that inherits Nashorn's protection domain, and has either
- * the original Class' class loader or the Nashorn's class loader as its parent - the parent class loader is chosen so
- * that both the original Class and the Nashorn core classes are visible from it (as the adapter will have constant pool
- * references to ScriptObject and ScriptFunction classes). In case neither candidate class loader has visibility into
- * the other set of classes, an error is thrown.
+ * 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.
*
- * For every protected or public constructor in the extended class (which is either the original class, or Object when
- * an interface is implemented), 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 (from interface implementations too).
- * The only restriction is that since every JavaScript object already has a {@code toString} function through the
+ * 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.
*
- * For abstract classes or interfaces that only have one abstract method, or have several of them, but all share the
+ * 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".
*
- * If the superclass has a protected or public default constructor, then a generated constructor that only takes a
- * ScriptFunction is also implicitly used as an automatic conversion whenever a ScriptFunction is passed in an
- * invocation of any Java method that expects such SAM type.
- *
* 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.
*
@@ -125,7 +129,7 @@ import org.dynalang.dynalink.support.LinkRequestImpl;
* to resemble Java anonymous classes) is actually equivalent to new X(a, b, { ... })
.
*
* 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
+ * {@link NativeJava#extend(Object, Object...)}, using the {@code new} operator on abstract classes and interfaces (see
* {@link NativeJava#type(Object, Object)}), or implicitly when passing script functions to Java methods expecting SAM
* types.
*
@@ -169,25 +173,28 @@ public class JavaAdapterFactory {
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 = "";
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 three possible error outcomes: a class is final,
- * a class is not public, and the class has no public or protected constructor. 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.
+ * 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_NO_ACCESSIBLE_CONSTRUCTOR,
+ ERROR_MULTIPLE_SUPERCLASSES,
+ ERROR_NO_COMMON_LOADER
}
/**
@@ -198,27 +205,28 @@ public class JavaAdapterFactory {
/**
* A mapping from an original Class object to AdapterInfo representing the adapter for the class it represents.
*/
- private static final ClassValue ADAPTER_INFOS = new ClassValue() {
+ private static final ClassValue