Path: blob/master/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AbstractJavaLinker.java
41161 views
/*1* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file is available under and governed by the GNU General Public27* License version 2 only, as published by the Free Software Foundation.28* However, the following notice accompanied the original version of this29* file, and Oracle licenses the original version of this file under the BSD30* license:31*/32/*33Copyright 2009-2013 Attila Szegedi3435Redistribution and use in source and binary forms, with or without36modification, are permitted provided that the following conditions are37met:38* Redistributions of source code must retain the above copyright39notice, this list of conditions and the following disclaimer.40* Redistributions in binary form must reproduce the above copyright41notice, this list of conditions and the following disclaimer in the42documentation and/or other materials provided with the distribution.43* Neither the name of the copyright holder nor the names of44contributors may be used to endorse or promote products derived from45this software without specific prior written permission.4647THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS48IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED49TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A50PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER51BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR52CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF53SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR54BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,55WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR56OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF57ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.58*/5960package jdk.dynalink.beans;6162import java.lang.invoke.MethodHandle;63import java.lang.invoke.MethodHandles;64import java.lang.invoke.MethodType;65import java.lang.reflect.Constructor;66import java.lang.reflect.Executable;67import java.lang.reflect.Field;68import java.lang.reflect.Method;69import java.lang.reflect.Modifier;70import java.util.Arrays;71import java.util.Collections;72import java.util.HashMap;73import java.util.List;74import java.util.Map;75import java.util.Set;76import jdk.dynalink.CallSiteDescriptor;77import jdk.dynalink.NamedOperation;78import jdk.dynalink.Namespace;79import jdk.dynalink.NamespaceOperation;80import jdk.dynalink.Operation;81import jdk.dynalink.StandardNamespace;82import jdk.dynalink.StandardOperation;83import jdk.dynalink.beans.GuardedInvocationComponent.ValidationType;84import jdk.dynalink.internal.InternalTypeUtilities;85import jdk.dynalink.linker.GuardedInvocation;86import jdk.dynalink.linker.GuardingDynamicLinker;87import jdk.dynalink.linker.LinkRequest;88import jdk.dynalink.linker.LinkerServices;89import jdk.dynalink.linker.support.Guards;90import jdk.dynalink.linker.support.Lookup;91import jdk.internal.reflect.CallerSensitive;9293/**94* A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property95* exposure and method calls for both static and instance facets of a class.96*/97abstract class AbstractJavaLinker implements GuardingDynamicLinker {9899final Class<?> clazz;100private final MethodHandle classGuard;101private final MethodHandle assignableGuard;102private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>();103private final Map<String, DynamicMethod> propertySetters = new HashMap<>();104private final Map<String, DynamicMethod> methods = new HashMap<>();105106AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard) {107this(clazz, classGuard, classGuard);108}109110AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard, final MethodHandle assignableGuard) {111this.clazz = clazz;112this.classGuard = classGuard;113this.assignableGuard = assignableGuard;114115final FacetIntrospector introspector = createFacetIntrospector();116// Add record component getters117for (final Method rcg: introspector.getRecordComponentGetters()) {118setPropertyGetter(rcg, 0);119}120// Add methods and properties121for(final Method method: introspector.getMethods()) {122final String name = method.getName();123// Add method124addMember(name, method, methods);125// Add the method as a property getter and/or setter126if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) {127// Property getter128setPropertyGetter(method, 3);129} else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 &&130method.getReturnType() == boolean.class) {131// Boolean property getter132setPropertyGetter(method, 2);133} else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {134// Property setter135addMember(decapitalize(name.substring(3)), method, propertySetters);136}137}138139// Add field getter/setters as property getters/setters.140for(final Field field: introspector.getFields()) {141final String name = field.getName();142// Only add a property getter when one is not defined already as a getXxx()/isXxx() method.143setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS);144if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) {145addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name),146propertySetters);147}148}149150// Add inner classes, but only those for which we don't hide a property with it151for(final Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {152setPropertyGetter(innerClassSpec.getKey(), innerClassSpec.getValue(), ValidationType.EXACT_CLASS);153}154}155156private static String decapitalize(final String str) {157assert str != null;158if(str.isEmpty()) {159return str;160}161162final char c0 = str.charAt(0);163if(Character.isLowerCase(c0)) {164return str;165}166167// If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize168if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) {169return str;170}171172final char[] c = str.toCharArray();173c[0] = Character.toLowerCase(c0);174return new String(c);175}176177abstract FacetIntrospector createFacetIntrospector();178179Set<String> getReadablePropertyNames() {180return getUnmodifiableKeys(propertyGetters);181}182183Set<String> getWritablePropertyNames() {184return getUnmodifiableKeys(propertySetters);185}186187Set<String> getMethodNames() {188return getUnmodifiableKeys(methods);189}190191private static Set<String> getUnmodifiableKeys(final Map<String, ?> m) {192return Collections.unmodifiableSet(m.keySet());193}194195/**196* Sets the specified dynamic method to be the property getter for the specified property. Note that you can only197* use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties198* that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}199* instead.200* @param name name of the property201* @param handle the method handle that implements the property getter202* @param validationType the validation type for the property203*/204private void setPropertyGetter(final String name, final SingleDynamicMethod handle, final ValidationType validationType) {205if (!propertyGetters.containsKey(name)) {206propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType));207}208}209210/**211* Sets the specified reflective method to be the property getter for the specified property.212* @param getter the getter method213* @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for214* names starting with "is".215*/216private void setPropertyGetter(final Method getter, final int prefixLen) {217setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod(218getMostGenericGetter(getter)), ValidationType.INSTANCE_OF);219}220221/**222* Sets the specified method handle to be the property getter for the specified property. Note that you can only223* use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties224* that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}225* instead.226* @param name name of the property227* @param handle the method handle that implements the property getter228* @param validationType the validation type for the property229*/230void setPropertyGetter(final String name, final MethodHandle handle, final ValidationType validationType) {231setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);232}233234private void addMember(final String name, final Executable m, final Map<String, DynamicMethod> methodMap) {235addMember(name, createDynamicMethod(m), methodMap);236}237238private void addMember(final String name, final SingleDynamicMethod method, final Map<String, DynamicMethod> methodMap) {239final DynamicMethod existingMethod = methodMap.get(name);240final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name);241if(newMethod != existingMethod) {242methodMap.put(name, newMethod);243}244}245246/**247* Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The248* methods should represent all overloads of the same name (or all constructors of the class).249* @param members the reflective members250* @param clazz the class declaring the reflective members251* @param name the common name of the reflective members.252* @return a dynamic method representing all the specified reflective members.253*/254static DynamicMethod createDynamicMethod(final Iterable<? extends Executable> members, final Class<?> clazz, final String name) {255DynamicMethod dynMethod = null;256for(final Executable method: members) {257dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name);258}259return dynMethod;260}261262/**263* Given a reflective method or a constructor, creates a dynamic method that represents it. This method will264* distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive265* dynamic method when needed.266* @param m the reflective member267* @return the single dynamic method representing the reflective member268*/269private static SingleDynamicMethod createDynamicMethod(final Executable m) {270if (m.isAnnotationPresent(CallerSensitive.class)) {271// Method has @CallerSensitive annotation272return new CallerSensitiveDynamicMethod(m);273}274// Method has no @CallerSensitive annotation275final MethodHandle mh;276try {277mh = unreflectSafely(m);278} catch (final IllegalAccessError e) {279// java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't280// marked with the annotation. In this case, we'll fall back to treating it as caller sensitive.281return new CallerSensitiveDynamicMethod(m);282}283// Proceed with non-caller sensitive284return new SimpleDynamicMethod(mh, m.getDeclaringClass(), m.getName(), m instanceof Constructor);285}286287/**288* Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be289* only used for methods and constructors that are not caller sensitive. If a caller sensitive method were290* unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege291* unreflector as its caller, and thus completely useless.292* @param m the method or constructor293* @return the method handle294*/295private static MethodHandle unreflectSafely(final Executable m) {296if(m instanceof Method) {297final Method reflMethod = (Method)m;298final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod);299if(Modifier.isStatic(reflMethod.getModifiers())) {300return StaticClassIntrospector.editStaticMethodHandle(handle);301}302return handle;303}304return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m));305}306307private static DynamicMethod mergeMethods(final SingleDynamicMethod method, final DynamicMethod existing, final Class<?> clazz, final String name) {308if(existing == null) {309return method;310} else if(existing.contains(method)) {311return existing;312} else if(existing instanceof SingleDynamicMethod) {313final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);314odm.addMethod(((SingleDynamicMethod)existing));315odm.addMethod(method);316return odm;317} else if(existing instanceof OverloadedDynamicMethod) {318((OverloadedDynamicMethod)existing).addMethod(method);319return existing;320}321throw new AssertionError();322}323324@Override325public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)326throws Exception {327final MissingMemberHandlerFactory missingMemberHandlerFactory;328final LinkerServices directLinkerServices;329if (linkerServices instanceof LinkerServicesWithMissingMemberHandlerFactory) {330final LinkerServicesWithMissingMemberHandlerFactory lswmmhf = ((LinkerServicesWithMissingMemberHandlerFactory)linkerServices);331missingMemberHandlerFactory = lswmmhf.missingMemberHandlerFactory;332directLinkerServices = lswmmhf.linkerServices;333} else {334missingMemberHandlerFactory = null;335directLinkerServices = linkerServices;336}337338final GuardedInvocationComponent gic = getGuardedInvocationComponent(339new ComponentLinkRequest(request, directLinkerServices,340missingMemberHandlerFactory));341return gic != null ? gic.getGuardedInvocation() : null;342}343344static final class ComponentLinkRequest {345final LinkRequest linkRequest;346final LinkerServices linkerServices;347final MissingMemberHandlerFactory missingMemberHandlerFactory;348final Operation baseOperation;349final List<Namespace> namespaces;350final Object name;351352ComponentLinkRequest(final LinkRequest linkRequest,353final LinkerServices linkerServices,354final MissingMemberHandlerFactory missingMemberHandlerFactory) {355this.linkRequest = linkRequest;356this.linkerServices = linkerServices;357this.missingMemberHandlerFactory = missingMemberHandlerFactory;358final Operation namedOp = linkRequest.getCallSiteDescriptor().getOperation();359this.name = NamedOperation.getName(namedOp);360final Operation namespaceOp = NamedOperation.getBaseOperation(namedOp);361this.baseOperation = NamespaceOperation.getBaseOperation(namespaceOp);362this.namespaces = Arrays.asList(NamespaceOperation.getNamespaces(namespaceOp));363}364365private ComponentLinkRequest(final LinkRequest linkRequest,366final LinkerServices linkerServices,367final MissingMemberHandlerFactory missingMemberHandlerFactory,368final Operation baseOperation, final List<Namespace> namespaces, final Object name) {369this.linkRequest = linkRequest;370this.linkerServices = linkerServices;371this.missingMemberHandlerFactory = missingMemberHandlerFactory;372this.baseOperation = baseOperation;373this.namespaces = namespaces;374this.name = name;375}376377CallSiteDescriptor getDescriptor() {378return linkRequest.getCallSiteDescriptor();379}380381ComponentLinkRequest popNamespace() {382return new ComponentLinkRequest(linkRequest, linkerServices,383missingMemberHandlerFactory, baseOperation,384namespaces.subList(1, namespaces.size()), name);385}386}387388protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req)389throws Exception {390if (req.namespaces.isEmpty()) {391return null;392}393final Namespace ns = req.namespaces.get(0);394final Operation op = req.baseOperation;395if (op == StandardOperation.GET) {396if (ns == StandardNamespace.PROPERTY) {397return getPropertyGetter(req.popNamespace());398} else if (ns == StandardNamespace.METHOD) {399return getMethodGetter(req.popNamespace());400}401} else if (op == StandardOperation.SET && ns == StandardNamespace.PROPERTY) {402return getPropertySetter(req.popNamespace());403}404return getNextComponent(req.popNamespace());405}406407GuardedInvocationComponent getNextComponent(final ComponentLinkRequest req) throws Exception {408if (req.namespaces.isEmpty()) {409return createNoSuchMemberHandler(req.missingMemberHandlerFactory,410req.linkRequest, req.linkerServices);411}412final GuardedInvocationComponent gic = getGuardedInvocationComponent(req);413if (gic != null) {414return gic;415}416return getNextComponent(req.popNamespace());417}418419private GuardedInvocationComponent createNoSuchMemberHandler(420final MissingMemberHandlerFactory missingMemberHandlerFactory,421final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {422if (missingMemberHandlerFactory == null) {423return null;424}425final MethodHandle handler = missingMemberHandlerFactory.createMissingMemberHandler(linkRequest, linkerServices);426if (handler == null) {427return null;428}429final MethodType type = linkRequest.getCallSiteDescriptor().getMethodType();430// The returned handler is allowed to differ in return type.431assert handler.type().changeReturnType(type.returnType()).equals(type);432return getClassGuardedInvocationComponent(handler, type);433}434435MethodHandle getClassGuard(final MethodType type) {436return Guards.asType(classGuard, type);437}438439GuardedInvocationComponent getClassGuardedInvocationComponent(final MethodHandle invocation, final MethodType type) {440return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);441}442443abstract SingleDynamicMethod getConstructorMethod(final String signature);444445private MethodHandle getAssignableGuard(final MethodType type) {446return Guards.asType(assignableGuard, type);447}448449private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,450final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){451final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);452return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));453}454455private MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,456final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) {457final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);458return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;459}460461private DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {462final DynamicMethod dynaMethod = methodMap.get(methodName);463return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);464}465466private SingleDynamicMethod getExplicitSignatureDynamicMethod(final String fullName,467final Map<String, DynamicMethod> methodsMap) {468// What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name469// to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method470// resolution works correctly in almost every situation. However, in presence of many language-specific471// conversions with a radically dynamic language, most overloaded methods will end up being constantly selected472// at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload473// for performance reasons.474475// Is the method name lexically of the form "name(types)"?476final int lastChar = fullName.length() - 1;477if(fullName.charAt(lastChar) != ')') {478return null;479}480final int openBrace = fullName.indexOf('(');481if(openBrace == -1) {482return null;483}484485final String name = fullName.substring(0, openBrace);486final String signature = fullName.substring(openBrace + 1, lastChar);487488// Find an existing method for the "name" part489final DynamicMethod simpleNamedMethod = methodsMap.get(name);490if(simpleNamedMethod == null) {491// explicit signature constructor access492// Java.type("java.awt.Color")["(int,int,int)"]493// will get Color(int,int,int) constructor of Color class.494if (name.isEmpty()) {495return getConstructorMethod(signature);496}497498return null;499}500501// Try to get a narrowed dynamic method for the explicit parameter types.502return simpleNamedMethod.getMethodForExactParamTypes(signature);503}504505private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(506boolean.class, MethodHandle.class));507private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(508MethodHandles.constant(Object.class, null), 0, MethodHandle.class);509510private GuardedInvocationComponent getPropertySetter(final ComponentLinkRequest req) throws Exception {511if (req.name == null) {512return getUnnamedPropertySetter(req);513}514return getNamedPropertySetter(req);515}516517private GuardedInvocationComponent getUnnamedPropertySetter(final ComponentLinkRequest req) throws Exception {518final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();519// Must have three arguments: target object, property name, and property value.520assertParameterCount(callSiteDescriptor, 3);521522// We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be523// valid for us to convert return values proactively. Also, since we don't know what setters will be524// invoked, we'll conservatively presume Object return type. The one exception is void return.525final MethodType origType = callSiteDescriptor.getMethodType();526final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class);527final LinkerServices linkerServices = req.linkerServices;528529// What's below is basically:530// foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),531// get_setter_handle(type, linkerServices))532// only with a bunch of method signature adjustments. Basically, retrieve method setter533// MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next534// component's invocation.535536// Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll537// abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using538// Object return type).539final MethodType setterType = type.dropParameterTypes(1, 2);540// Bind property setter handle to the expected setter type and linker services. Type is541// MethodHandle(Object, String, Object)542final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,543callSiteDescriptor.changeMethodType(setterType), linkerServices);544545// Cast getter to MethodHandle(O, N, V)546final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(547MethodHandle.class));548549// Handle to invoke the setter R(MethodHandle, O, V)550final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);551// Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)552final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(5531));554final GuardedInvocationComponent nextComponent = getNextComponent(req);555556final MethodHandle fallbackFolded;557if (nextComponent == null) {558// Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null559fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,560type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));561} else {562// Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the563// extra argument resulting from fold564fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),5650, MethodHandle.class);566}567568// fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))569final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(570IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);571if(nextComponent == null) {572return getClassGuardedInvocationComponent(compositeSetter, type);573}574return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);575}576577private GuardedInvocationComponent getNamedPropertySetter(final ComponentLinkRequest req) throws Exception {578final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();579// Must have two arguments: target object and property value580assertParameterCount(callSiteDescriptor, 2);581final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, req.linkerServices,582req.name.toString(), propertySetters);583// If we have a property setter with this name, this composite operation will always stop here584if(gi != null) {585return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);586}587// If we don't have a property setter with this name, always fall back to the next namespace (if any).588return getNextComponent(req);589}590591private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());592593private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(594boolean.class, AnnotatedDynamicMethod.class));595private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(596MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);597private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,598"getTarget", MethodType.methodType(MethodHandle.class, CallSiteDescriptor.class, LinkerServices.class));599private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));600601private GuardedInvocationComponent getPropertyGetter(final ComponentLinkRequest req) throws Exception {602if (req.name == null) {603return getUnnamedPropertyGetter(req);604}605return getNamedPropertyGetter(req);606}607608private GuardedInvocationComponent getUnnamedPropertyGetter(final ComponentLinkRequest req) throws Exception {609// Since we can't know what kind of a getter we'll get back on different invocations, we'll just610// conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking611// runtime might not allow coercing at that call site.612final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();613final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);614// Must have exactly two arguments: receiver and name615assertParameterCount(callSiteDescriptor, 2);616617// What's below is basically:618// foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)619// only with a bunch of method signature adjustments. Basically, retrieve method getter620// AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,621// or delegate to next component's invocation.622623final LinkerServices linkerServices = req.linkerServices;624final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(625AnnotatedDynamicMethod.class));626final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(627GET_ANNOTATED_METHOD, 1, callSiteDescriptor, linkerServices);628final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,629callSiteBoundMethodGetter);630// Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)631final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,632MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));633// Since it's in the target of a fold, drop the unnecessary second argument634// Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)635final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,636type.parameterType(1));637final GuardedInvocationComponent nextComponent = getNextComponent(req);638639final MethodHandle fallbackFolded;640if(nextComponent == null) {641// Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null642fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,643type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));644} else {645// Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to646// drop the extra argument resulting from fold and to change its return type to Object.647final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();648final MethodType nextType = nextInvocation.type();649fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(650nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);651}652653// fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))654final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(655IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);656if(nextComponent == null) {657return getClassGuardedInvocationComponent(compositeGetter, type);658}659return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);660}661662private GuardedInvocationComponent getNamedPropertyGetter(final ComponentLinkRequest req) throws Exception {663final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();664// Must have exactly one argument: receiver665assertParameterCount(callSiteDescriptor, 1);666// Fixed name667final AnnotatedDynamicMethod annGetter = propertyGetters.get(req.name.toString());668if(annGetter == null) {669// We have no such property, always delegate to the next component operation670return getNextComponent(req);671}672final MethodHandle getter = annGetter.getInvocation(req);673// NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being674// overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the675// method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If676// we're linking against a field getter, don't make the assumption.677// NOTE: No delegation to the next component operation if we have a property with this name, even if its678// value is null.679final ValidationType validationType = annGetter.validationType;680// TODO: we aren't using the type that declares the most generic getter here!681return new GuardedInvocationComponent(getter, getGuard(validationType,682callSiteDescriptor.getMethodType()), clazz, validationType);683}684685private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) {686switch(validationType) {687case EXACT_CLASS: {688return getClassGuard(methodType);689}690case INSTANCE_OF: {691return getAssignableGuard(methodType);692}693case IS_ARRAY: {694return Guards.isArray(0, methodType);695}696case NONE: {697return null;698}699default: {700throw new AssertionError();701}702}703}704705private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,706MethodType.methodType(boolean.class, Object.class));707private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);708709private GuardedInvocationComponent getMethodGetter(final ComponentLinkRequest req) throws Exception {710if (req.name == null) {711return getUnnamedMethodGetter(req);712}713return getNamedMethodGetter(req);714}715716private static MethodType getMethodGetterType(final ComponentLinkRequest req) {717// The created method handle will always return a DynamicMethod (or null), but since we don't want that type to718// be visible outside of this linker, declare it to return Object.719return req.getDescriptor().getMethodType().changeReturnType(Object.class);720}721722private GuardedInvocationComponent getUnnamedMethodGetter(final ComponentLinkRequest req) throws Exception {723// Must have exactly two arguments: receiver and name724assertParameterCount(req.getDescriptor(), 2);725final GuardedInvocationComponent nextComponent = getNextComponent(req);726final LinkerServices linkerServices = req.linkerServices;727final MethodType type = getMethodGetterType(req);728if(nextComponent == null) {729// No next component operation; just return a component for this operation.730return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);731}732733// What's below is basically:734// foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a735// bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null736// DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.737738final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);739// Since it is part of the foldArgument() target, it will have extra args that we need to drop.740final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(741OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));742final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();743// The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the744// return type.745assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);746// Since it is part of the foldArgument() target, we have to drop an extra arg it receives.747final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,748Object.class);749// Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)750// Note that nextCombinedInvocation needs to have its return type changed to Object751final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(752IS_DYNAMIC_METHOD, returnMethodHandle,753nextCombinedInvocation.asType(nextCombinedInvocation.type().changeReturnType(Object.class))),754typedGetter);755756return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);757}758759private GuardedInvocationComponent getNamedMethodGetter(final ComponentLinkRequest req)760throws Exception {761// Must have exactly one argument: receiver762assertParameterCount(req.getDescriptor(), 1);763final DynamicMethod method = getDynamicMethod(req.name.toString());764if(method == null) {765// We have no such method, always delegate to the next component766return getNextComponent(req);767}768// No delegation to the next namespace; if we have a method with that name, we'll always return it at769// this point.770final MethodType type = getMethodGetterType(req);771return getClassGuardedInvocationComponent(req.linkerServices.asType(MethodHandles.dropArguments(772MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);773}774775static class MethodPair {776final MethodHandle method1;777final MethodHandle method2;778779MethodPair(final MethodHandle method1, final MethodHandle method2) {780this.method1 = method1;781this.method2 = method2;782}783784MethodHandle guardWithTest(final MethodHandle test) {785return MethodHandles.guardWithTest(test, method1, method2);786}787}788789static MethodPair matchReturnTypes(final MethodHandle m1, final MethodHandle m2) {790final MethodType type1 = m1.type();791final MethodType type2 = m2.type();792final Class<?> commonRetType = InternalTypeUtilities.getCommonLosslessConversionType(type1.returnType(),793type2.returnType());794return new MethodPair(795m1.asType(type1.changeReturnType(commonRetType)),796m2.asType(type2.changeReturnType(commonRetType)));797}798799private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {800if(descriptor.getMethodType().parameterCount() != paramCount) {801throw new BootstrapMethodError(descriptor.getOperation() + " must have exactly " + paramCount + " parameters.");802}803}804805private static final MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial(806"getPropertyGetterHandle", Object.class, Object.class), 1, Object.class);807private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this);808809/**810* @param id the property ID811* @return the method handle for retrieving the property, or null if the property does not exist812*/813@SuppressWarnings("unused")814private Object getPropertyGetterHandle(final Object id) {815return propertyGetters.get(String.valueOf(id));816}817818// Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object"819// args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is820// a typical property setter with variable name signature (target, name, value).821private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(822privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class,823LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);824// Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)825private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);826827@SuppressWarnings("unused")828private MethodHandle getPropertySetterHandle(final CallSiteDescriptor setterDescriptor, final LinkerServices linkerServices,829final Object id) {830return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);831}832833private static final MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(834"getDynamicMethod", Object.class, Object.class), 1, Object.class);835private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);836837@SuppressWarnings("unused")838// This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't839// want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for840// GET:METHOD linking).841private Object getDynamicMethod(final Object name) {842return getDynamicMethod(String.valueOf(name), methods);843}844845/**846* Returns a dynamic method of the specified name.847*848* @param name name of the method849* @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the850* method with the specified name does not exist.851*/852DynamicMethod getDynamicMethod(final String name) {853return getDynamicMethod(name, methods);854}855856/**857* Find the most generic superclass that declares this getter. Since getters have zero args (aside from the858* receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one,859* creating more stable call sites.860* @param getter the getter861* @return getter with same name, declared on the most generic superclass/interface of the declaring class862*/863private static Method getMostGenericGetter(final Method getter) {864return getMostGenericGetter(getter.getName(), getter.getDeclaringClass());865}866867private static Method getMostGenericGetter(final String name, final Class<?> declaringClass) {868if(declaringClass == null) {869return null;870}871// Prefer interfaces872for(final Class<?> itf: declaringClass.getInterfaces()) {873final Method itfGetter = getMostGenericGetter(name, itf);874if(itfGetter != null) {875return itfGetter;876}877}878final Method superGetter = getMostGenericGetter(name, declaringClass.getSuperclass());879if(superGetter != null) {880return superGetter;881}882if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {883try {884return declaringClass.getMethod(name);885} catch(final NoSuchMethodException e) {886// Intentionally ignored, meant to fall through887}888}889return null;890}891892private static final class AnnotatedDynamicMethod {893private final SingleDynamicMethod method;894/*private*/ final ValidationType validationType;895896AnnotatedDynamicMethod(final SingleDynamicMethod method, final ValidationType validationType) {897this.method = method;898this.validationType = validationType;899}900901MethodHandle getInvocation(final ComponentLinkRequest req) {902return method.getInvocation(req.getDescriptor(), req.linkerServices);903}904905@SuppressWarnings("unused")906MethodHandle getTarget(final CallSiteDescriptor desc, final LinkerServices linkerServices) {907final MethodHandle inv = linkerServices.filterInternalObjects(method.getTarget(desc));908assert inv != null;909return inv;910}911}912}913914915