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