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