AbstractJavaLinker.java revision 1805:7caf1f762f1d
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.NamedOperation; 103import jdk.dynalink.Namespace; 104import jdk.dynalink.NamespaceOperation; 105import jdk.dynalink.Operation; 106import jdk.dynalink.StandardNamespace; 107import jdk.dynalink.StandardOperation; 108import jdk.dynalink.beans.GuardedInvocationComponent.ValidationType; 109import jdk.dynalink.internal.InternalTypeUtilities; 110import jdk.dynalink.linker.GuardedInvocation; 111import jdk.dynalink.linker.GuardingDynamicLinker; 112import jdk.dynalink.linker.LinkRequest; 113import jdk.dynalink.linker.LinkerServices; 114import jdk.dynalink.linker.support.Guards; 115import jdk.dynalink.linker.support.Lookup; 116import jdk.internal.reflect.CallerSensitive; 117 118/** 119 * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property 120 * exposure and method calls for both static and instance facets of a class. 121 */ 122abstract class AbstractJavaLinker implements GuardingDynamicLinker { 123 124 final Class<?> clazz; 125 private final MethodHandle classGuard; 126 private final MethodHandle assignableGuard; 127 private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>(); 128 private final Map<String, DynamicMethod> propertySetters = new HashMap<>(); 129 private final Map<String, DynamicMethod> methods = new HashMap<>(); 130 131 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard) { 132 this(clazz, classGuard, classGuard); 133 } 134 135 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard, final MethodHandle assignableGuard) { 136 this.clazz = clazz; 137 this.classGuard = classGuard; 138 this.assignableGuard = assignableGuard; 139 140 final FacetIntrospector introspector = createFacetIntrospector(); 141 // Add methods and properties 142 for(final Method method: introspector.getMethods()) { 143 final String name = method.getName(); 144 // Add method 145 addMember(name, method, methods); 146 // Add the method as a property getter and/or setter 147 if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) { 148 // Property getter 149 setPropertyGetter(method, 3); 150 } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 && 151 method.getReturnType() == boolean.class) { 152 // Boolean property getter 153 setPropertyGetter(method, 2); 154 } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) { 155 // Property setter 156 addMember(decapitalize(name.substring(3)), method, propertySetters); 157 } 158 } 159 160 // Add field getter/setters as property getters/setters. 161 for(final Field field: introspector.getFields()) { 162 final String name = field.getName(); 163 // Only add a property getter when one is not defined already as a getXxx()/isXxx() method. 164 if(!propertyGetters.containsKey(name)) { 165 setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS); 166 } 167 if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) { 168 addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name), 169 propertySetters); 170 } 171 } 172 173 // Add inner classes, but only those for which we don't hide a property with it 174 for(final Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) { 175 final String name = innerClassSpec.getKey(); 176 if(!propertyGetters.containsKey(name)) { 177 setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS); 178 } 179 } 180 } 181 182 private static String decapitalize(final String str) { 183 assert str != null; 184 if(str.isEmpty()) { 185 return str; 186 } 187 188 final char c0 = str.charAt(0); 189 if(Character.isLowerCase(c0)) { 190 return str; 191 } 192 193 // If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize 194 if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) { 195 return str; 196 } 197 198 final char c[] = str.toCharArray(); 199 c[0] = Character.toLowerCase(c0); 200 return new String(c); 201 } 202 203 abstract FacetIntrospector createFacetIntrospector(); 204 205 Set<String> getReadablePropertyNames() { 206 return getUnmodifiableKeys(propertyGetters); 207 } 208 209 Set<String> getWritablePropertyNames() { 210 return getUnmodifiableKeys(propertySetters); 211 } 212 213 Set<String> getMethodNames() { 214 return getUnmodifiableKeys(methods); 215 } 216 217 private static Set<String> getUnmodifiableKeys(final Map<String, ?> m) { 218 return Collections.unmodifiableSet(m.keySet()); 219 } 220 221 /** 222 * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only 223 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties 224 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)} 225 * instead. 226 * @param name name of the property 227 * @param handle the method handle that implements the property getter 228 * @param validationType the validation type for the property 229 */ 230 private void setPropertyGetter(final String name, final SingleDynamicMethod handle, final ValidationType validationType) { 231 propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType)); 232 } 233 234 /** 235 * Sets the specified reflective method to be the property getter for the specified property. 236 * @param getter the getter method 237 * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for 238 * names starting with "is". 239 */ 240 private void setPropertyGetter(final Method getter, final int prefixLen) { 241 setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod( 242 getMostGenericGetter(getter)), ValidationType.INSTANCE_OF); 243 } 244 245 /** 246 * Sets the specified method handle to be the property getter for the specified property. Note that you can only 247 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties 248 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)} 249 * instead. 250 * @param name name of the property 251 * @param handle the method handle that implements the property getter 252 * @param validationType the validation type for the property 253 */ 254 void setPropertyGetter(final String name, final MethodHandle handle, final ValidationType validationType) { 255 setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType); 256 } 257 258 private void addMember(final String name, final AccessibleObject ao, final Map<String, DynamicMethod> methodMap) { 259 addMember(name, createDynamicMethod(ao), methodMap); 260 } 261 262 private void addMember(final String name, final SingleDynamicMethod method, final Map<String, DynamicMethod> methodMap) { 263 final DynamicMethod existingMethod = methodMap.get(name); 264 final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name); 265 if(newMethod != existingMethod) { 266 methodMap.put(name, newMethod); 267 } 268 } 269 270 /** 271 * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The 272 * methods should represent all overloads of the same name (or all constructors of the class). 273 * @param members the reflective members 274 * @param clazz the class declaring the reflective members 275 * @param name the common name of the reflective members. 276 * @return a dynamic method representing all the specified reflective members. 277 */ 278 static DynamicMethod createDynamicMethod(final Iterable<? extends AccessibleObject> members, final Class<?> clazz, final String name) { 279 DynamicMethod dynMethod = null; 280 for(final AccessibleObject method: members) { 281 dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name); 282 } 283 return dynMethod; 284 } 285 286 /** 287 * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will 288 * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive 289 * dynamic method when needed. 290 * @param m the reflective member 291 * @return the single dynamic method representing the reflective member 292 */ 293 private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) { 294 if (m.isAnnotationPresent(CallerSensitive.class)) { 295 // Method has @CallerSensitive annotation 296 return new CallerSensitiveDynamicMethod(m); 297 } 298 // Method has no @CallerSensitive annotation 299 final MethodHandle mh; 300 try { 301 mh = unreflectSafely(m); 302 } catch (final IllegalAccessError e) { 303 // java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't 304 // marked with the annotation. In this case, we'll fall back to treating it as caller sensitive. 305 return new CallerSensitiveDynamicMethod(m); 306 } 307 // Proceed with non-caller sensitive 308 final Member member = (Member)m; 309 return new SimpleDynamicMethod(mh, member.getDeclaringClass(), member.getName(), m instanceof Constructor); 310 } 311 312 /** 313 * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be 314 * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were 315 * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege 316 * unreflector as its caller, and thus completely useless. 317 * @param m the method or constructor 318 * @return the method handle 319 */ 320 private static MethodHandle unreflectSafely(final AccessibleObject m) { 321 if(m instanceof Method) { 322 final Method reflMethod = (Method)m; 323 final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod); 324 if(Modifier.isStatic(reflMethod.getModifiers())) { 325 return StaticClassIntrospector.editStaticMethodHandle(handle); 326 } 327 return handle; 328 } 329 return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m)); 330 } 331 332 private static DynamicMethod mergeMethods(final SingleDynamicMethod method, final DynamicMethod existing, final Class<?> clazz, final String name) { 333 if(existing == null) { 334 return method; 335 } else if(existing.contains(method)) { 336 return existing; 337 } else if(existing instanceof SingleDynamicMethod) { 338 final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name); 339 odm.addMethod(((SingleDynamicMethod)existing)); 340 odm.addMethod(method); 341 return odm; 342 } else if(existing instanceof OverloadedDynamicMethod) { 343 ((OverloadedDynamicMethod)existing).addMethod(method); 344 return existing; 345 } 346 throw new AssertionError(); 347 } 348 349 @Override 350 public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) 351 throws Exception { 352 final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor(); 353 354 final MissingMemberHandlerFactory missingMemberHandlerFactory; 355 final LinkerServices directLinkerServices; 356 if (linkerServices instanceof LinkerServicesWithMissingMemberHandlerFactory) { 357 final LinkerServicesWithMissingMemberHandlerFactory lswmmhf = ((LinkerServicesWithMissingMemberHandlerFactory)linkerServices); 358 missingMemberHandlerFactory = lswmmhf.missingMemberHandlerFactory; 359 directLinkerServices = lswmmhf.linkerServices; 360 } else { 361 missingMemberHandlerFactory = null; 362 directLinkerServices = linkerServices; 363 } 364 365 final GuardedInvocationComponent gic = getGuardedInvocationComponent( 366 new ComponentLinkRequest(request, directLinkerServices, 367 missingMemberHandlerFactory)); 368 return gic != null ? gic.getGuardedInvocation() : null; 369 } 370 371 static final class ComponentLinkRequest { 372 final LinkRequest linkRequest; 373 final LinkerServices linkerServices; 374 final MissingMemberHandlerFactory missingMemberHandlerFactory; 375 final Operation baseOperation; 376 final List<Namespace> namespaces; 377 final Object name; 378 379 ComponentLinkRequest(final LinkRequest linkRequest, 380 final LinkerServices linkerServices, 381 final MissingMemberHandlerFactory missingMemberHandlerFactory) { 382 this.linkRequest = linkRequest; 383 this.linkerServices = linkerServices; 384 this.missingMemberHandlerFactory = missingMemberHandlerFactory; 385 final Operation namedOp = linkRequest.getCallSiteDescriptor().getOperation(); 386 this.name = NamedOperation.getName(namedOp); 387 final Operation namespaceOp = NamedOperation.getBaseOperation(namedOp); 388 this.baseOperation = NamespaceOperation.getBaseOperation(namespaceOp); 389 this.namespaces = Arrays.asList(NamespaceOperation.getNamespaces(namespaceOp)); 390 } 391 392 private ComponentLinkRequest(final LinkRequest linkRequest, 393 final LinkerServices linkerServices, 394 final MissingMemberHandlerFactory missingMemberHandlerFactory, 395 final Operation baseOperation, final List<Namespace> namespaces, final Object name) { 396 this.linkRequest = linkRequest; 397 this.linkerServices = linkerServices; 398 this.missingMemberHandlerFactory = missingMemberHandlerFactory; 399 this.baseOperation = baseOperation; 400 this.namespaces = namespaces; 401 this.name = name; 402 } 403 404 CallSiteDescriptor getDescriptor() { 405 return linkRequest.getCallSiteDescriptor(); 406 } 407 408 ComponentLinkRequest popNamespace() { 409 return new ComponentLinkRequest(linkRequest, linkerServices, 410 missingMemberHandlerFactory, baseOperation, 411 namespaces.subList(1, namespaces.size()), name); 412 } 413 } 414 415 protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req) 416 throws Exception { 417 if (!req.namespaces.isEmpty()) { 418 final Namespace ns = req.namespaces.get(0); 419 final Operation op = req.baseOperation; 420 if (op == StandardOperation.GET) { 421 if (ns == StandardNamespace.PROPERTY) { 422 return getPropertyGetter(req.popNamespace()); 423 } else if (ns == StandardNamespace.METHOD) { 424 return getMethodGetter(req.popNamespace()); 425 } 426 } else if (op == StandardOperation.SET && ns == StandardNamespace.PROPERTY) { 427 return getPropertySetter(req.popNamespace()); 428 } 429 } 430 return null; 431 } 432 433 GuardedInvocationComponent getNextComponent(final ComponentLinkRequest req) throws Exception { 434 if (req.namespaces.isEmpty()) { 435 return createNoSuchMemberHandler(req.missingMemberHandlerFactory, 436 req.linkRequest, req.linkerServices); 437 } 438 final GuardedInvocationComponent gic = getGuardedInvocationComponent(req); 439 if (gic != null) { 440 return gic; 441 } 442 return getNextComponent(req.popNamespace()); 443 } 444 445 private GuardedInvocationComponent createNoSuchMemberHandler( 446 final MissingMemberHandlerFactory missingMemberHandlerFactory, 447 final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { 448 if (missingMemberHandlerFactory == null) { 449 return null; 450 } 451 final MethodHandle handler = missingMemberHandlerFactory.createMissingMemberHandler(linkRequest, linkerServices); 452 if (handler == null) { 453 return null; 454 } 455 final MethodType type = linkRequest.getCallSiteDescriptor().getMethodType(); 456 // The returned handler is allowed to differ in return type. 457 assert handler.type().changeReturnType(type.returnType()).equals(type); 458 return getClassGuardedInvocationComponent(handler, type); 459 } 460 461 static final <T> List<T> pop(final List<T> l) { 462 return l.subList(1, l.size()); 463 } 464 465 MethodHandle getClassGuard(final CallSiteDescriptor desc) { 466 return getClassGuard(desc.getMethodType()); 467 } 468 469 MethodHandle getClassGuard(final MethodType type) { 470 return Guards.asType(classGuard, type); 471 } 472 473 GuardedInvocationComponent getClassGuardedInvocationComponent(final MethodHandle invocation, final MethodType type) { 474 return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); 475 } 476 477 abstract SingleDynamicMethod getConstructorMethod(final String signature); 478 479 private MethodHandle getAssignableGuard(final MethodType type) { 480 return Guards.asType(assignableGuard, type); 481 } 482 483 private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor, 484 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){ 485 final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap); 486 return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType())); 487 } 488 489 private MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor, 490 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) { 491 final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap); 492 return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null; 493 } 494 495 private DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) { 496 final DynamicMethod dynaMethod = methodMap.get(methodName); 497 return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap); 498 } 499 500 private SingleDynamicMethod getExplicitSignatureDynamicMethod(final String fullName, 501 final Map<String, DynamicMethod> methodsMap) { 502 // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name 503 // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method 504 // resolution works correctly in almost every situation. However, in presence of many language-specific 505 // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected 506 // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload 507 // for performance reasons. 508 509 // Is the method name lexically of the form "name(types)"? 510 final int lastChar = fullName.length() - 1; 511 if(fullName.charAt(lastChar) != ')') { 512 return null; 513 } 514 final int openBrace = fullName.indexOf('('); 515 if(openBrace == -1) { 516 return null; 517 } 518 519 final String name = fullName.substring(0, openBrace); 520 final String signature = fullName.substring(openBrace + 1, lastChar); 521 522 // Find an existing method for the "name" part 523 final DynamicMethod simpleNamedMethod = methodsMap.get(name); 524 if(simpleNamedMethod == null) { 525 // explicit signature constructor access 526 // Java.type("java.awt.Color")["(int,int,int)"] 527 // will get Color(int,int,int) constructor of Color class. 528 if (name.isEmpty()) { 529 return getConstructorMethod(signature); 530 } 531 532 return null; 533 } 534 535 // Try to get a narrowed dynamic method for the explicit parameter types. 536 return simpleNamedMethod.getMethodForExactParamTypes(signature); 537 } 538 539 private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType( 540 boolean.class, MethodHandle.class)); 541 private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments( 542 MethodHandles.constant(Object.class, null), 0, MethodHandle.class); 543 544 private GuardedInvocationComponent getPropertySetter(final ComponentLinkRequest req) throws Exception { 545 if (req.name == null) { 546 return getUnnamedPropertySetter(req); 547 } 548 return getNamedPropertySetter(req); 549 } 550 551 private GuardedInvocationComponent getUnnamedPropertySetter(final ComponentLinkRequest req) throws Exception { 552 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); 553 // Must have three arguments: target object, property name, and property value. 554 assertParameterCount(callSiteDescriptor, 3); 555 556 // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be 557 // valid for us to convert return values proactively. Also, since we don't know what setters will be 558 // invoked, we'll conservatively presume Object return type. The one exception is void return. 559 final MethodType origType = callSiteDescriptor.getMethodType(); 560 final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class); 561 final LinkerServices linkerServices = req.linkerServices; 562 563 // What's below is basically: 564 // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation), 565 // get_setter_handle(type, linkerServices)) 566 // only with a bunch of method signature adjustments. Basically, retrieve method setter 567 // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next 568 // component's invocation. 569 570 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll 571 // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using 572 // Object return type). 573 final MethodType setterType = type.dropParameterTypes(1, 2); 574 // Bind property setter handle to the expected setter type and linker services. Type is 575 // MethodHandle(Object, String, Object) 576 final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0, 577 callSiteDescriptor.changeMethodType(setterType), linkerServices); 578 579 // Cast getter to MethodHandle(O, N, V) 580 final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType( 581 MethodHandle.class)); 582 583 // Handle to invoke the setter R(MethodHandle, O, V) 584 final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType); 585 // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V) 586 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType( 587 1)); 588 final GuardedInvocationComponent nextComponent = getNextComponent(req); 589 590 final MethodHandle fallbackFolded; 591 if (nextComponent == null) { 592 // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null 593 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1, 594 type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class)); 595 } else { 596 // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the 597 // extra argument resulting from fold 598 fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(), 599 0, MethodHandle.class); 600 } 601 602 // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V)) 603 final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( 604 IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); 605 if(nextComponent == null) { 606 return getClassGuardedInvocationComponent(compositeSetter, type); 607 } 608 return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); 609 } 610 611 private GuardedInvocationComponent getNamedPropertySetter(final ComponentLinkRequest req) throws Exception { 612 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); 613 // Must have two arguments: target object and property value 614 assertParameterCount(callSiteDescriptor, 2); 615 final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, req.linkerServices, 616 req.name.toString(), propertySetters); 617 // If we have a property setter with this name, this composite operation will always stop here 618 if(gi != null) { 619 return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS); 620 } 621 // If we don't have a property setter with this name, always fall back to the next namespace (if any). 622 return getNextComponent(req); 623 } 624 625 private static final Lookup privateLookup = new Lookup(MethodHandles.lookup()); 626 627 private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType( 628 boolean.class, AnnotatedDynamicMethod.class)); 629 private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments( 630 MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class); 631 private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class, 632 "getTarget", MethodType.methodType(MethodHandle.class, CallSiteDescriptor.class, LinkerServices.class)); 633 private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class)); 634 635 private GuardedInvocationComponent getPropertyGetter(final ComponentLinkRequest req) throws Exception { 636 if (req.name == null) { 637 return getUnnamedPropertyGetter(req); 638 } 639 return getNamedPropertyGetter(req); 640 } 641 642 private GuardedInvocationComponent getUnnamedPropertyGetter(final ComponentLinkRequest req) throws Exception { 643 // Since we can't know what kind of a getter we'll get back on different invocations, we'll just 644 // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking 645 // runtime might not allow coercing at that call site. 646 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); 647 final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class); 648 // Must have exactly two arguments: receiver and name 649 assertParameterCount(callSiteDescriptor, 2); 650 651 // What's below is basically: 652 // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle) 653 // only with a bunch of method signature adjustments. Basically, retrieve method getter 654 // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null, 655 // or delegate to next component's invocation. 656 657 final LinkerServices linkerServices = req.linkerServices; 658 final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType( 659 AnnotatedDynamicMethod.class)); 660 final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments( 661 GET_ANNOTATED_METHOD, 1, callSiteDescriptor, linkerServices); 662 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0, 663 callSiteBoundMethodGetter); 664 // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0) 665 final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker, 666 MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0))); 667 // Since it's in the target of a fold, drop the unnecessary second argument 668 // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1) 669 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2, 670 type.parameterType(1)); 671 final GuardedInvocationComponent nextComponent = getNextComponent(req); 672 673 final MethodHandle fallbackFolded; 674 if(nextComponent == null) { 675 // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null 676 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1, 677 type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class)); 678 } else { 679 // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to 680 // drop the extra argument resulting from fold and to change its return type to Object. 681 final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation(); 682 final MethodType nextType = nextInvocation.type(); 683 fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType( 684 nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class); 685 } 686 687 // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1)) 688 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( 689 IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); 690 if(nextComponent == null) { 691 return getClassGuardedInvocationComponent(compositeGetter, type); 692 } 693 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); 694 } 695 696 private GuardedInvocationComponent getNamedPropertyGetter(final ComponentLinkRequest req) throws Exception { 697 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); 698 // Must have exactly one argument: receiver 699 assertParameterCount(callSiteDescriptor, 1); 700 // Fixed name 701 final AnnotatedDynamicMethod annGetter = propertyGetters.get(req.name.toString()); 702 if(annGetter == null) { 703 // We have no such property, always delegate to the next component operation 704 return getNextComponent(req); 705 } 706 final MethodHandle getter = annGetter.getInvocation(req); 707 // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being 708 // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the 709 // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If 710 // we're linking against a field getter, don't make the assumption. 711 // NOTE: No delegation to the next component operation if we have a property with this name, even if its 712 // value is null. 713 final ValidationType validationType = annGetter.validationType; 714 // TODO: we aren't using the type that declares the most generic getter here! 715 return new GuardedInvocationComponent(getter, getGuard(validationType, 716 callSiteDescriptor.getMethodType()), clazz, validationType); 717 } 718 719 private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) { 720 switch(validationType) { 721 case EXACT_CLASS: { 722 return getClassGuard(methodType); 723 } 724 case INSTANCE_OF: { 725 return getAssignableGuard(methodType); 726 } 727 case IS_ARRAY: { 728 return Guards.isArray(0, methodType); 729 } 730 case NONE: { 731 return null; 732 } 733 default: { 734 throw new AssertionError(); 735 } 736 } 737 } 738 739 private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class, 740 MethodType.methodType(boolean.class, Object.class)); 741 private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class); 742 743 private GuardedInvocationComponent getMethodGetter(final ComponentLinkRequest req) throws Exception { 744 if (req.name == null) { 745 return getUnnamedMethodGetter(req); 746 } 747 return getNamedMethodGetter(req); 748 } 749 750 private static MethodType getMethodGetterType(final ComponentLinkRequest req) { 751 // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to 752 // be visible outside of this linker, declare it to return Object. 753 return req.getDescriptor().getMethodType().changeReturnType(Object.class); 754 } 755 756 private GuardedInvocationComponent getUnnamedMethodGetter(final ComponentLinkRequest req) throws Exception { 757 // Must have exactly two arguments: receiver and name 758 assertParameterCount(req.getDescriptor(), 2); 759 final GuardedInvocationComponent nextComponent = getNextComponent(req); 760 final LinkerServices linkerServices = req.linkerServices; 761 final MethodType type = getMethodGetterType(req); 762 if(nextComponent == null) { 763 // No next component operation; just return a component for this operation. 764 return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type); 765 } 766 767 // What's below is basically: 768 // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a 769 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null 770 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation. 771 772 final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type); 773 // Since it is part of the foldArgument() target, it will have extra args that we need to drop. 774 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments( 775 OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class)); 776 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation(); 777 // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the 778 // return type. 779 assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type); 780 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives. 781 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0, 782 Object.class); 783 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get) 784 // Note that nextCombinedInvocation needs to have its return type changed to Object 785 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( 786 IS_DYNAMIC_METHOD, returnMethodHandle, 787 nextCombinedInvocation.asType(nextCombinedInvocation.type().changeReturnType(Object.class))), 788 typedGetter); 789 790 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); 791 } 792 793 private GuardedInvocationComponent getNamedMethodGetter(final ComponentLinkRequest req) 794 throws Exception { 795 // Must have exactly one argument: receiver 796 assertParameterCount(req.getDescriptor(), 1); 797 final DynamicMethod method = getDynamicMethod(req.name.toString()); 798 if(method == null) { 799 // We have no such method, always delegate to the next component 800 return getNextComponent(req); 801 } 802 // No delegation to the next namespace; if we have a method with that name, we'll always return it at 803 // this point. 804 final MethodType type = getMethodGetterType(req); 805 return getClassGuardedInvocationComponent(req.linkerServices.asType(MethodHandles.dropArguments( 806 MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type); 807 } 808 809 static class MethodPair { 810 final MethodHandle method1; 811 final MethodHandle method2; 812 813 MethodPair(final MethodHandle method1, final MethodHandle method2) { 814 this.method1 = method1; 815 this.method2 = method2; 816 } 817 818 MethodHandle guardWithTest(final MethodHandle test) { 819 return MethodHandles.guardWithTest(test, method1, method2); 820 } 821 } 822 823 static MethodPair matchReturnTypes(final MethodHandle m1, final MethodHandle m2) { 824 final MethodType type1 = m1.type(); 825 final MethodType type2 = m2.type(); 826 final Class<?> commonRetType = InternalTypeUtilities.getCommonLosslessConversionType(type1.returnType(), 827 type2.returnType()); 828 return new MethodPair( 829 m1.asType(type1.changeReturnType(commonRetType)), 830 m2.asType(type2.changeReturnType(commonRetType))); 831 } 832 833 private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) { 834 if(descriptor.getMethodType().parameterCount() != paramCount) { 835 throw new BootstrapMethodError(descriptor.getOperation() + " must have exactly " + paramCount + " parameters."); 836 } 837 } 838 839 private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial( 840 "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class); 841 private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this); 842 843 /** 844 * @param id the property ID 845 * @return the method handle for retrieving the property, or null if the property does not exist 846 */ 847 @SuppressWarnings("unused") 848 private Object getPropertyGetterHandle(final Object id) { 849 return propertyGetters.get(String.valueOf(id)); 850 } 851 852 // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object" 853 // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is 854 // a typical property setter with variable name signature (target, name, value). 855 private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments( 856 privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class, 857 LinkerServices.class, Object.class), 3, Object.class), 5, Object.class); 858 // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object) 859 private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this); 860 861 @SuppressWarnings("unused") 862 private MethodHandle getPropertySetterHandle(final CallSiteDescriptor setterDescriptor, final LinkerServices linkerServices, 863 final Object id) { 864 return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters); 865 } 866 867 private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial( 868 "getDynamicMethod", Object.class, Object.class), 1, Object.class); 869 private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this); 870 871 @SuppressWarnings("unused") 872 // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't 873 // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for 874 // GET:METHOD linking). 875 private Object getDynamicMethod(final Object name) { 876 return getDynamicMethod(String.valueOf(name), methods); 877 } 878 879 /** 880 * Returns a dynamic method of the specified name. 881 * 882 * @param name name of the method 883 * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the 884 * method with the specified name does not exist. 885 */ 886 DynamicMethod getDynamicMethod(final String name) { 887 return getDynamicMethod(name, methods); 888 } 889 890 /** 891 * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the 892 * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one, 893 * creating more stable call sites. 894 * @param getter the getter 895 * @return getter with same name, declared on the most generic superclass/interface of the declaring class 896 */ 897 private static Method getMostGenericGetter(final Method getter) { 898 return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass()); 899 } 900 901 private static Method getMostGenericGetter(final String name, final Class<?> returnType, final Class<?> declaringClass) { 902 if(declaringClass == null) { 903 return null; 904 } 905 // Prefer interfaces 906 for(final Class<?> itf: declaringClass.getInterfaces()) { 907 final Method itfGetter = getMostGenericGetter(name, returnType, itf); 908 if(itfGetter != null) { 909 return itfGetter; 910 } 911 } 912 final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass()); 913 if(superGetter != null) { 914 return superGetter; 915 } 916 if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) { 917 try { 918 return declaringClass.getMethod(name); 919 } catch(final NoSuchMethodException e) { 920 // Intentionally ignored, meant to fall through 921 } 922 } 923 return null; 924 } 925 926 private static final class AnnotatedDynamicMethod { 927 private final SingleDynamicMethod method; 928 /*private*/ final ValidationType validationType; 929 930 AnnotatedDynamicMethod(final SingleDynamicMethod method, final ValidationType validationType) { 931 this.method = method; 932 this.validationType = validationType; 933 } 934 935 MethodHandle getInvocation(final ComponentLinkRequest req) { 936 return method.getInvocation(req.getDescriptor(), req.linkerServices); 937 } 938 939 @SuppressWarnings("unused") 940 MethodHandle getTarget(final CallSiteDescriptor desc, final LinkerServices linkerServices) { 941 final MethodHandle inv = linkerServices.filterInternalObjects(method.getTarget(desc)); 942 assert inv != null; 943 return inv; 944 } 945 } 946} 947