nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
author attila
Wed, 21 Aug 2013 13:39:09 +0200
changeset 19617 310246d552b7
parent 19459 79e75274df99
child 19627 90d910ec15a3
permissions -rw-r--r--
8023373: allow super invocation for adapters Reviewed-by: lagergren, sundar
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     1
/*
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     2
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     4
 *
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     5
 * This code is free software; you can redistribute it and/or modify it
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     6
 * under the terms of the GNU General Public License version 2 only, as
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     7
 * published by the Free Software Foundation.  Oracle designates this
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     8
 * particular file as subject to the "Classpath" exception as provided
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
     9
 * by Oracle in the LICENSE file that accompanied this code.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    10
 *
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    14
 * version 2 for more details (a copy is included in the LICENSE file that
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    15
 * accompanied this code).
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    16
 *
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    17
 * You should have received a copy of the GNU General Public License version
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    20
 *
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    22
 * or visit www.oracle.com if you need additional information or have any
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    23
 * questions.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    24
 */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    25
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    26
package jdk.nashorn.internal.runtime.linker;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    27
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    28
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    29
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    30
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    31
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    32
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    33
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    34
import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    35
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    36
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    37
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    38
import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    39
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    40
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    41
import static jdk.internal.org.objectweb.asm.Opcodes.POP;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    42
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    43
import static jdk.nashorn.internal.lookup.Lookup.MH;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    44
import static jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    45
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    46
import java.lang.invoke.MethodHandle;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    47
import java.lang.invoke.MethodType;
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
    48
import java.lang.reflect.AccessibleObject;
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    49
import java.lang.reflect.Constructor;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    50
import java.lang.reflect.Method;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    51
import java.lang.reflect.Modifier;
19459
79e75274df99 8022707: Revisit all doPrivileged blocks
sundar
parents: 19105
diff changeset
    52
import java.security.AccessControlContext;
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    53
import java.security.AccessController;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    54
import java.security.PrivilegedAction;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    55
import java.util.Arrays;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    56
import java.util.Collection;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    57
import java.util.HashSet;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    58
import java.util.Iterator;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    59
import java.util.List;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    60
import java.util.Set;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    61
import jdk.internal.org.objectweb.asm.ClassWriter;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    62
import jdk.internal.org.objectweb.asm.Label;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    63
import jdk.internal.org.objectweb.asm.Opcodes;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    64
import jdk.internal.org.objectweb.asm.Type;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    65
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    66
import jdk.nashorn.internal.runtime.Context;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    67
import jdk.nashorn.internal.runtime.ScriptFunction;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    68
import jdk.nashorn.internal.runtime.ScriptObject;
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
    69
import sun.reflect.CallerSensitive;
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    70
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    71
/**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    72
 * Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    73
 * </p><p>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    74
 * For every protected or public constructor in the extended class, the adapter class will have between one to three
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    75
 * public constructors (visibility of protected constructors in the extended class is promoted to public).
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    76
 * <ul>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    77
 * <li>In every case, a constructor taking a trailing ScriptObject argument preceded by original constructor arguments
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    78
 * is always created on the adapter class. When such a constructor is invoked, the passed ScriptObject's member
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    79
 * functions are used to implement and/or override methods on the original class, dispatched by name. A single
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    80
 * JavaScript function will act as the implementation for all overloaded methods of the same name. When methods on an
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    81
 * adapter instance are invoked, the functions are invoked having the ScriptObject passed in the instance constructor as
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    82
 * their "this". Subsequent changes to the ScriptObject (reassignment or removal of its functions) are not reflected in
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    83
 * the adapter instance; the method implementations are bound to functions at constructor invocation time.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    84
 * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    85
 * only restriction is that since every JavaScript object already has a {@code toString} function through the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    86
 * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    87
 * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    88
 * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    89
 * </li>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    90
 * <li>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    91
 * If the original types collectively have only one abstract method, or have several of them, but all share the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    92
 * same name, an additional constructor is provided for every original constructor; this one takes a ScriptFunction as
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    93
 * its last argument preceded by original constructor arguments. This constructor will use the passed function as the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    94
 * implementation for all abstract methods. For consistency, any concrete methods sharing the single abstract method
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    95
 * name will also be overridden by the function. When methods on the adapter instance are invoked, the ScriptFunction is
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    96
 * invoked with {@code null} as its "this".
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    97
 * </li>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    98
 * <li>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    99
 * If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   100
 * constructors are also created. These constructors simply delegate to the superclass constructor. They are used to
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   101
 * create instances of the adapter class with no instance-level overrides.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   102
 * </li>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   103
 * </ul>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   104
 * </p><p>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   105
 * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   106
 * to coerce the JavaScript function return value to the expected Java return type.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   107
 * </p><p>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   108
 * Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   109
 * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   110
 * reason we are passing the additional argument at the end of the argument list instead at the front is that the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   111
 * source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   112
 * to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   113
 * </p><p>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   114
 * It is possible to create two different classes: those that can have both class-level and instance-level overrides,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   115
 * and those that can only have instance-level overrides. When
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   116
 * {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked with non-null {@code classOverrides}
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   117
 * parameter, an adapter class is created that can have class-level overrides, and the passed script object will be used
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   118
 * as the implementations for its methods, just as in the above case of the constructor taking a script object. Note
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   119
 * that in the case of class-level overrides, a new adapter class is created on every invocation, and the implementation
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   120
 * object is bound to the class, not to any instance. All created instances will share these functions. Of course, when
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   121
 * instances of such a class are being created, they can still take another object (or possibly a function) in their
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   122
 * constructor's trailing position and thus provide further instance-specific overrides. The order of invocation is
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   123
 * always instance-specified method, then a class-specified method, and finally the superclass method.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   124
 */
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   125
final class JavaAdapterBytecodeGenerator {
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   126
    static final Type CONTEXT_TYPE       = Type.getType(Context.class);
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   127
    static final Type OBJECT_TYPE        = Type.getType(Object.class);
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   128
    static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class);
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   129
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   130
    static final String CONTEXT_TYPE_NAME = CONTEXT_TYPE.getInternalName();
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   131
    static final String OBJECT_TYPE_NAME  = OBJECT_TYPE.getInternalName();
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   132
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   133
    static final String INIT = "<init>";
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   134
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   135
    static final String GLOBAL_FIELD_NAME = "global";
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   136
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   137
    static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor();
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   138
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   139
    static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, SCRIPT_OBJECT_TYPE);
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   140
    static final String VOID_NOARG_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE);
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   141
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   142
    private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   143
    private static final Type STRING_TYPE = Type.getType(String.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   144
    private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   145
    private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   146
    private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
17239
6dd68632cdcd 8011065: Problems when script implements an interface with variadic methods
attila
parents: 16777
diff changeset
   147
            OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   148
    private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
17239
6dd68632cdcd 8011065: Problems when script implements an interface with variadic methods
attila
parents: 16777
diff changeset
   149
            SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   150
    private static final String GET_CLASS_INITIALIZER_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   151
    private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   152
    private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   153
    private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   154
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   155
    private static final String SERVICES_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterServices.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   156
    private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   157
    private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   158
    private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   159
    private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   160
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   161
    private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   162
    private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   163
    private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Class.class));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   164
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   165
    // Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   166
    // it's a java.* package.
19094
40665ad691ca 8021246: Fix regression for 8021189
attila
parents: 19088
diff changeset
   167
    private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/javaadapters/";
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   168
    // Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   169
    private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter";
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   170
    private static final String JAVA_PACKAGE_PREFIX = "java/";
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   171
    private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255;
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   172
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   173
    private static final String CLASS_INIT = "<clinit>";
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   174
    private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal";
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   175
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   176
    // Method name prefix for invoking super-methods
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   177
    private static final String SUPER_PREFIX = "super$";
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   178
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   179
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   180
     * Collection of methods we never override: Object.clone(), Object.finalize().
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   181
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   182
    private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   183
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   184
    // This is the superclass for our generated adapter.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   185
    private final Class<?> superClass;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   186
    // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   187
    // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   188
    // Nashorn classes.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   189
    private final ClassLoader commonLoader;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   190
    // Is this a generator for the version of the class that can have overrides on the class level?
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   191
    private final boolean classOverride;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   192
    // Binary name of the superClass
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   193
    private final String superClassName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   194
    // Binary name of the generated class.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   195
    private final String generatedClassName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   196
    private final Set<String> usedFieldNames = new HashSet<>();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   197
    private final Set<String> abstractMethodNames = new HashSet<>();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   198
    private final String samName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   199
    private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   200
    private final Set<MethodInfo> methodInfos = new HashSet<>();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   201
    private boolean autoConvertibleFromFunction = false;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   202
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   203
    private final ClassWriter cw;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   204
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   205
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   206
     * Creates a generator for the bytecode for the adapter for the specified superclass and interfaces.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   207
     * @param superClass the superclass the adapter will extend.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   208
     * @param interfaces the interfaces the adapter will implement.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   209
     * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   210
     * @param classOverride true to generate the bytecode for the adapter that has both class-level and instance-level
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   211
     * overrides, false to generate the bytecode for the adapter that only has instance-level overrides.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   212
     * @throws AdaptationException if the adapter can not be generated for some reason.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   213
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   214
    JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   215
            final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   216
        assert superClass != null && !superClass.isInterface();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   217
        assert interfaces != null;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   218
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   219
        this.superClass = superClass;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   220
        this.classOverride = classOverride;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   221
        this.commonLoader = commonLoader;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   222
        cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   223
            @Override
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   224
            protected String getCommonSuperClass(final String type1, final String type2) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   225
                // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   226
                // loader to find the common superclass of two types when needed.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   227
                return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   228
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   229
        };
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   230
        superClassName = Type.getInternalName(superClass);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   231
        generatedClassName = getGeneratedClassName(superClass, interfaces);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   232
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   233
        cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   234
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   235
        generateGlobalFields();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   236
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   237
        gatherMethods(superClass);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   238
        gatherMethods(interfaces);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   239
        samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   240
        generateHandleFields();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   241
        if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   242
            generateClassInit();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   243
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   244
        generateConstructors();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   245
        generateMethods();
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   246
        generateSuperMethods();
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   247
        // }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   248
        cw.visitEnd();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   249
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   250
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   251
    private void generateGlobalFields() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   252
        cw.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   253
        usedFieldNames.add(GLOBAL_FIELD_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   254
        if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   255
            cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   256
            usedFieldNames.add(STATIC_GLOBAL_FIELD_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   257
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   258
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   259
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   260
    JavaAdapterClassLoader createAdapterClassLoader() {
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   261
        return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray());
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   262
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   263
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   264
    boolean isAutoConvertibleFromFunction() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   265
        return autoConvertibleFromFunction;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   266
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   267
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   268
    private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   269
        // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   270
        // just implementing interfaces or extending Object), then the first implemented interface or Object.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   271
        final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   272
        final Package pkg = namingType.getPackage();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   273
        final String namingTypeName = Type.getInternalName(namingType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   274
        final StringBuilder buf = new StringBuilder();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   275
        if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   276
            // Can't define new classes in java.* packages
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   277
            buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   278
        } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   279
            buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   280
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   281
        final Iterator<Class<?>> it = interfaces.iterator();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   282
        if(superType == Object.class && it.hasNext()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   283
            it.next(); // Skip first interface, it was used to primarily name the adapter
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   284
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   285
        // Append interface names to the adapter name
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   286
        while(it.hasNext()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   287
            buf.append("$$").append(it.next().getSimpleName());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   288
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   289
        return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length()));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   290
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   291
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   292
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   293
     * Given a list of class objects, return an array with their binary names. Used to generate the array of interface
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   294
     * names to implement.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   295
     * @param classes the classes
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   296
     * @return an array of names
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   297
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   298
    private static String[] getInternalTypeNames(final List<Class<?>> classes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   299
        final int interfaceCount = classes.size();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   300
        final String[] interfaceNames = new String[interfaceCount];
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   301
        for(int i = 0; i < interfaceCount; ++i) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   302
            interfaceNames[i] = Type.getInternalName(classes.get(i));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   303
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   304
        return interfaceNames;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   305
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   306
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   307
    private void generateHandleFields() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   308
        for (final MethodInfo mi: methodInfos) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   309
            cw.visitField(ACC_PRIVATE | ACC_FINAL, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   310
            if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   311
                cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   312
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   313
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   314
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   315
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   316
    private void generateClassInit() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   317
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   318
                Type.getMethodDescriptor(Type.VOID_TYPE), null, null));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   319
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   320
        mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR);
17750
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   321
        final Label initGlobal;
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   322
        if(samName != null) {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   323
            // If the class is a SAM, allow having a ScriptFunction passed as class overrides
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   324
            final Label notAFunction = new Label();
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   325
            mv.dup();
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   326
            mv.instanceOf(SCRIPT_FUNCTION_TYPE);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   327
            mv.ifeq(notAFunction);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   328
            mv.checkcast(SCRIPT_FUNCTION_TYPE);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   329
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   330
            // Assign MethodHandle fields through invoking getHandle() for a ScriptFunction, only assigning the SAM
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   331
            // method(s).
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   332
            for (final MethodInfo mi : methodInfos) {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   333
                if(mi.getName().equals(samName)) {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   334
                    mv.dup();
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   335
                    mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   336
                    mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_FUNCTION_DESCRIPTOR);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   337
                } else {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   338
                    mv.visitInsn(ACONST_NULL);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   339
                }
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   340
                mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   341
            }
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   342
            initGlobal = new Label();
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   343
            mv.goTo(initGlobal);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   344
            mv.visitLabel(notAFunction);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   345
        } else {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   346
            initGlobal = null;
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   347
        }
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   348
        // Assign MethodHandle fields through invoking getHandle() for a ScriptObject
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   349
        for (final MethodInfo mi : methodInfos) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   350
            mv.dup();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   351
            mv.aconst(mi.getName());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   352
            mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   353
            mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   354
            mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   355
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   356
17750
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   357
        if(initGlobal != null) {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   358
            mv.visitLabel(initGlobal);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   359
        }
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   360
        // Assign "staticGlobal = Context.getGlobal()"
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   361
        invokeGetGlobalWithNullCheck(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   362
        mv.putstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   363
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   364
        endInitMethod(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   365
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   366
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   367
    private static void invokeGetGlobalWithNullCheck(final InstructionAdapter mv) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   368
        invokeGetGlobal(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   369
        mv.dup();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   370
        mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR); // check against null Context
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   371
        mv.pop();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   372
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   373
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   374
    private void generateConstructors() throws AdaptationException {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   375
        boolean gotCtor = false;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   376
        for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   377
            final int modifier = ctor.getModifiers();
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
   378
            if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0 && !isCallerSensitive(ctor)) {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   379
                generateConstructors(ctor);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   380
                gotCtor = true;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   381
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   382
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   383
        if(!gotCtor) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   384
            throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   385
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   386
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   387
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   388
    private void generateConstructors(final Constructor<?> ctor) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   389
        if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   390
            // Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   391
            // to create instances without further per-instance overrides.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   392
            generateDelegatingConstructor(ctor);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   393
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   394
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   395
        // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   396
        // beginning of its parameter list.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   397
        generateOverridingConstructor(ctor, false);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   398
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   399
        if (samName != null) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   400
            if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   401
                // If the original type only has a single abstract method name, as well as a default ctor, then it can
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   402
                // be automatically converted from JS function.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   403
                autoConvertibleFromFunction = true;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   404
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   405
            // If all our abstract methods have a single name, generate an additional constructor, one that takes a
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   406
            // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   407
            generateOverridingConstructor(ctor, true);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   408
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   409
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   410
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   411
    private void generateDelegatingConstructor(final Constructor<?> ctor) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   412
        final Type originalCtorType = Type.getType(ctor);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   413
        final Type[] argTypes = originalCtorType.getArgumentTypes();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   414
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   415
        // All constructors must be public, even if in the superclass they were protected.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   416
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   417
                Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   418
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   419
        mv.visitCode();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   420
        // Invoke super constructor with the same arguments.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   421
        mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   422
        int offset = 1; // First arg is at position 1, after this.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   423
        for (Type argType: argTypes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   424
            mv.load(offset, argType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   425
            offset += argType.getSize();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   426
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   427
        mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   428
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   429
        endInitMethod(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   430
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   431
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   432
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   433
     * Generates a constructor for the adapter class. This constructor will take the same arguments as the supertype
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   434
     * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   435
     * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   436
     * all the method handle fields of the adapter instance with functions from the script object (or the script
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   437
     * function itself, if that's what's passed). There is one method handle field in the adapter class for every method
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   438
     * that can be implemented or overridden; the name of every field is same as the name of the method, with a number
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   439
     * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   440
     * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   441
     * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   442
     * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   443
     * The constructor that takes a script function will only initialize the methods with the same name as the single
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   444
     * abstract method. The constructor will also store the Nashorn global that was current at the constructor
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   445
     * invocation time in a field named "global". The generated constructor will be public, regardless of whether the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   446
     * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   447
     * supertype constructor was.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   448
     * @param ctor the supertype constructor that is serving as the base for the generated constructor.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   449
     * @param fromFunction true if we're generating a constructor that initializes SAM types from a single
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   450
     * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   451
     * ScriptObject passed to it.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   452
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   453
    private void generateOverridingConstructor(final Constructor<?> ctor, final boolean fromFunction) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   454
        final Type originalCtorType = Type.getType(ctor);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   455
        final Type[] originalArgTypes = originalCtorType.getArgumentTypes();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   456
        final int argLen = originalArgTypes.length;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   457
        final Type[] newArgTypes = new Type[argLen + 1];
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   458
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   459
        // Insert ScriptFunction|Object as the last argument to the constructor
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   460
        final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   461
        newArgTypes[argLen] = extraArgumentType;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   462
        System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   463
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   464
        // All constructors must be public, even if in the superclass they were protected.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   465
        // Existing super constructor <init>(this, args...) triggers generating <init>(this, scriptObj, args...).
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   466
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   467
                Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   468
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   469
        mv.visitCode();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   470
        // First, invoke super constructor with original arguments. If the form of the constructor we're generating is
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   471
        // <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...).
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   472
        mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   473
        final Class<?>[] argTypes = ctor.getParameterTypes();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   474
        int offset = 1; // First arg is at position 1, after this.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   475
        for (int i = 0; i < argLen; ++i) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   476
            final Type argType = Type.getType(argTypes[i]);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   477
            mv.load(offset, argType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   478
            offset += argType.getSize();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   479
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   480
        mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   481
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   482
        // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   483
        final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   484
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   485
        // Assign MethodHandle fields through invoking getHandle()
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   486
        for (final MethodInfo mi : methodInfos) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   487
            mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   488
            if (fromFunction && !mi.getName().equals(samName)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   489
                // Constructors initializing from a ScriptFunction only initialize methods with the SAM name.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   490
                // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   491
                // is a deliberate design choice. All other method handles are initialized to null.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   492
                mv.visitInsn(ACONST_NULL);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   493
            } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   494
                mv.visitVarInsn(ALOAD, offset);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   495
                if(!fromFunction) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   496
                    mv.aconst(mi.getName());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   497
                }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   498
                mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   499
                mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   500
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   501
            mv.putfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   502
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   503
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   504
        // Assign "this.global = Context.getGlobal()"
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   505
        mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   506
        invokeGetGlobalWithNullCheck(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   507
        mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   508
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   509
        endInitMethod(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   510
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   511
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   512
    private static void endInitMethod(final InstructionAdapter mv) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   513
        mv.visitInsn(RETURN);
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   514
        endMethod(mv);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   515
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   516
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   517
    private static void endMethod(final InstructionAdapter mv) {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   518
        mv.visitMaxs(0, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   519
        mv.visitEnd();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   520
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   521
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   522
    private static void invokeGetGlobal(final InstructionAdapter mv) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   523
        mv.invokestatic(CONTEXT_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   524
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   525
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   526
    private static void invokeSetGlobal(final InstructionAdapter mv) {
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   527
        mv.invokestatic(CONTEXT_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   528
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   529
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   530
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   531
     * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   532
     * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   533
     * method handle serving as the implementation of this method in adapter instances.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   534
     *
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   535
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   536
    private static class MethodInfo {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   537
        private final Method method;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   538
        private final MethodType type;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   539
        private String methodHandleInstanceFieldName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   540
        private String methodHandleClassFieldName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   541
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   542
        private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   543
            this(clazz.getDeclaredMethod(name, argTypes));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   544
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   545
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   546
        private MethodInfo(final Method method) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   547
            this.method = method;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   548
            this.type   = MH.type(method.getReturnType(), method.getParameterTypes());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   549
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   550
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   551
        @Override
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   552
        public boolean equals(final Object obj) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   553
            return obj instanceof MethodInfo && equals((MethodInfo)obj);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   554
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   555
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   556
        private boolean equals(final MethodInfo other) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   557
            // Only method name and type are used for comparison; method handle field name is not.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   558
            return getName().equals(other.getName()) && type.equals(other.type);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   559
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   560
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   561
        String getName() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   562
            return method.getName();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   563
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   564
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   565
        @Override
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   566
        public int hashCode() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   567
            return getName().hashCode() ^ type.hashCode();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   568
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   569
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   570
        void setIsCanonical(final Set<String> usedFieldNames, boolean classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   571
            methodHandleInstanceFieldName = nextName(usedFieldNames);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   572
            if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   573
                methodHandleClassFieldName = nextName(usedFieldNames);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   574
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   575
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   576
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   577
        String nextName(final Set<String> usedFieldNames) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   578
            int i = 0;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   579
            final String name = getName();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   580
            String nextName = name;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   581
            while (!usedFieldNames.add(nextName)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   582
                final String ordinal = String.valueOf(i++);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   583
                final int maxNameLen = 255 - ordinal.length();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   584
                nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   585
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   586
            return nextName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   587
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   588
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   589
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   590
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   591
    private void generateMethods() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   592
        for(final MethodInfo mi: methodInfos) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   593
            generateMethod(mi);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   594
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   595
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   596
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   597
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   598
     * Generates a method in the adapter class that adapts a method from the original class. The generated methods will
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   599
     * inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   600
     * for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   601
     * {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   602
     * invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   603
     * current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   604
     * instance, the creating global is set to be the current global. In this case, the previously current global is
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   605
     * restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   606
     * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   607
     * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   608
     * the method; this is guaranteed by the way constructors of the adapter class obtain them using
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   609
     * {@link #getHandle(Object, String, MethodType, boolean)}.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   610
     * @param mi the method info describing the method to be generated.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   611
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   612
    private void generateMethod(final MethodInfo mi) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   613
        final Method method = mi.method;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   614
        final Class<?>[] exceptions = method.getExceptionTypes();
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   615
        final String[] exceptionNames = getExceptionNames(exceptions);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   616
        final MethodType type = mi.type;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   617
        final String methodDesc = type.toMethodDescriptorString();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   618
        final String name = mi.getName();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   619
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   620
        final Type asmType = Type.getMethodType(methodDesc);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   621
        final Type[] asmArgTypes = asmType.getArgumentTypes();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   622
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   623
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), name,
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   624
                methodDesc, null, exceptionNames));
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   625
        mv.visitCode();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   626
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   627
        final Label instanceHandleDefined = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   628
        final Label classHandleDefined = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   629
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   630
        final Type asmReturnType = Type.getType(type.returnType());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   631
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   632
        // See if we have instance handle defined
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   633
        mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   634
        mv.getfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   635
        // stack: [instanceHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   636
        jumpIfNonNullKeepOperand(mv, instanceHandleDefined);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   637
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   638
        if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   639
            // See if we have the static handle
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   640
            mv.getstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   641
            // stack: [classHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   642
            jumpIfNonNullKeepOperand(mv, classHandleDefined);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   643
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   644
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   645
        // No handle is available, fall back to default behavior
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   646
        if(Modifier.isAbstract(method.getModifiers())) {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   647
            // If the super method is abstract, throw an exception
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   648
            mv.anew(UNSUPPORTED_OPERATION_TYPE);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   649
            mv.dup();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   650
            mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   651
            mv.athrow();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   652
        } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   653
            // If the super method is not abstract, delegate to it.
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   654
            emitSuperCall(mv, name, methodDesc);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   655
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   656
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   657
        final Label setupGlobal = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   658
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   659
        if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   660
            mv.visitLabel(classHandleDefined);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   661
            // If class handle is defined, load the static defining global
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   662
            mv.getstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   663
            // stack: [creatingGlobal := classGlobal, classHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   664
            mv.goTo(setupGlobal);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   665
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   666
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   667
        mv.visitLabel(instanceHandleDefined);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   668
        // If instance handle is defined, load the instance defining global
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   669
        mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   670
        mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   671
        // stack: [creatingGlobal := instanceGlobal, instanceHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   672
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   673
        // fallthrough to setupGlobal
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   674
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   675
        // stack: [creatingGlobal, someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   676
        mv.visitLabel(setupGlobal);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   677
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   678
        // Determine the first index for a local variable
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   679
        int nextLocalVar = 1; // "this" is at 0
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   680
        for(final Type t: asmArgTypes) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   681
            nextLocalVar += t.getSize();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   682
        }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   683
        // Set our local variable indices
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   684
        final int currentGlobalVar  = nextLocalVar++;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   685
        final int globalsDifferVar  = nextLocalVar++;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   686
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   687
        mv.dup();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   688
        // stack: [creatingGlobal, creatingGlobal, someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   689
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   690
        // Emit code for switching to the creating global
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   691
        // ScriptObject currentGlobal = Context.getGlobal();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   692
        invokeGetGlobal(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   693
        mv.dup();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   694
        mv.visitVarInsn(ASTORE, currentGlobalVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   695
        // stack: [currentGlobal, creatingGlobal, creatingGlobal, someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   696
        // if(definingGlobal == currentGlobal) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   697
        final Label globalsDiffer = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   698
        mv.ifacmpne(globalsDiffer);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   699
        // stack: [someGlobal, someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   700
        //     globalsDiffer = false
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   701
        mv.pop();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   702
        // stack: [someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   703
        mv.iconst(0); // false
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   704
        // stack: [false, someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   705
        final Label invokeHandle = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   706
        mv.goTo(invokeHandle);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   707
        mv.visitLabel(globalsDiffer);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   708
        // } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   709
        //     Context.setGlobal(definingGlobal);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   710
        // stack: [someGlobal, someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   711
        invokeSetGlobal(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   712
        // stack: [someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   713
        //     globalsDiffer = true
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   714
        mv.iconst(1);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   715
        // stack: [true, someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   716
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   717
        mv.visitLabel(invokeHandle);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   718
        mv.visitVarInsn(ISTORE, globalsDifferVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   719
        // stack: [someHandle]
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   720
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   721
        // Load all parameters back on stack for dynamic invocation.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   722
        int varOffset = 1;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   723
        for (final Type t : asmArgTypes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   724
            mv.load(varOffset, t);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   725
            varOffset += t.getSize();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   726
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   727
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   728
        // Invoke the target method handle
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   729
        final Label tryBlockStart = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   730
        mv.visitLabel(tryBlockStart);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   731
        mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   732
        final Label tryBlockEnd = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   733
        mv.visitLabel(tryBlockEnd);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   734
        emitFinally(mv, currentGlobalVar, globalsDifferVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   735
        mv.areturn(asmReturnType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   736
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   737
        // If Throwable is not declared, we need an adapter from Throwable to RuntimeException
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   738
        final boolean throwableDeclared = isThrowableDeclared(exceptions);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   739
        final Label throwableHandler;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   740
        if (!throwableDeclared) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   741
            // Add "throw new RuntimeException(Throwable)" handler for Throwable
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   742
            throwableHandler = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   743
            mv.visitLabel(throwableHandler);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   744
            mv.anew(RUNTIME_EXCEPTION_TYPE);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   745
            mv.dupX1();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   746
            mv.swap();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   747
            mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   748
            // Fall through to rethrow handler
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   749
        } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   750
            throwableHandler = null;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   751
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   752
        final Label rethrowHandler = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   753
        mv.visitLabel(rethrowHandler);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   754
        // Rethrow handler for RuntimeException, Error, and all declared exception types
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   755
        emitFinally(mv, currentGlobalVar, globalsDifferVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   756
        mv.athrow();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   757
        final Label methodEnd = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   758
        mv.visitLabel(methodEnd);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   759
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   760
        mv.visitLocalVariable("currentGlobal", SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, setupGlobal, methodEnd, currentGlobalVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   761
        mv.visitLocalVariable("globalsDiffer", Type.INT_TYPE.getDescriptor(), null, setupGlobal, methodEnd, globalsDifferVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   762
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   763
        if(throwableDeclared) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   764
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   765
            assert throwableHandler == null;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   766
        } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   767
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   768
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   769
            for(final String excName: exceptionNames) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   770
                mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   771
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   772
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   773
        }
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   774
        endMethod(mv);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   775
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   776
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   777
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   778
     * Emits code for jumping to a label if the top stack operand is not null. The operand is kept on the stack if it
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   779
     * is not null (so is available to code at the jump address) and is popped if it is null.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   780
     * @param mv the instruction adapter being used to emit code
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   781
     * @param label the label to jump to
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   782
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   783
    private static void jumpIfNonNullKeepOperand(final InstructionAdapter mv, final Label label) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   784
        mv.visitInsn(DUP);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   785
        mv.visitJumpInsn(IFNONNULL, label);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   786
        mv.visitInsn(POP);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   787
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   788
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   789
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   790
     * Emit code to restore the previous Nashorn Context when needed.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   791
     * @param mv the instruction adapter
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   792
     * @param currentGlobalVar index of the local variable holding the reference to the current global at method
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   793
     * entry.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   794
     * @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   795
     */
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   796
    private static void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   797
        // Emit code to restore the previous Nashorn global if needed
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   798
        mv.visitVarInsn(ILOAD, globalsDifferVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   799
        final Label skip = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   800
        mv.ifeq(skip);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   801
        mv.visitVarInsn(ALOAD, currentGlobalVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   802
        invokeSetGlobal(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   803
        mv.visitLabel(skip);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   804
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   805
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   806
    private static boolean isThrowableDeclared(final Class<?>[] exceptions) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   807
        for (final Class<?> exception : exceptions) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   808
            if (exception == Throwable.class) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   809
                return true;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   810
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   811
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   812
        return false;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   813
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   814
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   815
    private void generateSuperMethods() {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   816
        for(final MethodInfo mi: methodInfos) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   817
            if(!Modifier.isAbstract(mi.method.getModifiers())) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   818
                generateSuperMethod(mi);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   819
            }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   820
        }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   821
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   822
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   823
    private void generateSuperMethod(MethodInfo mi) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   824
        final Method method = mi.method;
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   825
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   826
        final String methodDesc = mi.type.toMethodDescriptorString();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   827
        final String name = mi.getName();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   828
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   829
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method),
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   830
                SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes())));
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   831
        mv.visitCode();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   832
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   833
        emitSuperCall(mv, name, methodDesc);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   834
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   835
        endMethod(mv);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   836
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   837
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   838
    private void emitSuperCall(final InstructionAdapter mv, final String name, final String methodDesc) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   839
        mv.visitVarInsn(ALOAD, 0);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   840
        int nextParam = 1;
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   841
        final Type methodType = Type.getMethodType(methodDesc);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   842
        for(final Type t: methodType.getArgumentTypes()) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   843
            mv.load(nextParam, t);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   844
            nextParam += t.getSize();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   845
        }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   846
        mv.invokespecial(superClassName, name, methodDesc);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   847
        mv.areturn(methodType.getReturnType());
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   848
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   849
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   850
    private static String[] getExceptionNames(final Class<?>[] exceptions) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   851
        final String[] exceptionNames = new String[exceptions.length];
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   852
        for (int i = 0; i < exceptions.length; ++i) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   853
            exceptionNames[i] = Type.getInternalName(exceptions[i]);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   854
        }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   855
        return exceptionNames;
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   856
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   857
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   858
    private static int getAccessModifiers(final Method method) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   859
        return ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   860
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   861
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   862
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   863
     * Gathers methods that can be implemented or overridden from the specified type into this factory's
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   864
     * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   865
     * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   866
     * superclass and the interfaces it implements, and add further methods that were not directly declared on the
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   867
     * class.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   868
     * @param type the type defining the methods.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   869
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   870
    private void gatherMethods(final Class<?> type) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   871
        if (Modifier.isPublic(type.getModifiers())) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   872
            final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   873
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   874
            for (final Method typeMethod: typeMethods) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   875
                final int m = typeMethod.getModifiers();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   876
                if (Modifier.isStatic(m)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   877
                    continue;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   878
                }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   879
                if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   880
                    final MethodInfo mi = new MethodInfo(typeMethod);
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
   881
                    if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   882
                        finalMethods.add(mi);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   883
                    } else if (!finalMethods.contains(mi) && methodInfos.add(mi)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   884
                        if (Modifier.isAbstract(m)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   885
                            abstractMethodNames.add(mi.getName());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   886
                        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   887
                        mi.setIsCanonical(usedFieldNames, classOverride);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   888
                    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   889
                }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   890
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   891
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   892
        // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   893
        // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   894
        // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   895
        // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   896
        // getMethods() does provide those declared in a superinterface.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   897
        if (!type.isInterface()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   898
            final Class<?> superType = type.getSuperclass();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   899
            if (superType != null) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   900
                gatherMethods(superType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   901
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   902
            for (final Class<?> itf: type.getInterfaces()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   903
                gatherMethods(itf);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   904
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   905
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   906
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   907
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   908
    private void gatherMethods(final List<Class<?>> classes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   909
        for(final Class<?> c: classes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   910
            gatherMethods(c);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   911
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   912
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   913
19459
79e75274df99 8022707: Revisit all doPrivileged blocks
sundar
parents: 19105
diff changeset
   914
    private static final AccessControlContext GET_DECLARED_MEMBERS_ACC_CTXT = ClassAndLoader.createPermAccCtxt("accessDeclaredMembers");
79e75274df99 8022707: Revisit all doPrivileged blocks
sundar
parents: 19105
diff changeset
   915
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   916
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   917
     * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   918
     * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   919
     * {@code Object.clone()}.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   920
     * @return a collection of method infos representing those methods that we never override in adapter classes.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   921
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   922
    private static Collection<MethodInfo> getExcludedMethods() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   923
        return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   924
            @Override
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   925
            public Collection<MethodInfo> run() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   926
                try {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   927
                    return Arrays.asList(
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   928
                            new MethodInfo(Object.class, "finalize"),
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   929
                            new MethodInfo(Object.class, "clone"));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   930
                } catch (final NoSuchMethodException e) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   931
                    throw new AssertionError(e);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   932
                }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   933
            }
19459
79e75274df99 8022707: Revisit all doPrivileged blocks
sundar
parents: 19105
diff changeset
   934
        }, GET_DECLARED_MEMBERS_ACC_CTXT);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   935
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   936
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   937
    private String getCommonSuperClass(final String type1, final String type2) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   938
        try {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   939
            final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   940
            final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   941
            if (c1.isAssignableFrom(c2)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   942
                return type1;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   943
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   944
            if (c2.isAssignableFrom(c1)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   945
                return type2;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   946
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   947
            if (c1.isInterface() || c2.isInterface()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   948
                return OBJECT_TYPE_NAME;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   949
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   950
            return assignableSuperClass(c1, c2).getName().replace('.', '/');
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   951
        } catch(final ClassNotFoundException e) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   952
            throw new RuntimeException(e);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   953
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   954
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   955
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   956
    private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   957
        final Class<?> superClass = c1.getSuperclass();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   958
        return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   959
    }
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
   960
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
   961
    private static boolean isCallerSensitive(final AccessibleObject e) {
19088
153f268bfa72 8021122: Not all callables are handled for toString and other function valued properties
sundar
parents: 19086
diff changeset
   962
        return e.isAnnotationPresent(CallerSensitive.class);
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
   963
    }
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   964
}