NativeJava.java revision 971:c93b6091b11e
11573Srgrimes/* 21573Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 31573Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 41573Srgrimes * 51573Srgrimes * This code is free software; you can redistribute it and/or modify it 61573Srgrimes * under the terms of the GNU General Public License version 2 only, as 71573Srgrimes * published by the Free Software Foundation. Oracle designates this 81573Srgrimes * particular file as subject to the "Classpath" exception as provided 91573Srgrimes * by Oracle in the LICENSE file that accompanied this code. 101573Srgrimes * 111573Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT 121573Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 131573Srgrimes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 141573Srgrimes * version 2 for more details (a copy is included in the LICENSE file that 151573Srgrimes * accompanied this code). 161573Srgrimes * 171573Srgrimes * You should have received a copy of the GNU General Public License version 181573Srgrimes * 2 along with this work; if not, write to the Free Software Foundation, 191573Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 201573Srgrimes * 211573Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 221573Srgrimes * or visit www.oracle.com if you need additional information or have any 231573Srgrimes * questions. 241573Srgrimes */ 251573Srgrimes 261573Srgrimespackage jdk.nashorn.internal.objects; 271573Srgrimes 281573Srgrimesimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 2950476Speterimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 301573Srgrimes 3126826Ssteveimport java.lang.invoke.MethodHandles; 321573Srgrimesimport java.lang.reflect.Array; 331573Srgrimesimport java.util.Collection; 341573Srgrimesimport java.util.Deque; 351573Srgrimesimport java.util.List; 361573Srgrimesimport jdk.internal.dynalink.beans.StaticClass; 3759460Sphantomimport jdk.internal.dynalink.support.TypeUtilities; 3859460Sphantomimport jdk.nashorn.api.scripting.JSObject; 391573Srgrimesimport jdk.nashorn.api.scripting.ScriptUtils; 4084306Sruimport jdk.nashorn.internal.objects.annotations.Attribute; 4184306Sruimport jdk.nashorn.internal.objects.annotations.Function; 421573Srgrimesimport jdk.nashorn.internal.objects.annotations.ScriptClass; 431573Srgrimesimport jdk.nashorn.internal.objects.annotations.Where; 441573Srgrimesimport jdk.nashorn.internal.runtime.Context; 451573Srgrimesimport jdk.nashorn.internal.runtime.JSType; 4668946Sruimport jdk.nashorn.internal.runtime.ListAdapter; 471573Srgrimesimport jdk.nashorn.internal.runtime.PropertyMap; 481573Srgrimesimport jdk.nashorn.internal.runtime.ScriptFunction; 491573Srgrimesimport jdk.nashorn.internal.runtime.ScriptObject; 501573Srgrimesimport jdk.nashorn.internal.runtime.ScriptRuntime; 511573Srgrimesimport jdk.nashorn.internal.runtime.linker.Bootstrap; 521573Srgrimesimport jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; 531573Srgrimes 541573Srgrimes/** 551573Srgrimes * This class is the implementation for the {@code Java} global object exposed to programs running under Nashorn. This 561573Srgrimes * object acts as the API entry point to Java platform specific functionality, dealing with creating new instances of 571573Srgrimes * Java classes, subclassing Java classes, implementing Java interfaces, converting between Java arrays and ECMAScript 581573Srgrimes * arrays, and so forth. 5968946Sru */ 6068946Sru@ScriptClass("Java") 611573Srgrimespublic final class NativeJava { 6268946Sru 6368946Sru // initialized by nasgen 6468946Sru @SuppressWarnings("unused") 651573Srgrimes private static PropertyMap $nasgenmap$; 661573Srgrimes 671573Srgrimes private NativeJava() { 681573Srgrimes // don't create me 691573Srgrimes throw new UnsupportedOperationException(); 701573Srgrimes } 711573Srgrimes 721573Srgrimes /** 731573Srgrimes * Returns true if the specified object is a Java type object, that is an instance of {@link StaticClass}. 741573Srgrimes * @param self not used 751573Srgrimes * @param type the object that is checked if it is a type object or not 761573Srgrimes * @return tells whether given object is a Java type object or not. 771573Srgrimes * @see #type(Object, Object) 781573Srgrimes */ 791573Srgrimes @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 801573Srgrimes public static boolean isType(final Object self, final Object type) { 811573Srgrimes return type instanceof StaticClass; 821573Srgrimes } 831573Srgrimes 841573Srgrimes /** 851573Srgrimes * Returns synchronized wrapper version of the given ECMAScript function. 861573Srgrimes * @param self not used 8714038Smpp * @param func the ECMAScript function whose synchronized version is returned. 881573Srgrimes * @param obj the object (i.e, lock) on which the function synchronizes. 891573Srgrimes * @return synchronized wrapper version of the given ECMAScript function. 901573Srgrimes */ 911573Srgrimes @Function(name="synchronized", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 9268946Sru public static Object synchronizedFunc(final Object self, final Object func, final Object obj) { 931573Srgrimes return ScriptUtils.makeSynchronizedFunction(func, obj); 941573Srgrimes } 951573Srgrimes 96 /** 97 * Returns true if the specified object is a Java method. 98 * @param self not used 99 * @param obj the object that is checked if it is a Java method object or not 100 * @return tells whether given object is a Java method object or not. 101 */ 102 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 103 public static boolean isJavaMethod(final Object self, final Object obj) { 104 return Bootstrap.isDynamicMethod(obj); 105 } 106 107 /** 108 * Returns true if the specified object is a java function (but not script function) 109 * @param self not used 110 * @param obj the object that is checked if it is a Java function or not 111 * @return tells whether given object is a Java function or not 112 */ 113 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 114 public static boolean isJavaFunction(final Object self, final Object obj) { 115 return Bootstrap.isCallable(obj) && !(obj instanceof ScriptFunction); 116 } 117 118 /** 119 * Returns true if the specified object is a Java object but not a script object 120 * @param self not used 121 * @param obj the object that is checked 122 * @return tells whether given object is a Java object but not a script object 123 */ 124 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 125 public static boolean isJavaObject(final Object self, final Object obj) { 126 return obj != null && !(obj instanceof ScriptObject); 127 } 128 129 /** 130 * Returns true if the specified object is a ECMAScript object, that is an instance of {@link ScriptObject}. 131 * @param self not used 132 * @param obj the object that is checked if it is a ECMAScript object or not 133 * @return tells whether given object is a ECMAScript object or not. 134 */ 135 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 136 public static boolean isScriptObject(final Object self, final Object obj) { 137 return obj instanceof ScriptObject; 138 } 139 140 /** 141 * Returns true if the specified object is a ECMAScript function, that is an instance of {@link ScriptFunction}. 142 * @param self not used 143 * @param obj the object that is checked if it is a ECMAScript function or not 144 * @return tells whether given object is a ECMAScript function or not. 145 */ 146 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 147 public static boolean isScriptFunction(final Object self, final Object obj) { 148 return obj instanceof ScriptFunction; 149 } 150 151 /** 152 * <p> 153 * Given a name of a Java type, returns an object representing that type in Nashorn. The Java class of the objects 154 * used to represent Java types in Nashorn is not {@link java.lang.Class} but rather {@link StaticClass}. They are 155 * the objects that you can use with the {@code new} operator to create new instances of the class as well as to 156 * access static members of the class. In Nashorn, {@code Class} objects are just regular Java objects that aren't 157 * treated specially. Instead of them, {@link StaticClass} instances - which we sometimes refer to as "Java type 158 * objects" are used as constructors with the {@code new} operator, and they expose static fields, properties, and 159 * methods. While this might seem confusing at first, it actually closely matches the Java language: you use a 160 * different expression (e.g. {@code java.io.File}) as an argument in "new" and to address statics, and it is 161 * distinct from the {@code Class} object (e.g. {@code java.io.File.class}). Below we cover in details the 162 * properties of the type objects. 163 * </p> 164 * <p><b>Constructing Java objects</b></p> 165 * Examples: 166 * <pre> 167 * var arrayListType = Java.type("java.util.ArrayList") 168 * var intType = Java.type("int") 169 * var stringArrayType = Java.type("java.lang.String[]") 170 * var int2DArrayType = Java.type("int[][]") 171 * </pre> 172 * Note that the name of the type is always a string for a fully qualified name. You can use any of these types to 173 * create new instances, e.g.: 174 * <pre> 175 * var anArrayList = new Java.type("java.util.ArrayList") 176 * </pre> 177 * or 178 * <pre> 179 * var ArrayList = Java.type("java.util.ArrayList") 180 * var anArrayList = new ArrayList 181 * var anArrayListWithSize = new ArrayList(16) 182 * </pre> 183 * In the special case of inner classes, you can either use the JVM fully qualified name, meaning using {@code $} 184 * sign in the class name, or you can use the dot: 185 * <pre> 186 * var ftype = Java.type("java.awt.geom.Arc2D$Float") 187 * </pre> 188 * and 189 * <pre> 190 * var ftype = Java.type("java.awt.geom.Arc2D.Float") 191 * </pre> 192 * both work. Note however that using the dollar sign is faster, as Java.type first tries to resolve the class name 193 * as it is originally specified, and the internal JVM names for inner classes use the dollar sign. If you use the 194 * dot, Java.type will internally get a ClassNotFoundException and subsequently retry by changing the last dot to 195 * dollar sign. As a matter of fact, it'll keep replacing dots with dollar signs until it either successfully loads 196 * the class or runs out of all dots in the name. This way it can correctly resolve and load even multiply nested 197 * inner classes with the dot notation. Again, this will be slower than using the dollar signs in the name. An 198 * alternative way to access the inner class is as a property of the outer class: 199 * <pre> 200 * var arctype = Java.type("java.awt.geom.Arc2D") 201 * var ftype = arctype.Float 202 * </pre> 203 * <p> 204 * You can access both static and non-static inner classes. If you want to create an instance of a non-static 205 * inner class, remember to pass an instance of its outer class as the first argument to the constructor. 206 * </p> 207 * <p> 208 * If the type is abstract, you can instantiate an anonymous subclass of it using an argument list that is 209 * applicable to any of its public or protected constructors, but inserting a JavaScript object with functions 210 * properties that provide JavaScript implementations of the abstract methods. If method names are overloaded, the 211 * JavaScript function will provide implementation for all overloads. E.g.: 212 * </p> 213 * <pre> 214 * var TimerTask = Java.type("java.util.TimerTask") 215 * var task = new TimerTask({ run: function() { print("Hello World!") } }) 216 * </pre> 217 * <p> 218 * Nashorn supports a syntactic extension where a "new" expression followed by an argument is identical to 219 * invoking the constructor and passing the argument to it, so you can write the above example also as: 220 * </p> 221 * <pre> 222 * var task = new TimerTask { 223 * run: function() { 224 * print("Hello World!") 225 * } 226 * } 227 * </pre> 228 * <p> 229 * which is very similar to Java anonymous inner class definition. On the other hand, if the type is an abstract 230 * type with a single abstract method (commonly referred to as a "SAM type") or all abstract methods it has share 231 * the same overloaded name), then instead of an object, you can just pass a function, so the above example can 232 * become even more simplified to: 233 * </p> 234 * <pre> 235 * var task = new TimerTask(function() { print("Hello World!") }) 236 * </pre> 237 * <p> 238 * Note that in every one of these cases if you are trying to instantiate an abstract class that has constructors 239 * that take some arguments, you can invoke those simply by specifying the arguments after the initial 240 * implementation object or function. 241 * </p> 242 * <p>The use of functions can be taken even further; if you are invoking a Java method that takes a SAM type, 243 * you can just pass in a function object, and Nashorn will know what you meant: 244 * </p> 245 * <pre> 246 * var timer = new Java.type("java.util.Timer") 247 * timer.schedule(function() { print("Hello World!") }) 248 * </pre> 249 * <p> 250 * Here, {@code Timer.schedule()} expects a {@code TimerTask} as its argument, so Nashorn creates an instance of a 251 * {@code TimerTask} subclass and uses the passed function to implement its only abstract method, {@code run()}. In 252 * this usage though, you can't use non-default constructors; the type must be either an interface, or must have a 253 * protected or public no-arg constructor. 254 * </p> 255 * <p> 256 * You can also subclass non-abstract classes; for that you will need to use the {@link #extend(Object, Object...)} 257 * method. 258 * </p> 259 * <p><b>Accessing static members</b></p> 260 * Examples: 261 * <pre> 262 * var File = Java.type("java.io.File") 263 * var pathSep = File.pathSeparator 264 * var tmpFile1 = File.createTempFile("abcdefg", ".tmp") 265 * var tmpFile2 = File.createTempFile("abcdefg", ".tmp", new File("/tmp")) 266 * </pre> 267 * Actually, you can even assign static methods to variables, so the above example can be rewritten as: 268 * <pre> 269 * var File = Java.type("java.io.File") 270 * var createTempFile = File.createTempFile 271 * var tmpFile1 = createTempFile("abcdefg", ".tmp") 272 * var tmpFile2 = createTempFile("abcdefg", ".tmp", new File("/tmp")) 273 * </pre> 274 * If you need to access the actual {@code java.lang.Class} object for the type, you can use the {@code class} 275 * property on the object representing the type: 276 * <pre> 277 * var File = Java.type("java.io.File") 278 * var someFile = new File("blah") 279 * print(File.class === someFile.getClass()) // prints true 280 * </pre> 281 * Of course, you can also use the {@code getClass()} method or its equivalent {@code class} property on any 282 * instance of the class. Other way round, you can use the synthetic {@code static} property on any 283 * {@code java.lang.Class} object to retrieve its type-representing object: 284 * <pre> 285 * var File = Java.type("java.io.File") 286 * print(File.class.static === File) // prints true 287 * </pre> 288 * <p><b>{@code instanceof} operator</b></p> 289 * The standard ECMAScript {@code instanceof} operator is extended to recognize Java objects and their type objects: 290 * <pre> 291 * var File = Java.type("java.io.File") 292 * var aFile = new File("foo") 293 * print(aFile instanceof File) // prints true 294 * print(aFile instanceof File.class) // prints false - Class objects aren't type objects. 295 * </pre> 296 * @param self not used 297 * @param objTypeName the object whose JS string value represents the type name. You can use names of primitive Java 298 * types to obtain representations of them, and you can use trailing square brackets to represent Java array types. 299 * @return the object representing the named type 300 * @throws ClassNotFoundException if the class is not found 301 */ 302 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 303 public static Object type(final Object self, final Object objTypeName) throws ClassNotFoundException { 304 return type(objTypeName); 305 } 306 307 private static StaticClass type(final Object objTypeName) throws ClassNotFoundException { 308 return StaticClass.forClass(type(JSType.toString(objTypeName))); 309 } 310 311 private static Class<?> type(final String typeName) throws ClassNotFoundException { 312 if (typeName.endsWith("[]")) { 313 return arrayType(typeName); 314 } 315 316 return simpleType(typeName); 317 } 318 319 /** 320 * Returns name of a java type {@link StaticClass}. 321 * @param self not used 322 * @param type the type whose name is returned 323 * @return name of the given type 324 */ 325 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 326 public static Object typeName(final Object self, final Object type) { 327 if (type instanceof StaticClass) { 328 return ((StaticClass)type).getRepresentedClass().getName(); 329 } else if (type instanceof Class) { 330 return ((Class<?>)type).getName(); 331 } else { 332 return UNDEFINED; 333 } 334 } 335 336 /** 337 * Given a script object and a Java type, converts the script object into the desired Java type. Currently it 338 * performs shallow creation of Java arrays, as well as wrapping of objects in Lists and Dequeues. Example: 339 * <pre> 340 * var anArray = [1, "13", false] 341 * var javaIntArray = Java.to(anArray, "int[]") 342 * print(javaIntArray[0]) // prints 1 343 * print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion 344 * print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion 345 * </pre> 346 * @param self not used 347 * @param obj the script object. Can be null. 348 * @param objType either a {@link #type(Object, Object) type object} or a String describing the type of the Java 349 * object to create. Can not be null. If undefined, a "default" conversion is presumed (allowing the argument to be 350 * omitted). 351 * @return a Java object whose value corresponds to the original script object's value. Specifically, for array 352 * target types, returns a Java array of the same type with contents converted to the array's component type. Does 353 * not recursively convert for multidimensional arrays. For {@link List} or {@link Deque}, returns a live wrapper 354 * around the object, see {@link ListAdapter} for details. Returns null if obj is null. 355 * @throws ClassNotFoundException if the class described by objType is not found 356 */ 357 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 358 public static Object to(final Object self, final Object obj, final Object objType) throws ClassNotFoundException { 359 if (obj == null) { 360 return null; 361 } 362 363 if (!(obj instanceof ScriptObject) && !(obj instanceof JSObject)) { 364 throw typeError("not.an.object", ScriptRuntime.safeToString(obj)); 365 } 366 367 final Class<?> targetClass; 368 if(objType == UNDEFINED) { 369 targetClass = Object[].class; 370 } else { 371 final StaticClass targetType; 372 if(objType instanceof StaticClass) { 373 targetType = (StaticClass)objType; 374 } else { 375 targetType = type(objType); 376 } 377 targetClass = targetType.getRepresentedClass(); 378 } 379 380 if(targetClass.isArray()) { 381 return JSType.toJavaArray(obj, targetClass.getComponentType()); 382 } 383 384 if(targetClass == List.class || targetClass == Deque.class) { 385 return ListAdapter.create(obj); 386 } 387 388 throw typeError("unsupported.java.to.type", targetClass.getName()); 389 } 390 391 /** 392 * Given a Java array or {@link Collection}, returns a JavaScript array with a shallow copy of its contents. Note 393 * that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you 394 * need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will 395 * want to use this method. Example: 396 * <pre> 397 * var File = Java.type("java.io.File") 398 * var listHomeDir = new File("~").listFiles() 399 * var jsListHome = Java.from(listHomeDir) 400 * var jpegModifiedDates = jsListHome 401 * .filter(function(val) { return val.getName().endsWith(".jpg") }) 402 * .map(function(val) { return val.lastModified() }) 403 * </pre> 404 * @param self not used 405 * @param objArray the java array or collection. Can be null. 406 * @return a JavaScript array with the copy of Java array's or collection's contents. Returns null if objArray is 407 * null. 408 */ 409 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 410 public static NativeArray from(final Object self, final Object objArray) { 411 if (objArray == null) { 412 return null; 413 } else if (objArray instanceof Collection) { 414 return new NativeArray(((Collection<?>)objArray).toArray()); 415 } else if (objArray instanceof Object[]) { 416 return new NativeArray(((Object[])objArray).clone()); 417 } else if (objArray instanceof int[]) { 418 return new NativeArray(((int[])objArray).clone()); 419 } else if (objArray instanceof double[]) { 420 return new NativeArray(((double[])objArray).clone()); 421 } else if (objArray instanceof long[]) { 422 return new NativeArray(((long[])objArray).clone()); 423 } else if (objArray instanceof byte[]) { 424 return new NativeArray(copyArray((byte[])objArray)); 425 } else if (objArray instanceof short[]) { 426 return new NativeArray(copyArray((short[])objArray)); 427 } else if (objArray instanceof char[]) { 428 return new NativeArray(copyArray((char[])objArray)); 429 } else if (objArray instanceof float[]) { 430 return new NativeArray(copyArray((float[])objArray)); 431 } else if (objArray instanceof boolean[]) { 432 return new NativeArray(copyArray((boolean[])objArray)); 433 } 434 435 throw typeError("cant.convert.to.javascript.array", objArray.getClass().getName()); 436 } 437 438 private static int[] copyArray(final byte[] in) { 439 final int[] out = new int[in.length]; 440 for(int i = 0; i < in.length; ++i) { 441 out[i] = in[i]; 442 } 443 return out; 444 } 445 446 private static int[] copyArray(final short[] in) { 447 final int[] out = new int[in.length]; 448 for(int i = 0; i < in.length; ++i) { 449 out[i] = in[i]; 450 } 451 return out; 452 } 453 454 private static int[] copyArray(final char[] in) { 455 final int[] out = new int[in.length]; 456 for(int i = 0; i < in.length; ++i) { 457 out[i] = in[i]; 458 } 459 return out; 460 } 461 462 private static double[] copyArray(final float[] in) { 463 final double[] out = new double[in.length]; 464 for(int i = 0; i < in.length; ++i) { 465 out[i] = in[i]; 466 } 467 return out; 468 } 469 470 private static Object[] copyArray(final boolean[] in) { 471 final Object[] out = new Object[in.length]; 472 for(int i = 0; i < in.length; ++i) { 473 out[i] = in[i]; 474 } 475 return out; 476 } 477 478 private static Class<?> simpleType(final String typeName) throws ClassNotFoundException { 479 final Class<?> primClass = TypeUtilities.getPrimitiveTypeByName(typeName); 480 if(primClass != null) { 481 return primClass; 482 } 483 final Context ctx = Global.getThisContext(); 484 try { 485 return ctx.findClass(typeName); 486 } catch(final ClassNotFoundException e) { 487 // The logic below compensates for a frequent user error - when people use dot notation to separate inner 488 // class names, i.e. "java.lang.Character.UnicodeBlock" vs."java.lang.Character$UnicodeBlock". The logic 489 // below will try alternative class names, replacing dots at the end of the name with dollar signs. 490 final StringBuilder nextName = new StringBuilder(typeName); 491 int lastDot = nextName.length(); 492 for(;;) { 493 lastDot = nextName.lastIndexOf(".", lastDot - 1); 494 if(lastDot == -1) { 495 // Exhausted the search space, class not found - rethrow the original exception. 496 throw e; 497 } 498 nextName.setCharAt(lastDot, '$'); 499 try { 500 return ctx.findClass(nextName.toString()); 501 } catch(final ClassNotFoundException cnfe) { 502 // Intentionally ignored, so the loop retries with the next name 503 } 504 } 505 } 506 507 } 508 509 private static Class<?> arrayType(final String typeName) throws ClassNotFoundException { 510 return Array.newInstance(type(typeName.substring(0, typeName.length() - 2)), 0).getClass(); 511 } 512 513 /** 514 * Returns a type object for a subclass of the specified Java class (or implementation of the specified interface) 515 * that acts as a script-to-Java adapter for it. See {@link #type(Object, Object)} for a discussion of type objects, 516 * and see {@link JavaAdapterFactory} for details on script-to-Java adapters. Note that you can also implement 517 * interfaces and subclass abstract classes using {@code new} operator on a type object for an interface or abstract 518 * class. However, to extend a non-abstract class, you will have to use this method. Example: 519 * <pre> 520 * var ArrayList = Java.type("java.util.ArrayList") 521 * var ArrayListExtender = Java.extend(ArrayList) 522 * var printSizeInvokedArrayList = new ArrayListExtender() { 523 * size: function() { print("size invoked!"); } 524 * } 525 * var printAddInvokedArrayList = new ArrayListExtender() { 526 * add: function(x, y) { 527 * if(typeof(y) === "undefined") { 528 * print("add(e) invoked!"); 529 * } else { 530 * print("add(i, e) invoked!"); 531 * } 532 * } 533 * </pre> 534 * We can see several important concepts in the above example: 535 * <ul> 536 * <li>Every specified list of Java types will have one extender subclass in Nashorn per caller protection domain - 537 * repeated invocations of {@code extend} for the same list of types for scripts same protection domain will yield 538 * the same extender type. It's a generic adapter that delegates to whatever JavaScript functions its implementation 539 * object has on a per-instance basis.</li> 540 * <li>If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter 541 * must be prepared to deal with all overloads.</li> 542 * <li>To invoke super methods from adapters, call them on the adapter instance prefixing them with {@code super$}, 543 * or use the special {@link #_super(Object, Object) super-adapter}.</li> 544 * <li>It is also possible to specify an ordinary JavaScript object as the last argument to {@code extend}. In that 545 * case, it is treated as a class-level override. {@code extend} will return an extender class where all instances 546 * will have the methods implemented by functions on that object, just as if that object were passed as the last 547 * argument to their constructor. Example: 548 * <pre> 549 * var Runnable = Java.type("java.lang.Runnable") 550 * var R1 = Java.extend(Runnable, { 551 * run: function() { 552 * print("R1.run() invoked!") 553 * } 554 * }) 555 * var r1 = new R1 556 * var t = new java.lang.Thread(r1) 557 * t.start() 558 * t.join() 559 * </pre> 560 * As you can see, you don't have to pass any object when you create a new instance of {@code R1} as its 561 * {@code run()} function was defined already when extending the class. If you also want to add instance-level 562 * overrides on these objects, you will have to repeatedly use {@code extend()} to subclass the class-level adapter. 563 * For such adapters, the order of precedence is instance-level method, class-level method, superclass method, or 564 * {@code UnsupportedOperationException} if the superclass method is abstract. If we continue our previous example: 565 * <pre> 566 * var R2 = Java.extend(R1); 567 * var r2 = new R2(function() { print("r2.run() invoked!") }) 568 * r2.run() 569 * </pre> 570 * We'll see it'll print {@code "r2.run() invoked!"}, thus overriding on instance-level the class-level behavior. 571 * Note that you must use {@code Java.extend} to explicitly create an instance-override adapter class from a 572 * class-override adapter class, as the class-override adapter class is no longer abstract. 573 * </li> 574 * </ul> 575 * @param self not used 576 * @param types the original types. The caller must pass at least one Java type object of class {@link StaticClass} 577 * representing either a public interface or a non-final public class with at least one public or protected 578 * constructor. If more than one type is specified, at most one can be a class and the rest have to be interfaces. 579 * Invoking the method twice with exactly the same types in the same order - in absence of class-level overrides - 580 * will return the same adapter class, any reordering of types or even addition or removal of redundant types (i.e. 581 * interfaces that other types in the list already implement/extend, or {@code java.lang.Object} in a list of types 582 * consisting purely of interfaces) will result in a different adapter class, even though those adapter classes are 583 * functionally identical; we deliberately don't want to incur the additional processing cost of canonicalizing type 584 * lists. As a special case, the last argument can be a {@code ScriptObject} instead of a type. In this case, a 585 * separate adapter class is generated - new one for each invocation - that will use the passed script object as its 586 * implementation for all instances. Instances of such adapter classes can then be created without passing another 587 * script object in the constructor, as the class has a class-level behavior defined by the script object. However, 588 * you can still pass a script object (or if it's a SAM type, a function) to the constructor to provide further 589 * instance-level overrides. 590 * 591 * @return a new {@link StaticClass} that represents the adapter for the original types. 592 */ 593 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 594 public static Object extend(final Object self, final Object... types) { 595 if(types == null || types.length == 0) { 596 throw typeError("extend.expects.at.least.one.argument"); 597 } 598 final int l = types.length; 599 final int typesLen; 600 final ScriptObject classOverrides; 601 if(types[l - 1] instanceof ScriptObject) { 602 classOverrides = (ScriptObject)types[l - 1]; 603 typesLen = l - 1; 604 if(typesLen == 0) { 605 throw typeError("extend.expects.at.least.one.type.argument"); 606 } 607 } else { 608 classOverrides = null; 609 typesLen = l; 610 } 611 final Class<?>[] stypes = new Class<?>[typesLen]; 612 try { 613 for(int i = 0; i < typesLen; ++i) { 614 stypes[i] = ((StaticClass)types[i]).getRepresentedClass(); 615 } 616 } catch(final ClassCastException e) { 617 throw typeError("extend.expects.java.types"); 618 } 619 // Note that while the public API documentation claims self is not used, we actually use it. 620 // ScriptFunction.findCallMethod will bind the lookup object into it, and we can then use that lookup when 621 // requesting the adapter class. Note that if Java.extend is invoked with no lookup object, it'll pass the 622 // public lookup which'll result in generation of a no-permissions adapter. A typical situation this can happen 623 // is when the extend function is bound. 624 final MethodHandles.Lookup lookup; 625 if(self instanceof MethodHandles.Lookup) { 626 lookup = (MethodHandles.Lookup)self; 627 } else { 628 lookup = MethodHandles.publicLookup(); 629 } 630 return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides, lookup); 631 } 632 633 /** 634 * When given an object created using {@code Java.extend()} or equivalent mechanism (that is, any JavaScript-to-Java 635 * adapter), returns an object that can be used to invoke superclass methods on that object. E.g.: 636 * <pre> 637 * var cw = new FilterWriterAdapter(sw) { 638 * write: function(s, off, len) { 639 * s = capitalize(s, off, len) 640 * cw_super.write(s, 0, s.length()) 641 * } 642 * } 643 * var cw_super = Java.super(cw) 644 * </pre> 645 * @param self the {@code Java} object itself - not used. 646 * @param adapter the original Java adapter instance for which the super adapter is created. 647 * @return a super adapter for the original adapter 648 */ 649 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR, name="super") 650 public static Object _super(final Object self, final Object adapter) { 651 return Bootstrap.createSuperAdapter(adapter); 652 } 653} 654