aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/objects/NativeJava.java
blob: b863e24f2a5282b3ac00f7d0fb8d692fbb083688 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.nashorn.internal.objects;

import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ListAdapter;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;

/**
 * This class is the implementation for the {@code Java} global object exposed to programs running under Nashorn. This
 * object acts as the API entry point to Java platform specific functionality, dealing with creating new instances of
 * Java classes, subclassing Java classes, implementing Java interfaces, converting between Java arrays and ECMAScript
 * arrays, and so forth.
 */
@ScriptClass("Java")
public final class NativeJava {

    // initialized by nasgen
    @SuppressWarnings("unused")
    private static PropertyMap $nasgenmap$;

    private NativeJava() {
        // don't create me
        throw new UnsupportedOperationException();
    }

    /**
     * Returns true if the specified object is a Java type object, that is an instance of {@link StaticClass}.
     * @param self not used
     * @param type the object that is checked if it is a type object or not
     * @return tells whether given object is a Java type object or not.
     * @see #type(Object, Object)
     */
    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
    public static Object isType(final Object self, final Object type) {
        return type instanceof StaticClass;
    }

    /**
     * <p>
     * Given a name of a Java type, returns an object representing that type in Nashorn. The Java class of the objects
     * used to represent Java types in Nashorn is not {@link java.lang.Class} but rather {@link StaticClass}. They are
     * the objects that you can use with the {@code new} operator to create new instances of the class as well as to
     * access static members of the class. In Nashorn, {@code Class} objects are just regular Java objects that aren't
     * treated specially. Instead of them, {@link StaticClass} instances - which we sometimes refer to as "Java type
     * objects" are used as constructors with the {@code new} operator, and they expose static fields, properties, and
     * methods. While this might seem confusing at first, it actually closely matches the Java language: you use a
     * different expression (e.g. {@code java.io.File}) as an argument in "new" and to address statics, and it is
     * distinct from the {@code Class} object (e.g. {@code java.io.File.class}). Below we cover in details the
     * properties of the type objects.
     * </p>
     * <p><b>Constructing Java objects</b></p>
     * Examples:
     * <pre>
     * var arrayListType = Java.type("java.util.ArrayList")
     * var intType = Java.type("int")
     * var stringArrayType = Java.type("java.lang.String[]")
     * var int2DArrayType = Java.type("int[][]")
     * </pre>
     * Note that the name of the type is always a string for a fully qualified name. You can use any of these types to
     * create new instances, e.g.:
     * <pre>
     * var anArrayList = new Java.type("java.util.ArrayList")
     * </pre>
     * or
     * <pre>
     * var ArrayList = Java.type("java.util.ArrayList")
     * var anArrayList = new ArrayList
     * var anArrayListWithSize = new ArrayList(16)
     * </pre>
     * In the special case of inner classes, you can either use the JVM fully qualified name, meaning using {@code $}
     * sign in the class name, or you can use the dot:
     * <pre>
     * var ftype = Java.type("java.awt.geom.Arc2D$Float")
     * </pre>
     * and
     * <pre>
     * var ftype = Java.type("java.awt.geom.Arc2D.Float")
     * </pre>
     * both work. Note however that using the dollar sign is faster, as Java.type first tries to resolve the class name
     * as it is originally specified, and the internal JVM names for inner classes use the dollar sign. If you use the
     * dot, Java.type will internally get a ClassNotFoundException and subsequently retry by changing the last dot to
     * dollar sign. As a matter of fact, it'll keep replacing dots with dollar signs until it either successfully loads
     * the class or runs out of all dots in the name. This way it can correctly resolve and load even multiply nested
     * inner classes with the dot notation. Again, this will be slower than using the dollar signs in the name. An
     * alternative way to access the inner class is as a property of the outer class:
     * <pre>
     * var arctype = Java.type("java.awt.geom.Arc2D")
     * var ftype = arctype.Float
     * </pre>
     * <p>
     * You can access both static and non-static inner classes. If you want to create an instance of a non-static
     * inner class, remember to pass an instance of its outer class as the first argument to the constructor.
     * </p>
     * <p>
     * If the type is abstract, you can instantiate an anonymous subclass of it using an argument list that is
     * applicable to any of its public or protected constructors, but inserting a JavaScript object with functions
     * properties that provide JavaScript implementations of the abstract methods. If method names are overloaded, the
     * JavaScript function will provide implementation for all overloads. E.g.:
     * </p>
     * <pre>
     * var TimerTask =  Java.type("java.util.TimerTask")
     * var task = new TimerTask({ run: function() { print("Hello World!") } })
     * </pre>
     * <p>
     * Nashorn supports a syntactic extension where a "new" expression followed by an argument is identical to
     * invoking the constructor and passing the argument to it, so you can write the above example also as:
     * </p>
     * <pre>
     * var task = new TimerTask {
     *     run: function() {
     *       print("Hello World!")
     *     }
     * }
     * </pre>
     * <p>
     * which is very similar to Java anonymous inner class definition. On the other hand, if the type is an abstract
     * type with a single abstract method (commonly referred to as a "SAM type") or all abstract methods it has share
     * the same overloaded name), then instead of an object, you can just pass a function, so the above example can
     * become even more simplified to:
     * </p>
     * <pre>
     * var task = new TimerTask(function() { print("Hello World!") })
     * </pre>
     * <p>
     * Note that in every one of these cases if you are trying to instantiate an abstract class that has constructors
     * that take some arguments, you can invoke those simply by specifying the arguments after the initial
     * implementation object or function.
     * </p>
     * <p>The use of functions can be taken even further; if you are invoking a Java method that takes a SAM type,
     * you can just pass in a function object, and Nashorn will know what you meant:
     * </p>
     * <pre>
     * var timer = new Java.type("java.util.Timer")
     * timer.schedule(function() { print("Hello World!") })
     * </pre>
     * <p>
     * Here, {@code Timer.schedule()} expects a {@code TimerTask} as its argument, so Nashorn creates an instance of a
     * {@code TimerTask} subclass and uses the passed function to implement its only abstract method, {@code run()}. In
     * this usage though, you can't use non-default constructors; the type must be either an interface, or must have a
     * protected or public no-arg constructor.
     * </p>
     * <p>
     * You can also subclass non-abstract classes; for that you will need to use the {@link #extend(Object, Object...)}
     * method.
     * </p>
     * <p><b>Accessing static members</b></p>
     * Examples:
     * <pre>
     * var File = Java.type("java.io.File")
     * var pathSep = File.pathSeparator
     * var tmpFile1 = File.createTempFile("abcdefg", ".tmp")
     * var tmpFile2 = File.createTempFile("abcdefg", ".tmp", new File("/tmp"))
     * </pre>
     * Actually, you can even assign static methods to variables, so the above example can be rewritten as:
     * <pre>
     * var File = Java.type("java.io.File")
     * var createTempFile = File.createTempFile
     * var tmpFile1 = createTempFile("abcdefg", ".tmp")
     * var tmpFile2 = createTempFile("abcdefg", ".tmp", new File("/tmp"))
     * </pre>
     * If you need to access the actual {@code java.lang.Class} object for the type, you can use the {@code class}
     * property on the object representing the type:
     * <pre>
     * var File = Java.type("java.io.File")
     * var someFile = new File("blah")
     * print(File.class === someFile.getClass()) // prints true
     * </pre>
     * Of course, you can also use the {@code getClass()} method or its equivalent {@code class} property on any
     * instance of the class. Other way round, you can use the synthetic {@code static} property on any
     * {@code java.lang.Class} object to retrieve its type-representing object:
     * <pre>
     * var File = Java.type("java.io.File")
     * print(File.class.static === File) // prints true
     * </pre>
     * <p><b>{@code instanceof} operator</b></p>
     * The standard ECMAScript {@code instanceof} operator is extended to recognize Java objects and their type objects:
     * <pre>
     * var File = Java.type("java.io.File")
     * var aFile = new File("foo")
     * print(aFile instanceof File) // prints true
     * print(aFile instanceof File.class) // prints false - Class objects aren't type objects.
     * </pre>
     * @param self not used
     * @param objTypeName the object whose JS string value represents the type name. You can use names of primitive Java
     * types to obtain representations of them, and you can use trailing square brackets to represent Java array types.
     * @return the object representing the named type
     * @throws ClassNotFoundException if the class is not found
     */
    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
    public static Object type(final Object self, final Object objTypeName) throws ClassNotFoundException {
        return type(objTypeName);
    }

    private static StaticClass type(final Object objTypeName) throws ClassNotFoundException {
        return StaticClass.forClass(type(JSType.toString(objTypeName)));
    }

    private static Class<?> type(final String typeName) throws ClassNotFoundException {
        if (typeName.endsWith("[]")) {
            return arrayType(typeName);
        }

        return simpleType(typeName);
    }

    /**
     * Returns name of a java type {@link StaticClass}.
     * @param self not used
     * @param type the type whose name is returned
     * @return name of the given type
     */
    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
    public static Object typeName(final Object self, final Object type) {
        if (type instanceof StaticClass) {
            return ((StaticClass)type).getRepresentedClass().getName();
        } else if (type instanceof Class) {
            return ((Class<?>)type).getName();
        } else {
            return UNDEFINED;
        }
    }

    /**
     * Given a script object and a Java type, converts the script object into the desired Java type. Currently it
     * performs shallow creation of Java arrays, as well as wrapping of objects in Lists and Dequeues. Example:
     * <pre>
     * var anArray = [1, "13", false]
     * var javaIntArray = Java.to(anArray, "int[]")
     * print(javaIntArray[0]) // prints 1
     * print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
     * print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
     * </pre>
     * @param self not used
     * @param obj the script object. Can be null.
     * @param objType either a {@link #type(Object, Object) type object} or a String describing the type of the Java
     * object to create. Can not be null. If undefined, a "default" conversion is presumed (allowing the argument to be
     * omitted).
     * @return a Java object whose value corresponds to the original script object's value. Specifically, for array
     * target types, returns a Java array of the same type with contents converted to the array's component type. Does
     * not recursively convert for multidimensional arrays. For {@link List} or {@link Deque}, returns a live wrapper
     * around the object, see {@link ListAdapter} for details. Returns null if obj is null.
     * @throws ClassNotFoundException if the class described by objType is not found
     */
    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
    public static Object to(final Object self, final Object obj, final Object objType) throws ClassNotFoundException {
        if (obj == null) {
            return null;
        }

        if (!(obj instanceof ScriptObject) && !(obj instanceof JSObject)) {
            throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
        }

        final Class<?> targetClass;
        if(objType == UNDEFINED) {
            targetClass = Object[].class;
        } else {
            final StaticClass targetType;
            if(objType instanceof StaticClass) {
                targetType = (StaticClass)objType;
            } else {
                targetType = type(objType);
            }
            targetClass = targetType.getRepresentedClass();
        }

        if(targetClass.isArray()) {
            return JSType.toJavaArray(obj, targetClass.getComponentType());
        }

        if(targetClass == List.class || targetClass == Deque.class) {
            return ListAdapter.create(obj);
        }

        throw typeError("unsupported.java.to.type", targetClass.getName());
    }

    /**
     * Given a Java array or {@link Collection}, returns a JavaScript array with a shallow copy of its contents. Note
     * that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you
     * need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will
     * want to use this method. Example:
     * <pre>
     * var File = Java.type("java.io.File")
     * var listHomeDir = new File("~").listFiles()
     * var jsListHome = Java.from(listHomeDir)
     * var jpegModifiedDates = jsListHome
     *     .filter(function(val) { return val.getName().endsWith(".jpg") })
     *     .map(function(val) { return val.lastModified() })
     * </pre>
     * @param self not used
     * @param objArray the java array or collection. Can be null.
     * @return a JavaScript array with the copy of Java array's or collection's contents. Returns null if objArray is
     * null.
     */
    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
    public static Object from(final Object self, final Object objArray) {
        if (objArray == null) {
            return null;
        } else if (objArray instanceof Collection) {
            return new NativeArray(((Collection<?>)objArray).toArray());
        } else if (objArray instanceof Object[]) {
            return new NativeArray(((Object[])objArray).clone());
        } else if (objArray instanceof int[]) {
            return new NativeArray(((int[])objArray).clone());
        } else if (objArray instanceof double[]) {
            return new NativeArray(((double[])objArray).clone());
        } else if (objArray instanceof long[]) {
            return new NativeArray(((long[])objArray).clone());
        } else if (objArray instanceof byte[]) {
            return new NativeArray(copyArray((byte[])objArray));
        } else if (objArray instanceof short[]) {
            return new NativeArray(copyArray((short[])objArray));
        } else if (objArray instanceof char[]) {
            return new NativeArray(copyArray((char[])objArray));
        } else if (objArray instanceof float[]) {
            return new NativeArray(copyArray((float[])objArray));
        } else if (objArray instanceof boolean[]) {
            return new NativeArray(copyArray((boolean[])objArray));
        }

        throw typeError("cant.convert.to.javascript.array", objArray.getClass().getName());
    }

    private static int[] copyArray(final byte[] in) {
        final int[] out = new int[in.length];
        for(int i = 0; i < in.length; ++i) {
            out[i] = in[i];
        }
        return out;
    }

    private static int[] copyArray(final short[] in) {
        final int[] out = new int[in.length];
        for(int i = 0; i < in.length; ++i) {
            out[i] = in[i];
        }
        return out;
    }

    private static int[] copyArray(final char[] in) {
        final int[] out = new int[in.length];
        for(int i = 0; i < in.length; ++i) {
            out[i] = in[i];
        }
        return out;
    }

    private static double[] copyArray(final float[] in) {
        final double[] out = new double[in.length];
        for(int i = 0; i < in.length; ++i) {
            out[i] = in[i];
        }
        return out;
    }

    private static Object[] copyArray(final boolean[] in) {
        final Object[] out = new Object[in.length];
        for(int i = 0; i < in.length; ++i) {
            out[i] = in[i];
        }
        return out;
    }

    private static Class<?> simpleType(final String typeName) throws ClassNotFoundException {
        final Class<?> primClass = TypeUtilities.getPrimitiveTypeByName(typeName);
        if(primClass != null) {
            return primClass;
        }
        final Context ctx = Global.getThisContext();
        try {
            return ctx.findClass(typeName);
        } catch(ClassNotFoundException e) {
            // The logic below compensates for a frequent user error - when people use dot notation to separate inner
            // class names, i.e. "java.lang.Character.UnicodeBlock" vs."java.lang.Character$UnicodeBlock". The logic
            // below will try alternative class names, replacing dots at the end of the name with dollar signs.
            final StringBuilder nextName = new StringBuilder(typeName);
            int lastDot = nextName.length();
            for(;;) {
                lastDot = nextName.lastIndexOf(".", lastDot - 1);
                if(lastDot == -1) {
                    // Exhausted the search space, class not found - rethrow the original exception.
                    throw e;
                }
                nextName.setCharAt(lastDot, '$');
                try {
                    return ctx.findClass(nextName.toString());
                } catch(ClassNotFoundException cnfe) {
                    // Intentionally ignored, so the loop retries with the next name
                }
            }
        }

    }

    private static Class<?> arrayType(final String typeName) throws ClassNotFoundException {
        return Array.newInstance(type(typeName.substring(0, typeName.length() - 2)), 0).getClass();
    }

    /**
     * Returns a type object for a subclass of the specified Java class (or implementation of the specified interface)
     * that acts as a script-to-Java adapter for it. See {@link #type(Object, Object)} for a discussion of type objects,
     * and see {@link JavaAdapterFactory} for details on script-to-Java adapters. Note that you can also implement
     * interfaces and subclass abstract classes using {@code new} operator on a type object for an interface or abstract
     * class. However, to extend a non-abstract class, you will have to use this method. Example:
     * <pre>
     * var ArrayList = Java.type("java.util.ArrayList")
     * var ArrayListExtender = Java.extend(ArrayList)
     * var printSizeInvokedArrayList = new ArrayListExtender() {
     *     size: function() { print("size invoked!"); }
     * }
     * var printAddInvokedArrayList = new ArrayListExtender() {
     *     add: function(x, y) {
     *       if(typeof(y) === "undefined") {
     *           print("add(e) invoked!");
     *       } else {
     *           print("add(i, e) invoked!");
     *       }
     * }
     * </pre>
     * We can see several important concepts in the above example:
     * <ul>
     * <li>Every specified list of Java types will have one extender subclass in Nashorn per caller protection domain -
     * repeated invocations of {@code extend} for the same list of types for scripts same protection domain will yield
     * the same extender type. It's a generic adapter that delegates to whatever JavaScript functions its implementation
     * object has on a per-instance basis.</li>
     * <li>If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter
     * must be prepared to deal with all overloads.</li>
     * <li>To invoke super methods from adapters, call them on the adapter instance prefixing them with {@code super$},
     * or use the special {@link #_super(Object, Object) super-adapter}.</li>
     * <li>It is also possible to specify an ordinary JavaScript object as the last argument to {@code extend}. In that
     * case, it is treated as a class-level override. {@code extend} will return an extender class where all instances
     * will have the methods implemented by functions on that object, just as if that object were passed as the last
     * argument to their constructor. Example:
     * <pre>
     * var Runnable = Java.type("java.lang.Runnable")
     * var R1 = Java.extend(Runnable, {
     *     run: function() {
     *         print("R1.run() invoked!")
     *     }
     * })
     * var r1 = new R1
     * var t = new java.lang.Thread(r1)
     * t.start()
     * t.join()
     * </pre>
     * As you can see, you don't have to pass any object when you create a new instance of {@code R1} as its
     * {@code run()} function was defined already when extending the class. If you also want to add instance-level
     * overrides on these objects, you will have to repeatedly use {@code extend()} to subclass the class-level adapter.
     * For such adapters, the order of precedence is instance-level method, class-level method, superclass method, or
     * {@code UnsupportedOperationException} if the superclass method is abstract. If we continue our previous example:
     * <pre>
     * var R2 = Java.extend(R1);
     * var r2 = new R2(function() { print("r2.run() invoked!") })
     * r2.run()
     * </pre>
     * We'll see it'll print {@code "r2.run() invoked!"}, thus overriding on instance-level the class-level behavior.
     * Note that you must use {@code Java.extend} to explicitly create an instance-override adapter class from a
     * class-override adapter class, as the class-override adapter class is no longer abstract.
     * </li>
     * </ul>
     * @param self not used
     * @param types the original types. The caller must pass at least one Java type object of class {@link StaticClass}
     * representing either a public interface or a non-final public class with at least one public or protected
     * constructor. If more than one type is specified, at most one can be a class and the rest have to be interfaces.
     * Invoking the method twice with exactly the same types in the same order - in absence of class-level overrides -
     * will return the same adapter class, any reordering of types or even addition or removal of redundant types (i.e.
     * interfaces that other types in the list already implement/extend, or {@code java.lang.Object} in a list of types
     * consisting purely of interfaces) will result in a different adapter class, even though those adapter classes are
     * functionally identical; we deliberately don't want to incur the additional processing cost of canonicalizing type
     * lists. As a special case, the last argument can be a {@code ScriptObject} instead of a type. In this case, a
     * separate adapter class is generated - new one for each invocation - that will use the passed script object as its
     * implementation for all instances. Instances of such adapter classes can then be created without passing another
     * script object in the constructor, as the class has a class-level behavior defined by the script object. However,
     * you can still pass a script object (or if it's a SAM type, a function) to the constructor to provide further
     * instance-level overrides.
     *
     * @return a new {@link StaticClass} that represents the adapter for the original types.
     */
    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
    public static Object extend(final Object self, final Object... types) {
        if(types == null || types.length == 0) {
            throw typeError("extend.expects.at.least.one.argument");
        }
        final int l = types.length;
        final int typesLen;
        final ScriptObject classOverrides;
        if(types[l - 1] instanceof ScriptObject) {
            classOverrides = (ScriptObject)types[l - 1];
            typesLen = l - 1;
            if(typesLen == 0) {
                throw typeError("extend.expects.at.least.one.type.argument");
            }
        } else {
            classOverrides = null;
            typesLen = l;
        }
        final Class<?>[] stypes = new Class<?>[typesLen];
        try {
            for(int i = 0; i < typesLen; ++i) {
                stypes[i] = ((StaticClass)types[i]).getRepresentedClass();
            }
        } catch(final ClassCastException e) {
            throw typeError("extend.expects.java.types");
        }
        // Note that while the public API documentation claims self is not used, we actually use it.
        // ScriptFunction.findCallMethod will bind the lookup object into it, and we can then use that lookup when
        // requesting the adapter class. Note that if Java.extend is invoked with no lookup object, it'll pass the
        // public lookup which'll result in generation of a no-permissions adapter. A typical situation this can happen
        // is when the extend function is bound.
        final MethodHandles.Lookup lookup;
        if(self instanceof MethodHandles.Lookup) {
            lookup = (MethodHandles.Lookup)self;
        } else {
            lookup = MethodHandles.publicLookup();
        }
        return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides, lookup);
    }

    /**
     * When given an object created using {@code Java.extend()} or equivalent mechanism (that is, any JavaScript-to-Java
     * adapter), returns an object that can be used to invoke superclass methods on that object. E.g.:
     * <pre>
     * var cw = new FilterWriterAdapter(sw) {
     *     write: function(s, off, len) {
     *         s = capitalize(s, off, len)
     *         cw_super.write(s, 0, s.length())
     *     }
     * }
     * var cw_super = Java.super(cw)
     * </pre>
     * @param self the {@code Java} object itself - not used.
     * @param adapter the original Java adapter instance for which the super adapter is created.
     * @return a super adapter for the original adapter
     */
    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR, name="super")
    public static Object _super(final Object self, final Object adapter) {
        return Bootstrap.createSuperAdapter(adapter);
    }
}