1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.objects;
  27 
  28 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  29 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  30 
  31 import java.lang.reflect.Array;
  32 import java.util.Collection;
  33 import jdk.nashorn.internal.objects.annotations.Attribute;
  34 import jdk.nashorn.internal.objects.annotations.Function;
  35 import jdk.nashorn.internal.objects.annotations.ScriptClass;
  36 import jdk.nashorn.internal.objects.annotations.Where;
  37 import jdk.nashorn.internal.runtime.JSType;
  38 import jdk.nashorn.internal.runtime.ScriptObject;
  39 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
  40 import org.dynalang.dynalink.beans.StaticClass;
  41 import org.dynalang.dynalink.support.TypeUtilities;
  42 
  43 /**
  44  * This class is the implementation for the {@code Java} global object exposed to programs running under Nashorn. This
  45  * object acts as the API entry point to Java platform specific functionality, dealing with creating new instances of
  46  * Java classes, subclassing Java classes, implementing Java interfaces, converting between Java arrays and ECMAScript
  47  * arrays, and so forth.
  48  */
  49 @ScriptClass("Java")
  50 public class NativeJava {
  51 
  52     private NativeJava() {
  53     }
  54 
  55     /**
  56      * Returns true if the specified object is a Java type object, that is an instance of {@link StaticClass}.
  57      * @param self not used
  58      * @param type the object that is checked if it is a type object or not
  59      * @return tells whether given object is a Java type object or not.
  60      * @see #type(Object, Object)
  61      */
  62     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
  63     public static Object isType(final Object self, final Object type) {
  64         return type instanceof StaticClass;
  65     }
  66 
  67     /**
  68      * Given a name of a Java type, returns an object representing that type in Nashorn. The Java class of the objects
  69      * used to represent Java types in Nashorn is not {@link java.lang.Class} but rather {@link StaticClass}. They are
  70      * the objects that you can use with the {@code new} operator to create new instances of the class as well as to
  71      * access static members of the class. In Nashorn, {@code Class} objects are just regular Java objects that aren't
  72      * treated specially. Instead of them, {@link StaticClass} instances - which we sometimes refer to as "Java type
  73      * objects" are used as constructors with the {@code new} operator, and they expose static fields, properties, and
  74      * methods. While this might seem confusing at first, it actually closely matches the Java language: you use a
  75      * different expression (e.g. {@code java.io.File}) as an argument in "new" and to address statics, and it is
  76      * distinct from the {@code Class} object (e.g. {@code java.io.File.class}). Below we cover in details the
  77      * properties of the type objects.
  78      * <h2>Constructing Java objects</h2>
  79      * Examples:
  80      * <pre>
  81      * var arrayListType = Java.type("java.util.ArrayList")
  82      * var intType = Java.type("int")
  83      * var stringArrayType = Java.type("java.lang.String[]")
  84      * var int2DArrayType = Java.type("int[][]")
  85      * </pre>
  86      * Note that the name of the type is always a string for a fully qualified name. You can use any of these types to
  87      * create new instances, e.g.:
  88      * <pre>
  89      * var anArrayList = new Java.type("java.util.ArrayList")
  90      * </pre>
  91      * or
  92      * <pre>
  93      * var ArrayList = Java.type("java.util.ArrayList")
  94      * var anArrayList = new ArrayList
  95      * var anArrayListWithSize = new ArrayList(16)
  96      * </pre>
  97      * In the special case of inner classes, you need to use the JVM fully qualified name, meaning using {@code $} sign
  98      * in the class name:
  99      * <pre>
 100      * var ftype = Java.type("java.awt.geom.Arc2D$Float")
 101      * </pre>
 102      * However, once you retrieved the outer class, you can access the inner class as a property on it:
 103      * <pre>
 104      * var arctype = Java.type("java.awt.geom.Arc2D")
 105      * var ftype = arctype.Float
 106      * </pre>
 107      * You can access both static and non-static inner classes. If you want to create an instance of a non-static
 108      * inner class, remember to pass an instance of its outer class as the first argument to the constructor.
 109      * </p><p>
 110      * If the type is abstract, you can instantiate an anonymous subclass of it using an argument list that is
 111      * applicable to any of its public or protected constructors, but inserting a JavaScript object with functions
 112      * properties that provide JavaScript implementations of the abstract methods. If method names are overloaded, the
 113      * JavaScript function will provide implementation for all overloads. E.g.:
 114      * <pre>
 115      * var TimerTask =  Java.type("java.util.TimerTask")
 116      * var task = new TimerTask({ run: function() { print("Hello World!") } })
 117      * </pre>
 118      * Nashorn supports a syntactic extension where a "new" expression followed by an argument is identical to
 119      * invoking the constructor and passing the argument to it, so you can write the above example also as:
 120      * <pre>
 121      * var task = new TimerTask {
 122      *     run: function() {
 123      *       print("Hello World!")
 124      *     }
 125      * }
 126      * </pre>
 127      * which is very similar to Java anonymous inner class definition. On the other hand, if the type is an abstract
 128      * type with a single abstract method (commonly referred to as a "SAM type") or all abstract methods it has share
 129      * the same overloaded name), then instead of an object, you can just pass a function, so the above example can
 130      * become even more simplified to:
 131      * <pre>
 132      * var task = new TimerTask(function() { print("Hello World!") })
 133      * </pre>
 134      * Note that in every one of these cases if you are trying to instantiate an abstract class that has constructors
 135      * that take some arguments, you can invoke those simply by specifying the arguments after the initial
 136      * implementation object or function.
 137      * </p><p>The use of functions can be taken even further; if you are invoking a Java method that takes a SAM type,
 138      * you can just pass in a function object, and Nashorn will know what you meant:
 139      * <pre>
 140      * var timer = new Java.type("java.util.Timer")
 141      * timer.schedule(function() { print("Hello World!") })
 142      * </pre>
 143      * Here, {@code Timer.schedule()} expects a {@code TimerTask} as its argument, so Nashorn creates an instance of a
 144      * {@code TimerTask} subclass and uses the passed function to implement its only abstract method, {@code run()}. In
 145      * this usage though, you can't use non-default constructors; the type must be either an interface, or must have a
 146      * protected or public no-arg constructor.
 147      * </p><p>
 148      * You can also subclass non-abstract classes; for that you will need to use the {@link #extend(Object, Object...)}
 149      * method.
 150      * <h2>Accessing static members</h2>
 151      * Examples:
 152      * <pre>
 153      * var File = Java.type("java.io.File")
 154      * var pathSep = File.pathSeparator
 155      * var tmpFile1 = File.createTempFile("abcdefg", ".tmp")
 156      * var tmpFile2 = File.createTempFile("abcdefg", ".tmp", new File("/tmp"))
 157      * </pre>
 158      * Actually, you can even assign static methods to variables, so the above example can be rewritten as:
 159      * <pre>
 160      * var File = Java.type("java.io.File")
 161      * var createTempFile = File.createTempFile
 162      * var tmpFile1 = createTempFile("abcdefg", ".tmp")
 163      * var tmpFile2 = createTempFile("abcdefg", ".tmp", new File("/tmp"))
 164      * </pre>
 165      * If you need to access the actual {@code java.lang.Class} object for the type, you can use the {@code class}
 166      * property on the object representing the type:
 167      * <pre>
 168      * var File = Java.type("java.io.File")
 169      * var someFile = new File("blah")
 170      * print(File.class === someFile.getClass()) // prints true
 171      * </pre>
 172      * Of course, you can also use the {@code getClass()} method or its equivalent {@code class} property on any
 173      * instance of the class. Other way round, you can use the synthetic {@code static} property on any
 174      * {@code java.lang.Class} object to retrieve its type-representing object:
 175      * <pre>
 176      * var File = Java.type("java.io.File")
 177      * print(File.class.static === File) // prints true
 178      * </pre>
 179      * <h2>{@code instanceof} operator</h2>
 180      * The standard ECMAScript {@code instanceof} operator is extended to recognize Java objects and their type objects:
 181      * <pre>
 182      * var File = Java.type("java.io.File")
 183      * var aFile = new File("foo")
 184      * print(aFile instanceof File) // prints true
 185      * print(aFile instanceof File.class) // prints false - Class objects aren't type objects.
 186      * </pre>
 187      * @param self not used
 188      * @param objTypeName the object whose JS string value represents the type name. You can use names of primitive Java
 189      * types to obtain representations of them, and you can use trailing square brackets to represent Java array types.
 190      * @return the object representing the named type
 191      * @throws ClassNotFoundException if the class is not found
 192      */
 193     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
 194     public static Object type(final Object self, final Object objTypeName) throws ClassNotFoundException {
 195         return type(objTypeName);
 196     }
 197 
 198     private static StaticClass type(final Object objTypeName) throws ClassNotFoundException {
 199         return StaticClass.forClass(type(JSType.toString(objTypeName)));
 200     }
 201 
 202     private static Class<?> type(final String typeName) throws ClassNotFoundException {
 203         if (typeName.endsWith("[]")) {
 204             return arrayType(typeName);
 205         }
 206 
 207         return simpleType(typeName);
 208     }
 209 
 210     /**
 211      * Given a JavaScript array and a Java type, returns a Java array with the same initial contents, and with the
 212      * specified component type. Example:
 213      * <pre>
 214      * var anArray = [1, "13", false]
 215      * var javaIntArray = Java.toJavaArray(anArray, "int")
 216      * print(javaIntArray[0]) // prints 1
 217      * print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
 218      * print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
 219      * </pre>
 220      * @param self not used
 221      * @param objArray the JavaScript array. Can be null.
 222      * @param objType either a {@link #type(Object, Object) type object} or a String describing the component type of
 223      * the Java array to create. Can not be null. If undefined, Object is assumed (allowing the argument to be omitted).
 224      * @return a Java array with the copy of JavaScript array's contents, converted to the appropriate Java component
 225      * type. Returns null if objArray is null.
 226      * @throws ClassNotFoundException if the class described by objType is not found
 227      */
 228     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
 229     public static Object toJavaArray(final Object self, final Object objArray, final Object objType) throws ClassNotFoundException {
 230         final StaticClass componentType =
 231             objType instanceof StaticClass ?
 232                 (StaticClass)objType :
 233                 objType == UNDEFINED ?
 234                     StaticClass.forClass(Object.class) :
 235                     type(objType);
 236 
 237         if (objArray == null) {
 238             return null;
 239         }
 240 
 241         Global.checkObject(objArray);
 242 
 243         return ((ScriptObject)objArray).getArray().asArrayOfType(componentType.getRepresentedClass());
 244     }
 245 
 246     /**
 247      * Given a Java array or {@link Collection}, returns a JavaScript array with a shallow copy of its contents. Note
 248      * that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you
 249      * need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will
 250      * want to use this method. Example:
 251      * <pre>
 252      * var File = Java.type("java.io.File")
 253      * var listHomeDir = new File("~").listFiles()
 254      * var jsListHome = Java.toJavaScriptArray(listHomeDir)
 255      * var jpegModifiedDates = jsListHome
 256      *     .filter(function(val) { return val.getName().endsWith(".jpg") })
 257      *     .map(function(val) { return val.lastModified() })
 258      * </pre>
 259      * @param self not used
 260      * @param objArray the java array or collection. Can be null.
 261      * @return a JavaScript array with the copy of Java array's or collection's contents. Returns null if objArray is
 262      * null.
 263      */
 264     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
 265     public static Object toJavaScriptArray(final Object self, final Object objArray) {
 266         if (objArray == null) {
 267             return null;
 268         } else if (objArray instanceof Collection) {
 269             return new NativeArray(((Collection<?>)objArray).toArray());
 270         } else if (objArray instanceof Object[]) {
 271             return new NativeArray(((Object[])objArray).clone());
 272         } else if (objArray instanceof int[]) {
 273             return new NativeArray(((int[])objArray).clone());
 274         } else if (objArray instanceof double[]) {
 275             return new NativeArray(((double[])objArray).clone());
 276         } else if (objArray instanceof long[]) {
 277             return new NativeArray(((long[])objArray).clone());
 278         } else if (objArray instanceof byte[]) {
 279             return new NativeArray(copyArray((byte[])objArray));
 280         } else if (objArray instanceof short[]) {
 281             return new NativeArray(copyArray((short[])objArray));
 282         } else if (objArray instanceof char[]) {
 283             return new NativeArray(copyArray((char[])objArray));
 284         } else if (objArray instanceof float[]) {
 285             return new NativeArray(copyArray((float[])objArray));
 286         } else if (objArray instanceof boolean[]) {
 287             return new NativeArray(copyArray((boolean[])objArray));
 288         }
 289 
 290         typeError("cant.convert.to.javascript.array", objArray.getClass().getName());
 291 
 292         throw new AssertionError();
 293     }
 294 
 295     private static int[] copyArray(final byte[] in) {
 296         final int[] out = new int[in.length];
 297         for(int i = 0; i < in.length; ++i) {
 298             out[i] = in[i];
 299         }
 300         return out;
 301     }
 302 
 303     private static int[] copyArray(final short[] in) {
 304         final int[] out = new int[in.length];
 305         for(int i = 0; i < in.length; ++i) {
 306             out[i] = in[i];
 307         }
 308         return out;
 309     }
 310 
 311     private static int[] copyArray(final char[] in) {
 312         final int[] out = new int[in.length];
 313         for(int i = 0; i < in.length; ++i) {
 314             out[i] = in[i];
 315         }
 316         return out;
 317     }
 318 
 319     private static double[] copyArray(final float[] in) {
 320         final double[] out = new double[in.length];
 321         for(int i = 0; i < in.length; ++i) {
 322             out[i] = in[i];
 323         }
 324         return out;
 325     }
 326 
 327     private static Object[] copyArray(final boolean[] in) {
 328         final Object[] out = new Object[in.length];
 329         for(int i = 0; i < in.length; ++i) {
 330             out[i] = in[i];
 331         }
 332         return out;
 333     }
 334 
 335     private static Class<?> simpleType(final String typeName) throws ClassNotFoundException {
 336         final Class<?> primClass = TypeUtilities.getPrimitiveTypeByName(typeName);
 337         return primClass != null ? primClass : Global.getThisContext().findClass(typeName);
 338     }
 339 
 340     private static Class<?> arrayType(final String typeName) throws ClassNotFoundException {
 341         return Array.newInstance(type(typeName.substring(0, typeName.length() - 2)), 0).getClass();
 342     }
 343 
 344     /**
 345      * Returns a type object for a subclass of the specified Java class (or implementation of the specified interface)
 346      * that acts as a script-to-Java adapter for it. See {@link #type(Object, Object)} for a discussion of type objects,
 347      * and see {@link JavaAdapterFactory} for details on script-to-Java adapters. Note that you can also implement
 348      * interfaces and subclass abstract classes using {@code new} operator on a type object for an interface or abstract
 349      * class. However, to extend a non-abstract class, you will have to use this method. Example:
 350      * <pre>
 351      * var ArrayList = Java.type("java.util.ArrayList")
 352      * var ArrayListExtender = Java.extend(ArrayList)
 353      * var printSizeInvokedArrayList = new ArrayListExtender() {
 354      *     function size() { print("size invoked!"); }
 355      * }
 356      * var printAddInvokedArrayList = new ArrayListExtender() {
 357      *     function add(x, y) {
 358      *       if(typeof(y) === "undefined") {
 359      *           print("add(e) invoked!");
 360      *       } else {
 361      *           print("add(i, e) invoked!");
 362      *       }
 363      * }
 364      * </pre>
 365      * We can see several important concepts in the above example:
 366      * <ul>
 367      * <li>Every Java class will have exactly one extender subclass in Nashorn - repeated invocations of {@code extend}
 368      * for the same type will yield the same extender type. It's a generic adapter that delegates to whatever JavaScript
 369      * functions its implementation object has on a per-instance basis.</li>
 370      * <li>If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter
 371      * must be prepared to deal with all overloads.</li>
 372      * <li>You can't invoke {@code super.*()} from adapters for now.</li>
 373      * @param self not used
 374      * @param types the original types. The caller must pass at least one Java type object of class {@link StaticClass}
 375      * representing either a public interface or a non-final public class with at least one public or protected
 376      * constructor. If more than one type is specified, at most one can be a class and the rest have to be interfaces.
 377      * Invoking the method twice with exactly the same types in the same order will return the same adapter
 378      * class, any reordering of types or even addition or removal of redundant types (i.e. interfaces that other types
 379      * in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of
 380      * interfaces) will result in a different adapter class, even though those adapter classes are functionally
 381      * identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists.
 382      * @return a new {@link StaticClass} that represents the adapter for the original types.
 383      */
 384     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
 385     public static Object extend(final Object self, final Object... types) {
 386         if(types == null || types.length == 0) {
 387             typeError("extend.expects.at.least.one.argument");
 388         }
 389         final Class<?>[] stypes = new Class<?>[types.length];
 390         try {
 391             for(int i = 0; i < types.length; ++i) {
 392                 stypes[i] = ((StaticClass)types[i]).getRepresentedClass();
 393             }
 394         } catch(final ClassCastException e) {
 395             typeError("extend.expects.java.types");
 396         }
 397         return JavaAdapterFactory.getAdapterClassFor(stypes);
 398     }
 399 }