/* * 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.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Queue; import jdk.internal.dynalink.beans.BeansLinker; 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).makeSynchronizedFunction(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; } /** *
* 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. *
*Constructing Java objects
* Examples: ** var arrayListType = Java.type("java.util.ArrayList") * var intType = Java.type("int") * var stringArrayType = Java.type("java.lang.String[]") * var int2DArrayType = Java.type("int[][]") ** 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.: *
* var anArrayList = new Java.type("java.util.ArrayList") ** or *
* var ArrayList = Java.type("java.util.ArrayList") * var anArrayList = new ArrayList * var anArrayListWithSize = new ArrayList(16) ** 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: *
* var ftype = Java.type("java.awt.geom.Arc2D$Float") ** and *
* var ftype = Java.type("java.awt.geom.Arc2D.Float") ** 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: *
* var arctype = Java.type("java.awt.geom.Arc2D") * var ftype = arctype.Float **
* 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. *
** 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.: *
** var TimerTask = Java.type("java.util.TimerTask") * var task = new TimerTask({ run: function() { print("Hello World!") } }) **
* 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: *
** var task = new TimerTask { * run: function() { * print("Hello World!") * } * } **
* 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: *
** var task = new TimerTask(function() { print("Hello World!") }) **
* 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. *
*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: *
** var timer = new Java.type("java.util.Timer") * timer.schedule(function() { print("Hello World!") }) **
* 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. *
** You can also subclass non-abstract classes; for that you will need to use the {@link #extend(Object, Object...)} * method. *
*Accessing static members
* Examples: ** 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")) ** Actually, you can even assign static methods to variables, so the above example can be rewritten as: *
* var File = Java.type("java.io.File") * var createTempFile = File.createTempFile * var tmpFile1 = createTempFile("abcdefg", ".tmp") * var tmpFile2 = createTempFile("abcdefg", ".tmp", new File("/tmp")) ** 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: *
* var File = Java.type("java.io.File") * var someFile = new File("blah") * print(File.class === someFile.getClass()) // prints true ** 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: *
* var File = Java.type("java.io.File") * print(File.class.static === File) // prints true **
{@code instanceof} operator
* The standard ECMAScript {@code instanceof} operator is extended to recognize Java objects and their type objects: ** 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. ** @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. Example: *
* 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 ** @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()) { return JSType.toJavaArray(obj, targetClass.getComponentType()); } 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: *
* 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() }) ** @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()); } /** * Return properties of the given object. Properties also include "method names". * This is meant for source code completion in interactive shells or editors. * * @param object the object whose properties are returned. * @return list of properties */ public static List
* 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!"); * } * } ** We can see several important concepts in the above example: *
* 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() ** 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: *
* var R2 = Java.extend(R1); * var r2 = new R2(function() { print("r2.run() invoked!") }) * r2.run() ** 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. *
* 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) ** @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()); } }