| /* |
| * 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()); |
| } |
| } |