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