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