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 * size: function() { print("size invoked!"); } 355 * } 356 * var printAddInvokedArrayList = new ArrayListExtender() { 357 * add: function(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 } --- EOF ---