src/jdk/internal/dynalink/beans/AbstractJavaLinker.java

Wed, 20 Aug 2014 10:26:01 +0200

author
attila
date
Wed, 20 Aug 2014 10:26:01 +0200
changeset 963
e2497b11a021
parent 962
ac62e33a99b0
child 1081
a54684572f14
permissions
-rw-r--r--

8027043: Turn global accesses into MethodHandle.constant, with one chance of reassignment, e.g. x = value occuring once in the global scope is ok, twice is not.
8027958: NASHORN TEST: Create tests to test markdown javascript engine work with Nashorn
8028345: Remove nashorn repo "bin" scripts to avoid confusion with JDK bin launcher programs
8029090: Developers should be able to pass nashorn properties and enable/disable JFR from command line
8030169: Need regression test for bug JDK-8010731
8033105: Make sure Nashorn test harness can run zlib benchmark
8033334: Make sure that scope depth information is maintained in the RecompilableScriptFunctionDatas, to avoid unnecessary slow proto linkage when doing on demand compilation
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
8035820: Optimistic recompilation
8035836: Array performance improvements
8036127: Prototype filter needs to be applied to getter guard as well, not just getter
8036986: Test should check that correctly type is returned running with optimistic. If optimistic assumption was wrong we should get the right one.
8037086: Check that deoptimizing recompilations are correct
8037177: -Dnashorn.optimistic should be enabled by default, meaning that it has to be explicitly set to false to run with the jdk 8 style conservative types
8037534: Use scope types to determine optimistic types
8037572: Add more test cases to check static types
8037967: Broke the build, by commiting without saving the last review comment
8038223: Symbol trace debug output takes time
8038396: fix for the compiler expression evaluator to be more inquisitive about types
8038398: OptimisticRecompilationTest fails on staging repo nashorn/jdk9/nashorn due to test framework
8038406: Testability: as a first step of moving loggers away from the process global space, the Debug object now supports logging POJOs from log entries as an event queue, which can be introspected from test scripts. This is way better than screen scraping brittle and subject-to-change log output.
8038413: NPE in unboxInteger
8038416: Access to undefined scoped variables deoptimized too much
8038426: Move all loggers from process wide scope into Global scope
8038799: Guard and unbox boxed primitives types on setting them in Properties to avoid megamorphisism
8038945: Simplify strict undefined checks
8039044: Expand undefined intrinsics for all commutative combinators of scrict undefined checks
8039746: Transform applies to calls wherever possible, for ScriptFunctions and JSObjects.
8040024: BranchOptimizer produces bad code for NaN FP comparison
8040089: Apply to call transform was incomplete. Now passes all tests and performance is back
8040093: Make sure that optimistic splitting works in optimistic types
8040102: Remove all references to Unsafe and definition of anonymous clases from the code
8040655: When processing a RewriteException debug object, the return value has already been reset to null. We need to catch this value before that.
8041434: Add synchronization to the common global constants structure
8041625: AccessorProperty currentType must only by Object.class when non-primitive, and scoping followup problem for lazily generated with bodies
8041905: Fix apply2call bug that prevented avatar.js unit tests from running correctly
8041995: Problems when loading tree expressions with several optimistic program points when optimistically initializing ObjectNodes
8042118: Separate types from symbols
8043002: Improve performance of Nashorn equality operators
8043003: Use strongly referenced generic invokers
8043004: Reduce variability at JavaAdapter call sites
8043132: Nashorn : all tests failed with java.security.AccessControlException
8043133: Fix corner cases of JDK-8041995
8043137: Collapse long sequences of NOP in Nashorn bytecode output
8043232: Index selection of overloaded java new constructors
8043235: Type-based optimizations interfere with continuation methods
8043431: Fix yet another corner case of JDK-8041995
8043504: Octane test harness was missing argument to print_always at one callsite, causing erroneous logging
8043605: Enable history for empty property maps
8043608: Make equality tests inline better
8043611: Move timing dependent benchmark for apply2call specialization to currently_failing. It is dependent that nothing takes machine time when doing the two runs, causing spurious assertions. Suggest running octane.raytrace manually instead to verify that this works, or incorporating it in the nightly test suite
8043632: Parallelize class installation and various script fixes.
8043633: In order to remove global state outside of contexts, make sure Timing class is an instance and not a static global collection of data. Move into Context. Move -Dnashorn.timing to an official logging option.
8043956: Make code caching work with optimistic typing and lazy compilation
8044012: Integrate the latest best known performance flags int ant octane jobs, and make sure that it's easy to compare 'ant octane-nashorn' and 'ant octane-v8' at the push of a button. (or rather; the entry of a command line)
8044102: Ensure bechmark exclude list for Octane benchmarks is in only one place, project.properties, and fix benchmark harness
8044154: Nashorn : all tests failed with java.security.AccessControlException
8044171: Make optimistic exception handlers smaller
8044502: Get rid of global optimistic flag
8044518: Ensure exceptions related to optimistic recompilation are not serializable
8044533: Deoptimizing negation produces wrong result for zero
8044534: Constant folding for unary + should produce int for boolean literals
8044760: Avoid PropertyMap duplicate for global instances
8044786: Some tests fail with non-optimistic compilation
8044803: Unnecessary restOf check
8044816: On-demand compiled top-level program doesn't need :createProgramFunction
8044851: nashorn properties leak memory
8046013: TypeError: Cannot apply "with" to non script object
8046014: MultiGlobalCompiledScript should cache :createProgramFunction handle
8046025: AccessorProperty.getGetter is not threadsafe
8046026: CompiledFunction.relinkComposableInvoker assert is being hit
8046201: Avoid repeated flattening of nested ConsStrings
8046215: Running uncompilable scripts throws NullPointerException
8046898: Make sure that lazy compilation is the default, remove redundant "enable lazy compilation" flags, added warning message if compile logging is enabled and lazy is switched off. Verified existing test suite code coverage equivalence between lazy and eager.
8046905: apply on apply is broken
8046921: Deoptimization type information peristence
8047035: (function() "hello")() crashes in Lexer with jdk9
8047057: Add a regression test for the passing test cases from JDK-8042304
8047067: all eval arguments need to be copied in Lower
8047078: Fuzzing bug discovered when ArrayLiteralNodes weren't immutable
8047166: 'do with({}) break ; while(0);' crashes in CodeGenerator
8047331: Assertion in CompiledFunction when running earley-boyer after Merge
8047357: More precise synthetic return + unreachable throw
8047359: large string size RangeError should be thrown rather than reporting negative length
8047369: Add regression tests for passing test cases of JDK-8024971
8047371: local variable declaration in TypeEvaluator should use ScriptObject.addOwnProperty instead of .set
8047728: (function(x){var o={x:0}; with(o){delete x} return o.x})() evaluates to 0 instead of undefined
8047959: bindings created for declarations in eval code are not mutable
8048009: Type info caching accidentally defeated
8048071: eval within 'with' statement does not use correct scope if with scope expression has a copy of eval
8048079: Persistent code store is broken after optimistic types merge
8048505: ScriptingFunctions.readFully couldn't handle file names represented as ConsStrings
8048586: String concatenation with optimistic types is slow
8048718: JSON.parse('{"0":0, "64":0}') throws ArrayindexOutOfBoundsException
8048869: Reduce compile time by about 5% by removing the Class.casts from the AST nodes
8049086: Minor API convenience functions on "Java" object
8049222: JSType class exposes public mutable arrays
8049223: RewriteException class exposes public mutable arrays
8049242: Explicit constructor overload selection should work with StaticClass as well
8049318: Test hideLocationProperties.js fails on Window due to backslash in path
8049524: Global object initialization via javax.script API should be minimal
8050432: javax.script.filename variable should not be enumerable with nashorn engine's ENGINE_SCOPE bindings
8050964: OptimisticTypesPersistence.java should use java.util.Date instead of java.sql.Date
8051019: Separate src and test execution sandbox directories
8051346: Test262 tests for ECMAScript 5 now in branch "es5-tests"
8051439: Wrong type calculated for ADD operator with undefined operand
8051839: GuardedInvocation needs to clone an argument
8053908: jdeps is not PATH on Mac, results in ant clean test failure on Mac
8053910: ScriptObjectMirror causing havoc with Invocation interface
8053913: Auto format caused warning in CompositeTypeBasedGuardingDynamicLinker
8054223: Nashorn: AssertionError when use __DIR__ and ScriptEngine.eval()
8054411: Add nashorn.args.prepend system property
8054503: test/script/external/test262/test/suite/ch12/12.6/12.6.4/12.6.4-2.js fails with tip
8054651: Global.initConstructor and ScriptFunction.getPrototype(Object) can have stricter types
8054898: Avoid creation of empty type info files
8054993: type info cache may be disabled for test262 and tests explicitly changing that property should use @fork
8055034: jjs exits interactive mode if exception was thrown when trying to print value of last evaluated expression
8055042: Compile-time expression evaluator was missing variables
8055107: Extension directives to turn on callsite profiling, tracing, AST print and other debug features locally
8055139: test/script/trusted/JDK-8055107.js fails with access control exception
8055186: Backport Nashorn optimistic typing to 8u repository
8055529: Clean up the bin directory
Reviewed-by: jlaskey, lagergren, sundar
Contributed-by: marcus.largergren@oracle.com, hannes.wallnoefer@oracle.com, sundararajan.athijegannathan@oracle.com

attila@90 1 /*
attila@90 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
attila@90 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
attila@90 4 *
attila@90 5 * This code is free software; you can redistribute it and/or modify it
attila@90 6 * under the terms of the GNU General Public License version 2 only, as
attila@90 7 * published by the Free Software Foundation. Oracle designates this
attila@90 8 * particular file as subject to the "Classpath" exception as provided
attila@90 9 * by Oracle in the LICENSE file that accompanied this code.
attila@90 10 *
attila@90 11 * This code is distributed in the hope that it will be useful, but WITHOUT
attila@90 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
attila@90 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
attila@90 14 * version 2 for more details (a copy is included in the LICENSE file that
attila@90 15 * accompanied this code).
attila@90 16 *
attila@90 17 * You should have received a copy of the GNU General Public License version
attila@90 18 * 2 along with this work; if not, write to the Free Software Foundation,
attila@90 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
attila@90 20 *
attila@90 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
attila@90 22 * or visit www.oracle.com if you need additional information or have any
attila@90 23 * questions.
attila@90 24 */
attila@90 25
attila@90 26 /*
attila@90 27 * This file is available under and governed by the GNU General Public
attila@90 28 * License version 2 only, as published by the Free Software Foundation.
attila@90 29 * However, the following notice accompanied the original version of this
attila@90 30 * file, and Oracle licenses the original version of this file under the BSD
attila@90 31 * license:
attila@90 32 */
attila@90 33 /*
attila@90 34 Copyright 2009-2013 Attila Szegedi
attila@90 35
attila@90 36 Licensed under both the Apache License, Version 2.0 (the "Apache License")
attila@90 37 and the BSD License (the "BSD License"), with licensee being free to
attila@90 38 choose either of the two at their discretion.
attila@90 39
attila@90 40 You may not use this file except in compliance with either the Apache
attila@90 41 License or the BSD License.
attila@90 42
attila@90 43 If you choose to use this file in compliance with the Apache License, the
attila@90 44 following notice applies to you:
attila@90 45
attila@90 46 You may obtain a copy of the Apache License at
attila@90 47
attila@90 48 http://www.apache.org/licenses/LICENSE-2.0
attila@90 49
attila@90 50 Unless required by applicable law or agreed to in writing, software
attila@90 51 distributed under the License is distributed on an "AS IS" BASIS,
attila@90 52 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
attila@90 53 implied. See the License for the specific language governing
attila@90 54 permissions and limitations under the License.
attila@90 55
attila@90 56 If you choose to use this file in compliance with the BSD License, the
attila@90 57 following notice applies to you:
attila@90 58
attila@90 59 Redistribution and use in source and binary forms, with or without
attila@90 60 modification, are permitted provided that the following conditions are
attila@90 61 met:
attila@90 62 * Redistributions of source code must retain the above copyright
attila@90 63 notice, this list of conditions and the following disclaimer.
attila@90 64 * Redistributions in binary form must reproduce the above copyright
attila@90 65 notice, this list of conditions and the following disclaimer in the
attila@90 66 documentation and/or other materials provided with the distribution.
attila@90 67 * Neither the name of the copyright holder nor the names of
attila@90 68 contributors may be used to endorse or promote products derived from
attila@90 69 this software without specific prior written permission.
attila@90 70
attila@90 71 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
attila@90 72 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
attila@90 73 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
attila@90 74 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
attila@90 75 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
attila@90 76 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
attila@90 77 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
attila@90 78 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
attila@90 79 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
attila@90 80 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
attila@90 81 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
attila@90 82 */
attila@90 83
attila@90 84 package jdk.internal.dynalink.beans;
attila@90 85
attila@90 86 import java.lang.invoke.MethodHandle;
attila@90 87 import java.lang.invoke.MethodHandles;
attila@90 88 import java.lang.invoke.MethodType;
attila@404 89 import java.lang.reflect.AccessibleObject;
attila@404 90 import java.lang.reflect.Constructor;
attila@90 91 import java.lang.reflect.Field;
attila@404 92 import java.lang.reflect.Member;
attila@90 93 import java.lang.reflect.Method;
attila@90 94 import java.lang.reflect.Modifier;
attila@439 95 import java.util.Collection;
attila@439 96 import java.util.Collections;
attila@90 97 import java.util.HashMap;
attila@90 98 import java.util.List;
attila@90 99 import java.util.Map;
attila@90 100 import jdk.internal.dynalink.CallSiteDescriptor;
attila@90 101 import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
attila@90 102 import jdk.internal.dynalink.linker.GuardedInvocation;
attila@90 103 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
attila@90 104 import jdk.internal.dynalink.linker.LinkRequest;
attila@90 105 import jdk.internal.dynalink.linker.LinkerServices;
attila@90 106 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
attila@90 107 import jdk.internal.dynalink.support.Guards;
attila@90 108 import jdk.internal.dynalink.support.Lookup;
attila@963 109 import jdk.internal.dynalink.support.TypeUtilities;
attila@90 110
attila@90 111 /**
attila@90 112 * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
attila@90 113 * exposure and method calls for both static and instance facets of a class.
attila@90 114 *
attila@90 115 * @author Attila Szegedi
attila@90 116 */
attila@90 117 abstract class AbstractJavaLinker implements GuardingDynamicLinker {
attila@404 118
attila@90 119 final Class<?> clazz;
attila@90 120 private final MethodHandle classGuard;
attila@90 121 private final MethodHandle assignableGuard;
attila@404 122 private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>();
attila@90 123 private final Map<String, DynamicMethod> propertySetters = new HashMap<>();
attila@90 124 private final Map<String, DynamicMethod> methods = new HashMap<>();
attila@90 125
attila@962 126 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard) {
attila@90 127 this(clazz, classGuard, classGuard);
attila@90 128 }
attila@90 129
attila@962 130 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard, final MethodHandle assignableGuard) {
attila@90 131 this.clazz = clazz;
attila@90 132 this.classGuard = classGuard;
attila@90 133 this.assignableGuard = assignableGuard;
attila@90 134
attila@90 135 final FacetIntrospector introspector = createFacetIntrospector();
attila@101 136 // Add methods and properties
attila@962 137 for(final Method method: introspector.getMethods()) {
attila@101 138 final String name = method.getName();
attila@101 139 // Add method
attila@404 140 addMember(name, method, methods);
attila@101 141 // Add the method as a property getter and/or setter
attila@101 142 if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) {
attila@101 143 // Property getter
attila@404 144 setPropertyGetter(method, 3);
attila@101 145 } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 &&
attila@101 146 method.getReturnType() == boolean.class) {
attila@101 147 // Boolean property getter
attila@404 148 setPropertyGetter(method, 2);
attila@101 149 } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {
attila@101 150 // Property setter
attila@404 151 addMember(decapitalize(name.substring(3)), method, propertySetters);
attila@90 152 }
attila@101 153 }
attila@90 154
attila@101 155 // Add field getter/setters as property getters/setters.
attila@962 156 for(final Field field: introspector.getFields()) {
attila@101 157 final String name = field.getName();
attila@101 158 // Only add a property getter when one is not defined already as a getXxx()/isXxx() method.
attila@101 159 if(!propertyGetters.containsKey(name)) {
attila@101 160 setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS);
attila@90 161 }
attila@101 162 if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) {
attila@404 163 addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name),
attila@404 164 propertySetters);
attila@101 165 }
attila@101 166 }
attila@90 167
attila@101 168 // Add inner classes, but only those for which we don't hide a property with it
attila@962 169 for(final Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {
attila@101 170 final String name = innerClassSpec.getKey();
attila@101 171 if(!propertyGetters.containsKey(name)) {
attila@101 172 setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS);
attila@90 173 }
attila@90 174 }
attila@90 175 }
attila@90 176
attila@962 177 private static String decapitalize(final String str) {
attila@123 178 assert str != null;
attila@123 179 if(str.isEmpty()) {
attila@123 180 return str;
attila@123 181 }
attila@123 182
attila@123 183 final char c0 = str.charAt(0);
attila@123 184 if(Character.isLowerCase(c0)) {
attila@123 185 return str;
attila@123 186 }
attila@123 187
attila@123 188 // If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize
attila@123 189 if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) {
attila@123 190 return str;
attila@123 191 }
attila@123 192
attila@123 193 final char c[] = str.toCharArray();
attila@123 194 c[0] = Character.toLowerCase(c0);
attila@123 195 return new String(c);
attila@123 196 }
attila@123 197
attila@90 198 abstract FacetIntrospector createFacetIntrospector();
attila@90 199
attila@439 200 Collection<String> getReadablePropertyNames() {
attila@439 201 return getUnmodifiableKeys(propertyGetters);
attila@439 202 }
attila@439 203
attila@439 204 Collection<String> getWritablePropertyNames() {
attila@439 205 return getUnmodifiableKeys(propertySetters);
attila@439 206 }
attila@439 207
attila@439 208 Collection<String> getMethodNames() {
attila@439 209 return getUnmodifiableKeys(methods);
attila@439 210 }
attila@439 211
attila@962 212 private static Collection<String> getUnmodifiableKeys(final Map<String, ?> m) {
attila@439 213 return Collections.unmodifiableCollection(m.keySet());
attila@439 214 }
attila@439 215
attila@404 216 /**
attila@404 217 * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only
attila@404 218 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
attila@404 219 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
attila@404 220 * instead.
attila@404 221 * @param name name of the property
attila@404 222 * @param handle the method handle that implements the property getter
attila@404 223 * @param validationType the validation type for the property
attila@404 224 */
attila@962 225 private void setPropertyGetter(final String name, final SingleDynamicMethod handle, final ValidationType validationType) {
attila@404 226 propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType));
attila@90 227 }
attila@90 228
attila@404 229 /**
attila@404 230 * Sets the specified reflective method to be the property getter for the specified property.
attila@404 231 * @param getter the getter method
attila@404 232 * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for
attila@404 233 * names starting with "is".
attila@404 234 */
attila@962 235 private void setPropertyGetter(final Method getter, final int prefixLen) {
attila@404 236 setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod(
attila@404 237 getMostGenericGetter(getter)), ValidationType.INSTANCE_OF);
attila@404 238 }
attila@404 239
attila@404 240 /**
attila@404 241 * Sets the specified method handle to be the property getter for the specified property. Note that you can only
attila@404 242 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
attila@404 243 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
attila@404 244 * instead.
attila@404 245 * @param name name of the property
attila@404 246 * @param handle the method handle that implements the property getter
attila@404 247 * @param validationType the validation type for the property
attila@404 248 */
attila@962 249 void setPropertyGetter(final String name, final MethodHandle handle, final ValidationType validationType) {
attila@404 250 setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);
attila@404 251 }
attila@404 252
attila@962 253 private void addMember(final String name, final AccessibleObject ao, final Map<String, DynamicMethod> methodMap) {
attila@404 254 addMember(name, createDynamicMethod(ao), methodMap);
attila@404 255 }
attila@404 256
attila@962 257 private void addMember(final String name, final SingleDynamicMethod method, final Map<String, DynamicMethod> methodMap) {
attila@90 258 final DynamicMethod existingMethod = methodMap.get(name);
attila@404 259 final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name);
attila@90 260 if(newMethod != existingMethod) {
attila@90 261 methodMap.put(name, newMethod);
attila@90 262 }
attila@90 263 }
attila@90 264
attila@404 265 /**
attila@404 266 * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The
attila@404 267 * methods should represent all overloads of the same name (or all constructors of the class).
attila@404 268 * @param members the reflective members
attila@404 269 * @param clazz the class declaring the reflective members
attila@404 270 * @param name the common name of the reflective members.
attila@404 271 * @return a dynamic method representing all the specified reflective members.
attila@404 272 */
attila@962 273 static DynamicMethod createDynamicMethod(final Iterable<? extends AccessibleObject> members, final Class<?> clazz, final String name) {
attila@90 274 DynamicMethod dynMethod = null;
attila@962 275 for(final AccessibleObject method: members) {
attila@404 276 dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name);
attila@90 277 }
attila@90 278 return dynMethod;
attila@90 279 }
attila@90 280
attila@404 281 /**
attila@404 282 * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will
attila@404 283 * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive
attila@404 284 * dynamic method when needed.
attila@404 285 * @param m the reflective member
attila@404 286 * @return the single dynamic method representing the reflective member
attila@404 287 */
attila@962 288 private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
attila@404 289 if(CallerSensitiveDetector.isCallerSensitive(m)) {
attila@404 290 return new CallerSensitiveDynamicMethod(m);
attila@404 291 }
attila@404 292 final Member member = (Member)m;
attila@963 293 return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName(), m instanceof Constructor);
attila@404 294 }
attila@404 295
attila@404 296 /**
attila@404 297 * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be
attila@404 298 * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were
attila@404 299 * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege
attila@404 300 * unreflector as its caller, and thus completely useless.
attila@404 301 * @param m the method or constructor
attila@404 302 * @return the method handle
attila@404 303 */
attila@962 304 private static MethodHandle unreflectSafely(final AccessibleObject m) {
attila@404 305 if(m instanceof Method) {
attila@404 306 final Method reflMethod = (Method)m;
attila@464 307 final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod);
attila@404 308 if(Modifier.isStatic(reflMethod.getModifiers())) {
attila@404 309 return StaticClassIntrospector.editStaticMethodHandle(handle);
attila@404 310 }
attila@404 311 return handle;
attila@404 312 }
attila@464 313 return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m));
attila@404 314 }
attila@404 315
attila@962 316 private static DynamicMethod mergeMethods(final SingleDynamicMethod method, final DynamicMethod existing, final Class<?> clazz, final String name) {
attila@90 317 if(existing == null) {
attila@404 318 return method;
attila@404 319 } else if(existing.contains(method)) {
attila@90 320 return existing;
attila@404 321 } else if(existing instanceof SingleDynamicMethod) {
attila@90 322 final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);
attila@404 323 odm.addMethod(((SingleDynamicMethod)existing));
attila@404 324 odm.addMethod(method);
attila@90 325 return odm;
attila@90 326 } else if(existing instanceof OverloadedDynamicMethod) {
attila@404 327 ((OverloadedDynamicMethod)existing).addMethod(method);
attila@90 328 return existing;
attila@90 329 }
attila@90 330 throw new AssertionError();
attila@90 331 }
attila@90 332
attila@90 333 @Override
attila@962 334 public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
attila@90 335 throws Exception {
attila@90 336 final LinkRequest ncrequest = request.withoutRuntimeContext();
attila@90 337 // BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn".
attila@90 338 final CallSiteDescriptor callSiteDescriptor = ncrequest.getCallSiteDescriptor();
attila@90 339 final String op = callSiteDescriptor.getNameToken(CallSiteDescriptor.OPERATOR);
attila@90 340 // Either dyn:callMethod:name(this[,args]) or dyn:callMethod(this,name[,args]).
attila@90 341 if("callMethod" == op) {
attila@90 342 return getCallPropWithThis(callSiteDescriptor, linkerServices);
attila@90 343 }
attila@90 344 List<String> operations = CallSiteDescriptorFactory.tokenizeOperators(callSiteDescriptor);
attila@90 345 while(!operations.isEmpty()) {
attila@90 346 final GuardedInvocationComponent gic = getGuardedInvocationComponent(callSiteDescriptor, linkerServices,
attila@90 347 operations);
attila@90 348 if(gic != null) {
attila@90 349 return gic.getGuardedInvocation();
attila@90 350 }
attila@90 351 operations = pop(operations);
attila@90 352 }
attila@90 353 return null;
attila@90 354 }
attila@90 355
attila@962 356 protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
attila@962 357 final LinkerServices linkerServices, final List<String> operations) throws Exception {
attila@90 358 if(operations.isEmpty()) {
attila@90 359 return null;
attila@90 360 }
attila@90 361 final String op = operations.get(0);
attila@90 362 // Either dyn:getProp:name(this) or dyn:getProp(this, name)
attila@90 363 if("getProp".equals(op)) {
attila@90 364 return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations));
attila@90 365 }
attila@90 366 // Either dyn:setProp:name(this, value) or dyn:setProp(this, name, value)
attila@90 367 if("setProp".equals(op)) {
attila@90 368 return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations));
attila@90 369 }
attila@90 370 // Either dyn:getMethod:name(this), or dyn:getMethod(this, name)
attila@90 371 if("getMethod".equals(op)) {
attila@90 372 return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations));
attila@90 373 }
attila@90 374 return null;
attila@90 375 }
attila@90 376
attila@962 377 static final <T> List<T> pop(final List<T> l) {
attila@90 378 return l.subList(1, l.size());
attila@90 379 }
attila@90 380
attila@962 381 MethodHandle getClassGuard(final CallSiteDescriptor desc) {
attila@90 382 return getClassGuard(desc.getMethodType());
attila@90 383 }
attila@90 384
attila@962 385 MethodHandle getClassGuard(final MethodType type) {
attila@90 386 return Guards.asType(classGuard, type);
attila@90 387 }
attila@90 388
attila@962 389 GuardedInvocationComponent getClassGuardedInvocationComponent(final MethodHandle invocation, final MethodType type) {
attila@90 390 return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
attila@90 391 }
attila@90 392
attila@963 393 SingleDynamicMethod getConstructorMethod(final String signature) {
attila@963 394 return null;
attila@963 395 }
attila@963 396
attila@962 397 private MethodHandle getAssignableGuard(final MethodType type) {
attila@90 398 return Guards.asType(assignableGuard, type);
attila@90 399 }
attila@90 400
attila@962 401 private GuardedInvocation getCallPropWithThis(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
attila@90 402 switch(callSiteDescriptor.getNameTokenCount()) {
attila@90 403 case 3: {
attila@404 404 return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
attila@90 405 callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods);
attila@90 406 }
attila@90 407 default: {
attila@90 408 return null;
attila@90 409 }
attila@90 410 }
attila@90 411 }
attila@90 412
attila@962 413 private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
attila@962 414 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){
attila@404 415 final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
attila@404 416 return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
attila@90 417 }
attila@90 418
attila@963 419 private MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
attila@962 420 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) {
attila@90 421 final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
attila@404 422 return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
attila@90 423 }
attila@90 424
attila@963 425 private DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {
attila@90 426 final DynamicMethod dynaMethod = methodMap.get(methodName);
attila@90 427 return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
attila@90 428 }
attila@90 429
attila@963 430 private SingleDynamicMethod getExplicitSignatureDynamicMethod(final String fullName,
attila@962 431 final Map<String, DynamicMethod> methodsMap) {
attila@90 432 // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
attila@90 433 // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
attila@90 434 // resolution works correctly in almost every situation. However, in presence of many language-specific
attila@90 435 // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected
attila@404 436 // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload
attila@90 437 // for performance reasons.
attila@90 438
attila@90 439 // Is the method name lexically of the form "name(types)"?
attila@963 440 final int lastChar = fullName.length() - 1;
attila@963 441 if(fullName.charAt(lastChar) != ')') {
attila@90 442 return null;
attila@90 443 }
attila@963 444 final int openBrace = fullName.indexOf('(');
attila@90 445 if(openBrace == -1) {
attila@90 446 return null;
attila@90 447 }
attila@90 448
attila@963 449 final String name = fullName.substring(0, openBrace);
attila@963 450 final String signature = fullName.substring(openBrace + 1, lastChar);
attila@963 451
attila@90 452 // Find an existing method for the "name" part
attila@963 453 final DynamicMethod simpleNamedMethod = methodsMap.get(name);
attila@90 454 if(simpleNamedMethod == null) {
attila@963 455 // explicit signature constructor access
attila@963 456 // Java.type("java.awt.Color")["(int,int,int)"]
attila@963 457 // will get Color(int,int,int) constructor of Color class.
attila@963 458 if (name.isEmpty()) {
attila@963 459 return getConstructorMethod(signature);
attila@963 460 }
attila@963 461
attila@90 462 return null;
attila@90 463 }
attila@90 464
attila@90 465 // Try to get a narrowed dynamic method for the explicit parameter types.
attila@963 466 return simpleNamedMethod.getMethodForExactParamTypes(signature);
attila@90 467 }
attila@90 468
attila@90 469 private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
attila@90 470 boolean.class, MethodHandle.class));
attila@90 471 private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(
attila@90 472 MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
attila@90 473
attila@962 474 private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor,
attila@962 475 final LinkerServices linkerServices, final List<String> operations) throws Exception {
attila@90 476 switch(callSiteDescriptor.getNameTokenCount()) {
attila@90 477 case 2: {
attila@90 478 // Must have three arguments: target object, property name, and property value.
attila@90 479 assertParameterCount(callSiteDescriptor, 3);
attila@90 480
attila@963 481 // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
attila@963 482 // valid for us to convert return values proactively. Also, since we don't know what setters will be
attila@963 483 // invoked, we'll conservatively presume Object return type.
attila@963 484 final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
attila@963 485
attila@90 486 // What's below is basically:
attila@90 487 // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
attila@90 488 // get_setter_handle(type, linkerServices))
attila@90 489 // only with a bunch of method signature adjustments. Basically, retrieve method setter
attila@90 490 // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
attila@90 491 // component's invocation.
attila@90 492
attila@90 493 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
attila@963 494 // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
attila@963 495 // Object return type).
attila@90 496 final MethodType setterType = type.dropParameterTypes(1, 2);
attila@90 497 // Bind property setter handle to the expected setter type and linker services. Type is
attila@90 498 // MethodHandle(Object, String, Object)
attila@404 499 final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
attila@404 500 CallSiteDescriptorFactory.dropParameterTypes(callSiteDescriptor, 1, 2), linkerServices);
attila@90 501
attila@90 502 // Cast getter to MethodHandle(O, N, V)
attila@90 503 final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
attila@90 504 MethodHandle.class));
attila@90 505
attila@90 506 // Handle to invoke the setter R(MethodHandle, O, V)
attila@90 507 final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
attila@90 508 // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
attila@90 509 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
attila@90 510 1));
attila@90 511 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
attila@90 512 linkerServices, operations);
attila@90 513
attila@90 514 final MethodHandle fallbackFolded;
attila@90 515 if(nextComponent == null) {
attila@963 516 // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
attila@90 517 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
attila@90 518 type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
attila@90 519 } else {
attila@963 520 // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
attila@90 521 // extra argument resulting from fold
attila@90 522 fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
attila@90 523 0, MethodHandle.class);
attila@90 524 }
attila@90 525
attila@90 526 // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
attila@90 527 final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
attila@90 528 IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
attila@90 529 if(nextComponent == null) {
attila@90 530 return getClassGuardedInvocationComponent(compositeSetter, type);
attila@90 531 }
attila@101 532 return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
attila@90 533 }
attila@90 534 case 3: {
attila@90 535 // Must have two arguments: target object and property value
attila@90 536 assertParameterCount(callSiteDescriptor, 2);
attila@404 537 final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
attila@404 538 callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters);
attila@90 539 // If we have a property setter with this name, this composite operation will always stop here
attila@90 540 if(gi != null) {
attila@90 541 return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
attila@90 542 }
attila@90 543 // If we don't have a property setter with this name, always fall back to the next operation in the
attila@90 544 // composite (if any)
attila@90 545 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations);
attila@90 546 }
attila@90 547 default: {
attila@90 548 // More than two name components; don't know what to do with it.
attila@90 549 return null;
attila@90 550 }
attila@90 551 }
attila@90 552 }
attila@90 553
attila@90 554 private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
attila@90 555
attila@404 556 private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
attila@404 557 boolean.class, AnnotatedDynamicMethod.class));
attila@404 558 private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
attila@404 559 MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
attila@404 560 private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
attila@404 561 "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class));
attila@404 562 private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
attila@90 563
attila@962 564 private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
attila@962 565 final LinkerServices linkerServices, final List<String> ops) throws Exception {
attila@90 566 switch(callSiteDescriptor.getNameTokenCount()) {
attila@90 567 case 2: {
attila@963 568 // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
attila@963 569 // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
attila@963 570 // runtime might not allow coercing at that call site.
attila@963 571 final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
attila@90 572 // Must have exactly two arguments: receiver and name
attila@90 573 assertParameterCount(callSiteDescriptor, 2);
attila@90 574
attila@90 575 // What's below is basically:
attila@90 576 // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
attila@90 577 // only with a bunch of method signature adjustments. Basically, retrieve method getter
attila@404 578 // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
attila@90 579 // or delegate to next component's invocation.
attila@90 580
attila@90 581 final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
attila@404 582 AnnotatedDynamicMethod.class));
attila@404 583 final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
attila@404 584 GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
attila@404 585 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
attila@404 586 callSiteBoundMethodGetter);
attila@963 587 // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
attila@404 588 final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
attila@404 589 MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
attila@90 590 // Since it's in the target of a fold, drop the unnecessary second argument
attila@963 591 // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
attila@90 592 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
attila@90 593 type.parameterType(1));
attila@90 594 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
attila@90 595 linkerServices, ops);
attila@90 596
attila@90 597 final MethodHandle fallbackFolded;
attila@90 598 if(nextComponent == null) {
attila@963 599 // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
attila@404 600 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
attila@404 601 type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
attila@90 602 } else {
attila@963 603 // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
attila@963 604 // drop the extra argument resulting from fold and to change its return type to Object.
attila@963 605 final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
attila@963 606 final MethodType nextType = nextInvocation.type();
attila@963 607 fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
attila@963 608 nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
attila@90 609 }
attila@90 610
attila@963 611 // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
attila@90 612 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
attila@404 613 IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
attila@90 614 if(nextComponent == null) {
attila@90 615 return getClassGuardedInvocationComponent(compositeGetter, type);
attila@90 616 }
attila@101 617 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
attila@90 618 }
attila@90 619 case 3: {
attila@90 620 // Must have exactly one argument: receiver
attila@90 621 assertParameterCount(callSiteDescriptor, 1);
attila@90 622 // Fixed name
attila@404 623 final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken(
attila@90 624 CallSiteDescriptor.NAME_OPERAND));
attila@90 625 if(annGetter == null) {
attila@90 626 // We have no such property, always delegate to the next component operation
attila@90 627 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
attila@90 628 }
attila@404 629 final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices);
attila@90 630 // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
attila@90 631 // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
attila@90 632 // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
attila@90 633 // we're linking against a field getter, don't make the assumption.
attila@90 634 // NOTE: No delegation to the next component operation if we have a property with this name, even if its
attila@90 635 // value is null.
attila@90 636 final ValidationType validationType = annGetter.validationType;
attila@404 637 // TODO: we aren't using the type that declares the most generic getter here!
attila@963 638 return new GuardedInvocationComponent(getter, getGuard(validationType,
attila@963 639 callSiteDescriptor.getMethodType()), clazz, validationType);
attila@90 640 }
attila@90 641 default: {
attila@90 642 // Can't do anything with more than 3 name components
attila@90 643 return null;
attila@90 644 }
attila@90 645 }
attila@90 646 }
attila@90 647
attila@962 648 private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) {
attila@90 649 switch(validationType) {
attila@90 650 case EXACT_CLASS: {
attila@90 651 return getClassGuard(methodType);
attila@90 652 }
attila@90 653 case INSTANCE_OF: {
attila@90 654 return getAssignableGuard(methodType);
attila@90 655 }
attila@90 656 case IS_ARRAY: {
attila@90 657 return Guards.isArray(0, methodType);
attila@90 658 }
attila@90 659 case NONE: {
attila@90 660 return null;
attila@90 661 }
attila@101 662 default: {
attila@101 663 throw new AssertionError();
attila@101 664 }
attila@90 665 }
attila@90 666 }
attila@90 667
attila@963 668 private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
attila@963 669 MethodType.methodType(boolean.class, Object.class));
attila@963 670 private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
attila@90 671
attila@962 672 private GuardedInvocationComponent getMethodGetter(final CallSiteDescriptor callSiteDescriptor,
attila@962 673 final LinkerServices linkerServices, final List<String> ops) throws Exception {
attila@963 674 // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
attila@963 675 // be visible outside of this linker, declare it to return Object.
attila@963 676 final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
attila@90 677 switch(callSiteDescriptor.getNameTokenCount()) {
attila@90 678 case 2: {
attila@90 679 // Must have exactly two arguments: receiver and name
attila@90 680 assertParameterCount(callSiteDescriptor, 2);
attila@90 681 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
attila@90 682 linkerServices, ops);
attila@963 683 if(nextComponent == null || !TypeUtilities.areAssignable(DynamicMethod.class,
attila@963 684 nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
attila@963 685 // No next component operation, or it can never produce a dynamic method; just return a component
attila@963 686 // for this operation.
attila@90 687 return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
attila@101 688 }
attila@90 689
attila@101 690 // What's below is basically:
attila@101 691 // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
attila@101 692 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
attila@101 693 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
attila@90 694
attila@963 695 final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
attila@101 696 // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
attila@101 697 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
attila@963 698 OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
attila@101 699 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
attila@963 700 // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
attila@963 701 // return type.
attila@963 702 assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
attila@101 703 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
attila@101 704 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
attila@963 705 Object.class);
attila@101 706 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
attila@101 707 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
attila@963 708 IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
attila@101 709
attila@101 710 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
attila@90 711 }
attila@90 712 case 3: {
attila@90 713 // Must have exactly one argument: receiver
attila@90 714 assertParameterCount(callSiteDescriptor, 1);
attila@90 715 final DynamicMethod method = getDynamicMethod(callSiteDescriptor.getNameToken(
attila@90 716 CallSiteDescriptor.NAME_OPERAND));
attila@90 717 if(method == null) {
attila@90 718 // We have no such method, always delegate to the next component
attila@90 719 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
attila@90 720 }
attila@90 721 // No delegation to the next component of the composite operation; if we have a method with that name,
attila@90 722 // we'll always return it at this point.
attila@90 723 return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
attila@963 724 MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
attila@90 725 }
attila@90 726 default: {
attila@90 727 // Can't do anything with more than 3 name components
attila@90 728 return null;
attila@90 729 }
attila@90 730 }
attila@90 731 }
attila@90 732
attila@963 733 static class MethodPair {
attila@963 734 final MethodHandle method1;
attila@963 735 final MethodHandle method2;
attila@963 736
attila@963 737 MethodPair(final MethodHandle method1, final MethodHandle method2) {
attila@963 738 this.method1 = method1;
attila@963 739 this.method2 = method2;
attila@963 740 }
attila@963 741
attila@963 742 MethodHandle guardWithTest(final MethodHandle test) {
attila@963 743 return MethodHandles.guardWithTest(test, method1, method2);
attila@963 744 }
attila@963 745 }
attila@963 746
attila@963 747 static MethodPair matchReturnTypes(final MethodHandle m1, final MethodHandle m2) {
attila@963 748 final MethodType type1 = m1.type();
attila@963 749 final MethodType type2 = m2.type();
attila@963 750 final Class<?> commonRetType = TypeUtilities.getCommonLosslessConversionType(type1.returnType(),
attila@963 751 type2.returnType());
attila@963 752 return new MethodPair(
attila@963 753 m1.asType(type1.changeReturnType(commonRetType)),
attila@963 754 m2.asType(type2.changeReturnType(commonRetType)));
attila@963 755 }
attila@963 756
attila@962 757 private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
attila@90 758 if(descriptor.getMethodType().parameterCount() != paramCount) {
attila@90 759 throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
attila@90 760 }
attila@90 761 }
attila@90 762
attila@90 763 private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
attila@90 764 "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class);
attila@90 765 private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this);
attila@90 766
attila@90 767 /**
attila@90 768 * @param id the property ID
attila@90 769 * @return the method handle for retrieving the property, or null if the property does not exist
attila@90 770 */
attila@90 771 @SuppressWarnings("unused")
attila@962 772 private Object getPropertyGetterHandle(final Object id) {
attila@90 773 return propertyGetters.get(id);
attila@90 774 }
attila@90 775
attila@90 776 // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object"
attila@90 777 // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is
attila@90 778 // a typical property setter with variable name signature (target, name, value).
attila@90 779 private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(
attila@404 780 privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class,
attila@90 781 LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);
attila@90 782 // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)
attila@90 783 private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
attila@90 784
attila@90 785 @SuppressWarnings("unused")
attila@962 786 private MethodHandle getPropertySetterHandle(final CallSiteDescriptor setterDescriptor, final LinkerServices linkerServices,
attila@962 787 final Object id) {
attila@404 788 return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);
attila@90 789 }
attila@90 790
attila@90 791 private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
attila@963 792 "getDynamicMethod", Object.class, Object.class), 1, Object.class);
attila@90 793 private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
attila@90 794
attila@90 795 @SuppressWarnings("unused")
attila@963 796 // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
attila@963 797 // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
attila@963 798 // "dyn:getMethod" linking).
attila@963 799 private Object getDynamicMethod(final Object name) {
attila@90 800 return getDynamicMethod(String.valueOf(name), methods);
attila@90 801 }
attila@90 802
attila@90 803 /**
attila@90 804 * Returns a dynamic method of the specified name.
attila@90 805 *
attila@90 806 * @param name name of the method
attila@90 807 * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the
attila@90 808 * method with the specified name does not exist.
attila@90 809 */
attila@962 810 DynamicMethod getDynamicMethod(final String name) {
attila@90 811 return getDynamicMethod(name, methods);
attila@90 812 }
attila@90 813
attila@90 814 /**
attila@90 815 * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the
attila@90 816 * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one,
attila@90 817 * creating more stable call sites.
attila@90 818 * @param getter the getter
attila@90 819 * @return getter with same name, declared on the most generic superclass/interface of the declaring class
attila@90 820 */
attila@962 821 private static Method getMostGenericGetter(final Method getter) {
attila@90 822 return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass());
attila@90 823 }
attila@90 824
attila@962 825 private static Method getMostGenericGetter(final String name, final Class<?> returnType, final Class<?> declaringClass) {
attila@90 826 if(declaringClass == null) {
attila@90 827 return null;
attila@90 828 }
attila@90 829 // Prefer interfaces
attila@962 830 for(final Class<?> itf: declaringClass.getInterfaces()) {
attila@90 831 final Method itfGetter = getMostGenericGetter(name, returnType, itf);
attila@90 832 if(itfGetter != null) {
attila@90 833 return itfGetter;
attila@90 834 }
attila@90 835 }
attila@90 836 final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass());
attila@90 837 if(superGetter != null) {
attila@90 838 return superGetter;
attila@90 839 }
attila@90 840 if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
attila@90 841 try {
attila@90 842 return declaringClass.getMethod(name);
attila@962 843 } catch(final NoSuchMethodException e) {
attila@90 844 // Intentionally ignored, meant to fall through
attila@90 845 }
attila@90 846 }
attila@90 847 return null;
attila@90 848 }
attila@90 849
attila@404 850 private static final class AnnotatedDynamicMethod {
attila@404 851 private final SingleDynamicMethod method;
attila@90 852 /*private*/ final ValidationType validationType;
attila@90 853
attila@962 854 AnnotatedDynamicMethod(final SingleDynamicMethod method, final ValidationType validationType) {
attila@404 855 this.method = method;
attila@90 856 this.validationType = validationType;
attila@90 857 }
attila@404 858
attila@962 859 MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
attila@404 860 return method.getInvocation(callSiteDescriptor, linkerServices);
attila@404 861 }
attila@404 862
attila@404 863 @SuppressWarnings("unused")
attila@962 864 MethodHandle getTarget(final MethodHandles.Lookup lookup) {
attila@962 865 final MethodHandle inv = method.getTarget(lookup);
attila@404 866 assert inv != null;
attila@404 867 return inv;
attila@404 868 }
attila@90 869 }
attila@101 870 }

mercurial