nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
author attila
Fri, 25 Apr 2014 14:20:07 +0200
changeset 24205 0a7fbab84fb0
parent 23375 a1110f2cbe75
child 24769 de4dcfa9380f
permissions -rw-r--r--
8034967: Reduce access to Nashorn internals Reviewed-by: ahgross, jlaskey, 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;
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    62
import jdk.internal.org.objectweb.asm.Handle;
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    63
import jdk.internal.org.objectweb.asm.Label;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    64
import jdk.internal.org.objectweb.asm.Opcodes;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    65
import jdk.internal.org.objectweb.asm.Type;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    66
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    67
import jdk.nashorn.internal.runtime.Context;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    68
import jdk.nashorn.internal.runtime.ScriptFunction;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    69
import jdk.nashorn.internal.runtime.ScriptObject;
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    70
import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome;
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
    71
import sun.reflect.CallerSensitive;
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    72
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    73
/**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    74
 * 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
    75
 * </p><p>
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    76
 * For every protected or public constructor in the extended class, the adapter class will have either one or two
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    77
 * public constructors (visibility of protected constructors in the extended class is promoted to public).
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    78
 * <li>
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    79
 * <li>For adapter classes with instance-level overrides, a constructor taking a trailing ScriptObject argument preceded
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    80
 * by original constructor arguments is always created on the adapter class. When such a constructor is invoked, the
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    81
 * passed ScriptObject's member functions are used to implement and/or override methods on the original class,
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    82
 * dispatched by name. A single JavaScript function will act as the implementation for all overloaded methods of the
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    83
 * same name. When methods on an adapter instance are invoked, the functions are invoked having the ScriptObject passed
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    84
 * in the instance constructor as their "this". Subsequent changes to the ScriptObject (reassignment or removal of its
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    85
 * functions) are not reflected in the adapter instance; the method implementations are bound to functions at
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    86
 * constructor invocation time.
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    87
 * {@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
    88
 * 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
    89
 * {@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
    90
 * {@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
    91
 * 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
    92
 * </li>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    93
 * <li>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
    94
 * If the original types collectively have only one abstract method, or have several of them, but all share the
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    95
 * same name, an additional constructor for instance-level override adapter is provided for every original constructor;
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    96
 * this one takes a ScriptFunction as its last argument preceded by original constructor arguments. This constructor
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    97
 * will use the passed function as the implementation for all abstract methods. For consistency, any concrete methods
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    98
 * sharing the single abstract method name will also be overridden by the function. When methods on the adapter instance
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
    99
 * are invoked, the ScriptFunction is invoked with UNDEFINED or Global as its "this" depending whether the function is
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   100
 * strict or not.
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   101
 * </li>
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
 * If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   104
 * constructors are created. These constructors simply delegate to the superclass constructor. They are simply used to
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   105
 * create instances of the adapter class, with no instance-level overrides, as they don't have them.
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   106
 * </li>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   107
 * </ul>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   108
 * </p><p>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   109
 * 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
   110
 * 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
   111
 * </p><p>
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   112
 * 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
   113
 * 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
   114
 * 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
   115
 * 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
   116
 * 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
   117
 * </p><p>
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   118
 * It is possible to create two different adapter classes: those that can have class-level overrides, and those that can
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   119
 * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   120
 * with non-null {@code classOverrides} parameter, an adapter class is created that can have class-level overrides, and
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   121
 * the passed script object will be used as the implementations for its methods, just as in the above case of the
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   122
 * constructor taking a script object. Note that in the case of class-level overrides, a new adapter class is created on
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   123
 * every invocation, and the implementation object is bound to the class, not to any instance. All created instances
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   124
 * will share these functions. If it is required to have both class-level overrides and instance-level overrides, the
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   125
 * class-level override adapter class should be subclassed with an instance-override adapter. Since adapters delegate to
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   126
 * super class when an overriding method handle is not specified, this will behave as expected. It is not possible to
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   127
 * have both class-level and instance-level overrides in the same class for security reasons: adapter classes are
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   128
 * defined with a protection domain of their creator code, and an adapter class that has both class and instance level
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   129
 * overrides would need to have two potentially different protection domains: one for class-based behavior and one for
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   130
 * instance-based behavior; since Java classes can only belong to a single protection domain, this could not be
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   131
 * implemented securely.
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   132
 */
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   133
final class JavaAdapterBytecodeGenerator {
24205
0a7fbab84fb0 8034967: Reduce access to Nashorn internals
attila
parents: 23375
diff changeset
   134
    static final Type OBJECT_TYPE = Type.getType(Object.class);
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   135
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   136
    static final String OBJECT_TYPE_NAME  = OBJECT_TYPE.getInternalName();
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   137
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   138
    static final String INIT = "<init>";
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   139
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   140
    static final String GLOBAL_FIELD_NAME = "global";
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   141
24205
0a7fbab84fb0 8034967: Reduce access to Nashorn internals
attila
parents: 23375
diff changeset
   142
    // "global" is declared as Object instead of Global - avoid static references to internal Nashorn classes when possible.
0a7fbab84fb0 8034967: Reduce access to Nashorn internals
attila
parents: 23375
diff changeset
   143
    static final String GLOBAL_TYPE_DESCRIPTOR = OBJECT_TYPE.getDescriptor();
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   144
24205
0a7fbab84fb0 8034967: Reduce access to Nashorn internals
attila
parents: 23375
diff changeset
   145
    static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE);
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   146
    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
   147
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   148
    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
   149
    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
   150
    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
   151
    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
   152
    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
   153
            OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   154
    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
   155
            SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE);
24205
0a7fbab84fb0 8034967: Reduce access to Nashorn internals
attila
parents: 23375
diff changeset
   156
    private static final String GET_CLASS_INITIALIZER_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   157
    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
   158
    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
   159
    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
   160
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   161
    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
   162
    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
   163
    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
   164
    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
   165
    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
   166
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   167
    private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor();
24205
0a7fbab84fb0 8034967: Reduce access to Nashorn internals
attila
parents: 23375
diff changeset
   168
    private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   169
    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
   170
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   171
    // 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
   172
    // it's a java.* package.
19094
40665ad691ca 8021246: Fix regression for 8021189
attila
parents: 19088
diff changeset
   173
    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
   174
    // 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
   175
    private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter";
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   176
    private static final String JAVA_PACKAGE_PREFIX = "java/";
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   177
    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
   178
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   179
    private static final String CLASS_INIT = "<clinit>";
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   180
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   181
    // Method name prefix for invoking super-methods
19627
90d910ec15a3 8023630: Implement Java.super() as the preferred way to call super methods
attila
parents: 19617
diff changeset
   182
    static final String SUPER_PREFIX = "super$";
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   183
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   184
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   185
     * 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
   186
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   187
    private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   188
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   189
    // This is the superclass for our generated adapter.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   190
    private final Class<?> superClass;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   191
    // 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
   192
    // 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
   193
    // Nashorn classes.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   194
    private final ClassLoader commonLoader;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   195
    // 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
   196
    private final boolean classOverride;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   197
    // Binary name of the superClass
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   198
    private final String superClassName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   199
    // Binary name of the generated class.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   200
    private final String generatedClassName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   201
    private final Set<String> usedFieldNames = new HashSet<>();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   202
    private final Set<String> abstractMethodNames = new HashSet<>();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   203
    private final String samName;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   204
    private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   205
    private final Set<MethodInfo> methodInfos = new HashSet<>();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   206
    private boolean autoConvertibleFromFunction = false;
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   207
    private boolean hasExplicitFinalizer = false;
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   208
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   209
    private final ClassWriter cw;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   210
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   211
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   212
     * 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
   213
     * @param superClass the superclass the adapter will extend.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   214
     * @param interfaces the interfaces the adapter will implement.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   215
     * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes.
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   216
     * @param classOverride true to generate the bytecode for the adapter that has class-level overrides, false to
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   217
     * generate the bytecode for the adapter that has instance-level overrides.
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   218
     * @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
   219
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   220
    JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces,
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   221
            final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   222
        assert superClass != null && !superClass.isInterface();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   223
        assert interfaces != null;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   224
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   225
        this.superClass = superClass;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   226
        this.classOverride = classOverride;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   227
        this.commonLoader = commonLoader;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   228
        cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   229
            @Override
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   230
            protected String getCommonSuperClass(final String type1, final String type2) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   231
                // 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
   232
                // 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
   233
                return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2);
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
        };
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   236
        superClassName = Type.getInternalName(superClass);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   237
        generatedClassName = getGeneratedClassName(superClass, interfaces);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   238
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   239
        cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   240
        generateGlobalFields();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   241
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   242
        gatherMethods(superClass);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   243
        gatherMethods(interfaces);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   244
        samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   245
        generateHandleFields();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   246
        if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   247
            generateClassInit();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   248
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   249
        generateConstructors();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   250
        generateMethods();
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   251
        generateSuperMethods();
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   252
        if (hasExplicitFinalizer) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   253
            generateFinalizerMethods();
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   254
        }
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   255
        // }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   256
        cw.visitEnd();
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
    private void generateGlobalFields() {
23375
a1110f2cbe75 8037400: Remove getInitialMap getters and GlobalObject interface
sundar
parents: 22669
diff changeset
   260
        cw.visitField(ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0), GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR, null, null).visitEnd();
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   261
        usedFieldNames.add(GLOBAL_FIELD_NAME);
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
    JavaAdapterClassLoader createAdapterClassLoader() {
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   265
        return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray());
16777
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
    boolean isAutoConvertibleFromFunction() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   269
        return autoConvertibleFromFunction;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   270
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   271
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   272
    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
   273
        // 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
   274
        // 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
   275
        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
   276
        final Package pkg = namingType.getPackage();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   277
        final String namingTypeName = Type.getInternalName(namingType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   278
        final StringBuilder buf = new StringBuilder();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   279
        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
   280
            // Can't define new classes in java.* packages
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   281
            buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   282
        } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   283
            buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX);
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
        final Iterator<Class<?>> it = interfaces.iterator();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   286
        if(superType == Object.class && it.hasNext()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   287
            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
   288
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   289
        // Append interface names to the adapter name
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   290
        while(it.hasNext()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   291
            buf.append("$$").append(it.next().getSimpleName());
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
        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
   294
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   295
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   296
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   297
     * 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
   298
     * names to implement.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   299
     * @param classes the classes
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   300
     * @return an array of names
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   301
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   302
    private static String[] getInternalTypeNames(final List<Class<?>> classes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   303
        final int interfaceCount = classes.size();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   304
        final String[] interfaceNames = new String[interfaceCount];
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   305
        for(int i = 0; i < interfaceCount; ++i) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   306
            interfaceNames[i] = Type.getInternalName(classes.get(i));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   307
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   308
        return interfaceNames;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   309
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   310
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   311
    private void generateHandleFields() {
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   312
        final int flags = ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   313
        for (final MethodInfo mi: methodInfos) {
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   314
            cw.visitField(flags, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
16777
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
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   317
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   318
    private void generateClassInit() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   319
        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
   320
                Type.getMethodDescriptor(Type.VOID_TYPE), null, null));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   321
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   322
        mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR, false);
17750
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   323
        final Label initGlobal;
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   324
        if(samName != null) {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   325
            // 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
   326
            final Label notAFunction = new Label();
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   327
            mv.dup();
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   328
            mv.instanceOf(SCRIPT_FUNCTION_TYPE);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   329
            mv.ifeq(notAFunction);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   330
            mv.checkcast(SCRIPT_FUNCTION_TYPE);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   331
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   332
            // 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
   333
            // method(s).
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   334
            for (final MethodInfo mi : methodInfos) {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   335
                if(mi.getName().equals(samName)) {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   336
                    mv.dup();
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   337
                    mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   338
                    mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_FUNCTION_DESCRIPTOR, false);
17750
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   339
                } else {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   340
                    mv.visitInsn(ACONST_NULL);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   341
                }
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   342
                mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
17750
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   343
            }
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   344
            initGlobal = new Label();
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   345
            mv.goTo(initGlobal);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   346
            mv.visitLabel(notAFunction);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   347
        } else {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   348
            initGlobal = null;
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   349
        }
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   350
        // 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
   351
        for (final MethodInfo mi : methodInfos) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   352
            mv.dup();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   353
            mv.aconst(mi.getName());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   354
            mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   355
            mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR, false);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   356
            mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   357
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   358
17750
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   359
        if(initGlobal != null) {
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   360
            mv.visitLabel(initGlobal);
eeab213edaa6 8014647: Allow class-based overrides to be initialized with a ScriptFunction
attila
parents: 17239
diff changeset
   361
        }
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   362
        // Assign "global = Context.getGlobal()"
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   363
        invokeGetGlobalWithNullCheck(mv);
23375
a1110f2cbe75 8037400: Remove getInitialMap getters and GlobalObject interface
sundar
parents: 22669
diff changeset
   364
        mv.putstatic(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR);
16777
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
        endInitMethod(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   367
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   368
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   369
    private static void invokeGetGlobalWithNullCheck(final InstructionAdapter mv) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   370
        invokeGetGlobal(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   371
        mv.dup();
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   372
        mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR, false); // check against null Context
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   373
        mv.pop();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   374
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   375
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   376
    private void generateConstructors() throws AdaptationException {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   377
        boolean gotCtor = false;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   378
        for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   379
            final int modifier = ctor.getModifiers();
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
   380
            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
   381
                generateConstructors(ctor);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   382
                gotCtor = true;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   383
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   384
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   385
        if(!gotCtor) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   386
            throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName());
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
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   389
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   390
    private void generateConstructors(final Constructor<?> ctor) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   391
        if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   392
            // 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
   393
            // to create instances without further per-instance overrides.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   394
            generateDelegatingConstructor(ctor);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   395
        } else {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   396
            // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   397
            // beginning of its parameter list.
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   398
            generateOverridingConstructor(ctor, false);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   399
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   400
            if (samName != null) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   401
                if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   402
                    // If the original type only has a single abstract method name, as well as a default ctor, then it can
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   403
                    // be automatically converted from JS function.
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   404
                    autoConvertibleFromFunction = true;
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   405
                }
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   406
                // If all our abstract methods have a single name, generate an additional constructor, one that takes a
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   407
                // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   408
                generateOverridingConstructor(ctor, true);
16777
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
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   412
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   413
    private void generateDelegatingConstructor(final Constructor<?> ctor) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   414
        final Type originalCtorType = Type.getType(ctor);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   415
        final Type[] argTypes = originalCtorType.getArgumentTypes();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   416
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   417
        // 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
   418
        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
   419
                Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   420
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   421
        mv.visitCode();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   422
        // Invoke super constructor with the same arguments.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   423
        mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   424
        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
   425
        for (Type argType: argTypes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   426
            mv.load(offset, argType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   427
            offset += argType.getSize();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   428
        }
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   429
        mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor(), false);
16777
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
        endInitMethod(mv);
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   434
    /**
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   435
     * Generates a constructor for the instance adapter class. This constructor will take the same arguments as the supertype
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   436
     * 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
   437
     * 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
   438
     * 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
   439
     * 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
   440
     * 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
   441
     * 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
   442
     * {@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
   443
     * 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
   444
     * 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
   445
     * 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
   446
     * 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
   447
     * 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
   448
     * 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
   449
     * supertype constructor was.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   450
     * @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
   451
     * @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
   452
     * 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
   453
     * ScriptObject passed to it.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   454
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   455
    private void generateOverridingConstructor(final Constructor<?> ctor, final boolean fromFunction) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   456
        final Type originalCtorType = Type.getType(ctor);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   457
        final Type[] originalArgTypes = originalCtorType.getArgumentTypes();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   458
        final int argLen = originalArgTypes.length;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   459
        final Type[] newArgTypes = new Type[argLen + 1];
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   460
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   461
        // 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
   462
        final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   463
        newArgTypes[argLen] = extraArgumentType;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   464
        System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   465
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   466
        // 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
   467
        // 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
   468
        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
   469
                Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   470
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   471
        mv.visitCode();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   472
        // 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
   473
        // <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
   474
        mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   475
        final Class<?>[] argTypes = ctor.getParameterTypes();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   476
        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
   477
        for (int i = 0; i < argLen; ++i) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   478
            final Type argType = Type.getType(argTypes[i]);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   479
            mv.load(offset, argType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   480
            offset += argType.getSize();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   481
        }
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   482
        mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor(), false);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   483
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   484
        // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   485
        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
   486
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   487
        // Assign MethodHandle fields through invoking getHandle()
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   488
        for (final MethodInfo mi : methodInfos) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   489
            mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   490
            if (fromFunction && !mi.getName().equals(samName)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   491
                // 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
   492
                // 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
   493
                // 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
   494
                mv.visitInsn(ACONST_NULL);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   495
            } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   496
                mv.visitVarInsn(ALOAD, offset);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   497
                if(!fromFunction) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   498
                    mv.aconst(mi.getName());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   499
                }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   500
                mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   501
                mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor, false);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   502
            }
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   503
            mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   504
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   505
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   506
        // Assign "this.global = Context.getGlobal()"
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   507
        mv.visitVarInsn(ALOAD, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   508
        invokeGetGlobalWithNullCheck(mv);
23375
a1110f2cbe75 8037400: Remove getInitialMap getters and GlobalObject interface
sundar
parents: 22669
diff changeset
   509
        mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR);
16777
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
        endInitMethod(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   512
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   513
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   514
    private static void endInitMethod(final InstructionAdapter mv) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   515
        mv.visitInsn(RETURN);
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   516
        endMethod(mv);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   517
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   518
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   519
    private static void endMethod(final InstructionAdapter mv) {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   520
        mv.visitMaxs(0, 0);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   521
        mv.visitEnd();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   522
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   523
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   524
    private static void invokeGetGlobal(final InstructionAdapter mv) {
24205
0a7fbab84fb0 8034967: Reduce access to Nashorn internals
attila
parents: 23375
diff changeset
   525
        mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR, false);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   526
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   527
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   528
    private static void invokeSetGlobal(final InstructionAdapter mv) {
24205
0a7fbab84fb0 8034967: Reduce access to Nashorn internals
attila
parents: 23375
diff changeset
   529
        mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, false);
16777
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   532
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   533
     * 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
   534
     * 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
   535
     * 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
   536
     *
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   537
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   538
    private static class MethodInfo {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   539
        private final Method method;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   540
        private final MethodType type;
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   541
        private String methodHandleFieldName;
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   542
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   543
        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
   544
            this(clazz.getDeclaredMethod(name, argTypes));
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   547
        private MethodInfo(final Method method) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   548
            this.method = method;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   549
            this.type   = MH.type(method.getReturnType(), method.getParameterTypes());
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   552
        @Override
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   553
        public boolean equals(final Object obj) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   554
            return obj instanceof MethodInfo && equals((MethodInfo)obj);
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   557
        private boolean equals(final MethodInfo other) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   558
            // 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
   559
            return getName().equals(other.getName()) && type.equals(other.type);
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   562
        String getName() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   563
            return method.getName();
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   566
        @Override
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   567
        public int hashCode() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   568
            return getName().hashCode() ^ type.hashCode();
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
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   571
        void setIsCanonical(final JavaAdapterBytecodeGenerator self) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   572
            methodHandleFieldName = self.nextName(getName());
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   573
        }
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   574
    }
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   575
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   576
    private String nextName(final String name) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   577
        int i = 0;
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   578
        String nextName = name;
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   579
        while (!usedFieldNames.add(nextName)) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   580
            final String ordinal = String.valueOf(i++);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   581
            final int maxNameLen = 255 - ordinal.length();
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   582
            nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   583
        }
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   584
        return nextName;
16777
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   587
    private void generateMethods() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   588
        for(final MethodInfo mi: methodInfos) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   589
            generateMethod(mi);
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
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   592
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   593
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   594
     * 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
   595
     * 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
   596
     * 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
   597
     * {@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
   598
     * 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
   599
     * 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
   600
     * 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
   601
     * 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
   602
     * 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
   603
     * 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
   604
     * 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
   605
     * {@link #getHandle(Object, String, MethodType, boolean)}.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   606
     * @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
   607
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   608
    private void generateMethod(final MethodInfo mi) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   609
        final Method method = mi.method;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   610
        final Class<?>[] exceptions = method.getExceptionTypes();
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   611
        final String[] exceptionNames = getExceptionNames(exceptions);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   612
        final MethodType type = mi.type;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   613
        final String methodDesc = type.toMethodDescriptorString();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   614
        final String name = mi.getName();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   615
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   616
        final Type asmType = Type.getMethodType(methodDesc);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   617
        final Type[] asmArgTypes = asmType.getArgumentTypes();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   618
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   619
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), name,
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   620
                methodDesc, null, exceptionNames));
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   621
        mv.visitCode();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   622
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   623
        final Label handleDefined = new Label();
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   624
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   625
        final Type asmReturnType = Type.getType(type.returnType());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   626
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   627
        // See if we have overriding method handle defined
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   628
        if(classOverride) {
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   629
            mv.getstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   630
        } else {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   631
            mv.visitVarInsn(ALOAD, 0);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   632
            mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   633
        }
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   634
        // stack: [handle]
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   635
        jumpIfNonNullKeepOperand(mv, handleDefined);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   636
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   637
        // No handle is available, fall back to default behavior
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   638
        if(Modifier.isAbstract(method.getModifiers())) {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   639
            // If the super method is abstract, throw an exception
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   640
            mv.anew(UNSUPPORTED_OPERATION_TYPE);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   641
            mv.dup();
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   642
            mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR, false);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   643
            mv.athrow();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   644
        } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   645
            // If the super method is not abstract, delegate to it.
22379
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   646
            emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   647
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   648
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   649
        mv.visitLabel(handleDefined);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   650
        // Load the creatingGlobal object
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   651
        if(classOverride) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   652
            // If class handle is defined, load the static defining global
23375
a1110f2cbe75 8037400: Remove getInitialMap getters and GlobalObject interface
sundar
parents: 22669
diff changeset
   653
            mv.getstatic(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   654
        } else {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   655
            mv.visitVarInsn(ALOAD, 0);
23375
a1110f2cbe75 8037400: Remove getInitialMap getters and GlobalObject interface
sundar
parents: 22669
diff changeset
   656
            mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   657
        }
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   658
        // stack: [creatingGlobal, handle]
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   659
        final Label setupGlobal = new Label();
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   660
        mv.visitLabel(setupGlobal);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   661
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   662
        // Determine the first index for a local variable
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   663
        int nextLocalVar = 1; // "this" is at 0
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   664
        for(final Type t: asmArgTypes) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   665
            nextLocalVar += t.getSize();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   666
        }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   667
        // Set our local variable indices
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   668
        final int currentGlobalVar  = nextLocalVar++;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   669
        final int globalsDifferVar  = nextLocalVar++;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   670
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   671
        mv.dup();
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   672
        // stack: [creatingGlobal, creatingGlobal, handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   673
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   674
        // Emit code for switching to the creating global
23375
a1110f2cbe75 8037400: Remove getInitialMap getters and GlobalObject interface
sundar
parents: 22669
diff changeset
   675
        // Global currentGlobal = Context.getGlobal();
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   676
        invokeGetGlobal(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   677
        mv.dup();
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   678
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   679
        mv.visitVarInsn(ASTORE, currentGlobalVar);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   680
        // stack: [currentGlobal, creatingGlobal, creatingGlobal, handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   681
        // if(definingGlobal == currentGlobal) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   682
        final Label globalsDiffer = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   683
        mv.ifacmpne(globalsDiffer);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   684
        // stack: [creatingGlobal, handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   685
        //     globalsDiffer = false
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   686
        mv.pop();
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   687
        // stack: [handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   688
        mv.iconst(0); // false
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   689
        // stack: [false, handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   690
        final Label invokeHandle = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   691
        mv.goTo(invokeHandle);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   692
        mv.visitLabel(globalsDiffer);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   693
        // } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   694
        //     Context.setGlobal(definingGlobal);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   695
        // stack: [creatingGlobal, handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   696
        invokeSetGlobal(mv);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   697
        // stack: [handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   698
        //     globalsDiffer = true
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   699
        mv.iconst(1);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   700
        // stack: [true, handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   701
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   702
        mv.visitLabel(invokeHandle);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   703
        mv.visitVarInsn(ISTORE, globalsDifferVar);
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   704
        // stack: [handle]
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   705
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   706
        // Load all parameters back on stack for dynamic invocation.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   707
        int varOffset = 1;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   708
        for (final Type t : asmArgTypes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   709
            mv.load(varOffset, t);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   710
            varOffset += t.getSize();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   711
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   712
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   713
        // Invoke the target method handle
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   714
        final Label tryBlockStart = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   715
        mv.visitLabel(tryBlockStart);
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   716
        mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString(), false);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   717
        final Label tryBlockEnd = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   718
        mv.visitLabel(tryBlockEnd);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   719
        emitFinally(mv, currentGlobalVar, globalsDifferVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   720
        mv.areturn(asmReturnType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   721
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   722
        // 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
   723
        final boolean throwableDeclared = isThrowableDeclared(exceptions);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   724
        final Label throwableHandler;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   725
        if (!throwableDeclared) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   726
            // Add "throw new RuntimeException(Throwable)" handler for Throwable
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   727
            throwableHandler = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   728
            mv.visitLabel(throwableHandler);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   729
            mv.anew(RUNTIME_EXCEPTION_TYPE);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   730
            mv.dupX1();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   731
            mv.swap();
22368
9cb3e7db27c6 8029031: "ant clean jar" on nashorn repo results in number of ASM deprecated warnings
sundar
parents: 20932
diff changeset
   732
            mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE), false);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   733
            // Fall through to rethrow handler
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   734
        } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   735
            throwableHandler = null;
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
        final Label rethrowHandler = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   738
        mv.visitLabel(rethrowHandler);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   739
        // 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
   740
        emitFinally(mv, currentGlobalVar, globalsDifferVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   741
        mv.athrow();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   742
        final Label methodEnd = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   743
        mv.visitLabel(methodEnd);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   744
23375
a1110f2cbe75 8037400: Remove getInitialMap getters and GlobalObject interface
sundar
parents: 22669
diff changeset
   745
        mv.visitLocalVariable("currentGlobal", GLOBAL_TYPE_DESCRIPTOR, null, setupGlobal, methodEnd, currentGlobalVar);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   746
        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
   747
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   748
        if(throwableDeclared) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   749
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   750
            assert throwableHandler == null;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   751
        } else {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   752
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   753
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   754
            for(final String excName: exceptionNames) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   755
                mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   756
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   757
            mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   758
        }
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   759
        endMethod(mv);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   760
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   761
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
     * 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
   764
     * 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
   765
     * @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
   766
     * @param label the label to jump to
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   767
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   768
    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
   769
        mv.visitInsn(DUP);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   770
        mv.visitJumpInsn(IFNONNULL, label);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   771
        mv.visitInsn(POP);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   772
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   773
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   774
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   775
     * 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
   776
     * @param mv the instruction adapter
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   777
     * @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
   778
     * entry.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   779
     * @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
   780
     */
19085
066c9e5afd79 8020731: Revisit checkPermission calls in Context class
sundar
parents: 17750
diff changeset
   781
    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
   782
        // 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
   783
        mv.visitVarInsn(ILOAD, globalsDifferVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   784
        final Label skip = new Label();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   785
        mv.ifeq(skip);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   786
        mv.visitVarInsn(ALOAD, currentGlobalVar);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   787
        invokeSetGlobal(mv);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   788
        mv.visitLabel(skip);
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
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   791
    private static boolean isThrowableDeclared(final Class<?>[] exceptions) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   792
        for (final Class<?> exception : exceptions) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   793
            if (exception == Throwable.class) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   794
                return true;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   795
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   796
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   797
        return false;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   798
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   799
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   800
    private void generateSuperMethods() {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   801
        for(final MethodInfo mi: methodInfos) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   802
            if(!Modifier.isAbstract(mi.method.getModifiers())) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   803
                generateSuperMethod(mi);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   804
            }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   805
        }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   806
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   807
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   808
    private void generateSuperMethod(MethodInfo mi) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   809
        final Method method = mi.method;
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   810
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   811
        final String methodDesc = mi.type.toMethodDescriptorString();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   812
        final String name = mi.getName();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   813
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   814
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method),
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   815
                SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes())));
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   816
        mv.visitCode();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   817
22379
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   818
        emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc);
19617
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
        endMethod(mv);
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
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   823
    private void emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) {
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   824
        mv.visitVarInsn(ALOAD, 0);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   825
        int nextParam = 1;
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   826
        final Type methodType = Type.getMethodType(methodDesc);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   827
        for(final Type t: methodType.getArgumentTypes()) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   828
            mv.load(nextParam, t);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   829
            nextParam += t.getSize();
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   830
        }
22379
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   831
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   832
        // default method - non-abstract, interface method
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   833
        if (Modifier.isInterface(owner.getModifiers())) {
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   834
            mv.invokespecial(Type.getInternalName(owner), name, methodDesc, false);
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   835
        } else {
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   836
            mv.invokespecial(superClassName, name, methodDesc, false);
5181d08e3440 8031359: Invocable.getInterface() works incorrectly if interface has default methods
sundar
parents: 22368
diff changeset
   837
        }
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   838
        mv.areturn(methodType.getReturnType());
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   839
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   840
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   841
    private void generateFinalizerMethods() {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   842
        final String finalizerDelegateName = nextName("access$");
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   843
        generateFinalizerDelegate(finalizerDelegateName);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   844
        generateFinalizerOverride(finalizerDelegateName);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   845
    }
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   846
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   847
    private void generateFinalizerDelegate(final String finalizerDelegateName) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   848
        // Generate a delegate that will be invoked from the no-permission trampoline. Note it can be private, as we'll
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   849
        // refer to it with a MethodHandle constant pool entry in the overridden finalize() method (see
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   850
        // generateFinalizerOverride()).
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   851
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PRIVATE | ACC_STATIC,
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   852
                finalizerDelegateName, Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE), null, null));
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   853
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   854
        // Simply invoke super.finalize()
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   855
        mv.visitVarInsn(ALOAD, 0);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   856
        mv.checkcast(Type.getType(generatedClassName));
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   857
        mv.invokespecial(superClassName, "finalize", Type.getMethodDescriptor(Type.VOID_TYPE), false);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   858
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   859
        mv.visitInsn(RETURN);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   860
        endMethod(mv);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   861
    }
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   862
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   863
    private void generateFinalizerOverride(final String finalizerDelegateName) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   864
        final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, "finalize",
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   865
                VOID_NOARG_METHOD_DESCRIPTOR, null, null));
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   866
        // Overridden finalizer will take a MethodHandle to the finalizer delegating method, ...
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   867
        mv.aconst(new Handle(Opcodes.H_INVOKESTATIC, generatedClassName, finalizerDelegateName,
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   868
                Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE)));
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   869
        mv.visitVarInsn(ALOAD, 0);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   870
        // ...and invoke it through JavaAdapterServices.invokeNoPermissions
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   871
        mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "invokeNoPermissions",
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   872
                Type.getMethodDescriptor(METHOD_HANDLE_TYPE, OBJECT_TYPE), false);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   873
        mv.visitInsn(RETURN);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   874
        endMethod(mv);
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   875
    }
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   876
19617
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   877
    private static String[] getExceptionNames(final Class<?>[] exceptions) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   878
        final String[] exceptionNames = new String[exceptions.length];
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   879
        for (int i = 0; i < exceptions.length; ++i) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   880
            exceptionNames[i] = Type.getInternalName(exceptions[i]);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   881
        }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   882
        return exceptionNames;
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   883
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   884
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   885
    private static int getAccessModifiers(final Method method) {
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   886
        return ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   887
    }
310246d552b7 8023373: allow super invocation for adapters
attila
parents: 19459
diff changeset
   888
16777
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
     * 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
   891
     * {@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
   892
     * 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
   893
     * 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
   894
     * class.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   895
     * @param type the type defining the methods.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   896
     */
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   897
    private void gatherMethods(final Class<?> type) throws AdaptationException {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   898
        if (Modifier.isPublic(type.getModifiers())) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   899
            final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   900
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   901
            for (final Method typeMethod: typeMethods) {
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   902
                final String name = typeMethod.getName();
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   903
                if(name.startsWith(SUPER_PREFIX)) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   904
                    continue;
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   905
                }
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   906
                final int m = typeMethod.getModifiers();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   907
                if (Modifier.isStatic(m)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   908
                    continue;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   909
                }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   910
                if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   911
                    // Is it a "finalize()"?
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   912
                    if(name.equals("finalize") && typeMethod.getParameterCount() == 0) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   913
                        if(type != Object.class) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   914
                            hasExplicitFinalizer = true;
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   915
                            if(Modifier.isFinal(m)) {
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   916
                                // Must be able to override an explicit finalizer
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   917
                                throw new AdaptationException(Outcome.ERROR_FINAL_FINALIZER, type.getCanonicalName());
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   918
                            }
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   919
                        }
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   920
                        continue;
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   921
                    }
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   922
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   923
                    final MethodInfo mi = new MethodInfo(typeMethod);
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
   924
                    if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   925
                        finalMethods.add(mi);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   926
                    } else if (!finalMethods.contains(mi) && methodInfos.add(mi)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   927
                        if (Modifier.isAbstract(m)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   928
                            abstractMethodNames.add(mi.getName());
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   929
                        }
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   930
                        mi.setIsCanonical(this);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   931
                    }
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
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   934
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   935
        // 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
   936
        // 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
   937
        // 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
   938
        // 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
   939
        // getMethods() does provide those declared in a superinterface.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   940
        if (!type.isInterface()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   941
            final Class<?> superType = type.getSuperclass();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   942
            if (superType != null) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   943
                gatherMethods(superType);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   944
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   945
            for (final Class<?> itf: type.getInterfaces()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   946
                gatherMethods(itf);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   947
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   948
        }
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
22669
75563515567f 8032681: Issues with Nashorn
attila
parents: 22379
diff changeset
   951
    private void gatherMethods(final List<Class<?>> classes) throws AdaptationException {
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   952
        for(final Class<?> c: classes) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   953
            gatherMethods(c);
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
19459
79e75274df99 8022707: Revisit all doPrivileged blocks
sundar
parents: 19105
diff changeset
   957
    private static final AccessControlContext GET_DECLARED_MEMBERS_ACC_CTXT = ClassAndLoader.createPermAccCtxt("accessDeclaredMembers");
79e75274df99 8022707: Revisit all doPrivileged blocks
sundar
parents: 19105
diff changeset
   958
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   959
    /**
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   960
     * 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
   961
     * 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
   962
     * {@code Object.clone()}.
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   963
     * @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
   964
     */
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   965
    private static Collection<MethodInfo> getExcludedMethods() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   966
        return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   967
            @Override
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   968
            public Collection<MethodInfo> run() {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   969
                try {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   970
                    return Arrays.asList(
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   971
                            new MethodInfo(Object.class, "finalize"),
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   972
                            new MethodInfo(Object.class, "clone"));
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   973
                } catch (final NoSuchMethodException e) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   974
                    throw new AssertionError(e);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   975
                }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   976
            }
19459
79e75274df99 8022707: Revisit all doPrivileged blocks
sundar
parents: 19105
diff changeset
   977
        }, GET_DECLARED_MEMBERS_ACC_CTXT);
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   978
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   979
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   980
    private String getCommonSuperClass(final String type1, final String type2) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   981
        try {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   982
            final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   983
            final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   984
            if (c1.isAssignableFrom(c2)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   985
                return type1;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   986
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   987
            if (c2.isAssignableFrom(c1)) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   988
                return type2;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   989
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   990
            if (c1.isInterface() || c2.isInterface()) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   991
                return OBJECT_TYPE_NAME;
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   992
            }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   993
            return assignableSuperClass(c1, c2).getName().replace('.', '/');
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   994
        } catch(final ClassNotFoundException e) {
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   995
            throw new RuntimeException(e);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   996
        }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   997
    }
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   998
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
   999
    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
  1000
        final Class<?> superClass = c1.getSuperclass();
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
  1001
        return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2);
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
  1002
    }
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
  1003
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
  1004
    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
  1005
        return e.isAnnotationPresent(CallerSensitive.class);
19086
18dcb7a4906f 8020809: Java adapter should not allow overriding of caller sensitive methods
attila
parents: 19085
diff changeset
  1006
    }
16777
207a993adb9a 8011544: Allow subclassing Java classes from script without creating instances
attila
parents:
diff changeset
  1007
}