blob: 79a190fe4165a7182cf15b65216be4888b9eb153 [file] [log] [blame]
/*
* 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.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ListAdapter;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
/**
* This class is the implementation for the {@code Java} global object exposed to programs running under Nashorn. This
* object acts as the API entry point to Java platform specific functionality, dealing with creating new instances of
* Java classes, subclassing Java classes, implementing Java interfaces, converting between Java arrays and ECMAScript
* arrays, and so forth.
*/
@ScriptClass("Java")
public final class NativeJava {
// initialized by nasgen
@SuppressWarnings("unused")
private static PropertyMap $nasgenmap$;
private NativeJava() {
// don't create me
throw new UnsupportedOperationException();
}
/**
* Returns true if the specified object is a Java type object, that is an instance of {@link StaticClass}.
* @param self not used
* @param type the object that is checked if it is a type object or not
* @return tells whether given object is a Java type object or not.
* @see #type(Object, Object)
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean isType(final Object self, final Object type) {
return type instanceof StaticClass;
}
/**
* Returns synchronized wrapper version of the given ECMAScript function.
* @param self not used
* @param func the ECMAScript function whose synchronized version is returned.
* @param obj the object (i.e, lock) on which the function synchronizes.
* @return synchronized wrapper version of the given ECMAScript function.
*/
@Function(name="synchronized", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object synchronizedFunc(final Object self, final Object func, final Object obj) {
if (func instanceof ScriptFunction) {
return ((ScriptFunction)func).createSynchronized(obj);
}
throw typeError("not.a.function", ScriptRuntime.safeToString(func));
}
/**
* Returns true if the specified object is a Java method.
* @param self not used
* @param obj the object that is checked if it is a Java method object or not
* @return tells whether given object is a Java method object or not.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean isJavaMethod(final Object self, final Object obj) {
return Bootstrap.isDynamicMethod(obj);
}
/**
* Returns true if the specified object is a java function (but not script function)
* @param self not used
* @param obj the object that is checked if it is a Java function or not
* @return tells whether given object is a Java function or not
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean isJavaFunction(final Object self, final Object obj) {
return Bootstrap.isCallable(obj) && !(obj instanceof ScriptFunction);
}
/**
* Returns true if the specified object is a Java object but not a script object
* @param self not used
* @param obj the object that is checked
* @return tells whether given object is a Java object but not a script object
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean isJavaObject(final Object self, final Object obj) {
return obj != null && !(obj instanceof ScriptObject);
}
/**
* Returns true if the specified object is a ECMAScript object, that is an instance of {@link ScriptObject}.
* @param self not used
* @param obj the object that is checked if it is a ECMAScript object or not
* @return tells whether given object is a ECMAScript object or not.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean isScriptObject(final Object self, final Object obj) {
return obj instanceof ScriptObject;
}
/**
* Returns true if the specified object is a ECMAScript function, that is an instance of {@link ScriptFunction}.
* @param self not used
* @param obj the object that is checked if it is a ECMAScript function or not
* @return tells whether given object is a ECMAScript function or not.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean isScriptFunction(final Object self, final Object obj) {
return obj instanceof ScriptFunction;
}
/**
* <p>
* Given a name of a Java type, returns an object representing that type in Nashorn. The Java class of the objects
* used to represent Java types in Nashorn is not {@link java.lang.Class} but rather {@link StaticClass}. They are
* the objects that you can use with the {@code new} operator to create new instances of the class as well as to
* access static members of the class. In Nashorn, {@code Class} objects are just regular Java objects that aren't
* treated specially. Instead of them, {@link StaticClass} instances - which we sometimes refer to as "Java type
* objects" are used as constructors with the {@code new} operator, and they expose static fields, properties, and
* methods. While this might seem confusing at first, it actually closely matches the Java language: you use a
* different expression (e.g. {@code java.io.File}) as an argument in "new" and to address statics, and it is
* distinct from the {@code Class} object (e.g. {@code java.io.File.class}). Below we cover in details the
* properties of the type objects.
* </p>
* <p><b>Constructing Java objects</b></p>
* Examples:
* <pre>
* var arrayListType = Java.type("java.util.ArrayList")
* var intType = Java.type("int")
* var stringArrayType = Java.type("java.lang.String[]")
* var int2DArrayType = Java.type("int[][]")
* </pre>
* Note that the name of the type is always a string for a fully qualified name. You can use any of these types to
* create new instances, e.g.:
* <pre>
* var anArrayList = new Java.type("java.util.ArrayList")
* </pre>
* or
* <pre>
* var ArrayList = Java.type("java.util.ArrayList")
* var anArrayList = new ArrayList
* var anArrayListWithSize = new ArrayList(16)
* </pre>
* In the special case of inner classes, you can either use the JVM fully qualified name, meaning using {@code $}
* sign in the class name, or you can use the dot:
* <pre>
* var ftype = Java.type("java.awt.geom.Arc2D$Float")
* </pre>
* and
* <pre>
* var ftype = Java.type("java.awt.geom.Arc2D.Float")
* </pre>
* both work. Note however that using the dollar sign is faster, as Java.type first tries to resolve the class name
* as it is originally specified, and the internal JVM names for inner classes use the dollar sign. If you use the
* dot, Java.type will internally get a ClassNotFoundException and subsequently retry by changing the last dot to
* dollar sign. As a matter of fact, it'll keep replacing dots with dollar signs until it either successfully loads
* the class or runs out of all dots in the name. This way it can correctly resolve and load even multiply nested
* inner classes with the dot notation. Again, this will be slower than using the dollar signs in the name. An
* alternative way to access the inner class is as a property of the outer class:
* <pre>
* var arctype = Java.type("java.awt.geom.Arc2D")
* var ftype = arctype.Float
* </pre>
* <p>
* You can access both static and non-static inner classes. If you want to create an instance of a non-static
* inner class, remember to pass an instance of its outer class as the first argument to the constructor.
* </p>
* <p>
* If the type is abstract, you can instantiate an anonymous subclass of it using an argument list that is
* applicable to any of its public or protected constructors, but inserting a JavaScript object with functions
* properties that provide JavaScript implementations of the abstract methods. If method names are overloaded, the
* JavaScript function will provide implementation for all overloads. E.g.:
* </p>
* <pre>
* var TimerTask = Java.type("java.util.TimerTask")
* var task = new TimerTask({ run: function() { print("Hello World!") } })
* </pre>
* <p>
* Nashorn supports a syntactic extension where a "new" expression followed by an argument is identical to
* invoking the constructor and passing the argument to it, so you can write the above example also as:
* </p>
* <pre>
* var task = new TimerTask {
* run: function() {
* print("Hello World!")
* }
* }
* </pre>
* <p>
* which is very similar to Java anonymous inner class definition. On the other hand, if the type is an abstract
* type with a single abstract method (commonly referred to as a "SAM type") or all abstract methods it has share
* the same overloaded name), then instead of an object, you can just pass a function, so the above example can
* become even more simplified to:
* </p>
* <pre>
* var task = new TimerTask(function() { print("Hello World!") })
* </pre>
* <p>
* Note that in every one of these cases if you are trying to instantiate an abstract class that has constructors
* that take some arguments, you can invoke those simply by specifying the arguments after the initial
* implementation object or function.
* </p>
* <p>The use of functions can be taken even further; if you are invoking a Java method that takes a SAM type,
* you can just pass in a function object, and Nashorn will know what you meant:
* </p>
* <pre>
* var timer = new Java.type("java.util.Timer")
* timer.schedule(function() { print("Hello World!") })
* </pre>
* <p>
* Here, {@code Timer.schedule()} expects a {@code TimerTask} as its argument, so Nashorn creates an instance of a
* {@code TimerTask} subclass and uses the passed function to implement its only abstract method, {@code run()}. In
* 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.
* </p>
* <p>
* You can also subclass non-abstract classes; for that you will need to use the {@link #extend(Object, Object...)}
* method.
* </p>
* <p><b>Accessing static members</b></p>
* Examples:
* <pre>
* var File = Java.type("java.io.File")
* var pathSep = File.pathSeparator
* var tmpFile1 = File.createTempFile("abcdefg", ".tmp")
* var tmpFile2 = File.createTempFile("abcdefg", ".tmp", new File("/tmp"))
* </pre>
* Actually, you can even assign static methods to variables, so the above example can be rewritten as:
* <pre>
* var File = Java.type("java.io.File")
* var createTempFile = File.createTempFile
* var tmpFile1 = createTempFile("abcdefg", ".tmp")
* var tmpFile2 = createTempFile("abcdefg", ".tmp", new File("/tmp"))
* </pre>
* If you need to access the actual {@code java.lang.Class} object for the type, you can use the {@code class}
* property on the object representing the type:
* <pre>
* var File = Java.type("java.io.File")
* var someFile = new File("blah")
* print(File.class === someFile.getClass()) // prints true
* </pre>
* Of course, you can also use the {@code getClass()} method or its equivalent {@code class} property on any
* instance of the class. Other way round, you can use the synthetic {@code static} property on any
* {@code java.lang.Class} object to retrieve its type-representing object:
* <pre>
* var File = Java.type("java.io.File")
* print(File.class.static === File) // prints true
* </pre>
* <p><b>{@code instanceof} operator</b></p>
* The standard ECMAScript {@code instanceof} operator is extended to recognize Java objects and their type objects:
* <pre>
* var File = Java.type("java.io.File")
* var aFile = new File("foo")
* print(aFile instanceof File) // prints true
* print(aFile instanceof File.class) // prints false - Class objects aren't type objects.
* </pre>
* @param self not used
* @param objTypeName the object whose JS string value represents the type name. You can use names of primitive Java
* types to obtain representations of them, and you can use trailing square brackets to represent Java array types.
* @return the object representing the named type
* @throws ClassNotFoundException if the class is not found
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object type(final Object self, final Object objTypeName) throws ClassNotFoundException {
return type(objTypeName);
}
private static StaticClass type(final Object objTypeName) throws ClassNotFoundException {
return StaticClass.forClass(type(JSType.toString(objTypeName)));
}
private static Class<?> type(final String typeName) throws ClassNotFoundException {
if (typeName.endsWith("[]")) {
return arrayType(typeName);
}
return simpleType(typeName);
}
/**
* Returns name of a java type {@link StaticClass}.
* @param self not used
* @param type the type whose name is returned
* @return name of the given type
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object typeName(final Object self, final Object type) {
if (type instanceof StaticClass) {
return ((StaticClass)type).getRepresentedClass().getName();
} else if (type instanceof Class) {
return ((Class<?>)type).getName();
} else {
return UNDEFINED;
}
}
/**
* Given a script object and a Java type, converts the script object into the desired Java type. Currently it
* performs shallow creation of Java arrays, as well as wrapping of objects in Lists, Dequeues, Queues,
* and Collections. If conversion is not possible or fails for some reason, TypeError is thrown.
* Example:
* <pre>
* var anArray = [1, "13", false]
* var javaIntArray = Java.to(anArray, "int[]")
* print(javaIntArray[0]) // prints 1
* print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
* print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
* </pre>
* @param self not used
* @param obj the script object. Can be null.
* @param objType either a {@link #type(Object, Object) type object} or a String describing the type of the Java
* object to create. Can not be null. If undefined, a "default" conversion is presumed (allowing the argument to be
* omitted).
* @return a Java object whose value corresponds to the original script object's value. Specifically, for array
* target types, returns a Java array of the same type with contents converted to the array's component type.
* Converts recursively when the target type is multidimensional array. For {@link List}, {@link Deque},
* {@link Queue}, or {@link Collection}, returns a live wrapper around the object, see {@link ListAdapter} for
* details. Returns null if obj is null.
* @throws ClassNotFoundException if the class described by objType is not found
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object to(final Object self, final Object obj, final Object objType) throws ClassNotFoundException {
if (obj == null) {
return null;
}
if (!(obj instanceof ScriptObject) && !(obj instanceof JSObject)) {
throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
}
final Class<?> targetClass;
if(objType == UNDEFINED) {
targetClass = Object[].class;
} else {
final StaticClass targetType;
if(objType instanceof StaticClass) {
targetType = (StaticClass)objType;
} else {
targetType = type(objType);
}
targetClass = targetType.getRepresentedClass();
}
if(targetClass.isArray()) {
try {
return JSType.toJavaArray(obj, targetClass.getComponentType());
} catch (final Exception exp) {
throw typeError(exp, "java.array.conversion.failed", targetClass.getName());
}
}
if (targetClass == List.class || targetClass == Deque.class || targetClass == Queue.class || targetClass == Collection.class) {
return ListAdapter.create(obj);
}
throw typeError("unsupported.java.to.type", targetClass.getName());
}
/**
* Given a Java array or {@link Collection}, returns a JavaScript array with a shallow copy of its contents. Note
* that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you
* need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will
* want to use this method. Example:
* <pre>
* var File = Java.type("java.io.File")
* var listHomeDir = new File("~").listFiles()
* var jsListHome = Java.from(listHomeDir)
* var jpegModifiedDates = jsListHome
* .filter(function(val) { return val.getName().endsWith(".jpg") })
* .map(function(val) { return val.lastModified() })
* </pre>
* @param self not used
* @param objArray the java array or collection. Can be null.
* @return a JavaScript array with the copy of Java array's or collection's contents. Returns null if objArray is
* null.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static NativeArray from(final Object self, final Object objArray) {
if (objArray == null) {
return null;
} else if (objArray instanceof Collection) {
return new NativeArray(((Collection<?>)objArray).toArray());
} else if (objArray instanceof Object[]) {
return new NativeArray(((Object[])objArray).clone());
} else if (objArray instanceof int[]) {
return new NativeArray(((int[])objArray).clone());
} else if (objArray instanceof double[]) {
return new NativeArray(((double[])objArray).clone());
} else if (objArray instanceof long[]) {
return new NativeArray(((long[])objArray).clone());
} else if (objArray instanceof byte[]) {
return new NativeArray(copyArray((byte[])objArray));
} else if (objArray instanceof short[]) {
return new NativeArray(copyArray((short[])objArray));
} else if (objArray instanceof char[]) {
return new NativeArray(copyArray((char[])objArray));
} else if (objArray instanceof float[]) {
return new NativeArray(copyArray((float[])objArray));
} else if (objArray instanceof boolean[]) {
return new NativeArray(copyArray((boolean[])objArray));
}
throw typeError("cant.convert.to.javascript.array", objArray.getClass().getName());
}
private static int[] copyArray(final byte[] in) {
final int[] out = new int[in.length];
for(int i = 0; i < in.length; ++i) {
out[i] = in[i];
}
return out;
}
private static int[] copyArray(final short[] in) {
final int[] out = new int[in.length];
for(int i = 0; i < in.length; ++i) {
out[i] = in[i];
}
return out;
}
private static int[] copyArray(final char[] in) {
final int[] out = new int[in.length];
for(int i = 0; i < in.length; ++i) {
out[i] = in[i];
}
return out;
}
private static double[] copyArray(final float[] in) {
final double[] out = new double[in.length];
for(int i = 0; i < in.length; ++i) {
out[i] = in[i];
}
return out;
}
private static Object[] copyArray(final boolean[] in) {
final Object[] out = new Object[in.length];
for(int i = 0; i < in.length; ++i) {
out[i] = in[i];
}
return out;
}
private static Class<?> simpleType(final String typeName) throws ClassNotFoundException {
final Class<?> primClass = TypeUtilities.getPrimitiveTypeByName(typeName);
if(primClass != null) {
return primClass;
}
final Context ctx = Global.getThisContext();
try {
return ctx.findClass(typeName);
} catch(final ClassNotFoundException e) {
// The logic below compensates for a frequent user error - when people use dot notation to separate inner
// class names, i.e. "java.lang.Character.UnicodeBlock" vs."java.lang.Character$UnicodeBlock". The logic
// below will try alternative class names, replacing dots at the end of the name with dollar signs.
final StringBuilder nextName = new StringBuilder(typeName);
int lastDot = nextName.length();
for(;;) {
lastDot = nextName.lastIndexOf(".", lastDot - 1);
if(lastDot == -1) {
// Exhausted the search space, class not found - rethrow the original exception.
throw e;
}
nextName.setCharAt(lastDot, '$');
try {
return ctx.findClass(nextName.toString());
} catch(final ClassNotFoundException cnfe) {
// Intentionally ignored, so the loop retries with the next name
}
}
}
}
private static Class<?> arrayType(final String typeName) throws ClassNotFoundException {
return Array.newInstance(type(typeName.substring(0, typeName.length() - 2)), 0).getClass();
}
/**
* Returns a type object for a subclass of the specified Java class (or implementation of the specified interface)
* that acts as a script-to-Java adapter for it. See {@link #type(Object, Object)} for a discussion of type objects,
* and see {@link JavaAdapterFactory} for details on script-to-Java adapters. Note that you can also implement
* interfaces and subclass abstract classes using {@code new} operator on a type object for an interface or abstract
* class. However, to extend a non-abstract class, you will have to use this method. Example:
* <pre>
* var ArrayList = Java.type("java.util.ArrayList")
* var ArrayListExtender = Java.extend(ArrayList)
* var printSizeInvokedArrayList = new ArrayListExtender() {
* size: function() { print("size invoked!"); }
* }
* var printAddInvokedArrayList = new ArrayListExtender() {
* add: function(x, y) {
* if(typeof(y) === "undefined") {
* print("add(e) invoked!");
* } else {
* print("add(i, e) invoked!");
* }
* }
* </pre>
* We can see several important concepts in the above example:
* <ul>
* <li>Every specified list of Java types will have one extender subclass in Nashorn per caller protection domain -
* repeated invocations of {@code extend} for the same list of types for scripts same protection domain will yield
* the same extender type. It's a generic adapter that delegates to whatever JavaScript functions its implementation
* object has on a per-instance basis.</li>
* <li>If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter
* must be prepared to deal with all overloads.</li>
* <li>To invoke super methods from adapters, call them on the adapter instance prefixing them with {@code super$},
* or use the special {@link #_super(Object, Object) super-adapter}.</li>
* <li>It is also possible to specify an ordinary JavaScript object as the last argument to {@code extend}. In that
* case, it is treated as a class-level override. {@code extend} will return an extender class where all instances
* will have the methods implemented by functions on that object, just as if that object were passed as the last
* 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. If you also want to add instance-level
* overrides on these objects, you will have to repeatedly use {@code extend()} to subclass the class-level adapter.
* For such adapters, the order of precedence is instance-level method, class-level method, superclass method, or
* {@code UnsupportedOperationException} if the superclass method is abstract. If we continue our previous example:
* <pre>
* var R2 = Java.extend(R1);
* var r2 = new R2(function() { print("r2.run() invoked!") })
* r2.run()
* </pre>
* We'll see it'll print {@code "r2.run() invoked!"}, thus overriding on instance-level the class-level behavior.
* Note that you must use {@code Java.extend} to explicitly create an instance-override adapter class from a
* class-override adapter class, as the class-override adapter class is no longer abstract.
* </li>
* </ul>
* @param self not used
* @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 - 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)
public static Object extend(final Object self, final Object... types) {
if(types == null || types.length == 0) {
throw typeError("extend.expects.at.least.one.argument");
}
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 < typesLen; ++i) {
stypes[i] = ((StaticClass)types[i]).getRepresentedClass();
}
} catch(final ClassCastException e) {
throw typeError("extend.expects.java.types");
}
// Note that while the public API documentation claims self is not used, we actually use it.
// ScriptFunction.findCallMethod will bind the lookup object into it, and we can then use that lookup when
// requesting the adapter class. Note that if Java.extend is invoked with no lookup object, it'll pass the
// public lookup which'll result in generation of a no-permissions adapter. A typical situation this can happen
// is when the extend function is bound.
final MethodHandles.Lookup lookup;
if(self instanceof MethodHandles.Lookup) {
lookup = (MethodHandles.Lookup)self;
} else {
lookup = MethodHandles.publicLookup();
}
return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides, lookup);
}
/**
* When given an object created using {@code Java.extend()} or equivalent mechanism (that is, any JavaScript-to-Java
* adapter), returns an object that can be used to invoke superclass methods on that object. E.g.:
* <pre>
* var cw = new FilterWriterAdapter(sw) {
* write: function(s, off, len) {
* s = capitalize(s, off, len)
* cw_super.write(s, 0, s.length())
* }
* }
* var cw_super = Java.super(cw)
* </pre>
* @param self the {@code Java} object itself - not used.
* @param adapter the original Java adapter instance for which the super adapter is created.
* @return a super adapter for the original adapter
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR, name="super")
public static Object _super(final Object self, final Object adapter) {
return Bootstrap.createSuperAdapter(adapter);
}
/**
* Returns an object that is compatible with Java JSON libraries expectations; namely, that if it itself, or any
* object transitively reachable through it is a JavaScript array, then such objects will be exposed as
* {@link JSObject} that also implements the {@link List} interface for exposing the array elements. An explicit
* API is required as otherwise Nashorn exposes all objects externally as {@link JSObject}s that also implement the
* {@link Map} interface instead. By using this method, arrays will be exposed as {@link List}s and all other
* objects as {@link Map}s.
* @param self not used
* @param obj the object to be exposed in a Java JSON library compatible manner.
* @return a wrapper around the object that will enforce Java JSON library compatible exposure.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object asJSONCompatible(final Object self, final Object obj) {
return ScriptObjectMirror.wrapAsJSONCompatible(obj, Context.getGlobal());
}
}