1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Apr 27 01:36:41 2016 +0800 1.3 @@ -0,0 +1,817 @@ 1.4 +/* 1.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +/* 1.30 + * This file is available under and governed by the GNU General Public 1.31 + * License version 2 only, as published by the Free Software Foundation. 1.32 + * However, the following notice accompanied the original version of this 1.33 + * file, and Oracle licenses the original version of this file under the BSD 1.34 + * license: 1.35 + */ 1.36 +/* 1.37 + Copyright 2009-2013 Attila Szegedi 1.38 + 1.39 + Licensed under both the Apache License, Version 2.0 (the "Apache License") 1.40 + and the BSD License (the "BSD License"), with licensee being free to 1.41 + choose either of the two at their discretion. 1.42 + 1.43 + You may not use this file except in compliance with either the Apache 1.44 + License or the BSD License. 1.45 + 1.46 + If you choose to use this file in compliance with the Apache License, the 1.47 + following notice applies to you: 1.48 + 1.49 + You may obtain a copy of the Apache License at 1.50 + 1.51 + http://www.apache.org/licenses/LICENSE-2.0 1.52 + 1.53 + Unless required by applicable law or agreed to in writing, software 1.54 + distributed under the License is distributed on an "AS IS" BASIS, 1.55 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 1.56 + implied. See the License for the specific language governing 1.57 + permissions and limitations under the License. 1.58 + 1.59 + If you choose to use this file in compliance with the BSD License, the 1.60 + following notice applies to you: 1.61 + 1.62 + Redistribution and use in source and binary forms, with or without 1.63 + modification, are permitted provided that the following conditions are 1.64 + met: 1.65 + * Redistributions of source code must retain the above copyright 1.66 + notice, this list of conditions and the following disclaimer. 1.67 + * Redistributions in binary form must reproduce the above copyright 1.68 + notice, this list of conditions and the following disclaimer in the 1.69 + documentation and/or other materials provided with the distribution. 1.70 + * Neither the name of the copyright holder nor the names of 1.71 + contributors may be used to endorse or promote products derived from 1.72 + this software without specific prior written permission. 1.73 + 1.74 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 1.75 + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1.76 + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 1.77 + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER 1.78 + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.79 + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.80 + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 1.81 + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 1.82 + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 1.83 + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 1.84 + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.85 +*/ 1.86 + 1.87 +package jdk.internal.dynalink.beans; 1.88 + 1.89 +import java.lang.invoke.MethodHandle; 1.90 +import java.lang.invoke.MethodHandles; 1.91 +import java.lang.invoke.MethodType; 1.92 +import java.lang.reflect.AccessibleObject; 1.93 +import java.lang.reflect.Constructor; 1.94 +import java.lang.reflect.Field; 1.95 +import java.lang.reflect.Member; 1.96 +import java.lang.reflect.Method; 1.97 +import java.lang.reflect.Modifier; 1.98 +import java.util.Collection; 1.99 +import java.util.Collections; 1.100 +import java.util.HashMap; 1.101 +import java.util.List; 1.102 +import java.util.Map; 1.103 + 1.104 +import jdk.internal.dynalink.CallSiteDescriptor; 1.105 +import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType; 1.106 +import jdk.internal.dynalink.linker.GuardedInvocation; 1.107 +import jdk.internal.dynalink.linker.GuardingDynamicLinker; 1.108 +import jdk.internal.dynalink.linker.LinkRequest; 1.109 +import jdk.internal.dynalink.linker.LinkerServices; 1.110 +import jdk.internal.dynalink.support.CallSiteDescriptorFactory; 1.111 +import jdk.internal.dynalink.support.Guards; 1.112 +import jdk.internal.dynalink.support.Lookup; 1.113 + 1.114 +/** 1.115 + * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property 1.116 + * exposure and method calls for both static and instance facets of a class. 1.117 + * 1.118 + * @author Attila Szegedi 1.119 + */ 1.120 +abstract class AbstractJavaLinker implements GuardingDynamicLinker { 1.121 + 1.122 + final Class<?> clazz; 1.123 + private final MethodHandle classGuard; 1.124 + private final MethodHandle assignableGuard; 1.125 + private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>(); 1.126 + private final Map<String, DynamicMethod> propertySetters = new HashMap<>(); 1.127 + private final Map<String, DynamicMethod> methods = new HashMap<>(); 1.128 + 1.129 + AbstractJavaLinker(Class<?> clazz, MethodHandle classGuard) { 1.130 + this(clazz, classGuard, classGuard); 1.131 + } 1.132 + 1.133 + AbstractJavaLinker(Class<?> clazz, MethodHandle classGuard, MethodHandle assignableGuard) { 1.134 + this.clazz = clazz; 1.135 + this.classGuard = classGuard; 1.136 + this.assignableGuard = assignableGuard; 1.137 + 1.138 + final FacetIntrospector introspector = createFacetIntrospector(); 1.139 + // Add methods and properties 1.140 + for(Method method: introspector.getMethods()) { 1.141 + final String name = method.getName(); 1.142 + // Add method 1.143 + addMember(name, method, methods); 1.144 + // Add the method as a property getter and/or setter 1.145 + if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) { 1.146 + // Property getter 1.147 + setPropertyGetter(method, 3); 1.148 + } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 && 1.149 + method.getReturnType() == boolean.class) { 1.150 + // Boolean property getter 1.151 + setPropertyGetter(method, 2); 1.152 + } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) { 1.153 + // Property setter 1.154 + addMember(decapitalize(name.substring(3)), method, propertySetters); 1.155 + } 1.156 + } 1.157 + 1.158 + // Add field getter/setters as property getters/setters. 1.159 + for(Field field: introspector.getFields()) { 1.160 + final String name = field.getName(); 1.161 + // Only add a property getter when one is not defined already as a getXxx()/isXxx() method. 1.162 + if(!propertyGetters.containsKey(name)) { 1.163 + setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS); 1.164 + } 1.165 + if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) { 1.166 + addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name), 1.167 + propertySetters); 1.168 + } 1.169 + } 1.170 + 1.171 + // Add inner classes, but only those for which we don't hide a property with it 1.172 + for(Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) { 1.173 + final String name = innerClassSpec.getKey(); 1.174 + if(!propertyGetters.containsKey(name)) { 1.175 + setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS); 1.176 + } 1.177 + } 1.178 + } 1.179 + 1.180 + private static String decapitalize(String str) { 1.181 + assert str != null; 1.182 + if(str.isEmpty()) { 1.183 + return str; 1.184 + } 1.185 + 1.186 + final char c0 = str.charAt(0); 1.187 + if(Character.isLowerCase(c0)) { 1.188 + return str; 1.189 + } 1.190 + 1.191 + // If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize 1.192 + if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) { 1.193 + return str; 1.194 + } 1.195 + 1.196 + final char c[] = str.toCharArray(); 1.197 + c[0] = Character.toLowerCase(c0); 1.198 + return new String(c); 1.199 + } 1.200 + 1.201 + abstract FacetIntrospector createFacetIntrospector(); 1.202 + 1.203 + Collection<String> getReadablePropertyNames() { 1.204 + return getUnmodifiableKeys(propertyGetters); 1.205 + } 1.206 + 1.207 + Collection<String> getWritablePropertyNames() { 1.208 + return getUnmodifiableKeys(propertySetters); 1.209 + } 1.210 + 1.211 + Collection<String> getMethodNames() { 1.212 + return getUnmodifiableKeys(methods); 1.213 + } 1.214 + 1.215 + private static Collection<String> getUnmodifiableKeys(Map<String, ?> m) { 1.216 + return Collections.unmodifiableCollection(m.keySet()); 1.217 + } 1.218 + 1.219 + /** 1.220 + * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only 1.221 + * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties 1.222 + * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)} 1.223 + * instead. 1.224 + * @param name name of the property 1.225 + * @param handle the method handle that implements the property getter 1.226 + * @param validationType the validation type for the property 1.227 + */ 1.228 + private void setPropertyGetter(String name, SingleDynamicMethod handle, ValidationType validationType) { 1.229 + propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType)); 1.230 + } 1.231 + 1.232 + /** 1.233 + * Sets the specified reflective method to be the property getter for the specified property. 1.234 + * @param getter the getter method 1.235 + * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for 1.236 + * names starting with "is". 1.237 + */ 1.238 + private void setPropertyGetter(Method getter, int prefixLen) { 1.239 + setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod( 1.240 + getMostGenericGetter(getter)), ValidationType.INSTANCE_OF); 1.241 + } 1.242 + 1.243 + /** 1.244 + * Sets the specified method handle to be the property getter for the specified property. Note that you can only 1.245 + * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties 1.246 + * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)} 1.247 + * instead. 1.248 + * @param name name of the property 1.249 + * @param handle the method handle that implements the property getter 1.250 + * @param validationType the validation type for the property 1.251 + */ 1.252 + void setPropertyGetter(String name, MethodHandle handle, ValidationType validationType) { 1.253 + setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType); 1.254 + } 1.255 + 1.256 + private void addMember(String name, AccessibleObject ao, Map<String, DynamicMethod> methodMap) { 1.257 + addMember(name, createDynamicMethod(ao), methodMap); 1.258 + } 1.259 + 1.260 + private void addMember(String name, SingleDynamicMethod method, Map<String, DynamicMethod> methodMap) { 1.261 + final DynamicMethod existingMethod = methodMap.get(name); 1.262 + final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name); 1.263 + if(newMethod != existingMethod) { 1.264 + methodMap.put(name, newMethod); 1.265 + } 1.266 + } 1.267 + 1.268 + /** 1.269 + * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The 1.270 + * methods should represent all overloads of the same name (or all constructors of the class). 1.271 + * @param members the reflective members 1.272 + * @param clazz the class declaring the reflective members 1.273 + * @param name the common name of the reflective members. 1.274 + * @return a dynamic method representing all the specified reflective members. 1.275 + */ 1.276 + static DynamicMethod createDynamicMethod(Iterable<? extends AccessibleObject> members, Class<?> clazz, String name) { 1.277 + DynamicMethod dynMethod = null; 1.278 + for(AccessibleObject method: members) { 1.279 + dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name); 1.280 + } 1.281 + return dynMethod; 1.282 + } 1.283 + 1.284 + /** 1.285 + * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will 1.286 + * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive 1.287 + * dynamic method when needed. 1.288 + * @param m the reflective member 1.289 + * @return the single dynamic method representing the reflective member 1.290 + */ 1.291 + private static SingleDynamicMethod createDynamicMethod(AccessibleObject m) { 1.292 + if(CallerSensitiveDetector.isCallerSensitive(m)) { 1.293 + return new CallerSensitiveDynamicMethod(m); 1.294 + } 1.295 + final Member member = (Member)m; 1.296 + return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName()); 1.297 + } 1.298 + 1.299 + /** 1.300 + * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be 1.301 + * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were 1.302 + * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege 1.303 + * unreflector as its caller, and thus completely useless. 1.304 + * @param m the method or constructor 1.305 + * @return the method handle 1.306 + */ 1.307 + private static MethodHandle unreflectSafely(AccessibleObject m) { 1.308 + if(m instanceof Method) { 1.309 + final Method reflMethod = (Method)m; 1.310 + final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod); 1.311 + if(Modifier.isStatic(reflMethod.getModifiers())) { 1.312 + return StaticClassIntrospector.editStaticMethodHandle(handle); 1.313 + } 1.314 + return handle; 1.315 + } 1.316 + return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m)); 1.317 + } 1.318 + 1.319 + private static DynamicMethod mergeMethods(SingleDynamicMethod method, DynamicMethod existing, Class<?> clazz, String name) { 1.320 + if(existing == null) { 1.321 + return method; 1.322 + } else if(existing.contains(method)) { 1.323 + return existing; 1.324 + } else if(existing instanceof SingleDynamicMethod) { 1.325 + final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name); 1.326 + odm.addMethod(((SingleDynamicMethod)existing)); 1.327 + odm.addMethod(method); 1.328 + return odm; 1.329 + } else if(existing instanceof OverloadedDynamicMethod) { 1.330 + ((OverloadedDynamicMethod)existing).addMethod(method); 1.331 + return existing; 1.332 + } 1.333 + throw new AssertionError(); 1.334 + } 1.335 + 1.336 + @Override 1.337 + public GuardedInvocation getGuardedInvocation(LinkRequest request, final LinkerServices linkerServices) 1.338 + throws Exception { 1.339 + final LinkRequest ncrequest = request.withoutRuntimeContext(); 1.340 + // BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn". 1.341 + final CallSiteDescriptor callSiteDescriptor = ncrequest.getCallSiteDescriptor(); 1.342 + final String op = callSiteDescriptor.getNameToken(CallSiteDescriptor.OPERATOR); 1.343 + // Either dyn:callMethod:name(this[,args]) or dyn:callMethod(this,name[,args]). 1.344 + if("callMethod" == op) { 1.345 + return getCallPropWithThis(callSiteDescriptor, linkerServices); 1.346 + } 1.347 + List<String> operations = CallSiteDescriptorFactory.tokenizeOperators(callSiteDescriptor); 1.348 + while(!operations.isEmpty()) { 1.349 + final GuardedInvocationComponent gic = getGuardedInvocationComponent(callSiteDescriptor, linkerServices, 1.350 + operations); 1.351 + if(gic != null) { 1.352 + return gic.getGuardedInvocation(); 1.353 + } 1.354 + operations = pop(operations); 1.355 + } 1.356 + return null; 1.357 + } 1.358 + 1.359 + protected GuardedInvocationComponent getGuardedInvocationComponent(CallSiteDescriptor callSiteDescriptor, 1.360 + LinkerServices linkerServices, List<String> operations) throws Exception { 1.361 + if(operations.isEmpty()) { 1.362 + return null; 1.363 + } 1.364 + final String op = operations.get(0); 1.365 + // Either dyn:getProp:name(this) or dyn:getProp(this, name) 1.366 + if("getProp".equals(op)) { 1.367 + return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations)); 1.368 + } 1.369 + // Either dyn:setProp:name(this, value) or dyn:setProp(this, name, value) 1.370 + if("setProp".equals(op)) { 1.371 + return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations)); 1.372 + } 1.373 + // Either dyn:getMethod:name(this), or dyn:getMethod(this, name) 1.374 + if("getMethod".equals(op)) { 1.375 + return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations)); 1.376 + } 1.377 + return null; 1.378 + } 1.379 + 1.380 + static final <T> List<T> pop(List<T> l) { 1.381 + return l.subList(1, l.size()); 1.382 + } 1.383 + 1.384 + MethodHandle getClassGuard(CallSiteDescriptor desc) { 1.385 + return getClassGuard(desc.getMethodType()); 1.386 + } 1.387 + 1.388 + MethodHandle getClassGuard(MethodType type) { 1.389 + return Guards.asType(classGuard, type); 1.390 + } 1.391 + 1.392 + GuardedInvocationComponent getClassGuardedInvocationComponent(MethodHandle invocation, MethodType type) { 1.393 + return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); 1.394 + } 1.395 + 1.396 + private MethodHandle getAssignableGuard(MethodType type) { 1.397 + return Guards.asType(assignableGuard, type); 1.398 + } 1.399 + 1.400 + private GuardedInvocation getCallPropWithThis(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) { 1.401 + switch(callSiteDescriptor.getNameTokenCount()) { 1.402 + case 3: { 1.403 + return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices, 1.404 + callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods); 1.405 + } 1.406 + default: { 1.407 + return null; 1.408 + } 1.409 + } 1.410 + } 1.411 + 1.412 + private GuardedInvocation createGuardedDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor, 1.413 + LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap){ 1.414 + final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap); 1.415 + return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType())); 1.416 + } 1.417 + 1.418 + private static MethodHandle getDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor, 1.419 + LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap) { 1.420 + final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap); 1.421 + return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null; 1.422 + } 1.423 + 1.424 + private static DynamicMethod getDynamicMethod(String methodName, Map<String, DynamicMethod> methodMap) { 1.425 + final DynamicMethod dynaMethod = methodMap.get(methodName); 1.426 + return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap); 1.427 + } 1.428 + 1.429 + private static SingleDynamicMethod getExplicitSignatureDynamicMethod(String methodName, 1.430 + Map<String, DynamicMethod> methodsMap) { 1.431 + // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name 1.432 + // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method 1.433 + // resolution works correctly in almost every situation. However, in presence of many language-specific 1.434 + // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected 1.435 + // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload 1.436 + // for performance reasons. 1.437 + 1.438 + // Is the method name lexically of the form "name(types)"? 1.439 + final int lastChar = methodName.length() - 1; 1.440 + if(methodName.charAt(lastChar) != ')') { 1.441 + return null; 1.442 + } 1.443 + final int openBrace = methodName.indexOf('('); 1.444 + if(openBrace == -1) { 1.445 + return null; 1.446 + } 1.447 + 1.448 + // Find an existing method for the "name" part 1.449 + final DynamicMethod simpleNamedMethod = methodsMap.get(methodName.substring(0, openBrace)); 1.450 + if(simpleNamedMethod == null) { 1.451 + return null; 1.452 + } 1.453 + 1.454 + // Try to get a narrowed dynamic method for the explicit parameter types. 1.455 + return simpleNamedMethod.getMethodForExactParamTypes(methodName.substring(openBrace + 1, lastChar)); 1.456 + } 1.457 + 1.458 + private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType( 1.459 + boolean.class, MethodHandle.class)); 1.460 + private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments( 1.461 + MethodHandles.constant(Object.class, null), 0, MethodHandle.class); 1.462 + 1.463 + private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor, 1.464 + LinkerServices linkerServices, List<String> operations) throws Exception { 1.465 + final MethodType type = callSiteDescriptor.getMethodType(); 1.466 + switch(callSiteDescriptor.getNameTokenCount()) { 1.467 + case 2: { 1.468 + // Must have three arguments: target object, property name, and property value. 1.469 + assertParameterCount(callSiteDescriptor, 3); 1.470 + 1.471 + // What's below is basically: 1.472 + // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation), 1.473 + // get_setter_handle(type, linkerServices)) 1.474 + // only with a bunch of method signature adjustments. Basically, retrieve method setter 1.475 + // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next 1.476 + // component's invocation. 1.477 + 1.478 + // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll 1.479 + // abbreviate to R(O, N, V) going forward. 1.480 + // We want setters that conform to "R(O, V)" 1.481 + final MethodType setterType = type.dropParameterTypes(1, 2); 1.482 + // Bind property setter handle to the expected setter type and linker services. Type is 1.483 + // MethodHandle(Object, String, Object) 1.484 + final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0, 1.485 + CallSiteDescriptorFactory.dropParameterTypes(callSiteDescriptor, 1, 2), linkerServices); 1.486 + 1.487 + // Cast getter to MethodHandle(O, N, V) 1.488 + final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType( 1.489 + MethodHandle.class)); 1.490 + 1.491 + // Handle to invoke the setter R(MethodHandle, O, V) 1.492 + final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType); 1.493 + // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V) 1.494 + final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType( 1.495 + 1)); 1.496 + final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, 1.497 + linkerServices, operations); 1.498 + 1.499 + final MethodHandle fallbackFolded; 1.500 + if(nextComponent == null) { 1.501 + // Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null 1.502 + fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1, 1.503 + type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class)); 1.504 + } else { 1.505 + // R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the 1.506 + // extra argument resulting from fold 1.507 + fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(), 1.508 + 0, MethodHandle.class); 1.509 + } 1.510 + 1.511 + // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V)) 1.512 + final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( 1.513 + IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); 1.514 + if(nextComponent == null) { 1.515 + return getClassGuardedInvocationComponent(compositeSetter, type); 1.516 + } 1.517 + return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); 1.518 + } 1.519 + case 3: { 1.520 + // Must have two arguments: target object and property value 1.521 + assertParameterCount(callSiteDescriptor, 2); 1.522 + final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices, 1.523 + callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters); 1.524 + // If we have a property setter with this name, this composite operation will always stop here 1.525 + if(gi != null) { 1.526 + return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS); 1.527 + } 1.528 + // If we don't have a property setter with this name, always fall back to the next operation in the 1.529 + // composite (if any) 1.530 + return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations); 1.531 + } 1.532 + default: { 1.533 + // More than two name components; don't know what to do with it. 1.534 + return null; 1.535 + } 1.536 + } 1.537 + } 1.538 + 1.539 + private static final Lookup privateLookup = new Lookup(MethodHandles.lookup()); 1.540 + 1.541 + private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType( 1.542 + boolean.class, AnnotatedDynamicMethod.class)); 1.543 + private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments( 1.544 + MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class); 1.545 + private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class, 1.546 + "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class)); 1.547 + private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class)); 1.548 + 1.549 + private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor, 1.550 + LinkerServices linkerServices, List<String> ops) throws Exception { 1.551 + final MethodType type = callSiteDescriptor.getMethodType(); 1.552 + switch(callSiteDescriptor.getNameTokenCount()) { 1.553 + case 2: { 1.554 + // Must have exactly two arguments: receiver and name 1.555 + assertParameterCount(callSiteDescriptor, 2); 1.556 + 1.557 + // What's below is basically: 1.558 + // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle) 1.559 + // only with a bunch of method signature adjustments. Basically, retrieve method getter 1.560 + // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null, 1.561 + // or delegate to next component's invocation. 1.562 + 1.563 + final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType( 1.564 + AnnotatedDynamicMethod.class)); 1.565 + final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments( 1.566 + GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup()); 1.567 + final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0, 1.568 + callSiteBoundMethodGetter); 1.569 + // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0) 1.570 + final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker, 1.571 + MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0))); 1.572 + // Since it's in the target of a fold, drop the unnecessary second argument 1.573 + // R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1) 1.574 + final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2, 1.575 + type.parameterType(1)); 1.576 + final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, 1.577 + linkerServices, ops); 1.578 + 1.579 + final MethodHandle fallbackFolded; 1.580 + if(nextComponent == null) { 1.581 + // Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null 1.582 + fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1, 1.583 + type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class)); 1.584 + } else { 1.585 + // R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the 1.586 + // extra argument resulting from fold 1.587 + fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(), 1.588 + 0, AnnotatedDynamicMethod.class); 1.589 + } 1.590 + 1.591 + // fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1)) 1.592 + final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( 1.593 + IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); 1.594 + if(nextComponent == null) { 1.595 + return getClassGuardedInvocationComponent(compositeGetter, type); 1.596 + } 1.597 + return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); 1.598 + } 1.599 + case 3: { 1.600 + // Must have exactly one argument: receiver 1.601 + assertParameterCount(callSiteDescriptor, 1); 1.602 + // Fixed name 1.603 + final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken( 1.604 + CallSiteDescriptor.NAME_OPERAND)); 1.605 + if(annGetter == null) { 1.606 + // We have no such property, always delegate to the next component operation 1.607 + return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops); 1.608 + } 1.609 + final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices); 1.610 + // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being 1.611 + // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the 1.612 + // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If 1.613 + // we're linking against a field getter, don't make the assumption. 1.614 + // NOTE: No delegation to the next component operation if we have a property with this name, even if its 1.615 + // value is null. 1.616 + final ValidationType validationType = annGetter.validationType; 1.617 + // TODO: we aren't using the type that declares the most generic getter here! 1.618 + return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType, 1.619 + type), clazz, validationType); 1.620 + } 1.621 + default: { 1.622 + // Can't do anything with more than 3 name components 1.623 + return null; 1.624 + } 1.625 + } 1.626 + } 1.627 + 1.628 + private MethodHandle getGuard(ValidationType validationType, MethodType methodType) { 1.629 + switch(validationType) { 1.630 + case EXACT_CLASS: { 1.631 + return getClassGuard(methodType); 1.632 + } 1.633 + case INSTANCE_OF: { 1.634 + return getAssignableGuard(methodType); 1.635 + } 1.636 + case IS_ARRAY: { 1.637 + return Guards.isArray(0, methodType); 1.638 + } 1.639 + case NONE: { 1.640 + return null; 1.641 + } 1.642 + default: { 1.643 + throw new AssertionError(); 1.644 + } 1.645 + } 1.646 + } 1.647 + 1.648 + private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(), 1.649 + MethodType.methodType(boolean.class, DynamicMethod.class)); 1.650 + private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class); 1.651 + 1.652 + private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor, 1.653 + LinkerServices linkerServices, List<String> ops) throws Exception { 1.654 + final MethodType type = callSiteDescriptor.getMethodType(); 1.655 + switch(callSiteDescriptor.getNameTokenCount()) { 1.656 + case 2: { 1.657 + // Must have exactly two arguments: receiver and name 1.658 + assertParameterCount(callSiteDescriptor, 2); 1.659 + final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, 1.660 + linkerServices, ops); 1.661 + if(nextComponent == null) { 1.662 + // No next component operation; just return a component for this operation. 1.663 + return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type); 1.664 + } 1.665 + 1.666 + // What's below is basically: 1.667 + // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a 1.668 + // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null 1.669 + // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation. 1.670 + 1.671 + final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType( 1.672 + DynamicMethod.class)); 1.673 + // Since it is part of the foldArgument() target, it will have extra args that we need to drop. 1.674 + final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments( 1.675 + DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, 1.676 + DynamicMethod.class)); 1.677 + final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation(); 1.678 + // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly 1.679 + assert nextComponentInvocation.type().equals(type); 1.680 + // Since it is part of the foldArgument() target, we have to drop an extra arg it receives. 1.681 + final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0, 1.682 + DynamicMethod.class); 1.683 + // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get) 1.684 + final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( 1.685 + IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter); 1.686 + 1.687 + return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS); 1.688 + } 1.689 + case 3: { 1.690 + // Must have exactly one argument: receiver 1.691 + assertParameterCount(callSiteDescriptor, 1); 1.692 + final DynamicMethod method = getDynamicMethod(callSiteDescriptor.getNameToken( 1.693 + CallSiteDescriptor.NAME_OPERAND)); 1.694 + if(method == null) { 1.695 + // We have no such method, always delegate to the next component 1.696 + return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops); 1.697 + } 1.698 + // No delegation to the next component of the composite operation; if we have a method with that name, 1.699 + // we'll always return it at this point. 1.700 + return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments( 1.701 + MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type); 1.702 + } 1.703 + default: { 1.704 + // Can't do anything with more than 3 name components 1.705 + return null; 1.706 + } 1.707 + } 1.708 + } 1.709 + 1.710 + private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) { 1.711 + if(descriptor.getMethodType().parameterCount() != paramCount) { 1.712 + throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters."); 1.713 + } 1.714 + } 1.715 + 1.716 + private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial( 1.717 + "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class); 1.718 + private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this); 1.719 + 1.720 + /** 1.721 + * @param id the property ID 1.722 + * @return the method handle for retrieving the property, or null if the property does not exist 1.723 + */ 1.724 + @SuppressWarnings("unused") 1.725 + private Object getPropertyGetterHandle(Object id) { 1.726 + return propertyGetters.get(id); 1.727 + } 1.728 + 1.729 + // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object" 1.730 + // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is 1.731 + // a typical property setter with variable name signature (target, name, value). 1.732 + private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments( 1.733 + privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class, 1.734 + LinkerServices.class, Object.class), 3, Object.class), 5, Object.class); 1.735 + // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object) 1.736 + private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this); 1.737 + 1.738 + @SuppressWarnings("unused") 1.739 + private MethodHandle getPropertySetterHandle(CallSiteDescriptor setterDescriptor, LinkerServices linkerServices, 1.740 + Object id) { 1.741 + return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters); 1.742 + } 1.743 + 1.744 + private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial( 1.745 + "getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class); 1.746 + private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this); 1.747 + 1.748 + @SuppressWarnings("unused") 1.749 + private DynamicMethod getDynamicMethod(Object name) { 1.750 + return getDynamicMethod(String.valueOf(name), methods); 1.751 + } 1.752 + 1.753 + /** 1.754 + * Returns a dynamic method of the specified name. 1.755 + * 1.756 + * @param name name of the method 1.757 + * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the 1.758 + * method with the specified name does not exist. 1.759 + */ 1.760 + DynamicMethod getDynamicMethod(String name) { 1.761 + return getDynamicMethod(name, methods); 1.762 + } 1.763 + 1.764 + /** 1.765 + * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the 1.766 + * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one, 1.767 + * creating more stable call sites. 1.768 + * @param getter the getter 1.769 + * @return getter with same name, declared on the most generic superclass/interface of the declaring class 1.770 + */ 1.771 + private static Method getMostGenericGetter(Method getter) { 1.772 + return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass()); 1.773 + } 1.774 + 1.775 + private static Method getMostGenericGetter(String name, Class<?> returnType, Class<?> declaringClass) { 1.776 + if(declaringClass == null) { 1.777 + return null; 1.778 + } 1.779 + // Prefer interfaces 1.780 + for(Class<?> itf: declaringClass.getInterfaces()) { 1.781 + final Method itfGetter = getMostGenericGetter(name, returnType, itf); 1.782 + if(itfGetter != null) { 1.783 + return itfGetter; 1.784 + } 1.785 + } 1.786 + final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass()); 1.787 + if(superGetter != null) { 1.788 + return superGetter; 1.789 + } 1.790 + if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) { 1.791 + try { 1.792 + return declaringClass.getMethod(name); 1.793 + } catch(NoSuchMethodException e) { 1.794 + // Intentionally ignored, meant to fall through 1.795 + } 1.796 + } 1.797 + return null; 1.798 + } 1.799 + 1.800 + private static final class AnnotatedDynamicMethod { 1.801 + private final SingleDynamicMethod method; 1.802 + /*private*/ final ValidationType validationType; 1.803 + 1.804 + AnnotatedDynamicMethod(SingleDynamicMethod method, ValidationType validationType) { 1.805 + this.method = method; 1.806 + this.validationType = validationType; 1.807 + } 1.808 + 1.809 + MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) { 1.810 + return method.getInvocation(callSiteDescriptor, linkerServices); 1.811 + } 1.812 + 1.813 + @SuppressWarnings("unused") 1.814 + MethodHandle getTarget(MethodHandles.Lookup lookup) { 1.815 + MethodHandle inv = method.getTarget(lookup); 1.816 + assert inv != null; 1.817 + return inv; 1.818 + } 1.819 + } 1.820 +}