1 /*
2 * Copyright 2014 - 2020 Rafael Winterhalter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.bytebuddy.implementation.bytecode.member;
17
18 import net.bytebuddy.ClassFileVersion;
19 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
20 import net.bytebuddy.description.method.MethodDescription;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.description.type.TypeList;
23 import net.bytebuddy.implementation.Implementation;
24 import net.bytebuddy.implementation.bytecode.StackManipulation;
25 import net.bytebuddy.implementation.bytecode.StackSize;
26 import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
27 import net.bytebuddy.jar.asm.Handle;
28 import net.bytebuddy.jar.asm.MethodVisitor;
29 import net.bytebuddy.jar.asm.Opcodes;
30
31 import java.util.List;
32
33 /**
34 * A builder for a method invocation.
35 */
36 public enum MethodInvocation {
37
38 /**
39 * A virtual method invocation.
40 */
41 VIRTUAL(Opcodes.INVOKEVIRTUAL, Opcodes.H_INVOKEVIRTUAL, Opcodes.INVOKEVIRTUAL, Opcodes.H_INVOKEVIRTUAL),
42
43 /**
44 * An interface-typed virtual method invocation.
45 */
46 INTERFACE(Opcodes.INVOKEINTERFACE, Opcodes.H_INVOKEINTERFACE, Opcodes.INVOKEINTERFACE, Opcodes.H_INVOKEINTERFACE),
47
48 /**
49 * A static method invocation.
50 */
51 STATIC(Opcodes.INVOKESTATIC, Opcodes.H_INVOKESTATIC, Opcodes.INVOKESTATIC, Opcodes.H_INVOKESTATIC),
52
53 /**
54 * A specialized pseudo-virtual method invocation for a non-constructor.
55 */
56 SPECIAL(Opcodes.INVOKESPECIAL, Opcodes.H_INVOKESPECIAL, Opcodes.INVOKESPECIAL, Opcodes.H_INVOKESPECIAL),
57
58 /**
59 * A specialized pseudo-virtual method invocation for a constructor.
60 */
61 SPECIAL_CONSTRUCTOR(Opcodes.INVOKESPECIAL, Opcodes.H_NEWINVOKESPECIAL, Opcodes.INVOKESPECIAL, Opcodes.H_NEWINVOKESPECIAL),
62
63 /**
64 * A private method call that is potentially virtual.
65 */
66 VIRTUAL_PRIVATE(Opcodes.INVOKEVIRTUAL, Opcodes.H_INVOKEVIRTUAL, Opcodes.INVOKESPECIAL, Opcodes.H_INVOKESPECIAL),
67
68 /**
69 * A private method call that is potentially virtual on an interface type.
70 */
71 INTERFACE_PRIVATE(Opcodes.INVOKEINTERFACE, Opcodes.H_INVOKEINTERFACE, Opcodes.INVOKESPECIAL, Opcodes.H_INVOKESPECIAL);
72
73 /**
74 * The opcode for invoking a method.
75 */
76 private final int opcode;
77
78 /**
79 * The handle being used for a dynamic method invocation.
80 */
81 private final int handle;
82
83 /**
84 * The opcode for invoking a method before Java 11.
85 */
86 private final int legacyOpcode;
87
88 /**
89 * The handle being used for a dynamic method invocation before Java 11.
90 */
91 private final int legacyHandle;
92
93 /**
94 * Creates a new type of method invocation.
95 *
96 * @param opcode The opcode for invoking a method.
97 * @param handle The handle being used for a dynamic method invocation.
98 * @param legacyOpcode The opcode for invoking a method before Java 11.
99 * @param legacyHandle The handle being used for a dynamic method invocation before Java 11.
100 */
101 MethodInvocation(int opcode, int handle, int legacyOpcode, int legacyHandle) {
102 this.opcode = opcode;
103 this.handle = handle;
104 this.legacyOpcode = legacyOpcode;
105 this.legacyHandle = legacyHandle;
106 }
107
108 /**
109 * Creates a method invocation with an implicitly determined invocation type.
110 *
111 * @param methodDescription The method to be invoked.
112 * @return A stack manipulation with implicitly determined invocation type.
113 */
114 public static WithImplicitInvocationTargetType invoke(MethodDescription.InDefinedShape methodDescription) {
115 if (methodDescription.isTypeInitializer()) {
116 return IllegalInvocation.INSTANCE;
117 } else if (methodDescription.isStatic()) { // Check this property first, private static methods must use INVOKESTATIC
118 return STATIC.new Invocation(methodDescription);
119 } else if (methodDescription.isConstructor()) {
120 return SPECIAL_CONSTRUCTOR.new Invocation(methodDescription); // Check this property second, constructors might be private
121 } else if (methodDescription.isPrivate()) {
122 return (methodDescription.getDeclaringType().isInterface()
123 ? INTERFACE_PRIVATE
124 : VIRTUAL_PRIVATE).new Invocation(methodDescription);
125 } else if (methodDescription.getDeclaringType().isInterface()) { // Check this property last, default methods must be called by INVOKESPECIAL
126 return INTERFACE.new Invocation(methodDescription);
127 } else {
128 return VIRTUAL.new Invocation(methodDescription);
129 }
130 }
131
132 /**
133 * Creates a method invocation with an implicitly determined invocation type. If the method's return type derives from its declared shape, the value
134 * is additionally casted to the value of the generically resolved method.
135 *
136 * @param methodDescription The method to be invoked.
137 * @return A stack manipulation with implicitly determined invocation type.
138 */
139 public static WithImplicitInvocationTargetType invoke(MethodDescription methodDescription) {
140 MethodDescription.InDefinedShape declaredMethod = methodDescription.asDefined();
141 return declaredMethod.getReturnType().asErasure().equals(methodDescription.getReturnType().asErasure())
142 ? invoke(declaredMethod)
143 : OfGenericMethod.of(methodDescription, invoke(declaredMethod));
144 }
145
146 /**
147 * An illegal implicit method invocation.
148 */
149 protected enum IllegalInvocation implements WithImplicitInvocationTargetType {
150
151 /**
152 * The singleton instance.
153 */
154 INSTANCE;
155
156 /**
157 * {@inheritDoc}
158 */
159 public StackManipulation virtual(TypeDescription invocationTarget) {
160 return Illegal.INSTANCE;
161 }
162
163 /**
164 * {@inheritDoc}
165 */
166 public StackManipulation special(TypeDescription invocationTarget) {
167 return Illegal.INSTANCE;
168 }
169
170 /**
171 * {@inheritDoc}
172 */
173 public StackManipulation dynamic(String methodName,
174 TypeDescription returnType,
175 List<? extends TypeDescription> methodType,
176 List<?> arguments) {
177 return Illegal.INSTANCE;
178 }
179
180 /**
181 * {@inheritDoc}
182 */
183 public StackManipulation onHandle(HandleType type) {
184 return Illegal.INSTANCE;
185 }
186
187 /**
188 * {@inheritDoc}
189 */
190 public boolean isValid() {
191 return false;
192 }
193
194 /**
195 * {@inheritDoc}
196 */
197 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
198 return Illegal.INSTANCE.apply(methodVisitor, implementationContext);
199 }
200 }
201
202 /**
203 * Represents a method invocation where the invocation type (static, virtual, special, interface) is derived
204 * from the given method's description.
205 */
206 public interface WithImplicitInvocationTargetType extends StackManipulation {
207
208 /**
209 * Transforms this method invocation into a virtual (or interface) method invocation on the given type.
210 *
211 * @param invocationTarget The type on which the method is to be invoked virtually on.
212 * @return A stack manipulation representing this method invocation.
213 */
214 StackManipulation virtual(TypeDescription invocationTarget);
215
216 /**
217 * Transforms this method invocation into a special invocation on the given type.
218 *
219 * @param invocationTarget The type on which the method is to be invoked specially on.
220 * @return A stack manipulation representing this method invocation.
221 */
222 StackManipulation special(TypeDescription invocationTarget);
223
224 /**
225 * Invokes the method as a bootstrap method to bind a call site with the given properties. Note that the
226 * Java virtual machine currently only knows how to resolve bootstrap methods that link static methods
227 * or a constructor.
228 *
229 * @param methodName The name of the method to be bound.
230 * @param returnType The return type of the method to be bound.
231 * @param methodType The parameter types of the method to be bound.
232 * @param arguments The arguments to be passed to the bootstrap method.
233 * @return A stack manipulation that represents the dynamic method invocation.
234 */
235 StackManipulation dynamic(String methodName,
236 TypeDescription returnType,
237 List<? extends TypeDescription> methodType,
238 List<?> arguments);
239
240 /**
241 * Invokes the method via a {@code MethodHandle}.
242 *
243 * @param type The type of invocation.
244 * @return A stack manipulation that represents a method call of the specified method via a method handle.
245 */
246 StackManipulation onHandle(HandleType type);
247 }
248
249 /**
250 * A method invocation of a generically resolved method.
251 */
252 @HashCodeAndEqualsPlugin.Enhance
253 protected static class OfGenericMethod implements WithImplicitInvocationTargetType {
254
255 /**
256 * The generically resolved return type of the method.
257 */
258 private final TypeDescription targetType;
259
260 /**
261 * The invocation of the method in its defined shape.
262 */
263 private final WithImplicitInvocationTargetType invocation;
264
265 /**
266 * Creates a generic method invocation.
267 *
268 * @param targetType The generically resolved return type of the method.
269 * @param invocation The invocation of the method in its defined shape.
270 */
271 protected OfGenericMethod(TypeDescription targetType, WithImplicitInvocationTargetType invocation) {
272 this.targetType = targetType;
273 this.invocation = invocation;
274 }
275
276 /**
277 * Creates a generic access dispatcher for a given method.
278 *
279 * @param methodDescription The generically resolved return type of the method.
280 * @param invocation The invocation of the method in its defined shape.
281 * @return A method access dispatcher for the given method.
282 */
283 protected static WithImplicitInvocationTargetType of(MethodDescription methodDescription, WithImplicitInvocationTargetType invocation) {
284 return new OfGenericMethod(methodDescription.getReturnType().asErasure(), invocation);
285 }
286
287 /**
288 * {@inheritDoc}
289 */
290 public StackManipulation virtual(TypeDescription invocationTarget) {
291 return new StackManipulation.Compound(invocation.virtual(invocationTarget), TypeCasting.to(targetType));
292 }
293
294 /**
295 * {@inheritDoc}
296 */
297 public StackManipulation special(TypeDescription invocationTarget) {
298 return new StackManipulation.Compound(invocation.special(invocationTarget), TypeCasting.to(targetType));
299 }
300
301 /**
302 * {@inheritDoc}
303 */
304 public StackManipulation dynamic(String methodName, TypeDescription returnType, List<? extends TypeDescription> methodType, List<?> arguments) {
305 return invocation.dynamic(methodName, returnType, methodType, arguments);
306 }
307
308 /**
309 * {@inheritDoc}
310 */
311 public StackManipulation onHandle(HandleType type) {
312 return new Compound(invocation.onHandle(type), TypeCasting.to(targetType));
313 }
314
315 /**
316 * {@inheritDoc}
317 */
318 public boolean isValid() {
319 return invocation.isValid();
320 }
321
322 /**
323 * {@inheritDoc}
324 */
325 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
326 return new Compound(invocation, TypeCasting.to(targetType)).apply(methodVisitor, implementationContext);
327 }
328 }
329
330 /**
331 * An implementation of a method invoking stack manipulation.
332 */
333 @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
334 protected class Invocation implements WithImplicitInvocationTargetType {
335
336 /**
337 * The method to be invoked.
338 */
339 private final TypeDescription typeDescription;
340
341 /**
342 * The type on which this method is to be invoked.
343 */
344 private final MethodDescription.InDefinedShape methodDescription;
345
346 /**
347 * Creates an invocation of a given method on its declaring type as an invocation target.
348 *
349 * @param methodDescription The method to be invoked.
350 */
351 protected Invocation(MethodDescription.InDefinedShape methodDescription) {
352 this(methodDescription, methodDescription.getDeclaringType());
353 }
354
355 /**
356 * Creates an invocation of a given method on a given invocation target type.
357 *
358 * @param methodDescription The method to be invoked.
359 * @param typeDescription The type on which this method is to be invoked.
360 */
361 protected Invocation(MethodDescription.InDefinedShape methodDescription, TypeDescription typeDescription) {
362 this.typeDescription = typeDescription;
363 this.methodDescription = methodDescription;
364 }
365
366 /**
367 * {@inheritDoc}
368 */
369 public boolean isValid() {
370 return true;
371 }
372
373 /**
374 * {@inheritDoc}
375 */
376 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
377 methodVisitor.visitMethodInsn(opcode == legacyOpcode || implementationContext.getClassFileVersion().isAtLeast(ClassFileVersion.JAVA_V11)
378 ? opcode
379 : legacyOpcode,
380 typeDescription.getInternalName(),
381 methodDescription.getInternalName(),
382 methodDescription.getDescriptor(),
383 typeDescription.isInterface());
384 int parameterSize = methodDescription.getStackSize(), returnValueSize = methodDescription.getReturnType().getStackSize().getSize();
385 return new Size(returnValueSize - parameterSize, Math.max(0, returnValueSize - parameterSize));
386 }
387
388 /**
389 * {@inheritDoc}
390 */
391 public StackManipulation virtual(TypeDescription invocationTarget) {
392 if (methodDescription.isConstructor() || methodDescription.isStatic()) {
393 return Illegal.INSTANCE;
394 } else if (methodDescription.isPrivate()) {
395 return methodDescription.getDeclaringType().equals(invocationTarget)
396 ? this
397 : Illegal.INSTANCE;
398 } else if (invocationTarget.isInterface()) {
399 return methodDescription.getDeclaringType().represents(Object.class)
400 ? this
401 : INTERFACE.new Invocation(methodDescription, invocationTarget);
402 } else {
403 return VIRTUAL.new Invocation(methodDescription, invocationTarget);
404 }
405 }
406
407 /**
408 * {@inheritDoc}
409 */
410 public StackManipulation special(TypeDescription invocationTarget) {
411 return methodDescription.isSpecializableFor(invocationTarget)
412 ? SPECIAL.new Invocation(methodDescription, invocationTarget)
413 : Illegal.INSTANCE;
414 }
415
416 /**
417 * {@inheritDoc}
418 */
419 public StackManipulation dynamic(String methodName,
420 TypeDescription returnType,
421 List<? extends TypeDescription> methodType,
422 List<?> arguments) {
423 return methodDescription.isInvokeBootstrap()
424 ? new DynamicInvocation(methodName, returnType, new TypeList.Explicit(methodType), methodDescription.asDefined(), arguments)
425 : Illegal.INSTANCE;
426 }
427
428 /**
429 * {@inheritDoc}
430 */
431 public StackManipulation onHandle(HandleType type) {
432 return new HandleInvocation(methodDescription, type);
433 }
434 }
435
436 /**
437 * Performs a dynamic method invocation of the given method.
438 */
439 @HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
440 protected class DynamicInvocation implements StackManipulation {
441
442 /**
443 * The internal name of the method that is to be bootstrapped.
444 */
445 private final String methodName;
446
447 /**
448 * The return type of the method to be bootstrapped.
449 */
450 private final TypeDescription returnType;
451
452 /**
453 * The parameter types of the method to be bootstrapped.
454 */
455 private final List<? extends TypeDescription> parameterTypes;
456
457 /**
458 * The bootstrap method.
459 */
460 private final MethodDescription.InDefinedShape bootstrapMethod;
461
462 /**
463 * The list of arguments to be handed over to the bootstrap method.
464 */
465 private final List<?> arguments;
466
467 /**
468 * Creates a new dynamic method invocation.
469 *
470 * @param methodName The internal name of the method that is to be bootstrapped.
471 * @param returnType The return type of the method to be bootstrapped.
472 * @param parameterTypes The type of the parameters to be bootstrapped.
473 * @param bootstrapMethod The bootstrap method.
474 * @param arguments The list of arguments to be handed over to the bootstrap method.
475 */
476 public DynamicInvocation(String methodName,
477 TypeDescription returnType,
478 List<? extends TypeDescription> parameterTypes,
479 MethodDescription.InDefinedShape bootstrapMethod,
480 List<?> arguments) {
481 this.methodName = methodName;
482 this.returnType = returnType;
483 this.parameterTypes = parameterTypes;
484 this.bootstrapMethod = bootstrapMethod;
485 this.arguments = arguments;
486 }
487
488 /**
489 * {@inheritDoc}
490 */
491 public boolean isValid() {
492 return true;
493 }
494
495 /**
496 * {@inheritDoc}
497 */
498 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
499 StringBuilder stringBuilder = new StringBuilder("(");
500 for (TypeDescription parameterType : parameterTypes) {
501 stringBuilder.append(parameterType.getDescriptor());
502 }
503 String methodDescriptor = stringBuilder.append(')').append(returnType.getDescriptor()).toString();
504 methodVisitor.visitInvokeDynamicInsn(methodName,
505 methodDescriptor,
506 new Handle(handle == legacyHandle || implementationContext.getClassFileVersion().isAtLeast(ClassFileVersion.JAVA_V11)
507 ? handle
508 : legacyHandle,
509 bootstrapMethod.getDeclaringType().getInternalName(),
510 bootstrapMethod.getInternalName(),
511 bootstrapMethod.getDescriptor(),
512 bootstrapMethod.getDeclaringType().isInterface()),
513 arguments.toArray(new Object[0]));
514 int stackSize = returnType.getStackSize().getSize() - StackSize.of(parameterTypes);
515 return new Size(stackSize, Math.max(stackSize, 0));
516 }
517 }
518
519 /**
520 * Performs a method invocation on a method handle with a polymorphic type signature.
521 */
522 @HashCodeAndEqualsPlugin.Enhance
523 protected static class HandleInvocation implements StackManipulation {
524
525 /**
526 * The internal name of the method handle type.
527 */
528 private static final String METHOD_HANDLE = "java/lang/invoke/MethodHandle";
529
530 /**
531 * The invoked method.
532 */
533 private final MethodDescription.InDefinedShape methodDescription;
534
535 /**
536 * The type of method handle invocation.
537 */
538 private final HandleType type;
539
540 /**
541 * Creates a new method handle invocation.
542 *
543 * @param methodDescription The invoked method.
544 * @param type The type of method handle invocation.
545 */
546 protected HandleInvocation(MethodDescription.InDefinedShape methodDescription, HandleType type) {
547 this.methodDescription = methodDescription;
548 this.type = type;
549 }
550
551 /**
552 * {@inheritDoc}
553 */
554 public boolean isValid() {
555 return true;
556 }
557
558 /**
559 * {@inheritDoc}
560 */
561 public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
562 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
563 METHOD_HANDLE,
564 type.getMethodName(),
565 methodDescription.isStatic() || methodDescription.isConstructor()
566 ? methodDescription.getDescriptor()
567 : "(" + methodDescription.getDeclaringType().getDescriptor() + methodDescription.getDescriptor().substring(1),
568 false);
569 int parameterSize = 1 + methodDescription.getStackSize(), returnValueSize = methodDescription.getReturnType().getStackSize().getSize();
570 return new Size(returnValueSize - parameterSize, Math.max(0, returnValueSize - parameterSize));
571 }
572 }
573
574 /**
575 * The type of method handle invocation.
576 */
577 public enum HandleType {
578
579 /**
580 * An exact invocation without type adjustments.
581 */
582 EXACT("invokeExact"),
583
584 /**
585 * A regular invocation with standard type adjustments.
586 */
587 REGULAR("invoke");
588
589 /**
590 * The name of the invoked method.
591 */
592 private final String methodName;
593
594 /**
595 * Creates a new handle type.
596 *
597 * @param methodName The name of the invoked method.
598 */
599 HandleType(String methodName) {
600 this.methodName = methodName;
601 }
602
603 /**
604 * Returns the name of the represented method.
605 *
606 * @return The name of the invoked method.
607 */
608 protected String getMethodName() {
609 return methodName;
610 }
611 }
612 }
613