NativeJava.java revision 1645:15d52fdd9168
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 26package jdk.nashorn.internal.objects; 27 28import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 29import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 30 31import java.lang.invoke.MethodHandles; 32import java.lang.reflect.Array; 33import java.util.ArrayList; 34import java.util.Collection; 35import java.util.Collections; 36import java.util.Deque; 37import java.util.List; 38import java.util.Map; 39import java.util.Queue; 40import jdk.dynalink.SecureLookupSupplier; 41import jdk.dynalink.beans.BeansLinker; 42import jdk.dynalink.beans.StaticClass; 43import jdk.dynalink.linker.support.TypeUtilities; 44import jdk.nashorn.api.scripting.JSObject; 45import jdk.nashorn.api.scripting.ScriptObjectMirror; 46import jdk.nashorn.internal.objects.annotations.Attribute; 47import jdk.nashorn.internal.objects.annotations.Function; 48import jdk.nashorn.internal.objects.annotations.ScriptClass; 49import jdk.nashorn.internal.objects.annotations.Where; 50import jdk.nashorn.internal.runtime.Context; 51import jdk.nashorn.internal.runtime.JSType; 52import jdk.nashorn.internal.runtime.ListAdapter; 53import jdk.nashorn.internal.runtime.PropertyMap; 54import jdk.nashorn.internal.runtime.ScriptFunction; 55import jdk.nashorn.internal.runtime.ScriptObject; 56import jdk.nashorn.internal.runtime.ScriptRuntime; 57import jdk.nashorn.internal.runtime.linker.Bootstrap; 58import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; 59 60/** 61 * This class is the implementation for the {@code Java} global object exposed to programs running under Nashorn. This 62 * object acts as the API entry point to Java platform specific functionality, dealing with creating new instances of 63 * Java classes, subclassing Java classes, implementing Java interfaces, converting between Java arrays and ECMAScript 64 * arrays, and so forth. 65 */ 66@ScriptClass("Java") 67public final class NativeJava { 68 // initialized by nasgen 69 @SuppressWarnings("unused") 70 private static PropertyMap $nasgenmap$; 71 72 private NativeJava() { 73 // don't create me 74 throw new UnsupportedOperationException(); 75 } 76 77 /** 78 * Returns true if the specified object is a Java type object, that is an instance of {@link StaticClass}. 79 * @param self not used 80 * @param type the object that is checked if it is a type object or not 81 * @return tells whether given object is a Java type object or not. 82 * @see #type(Object, Object) 83 */ 84 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 85 public static boolean isType(final Object self, final Object type) { 86 return type instanceof StaticClass; 87 } 88 89 /** 90 * Returns synchronized wrapper version of the given ECMAScript function. 91 * @param self not used 92 * @param func the ECMAScript function whose synchronized version is returned. 93 * @param obj the object (i.e, lock) on which the function synchronizes. 94 * @return synchronized wrapper version of the given ECMAScript function. 95 */ 96 @Function(name="synchronized", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 97 public static Object synchronizedFunc(final Object self, final Object func, final Object obj) { 98 if (func instanceof ScriptFunction) { 99 return ((ScriptFunction)func).createSynchronized(obj); 100 } 101 102 throw typeError("not.a.function", ScriptRuntime.safeToString(func)); 103 } 104 105 /** 106 * Returns true if the specified object is a Java method. 107 * @param self not used 108 * @param obj the object that is checked if it is a Java method object or not 109 * @return tells whether given object is a Java method object or not. 110 */ 111 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 112 public static boolean isJavaMethod(final Object self, final Object obj) { 113 return Bootstrap.isDynamicMethod(obj); 114 } 115 116 /** 117 * Returns true if the specified object is a java function (but not script function) 118 * @param self not used 119 * @param obj the object that is checked if it is a Java function or not 120 * @return tells whether given object is a Java function or not 121 */ 122 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 123 public static boolean isJavaFunction(final Object self, final Object obj) { 124 return Bootstrap.isCallable(obj) && !(obj instanceof ScriptFunction); 125 } 126 127 /** 128 * Returns true if the specified object is a Java object but not a script object 129 * @param self not used 130 * @param obj the object that is checked 131 * @return tells whether given object is a Java object but not a script object 132 */ 133 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 134 public static boolean isJavaObject(final Object self, final Object obj) { 135 return obj != null && !(obj instanceof ScriptObject); 136 } 137 138 /** 139 * Returns true if the specified object is a ECMAScript object, that is an instance of {@link ScriptObject}. 140 * @param self not used 141 * @param obj the object that is checked if it is a ECMAScript object or not 142 * @return tells whether given object is a ECMAScript object or not. 143 */ 144 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 145 public static boolean isScriptObject(final Object self, final Object obj) { 146 return obj instanceof ScriptObject; 147 } 148 149 /** 150 * Returns true if the specified object is a ECMAScript function, that is an instance of {@link ScriptFunction}. 151 * @param self not used 152 * @param obj the object that is checked if it is a ECMAScript function or not 153 * @return tells whether given object is a ECMAScript function or not. 154 */ 155 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 156 public static boolean isScriptFunction(final Object self, final Object obj) { 157 return obj instanceof ScriptFunction; 158 } 159 160 /** 161 * <p> 162 * Given a name of a Java type, returns an object representing that type in Nashorn. The Java class of the objects 163 * used to represent Java types in Nashorn is not {@link java.lang.Class} but rather {@link StaticClass}. They are 164 * the objects that you can use with the {@code new} operator to create new instances of the class as well as to 165 * access static members of the class. In Nashorn, {@code Class} objects are just regular Java objects that aren't 166 * treated specially. Instead of them, {@link StaticClass} instances - which we sometimes refer to as "Java type 167 * objects" are used as constructors with the {@code new} operator, and they expose static fields, properties, and 168 * methods. While this might seem confusing at first, it actually closely matches the Java language: you use a 169 * different expression (e.g. {@code java.io.File}) as an argument in "new" and to address statics, and it is 170 * distinct from the {@code Class} object (e.g. {@code java.io.File.class}). Below we cover in details the 171 * properties of the type objects. 172 * </p> 173 * <p><b>Constructing Java objects</b></p> 174 * Examples: 175 * <pre> 176 * var arrayListType = Java.type("java.util.ArrayList") 177 * var intType = Java.type("int") 178 * var stringArrayType = Java.type("java.lang.String[]") 179 * var int2DArrayType = Java.type("int[][]") 180 * </pre> 181 * Note that the name of the type is always a string for a fully qualified name. You can use any of these types to 182 * create new instances, e.g.: 183 * <pre> 184 * var anArrayList = new Java.type("java.util.ArrayList") 185 * </pre> 186 * or 187 * <pre> 188 * var ArrayList = Java.type("java.util.ArrayList") 189 * var anArrayList = new ArrayList 190 * var anArrayListWithSize = new ArrayList(16) 191 * </pre> 192 * In the special case of inner classes, you can either use the JVM fully qualified name, meaning using {@code $} 193 * sign in the class name, or you can use the dot: 194 * <pre> 195 * var ftype = Java.type("java.awt.geom.Arc2D$Float") 196 * </pre> 197 * and 198 * <pre> 199 * var ftype = Java.type("java.awt.geom.Arc2D.Float") 200 * </pre> 201 * both work. Note however that using the dollar sign is faster, as Java.type first tries to resolve the class name 202 * as it is originally specified, and the internal JVM names for inner classes use the dollar sign. If you use the 203 * dot, Java.type will internally get a ClassNotFoundException and subsequently retry by changing the last dot to 204 * dollar sign. As a matter of fact, it'll keep replacing dots with dollar signs until it either successfully loads 205 * the class or runs out of all dots in the name. This way it can correctly resolve and load even multiply nested 206 * inner classes with the dot notation. Again, this will be slower than using the dollar signs in the name. An 207 * alternative way to access the inner class is as a property of the outer class: 208 * <pre> 209 * var arctype = Java.type("java.awt.geom.Arc2D") 210 * var ftype = arctype.Float 211 * </pre> 212 * <p> 213 * You can access both static and non-static inner classes. If you want to create an instance of a non-static 214 * inner class, remember to pass an instance of its outer class as the first argument to the constructor. 215 * </p> 216 * <p> 217 * If the type is abstract, you can instantiate an anonymous subclass of it using an argument list that is 218 * applicable to any of its public or protected constructors, but inserting a JavaScript object with functions 219 * properties that provide JavaScript implementations of the abstract methods. If method names are overloaded, the 220 * JavaScript function will provide implementation for all overloads. E.g.: 221 * </p> 222 * <pre> 223 * var TimerTask = Java.type("java.util.TimerTask") 224 * var task = new TimerTask({ run: function() { print("Hello World!") } }) 225 * </pre> 226 * <p> 227 * Nashorn supports a syntactic extension where a "new" expression followed by an argument is identical to 228 * invoking the constructor and passing the argument to it, so you can write the above example also as: 229 * </p> 230 * <pre> 231 * var task = new TimerTask { 232 * run: function() { 233 * print("Hello World!") 234 * } 235 * } 236 * </pre> 237 * <p> 238 * which is very similar to Java anonymous inner class definition. On the other hand, if the type is an abstract 239 * type with a single abstract method (commonly referred to as a "SAM type") or all abstract methods it has share 240 * the same overloaded name), then instead of an object, you can just pass a function, so the above example can 241 * become even more simplified to: 242 * </p> 243 * <pre> 244 * var task = new TimerTask(function() { print("Hello World!") }) 245 * </pre> 246 * <p> 247 * Note that in every one of these cases if you are trying to instantiate an abstract class that has constructors 248 * that take some arguments, you can invoke those simply by specifying the arguments after the initial 249 * implementation object or function. 250 * </p> 251 * <p>The use of functions can be taken even further; if you are invoking a Java method that takes a SAM type, 252 * you can just pass in a function object, and Nashorn will know what you meant: 253 * </p> 254 * <pre> 255 * var timer = new Java.type("java.util.Timer") 256 * timer.schedule(function() { print("Hello World!") }) 257 * </pre> 258 * <p> 259 * Here, {@code Timer.schedule()} expects a {@code TimerTask} as its argument, so Nashorn creates an instance of a 260 * {@code TimerTask} subclass and uses the passed function to implement its only abstract method, {@code run()}. In 261 * this usage though, you can't use non-default constructors; the type must be either an interface, or must have a 262 * protected or public no-arg constructor. 263 * </p> 264 * <p> 265 * You can also subclass non-abstract classes; for that you will need to use the {@link #extend(Object, Object...)} 266 * method. 267 * </p> 268 * <p><b>Accessing static members</b></p> 269 * Examples: 270 * <pre> 271 * var File = Java.type("java.io.File") 272 * var pathSep = File.pathSeparator 273 * var tmpFile1 = File.createTempFile("abcdefg", ".tmp") 274 * var tmpFile2 = File.createTempFile("abcdefg", ".tmp", new File("/tmp")) 275 * </pre> 276 * Actually, you can even assign static methods to variables, so the above example can be rewritten as: 277 * <pre> 278 * var File = Java.type("java.io.File") 279 * var createTempFile = File.createTempFile 280 * var tmpFile1 = createTempFile("abcdefg", ".tmp") 281 * var tmpFile2 = createTempFile("abcdefg", ".tmp", new File("/tmp")) 282 * </pre> 283 * If you need to access the actual {@code java.lang.Class} object for the type, you can use the {@code class} 284 * property on the object representing the type: 285 * <pre> 286 * var File = Java.type("java.io.File") 287 * var someFile = new File("blah") 288 * print(File.class === someFile.getClass()) // prints true 289 * </pre> 290 * Of course, you can also use the {@code getClass()} method or its equivalent {@code class} property on any 291 * instance of the class. Other way round, you can use the synthetic {@code static} property on any 292 * {@code java.lang.Class} object to retrieve its type-representing object: 293 * <pre> 294 * var File = Java.type("java.io.File") 295 * print(File.class.static === File) // prints true 296 * </pre> 297 * <p><b>{@code instanceof} operator</b></p> 298 * The standard ECMAScript {@code instanceof} operator is extended to recognize Java objects and their type objects: 299 * <pre> 300 * var File = Java.type("java.io.File") 301 * var aFile = new File("foo") 302 * print(aFile instanceof File) // prints true 303 * print(aFile instanceof File.class) // prints false - Class objects aren't type objects. 304 * </pre> 305 * @param self not used 306 * @param objTypeName the object whose JS string value represents the type name. You can use names of primitive Java 307 * types to obtain representations of them, and you can use trailing square brackets to represent Java array types. 308 * @return the object representing the named type 309 * @throws ClassNotFoundException if the class is not found 310 */ 311 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 312 public static Object type(final Object self, final Object objTypeName) throws ClassNotFoundException { 313 return type(objTypeName); 314 } 315 316 private static StaticClass type(final Object objTypeName) throws ClassNotFoundException { 317 return StaticClass.forClass(type(JSType.toString(objTypeName))); 318 } 319 320 private static Class<?> type(final String typeName) throws ClassNotFoundException { 321 if (typeName.endsWith("[]")) { 322 return arrayType(typeName); 323 } 324 325 return simpleType(typeName); 326 } 327 328 /** 329 * Returns name of a java type {@link StaticClass}. 330 * @param self not used 331 * @param type the type whose name is returned 332 * @return name of the given type 333 */ 334 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 335 public static Object typeName(final Object self, final Object type) { 336 if (type instanceof StaticClass) { 337 return ((StaticClass)type).getRepresentedClass().getName(); 338 } else if (type instanceof Class) { 339 return ((Class<?>)type).getName(); 340 } else { 341 return UNDEFINED; 342 } 343 } 344 345 /** 346 * Given a script object and a Java type, converts the script object into the desired Java type. Currently it 347 * performs shallow creation of Java arrays, as well as wrapping of objects in Lists, Dequeues, Queues, 348 * and Collections. If conversion is not possible or fails for some reason, TypeError is thrown. 349 * Example: 350 * <pre> 351 * var anArray = [1, "13", false] 352 * var javaIntArray = Java.to(anArray, "int[]") 353 * print(javaIntArray[0]) // prints 1 354 * print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion 355 * print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion 356 * </pre> 357 * @param self not used 358 * @param obj the script object. Can be null. 359 * @param objType either a {@link #type(Object, Object) type object} or a String describing the type of the Java 360 * object to create. Can not be null. If undefined, a "default" conversion is presumed (allowing the argument to be 361 * omitted). 362 * @return a Java object whose value corresponds to the original script object's value. Specifically, for array 363 * target types, returns a Java array of the same type with contents converted to the array's component type. 364 * Converts recursively when the target type is multidimensional array. For {@link List}, {@link Deque}, 365 * {@link Queue}, or {@link Collection}, returns a live wrapper around the object, see {@link ListAdapter} for 366 * details. Returns null if obj is null. 367 * @throws ClassNotFoundException if the class described by objType is not found 368 */ 369 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 370 public static Object to(final Object self, final Object obj, final Object objType) throws ClassNotFoundException { 371 if (obj == null) { 372 return null; 373 } 374 375 if (!(obj instanceof ScriptObject) && !(obj instanceof JSObject)) { 376 throw typeError("not.an.object", ScriptRuntime.safeToString(obj)); 377 } 378 379 final Class<?> targetClass; 380 if(objType == UNDEFINED) { 381 targetClass = Object[].class; 382 } else { 383 final StaticClass targetType; 384 if(objType instanceof StaticClass) { 385 targetType = (StaticClass)objType; 386 } else { 387 targetType = type(objType); 388 } 389 targetClass = targetType.getRepresentedClass(); 390 } 391 392 if(targetClass.isArray()) { 393 try { 394 if (self instanceof SecureLookupSupplier) { 395 return JSType.toJavaArrayWithLookup(obj, targetClass.getComponentType(), (SecureLookupSupplier)self); 396 } 397 return JSType.toJavaArray(obj, targetClass.getComponentType()); 398 } catch (final Exception exp) { 399 throw typeError(exp, "java.array.conversion.failed", targetClass.getName()); 400 } 401 } 402 403 if (targetClass == List.class || targetClass == Deque.class || targetClass == Queue.class || targetClass == Collection.class) { 404 return ListAdapter.create(obj); 405 } 406 407 throw typeError("unsupported.java.to.type", targetClass.getName()); 408 } 409 410 /** 411 * Given a Java array or {@link Collection}, returns a JavaScript array with a shallow copy of its contents. Note 412 * that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you 413 * need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will 414 * want to use this method. Example: 415 * <pre> 416 * var File = Java.type("java.io.File") 417 * var listHomeDir = new File("~").listFiles() 418 * var jsListHome = Java.from(listHomeDir) 419 * var jpegModifiedDates = jsListHome 420 * .filter(function(val) { return val.getName().endsWith(".jpg") }) 421 * .map(function(val) { return val.lastModified() }) 422 * </pre> 423 * @param self not used 424 * @param objArray the java array or collection. Can be null. 425 * @return a JavaScript array with the copy of Java array's or collection's contents. Returns null if objArray is 426 * null. 427 */ 428 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 429 public static NativeArray from(final Object self, final Object objArray) { 430 if (objArray == null) { 431 return null; 432 } else if (objArray instanceof Collection) { 433 return new NativeArray(((Collection<?>)objArray).toArray()); 434 } else if (objArray instanceof Object[]) { 435 return new NativeArray(((Object[])objArray).clone()); 436 } else if (objArray instanceof int[]) { 437 return new NativeArray(((int[])objArray).clone()); 438 } else if (objArray instanceof double[]) { 439 return new NativeArray(((double[])objArray).clone()); 440 } else if (objArray instanceof long[]) { 441 return new NativeArray(((long[])objArray).clone()); 442 } else if (objArray instanceof byte[]) { 443 return new NativeArray(copyArray((byte[])objArray)); 444 } else if (objArray instanceof short[]) { 445 return new NativeArray(copyArray((short[])objArray)); 446 } else if (objArray instanceof char[]) { 447 return new NativeArray(copyArray((char[])objArray)); 448 } else if (objArray instanceof float[]) { 449 return new NativeArray(copyArray((float[])objArray)); 450 } else if (objArray instanceof boolean[]) { 451 return new NativeArray(copyArray((boolean[])objArray)); 452 } 453 454 throw typeError("cant.convert.to.javascript.array", objArray.getClass().getName()); 455 } 456 457 /** 458 * Return properties of the given object. Properties also include "method names". 459 * This is meant for source code completion in interactive shells or editors. 460 * 461 * @param object the object whose properties are returned. 462 * @return list of properties 463 */ 464 public static List<String> getProperties(final Object object) { 465 if (object instanceof StaticClass) { 466 // static properties of the given class 467 final Class<?> clazz = ((StaticClass)object).getRepresentedClass(); 468 final ArrayList<String> props = new ArrayList<>(); 469 try { 470 Bootstrap.checkReflectionAccess(clazz, true); 471 // Usually writable properties are a subset as 'write-only' properties are rare 472 props.addAll(BeansLinker.getReadableStaticPropertyNames(clazz)); 473 props.addAll(BeansLinker.getStaticMethodNames(clazz)); 474 } catch (final Exception ignored) {} 475 return props; 476 } else if (object instanceof JSObject) { 477 final JSObject jsObj = ((JSObject)object); 478 final ArrayList<String> props = new ArrayList<>(); 479 props.addAll(jsObj.keySet()); 480 return props; 481 } else if (object != null && object != UNDEFINED) { 482 // instance properties of the given object 483 final Class<?> clazz = object.getClass(); 484 final ArrayList<String> props = new ArrayList<>(); 485 try { 486 Bootstrap.checkReflectionAccess(clazz, false); 487 // Usually writable properties are a subset as 'write-only' properties are rare 488 props.addAll(BeansLinker.getReadableInstancePropertyNames(clazz)); 489 props.addAll(BeansLinker.getInstanceMethodNames(clazz)); 490 } catch (final Exception ignored) {} 491 return props; 492 } 493 494 // don't know about that object 495 return Collections.<String>emptyList(); 496 } 497 498 private static int[] copyArray(final byte[] in) { 499 final int[] out = new int[in.length]; 500 for(int i = 0; i < in.length; ++i) { 501 out[i] = in[i]; 502 } 503 return out; 504 } 505 506 private static int[] copyArray(final short[] in) { 507 final int[] out = new int[in.length]; 508 for(int i = 0; i < in.length; ++i) { 509 out[i] = in[i]; 510 } 511 return out; 512 } 513 514 private static int[] copyArray(final char[] in) { 515 final int[] out = new int[in.length]; 516 for(int i = 0; i < in.length; ++i) { 517 out[i] = in[i]; 518 } 519 return out; 520 } 521 522 private static double[] copyArray(final float[] in) { 523 final double[] out = new double[in.length]; 524 for(int i = 0; i < in.length; ++i) { 525 out[i] = in[i]; 526 } 527 return out; 528 } 529 530 private static Object[] copyArray(final boolean[] in) { 531 final Object[] out = new Object[in.length]; 532 for(int i = 0; i < in.length; ++i) { 533 out[i] = in[i]; 534 } 535 return out; 536 } 537 538 private static Class<?> simpleType(final String typeName) throws ClassNotFoundException { 539 final Class<?> primClass = TypeUtilities.getPrimitiveTypeByName(typeName); 540 if(primClass != null) { 541 return primClass; 542 } 543 final Context ctx = Global.getThisContext(); 544 try { 545 return ctx.findClass(typeName); 546 } catch(final ClassNotFoundException e) { 547 // The logic below compensates for a frequent user error - when people use dot notation to separate inner 548 // class names, i.e. "java.lang.Character.UnicodeBlock" vs."java.lang.Character$UnicodeBlock". The logic 549 // below will try alternative class names, replacing dots at the end of the name with dollar signs. 550 final StringBuilder nextName = new StringBuilder(typeName); 551 int lastDot = nextName.length(); 552 for(;;) { 553 lastDot = nextName.lastIndexOf(".", lastDot - 1); 554 if(lastDot == -1) { 555 // Exhausted the search space, class not found - rethrow the original exception. 556 throw e; 557 } 558 nextName.setCharAt(lastDot, '$'); 559 try { 560 return ctx.findClass(nextName.toString()); 561 } catch(final ClassNotFoundException cnfe) { 562 // Intentionally ignored, so the loop retries with the next name 563 } 564 } 565 } 566 567 } 568 569 private static Class<?> arrayType(final String typeName) throws ClassNotFoundException { 570 return Array.newInstance(type(typeName.substring(0, typeName.length() - 2)), 0).getClass(); 571 } 572 573 /** 574 * Returns a type object for a subclass of the specified Java class (or implementation of the specified interface) 575 * that acts as a script-to-Java adapter for it. See {@link #type(Object, Object)} for a discussion of type objects, 576 * and see {@link JavaAdapterFactory} for details on script-to-Java adapters. Note that you can also implement 577 * interfaces and subclass abstract classes using {@code new} operator on a type object for an interface or abstract 578 * class. However, to extend a non-abstract class, you will have to use this method. Example: 579 * <pre> 580 * var ArrayList = Java.type("java.util.ArrayList") 581 * var ArrayListExtender = Java.extend(ArrayList) 582 * var printSizeInvokedArrayList = new ArrayListExtender() { 583 * size: function() { print("size invoked!"); } 584 * } 585 * var printAddInvokedArrayList = new ArrayListExtender() { 586 * add: function(x, y) { 587 * if(typeof(y) === "undefined") { 588 * print("add(e) invoked!"); 589 * } else { 590 * print("add(i, e) invoked!"); 591 * } 592 * } 593 * </pre> 594 * We can see several important concepts in the above example: 595 * <ul> 596 * <li>Every specified list of Java types will have one extender subclass in Nashorn per caller protection domain - 597 * repeated invocations of {@code extend} for the same list of types for scripts same protection domain will yield 598 * the same extender type. It's a generic adapter that delegates to whatever JavaScript functions its implementation 599 * object has on a per-instance basis.</li> 600 * <li>If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter 601 * must be prepared to deal with all overloads.</li> 602 * <li>To invoke super methods from adapters, call them on the adapter instance prefixing them with {@code super$}, 603 * or use the special {@link #_super(Object, Object) super-adapter}.</li> 604 * <li>It is also possible to specify an ordinary JavaScript object as the last argument to {@code extend}. In that 605 * case, it is treated as a class-level override. {@code extend} will return an extender class where all instances 606 * will have the methods implemented by functions on that object, just as if that object were passed as the last 607 * argument to their constructor. Example: 608 * <pre> 609 * var Runnable = Java.type("java.lang.Runnable") 610 * var R1 = Java.extend(Runnable, { 611 * run: function() { 612 * print("R1.run() invoked!") 613 * } 614 * }) 615 * var r1 = new R1 616 * var t = new java.lang.Thread(r1) 617 * t.start() 618 * t.join() 619 * </pre> 620 * As you can see, you don't have to pass any object when you create a new instance of {@code R1} as its 621 * {@code run()} function was defined already when extending the class. If you also want to add instance-level 622 * overrides on these objects, you will have to repeatedly use {@code extend()} to subclass the class-level adapter. 623 * For such adapters, the order of precedence is instance-level method, class-level method, superclass method, or 624 * {@code UnsupportedOperationException} if the superclass method is abstract. If we continue our previous example: 625 * <pre> 626 * var R2 = Java.extend(R1); 627 * var r2 = new R2(function() { print("r2.run() invoked!") }) 628 * r2.run() 629 * </pre> 630 * We'll see it'll print {@code "r2.run() invoked!"}, thus overriding on instance-level the class-level behavior. 631 * Note that you must use {@code Java.extend} to explicitly create an instance-override adapter class from a 632 * class-override adapter class, as the class-override adapter class is no longer abstract. 633 * </li> 634 * </ul> 635 * @param self not used 636 * @param types the original types. The caller must pass at least one Java type object of class {@link StaticClass} 637 * representing either a public interface or a non-final public class with at least one public or protected 638 * constructor. If more than one type is specified, at most one can be a class and the rest have to be interfaces. 639 * Invoking the method twice with exactly the same types in the same order - in absence of class-level overrides - 640 * will return the same adapter class, any reordering of types or even addition or removal of redundant types (i.e. 641 * interfaces that other types in the list already implement/extend, or {@code java.lang.Object} in a list of types 642 * consisting purely of interfaces) will result in a different adapter class, even though those adapter classes are 643 * functionally identical; we deliberately don't want to incur the additional processing cost of canonicalizing type 644 * lists. As a special case, the last argument can be a {@code ScriptObject} instead of a type. In this case, a 645 * separate adapter class is generated - new one for each invocation - that will use the passed script object as its 646 * implementation for all instances. Instances of such adapter classes can then be created without passing another 647 * script object in the constructor, as the class has a class-level behavior defined by the script object. However, 648 * you can still pass a script object (or if it's a SAM type, a function) to the constructor to provide further 649 * instance-level overrides. 650 * 651 * @return a new {@link StaticClass} that represents the adapter for the original types. 652 */ 653 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 654 public static Object extend(final Object self, final Object... types) { 655 if(types == null || types.length == 0) { 656 throw typeError("extend.expects.at.least.one.argument"); 657 } 658 final int l = types.length; 659 final int typesLen; 660 final ScriptObject classOverrides; 661 if(types[l - 1] instanceof ScriptObject) { 662 classOverrides = (ScriptObject)types[l - 1]; 663 typesLen = l - 1; 664 if(typesLen == 0) { 665 throw typeError("extend.expects.at.least.one.type.argument"); 666 } 667 } else { 668 classOverrides = null; 669 typesLen = l; 670 } 671 final Class<?>[] stypes = new Class<?>[typesLen]; 672 try { 673 for(int i = 0; i < typesLen; ++i) { 674 stypes[i] = ((StaticClass)types[i]).getRepresentedClass(); 675 } 676 } catch(final ClassCastException e) { 677 throw typeError("extend.expects.java.types"); 678 } 679 // Note that while the public API documentation claims self is not used, we actually use it. 680 // ScriptFunction.findCallMethod will bind the lookup object into it, and we can then use that lookup when 681 // requesting the adapter class. Note that if Java.extend is invoked with no lookup object, it'll pass the 682 // public lookup which'll result in generation of a no-permissions adapter. A typical situation this can happen 683 // is when the extend function is bound. 684 final MethodHandles.Lookup lookup; 685 if(self instanceof MethodHandles.Lookup) { 686 lookup = (MethodHandles.Lookup)self; 687 } else { 688 lookup = MethodHandles.publicLookup(); 689 } 690 return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides, lookup); 691 } 692 693 /** 694 * When given an object created using {@code Java.extend()} or equivalent mechanism (that is, any JavaScript-to-Java 695 * adapter), returns an object that can be used to invoke superclass methods on that object. E.g.: 696 * <pre> 697 * var cw = new FilterWriterAdapter(sw) { 698 * write: function(s, off, len) { 699 * s = capitalize(s, off, len) 700 * cw_super.write(s, 0, s.length()) 701 * } 702 * } 703 * var cw_super = Java.super(cw) 704 * </pre> 705 * @param self the {@code Java} object itself - not used. 706 * @param adapter the original Java adapter instance for which the super adapter is created. 707 * @return a super adapter for the original adapter 708 */ 709 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR, name="super") 710 public static Object _super(final Object self, final Object adapter) { 711 return Bootstrap.createSuperAdapter(adapter); 712 } 713 714 /** 715 * Returns an object that is compatible with Java JSON libraries expectations; namely, that if it itself, or any 716 * object transitively reachable through it is a JavaScript array, then such objects will be exposed as 717 * {@link JSObject} that also implements the {@link List} interface for exposing the array elements. An explicit 718 * API is required as otherwise Nashorn exposes all objects externally as {@link JSObject}s that also implement the 719 * {@link Map} interface instead. By using this method, arrays will be exposed as {@link List}s and all other 720 * objects as {@link Map}s. 721 * @param self not used 722 * @param obj the object to be exposed in a Java JSON library compatible manner. 723 * @return a wrapper around the object that will enforce Java JSON library compatible exposure. 724 */ 725 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 726 public static Object asJSONCompatible(final Object self, final Object obj) { 727 return ScriptObjectMirror.wrapAsJSONCompatible(obj, Context.getGlobal()); 728 } 729} 730