View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.internal.expectations.mocking;
6   
7   import static java.lang.reflect.Modifier.isStatic;
8   
9   import static mockit.asm.jvmConstants.Access.FINAL;
10  import static mockit.asm.jvmConstants.Access.PUBLIC;
11  import static mockit.asm.jvmConstants.Access.isSynthetic;
12  import static mockit.asm.jvmConstants.Opcodes.ALOAD;
13  import static mockit.asm.jvmConstants.Opcodes.INVOKESPECIAL;
14  
15  import edu.umd.cs.findbugs.annotations.NonNull;
16  import edu.umd.cs.findbugs.annotations.Nullable;
17  
18  import java.lang.reflect.Type;
19  import java.util.ArrayList;
20  import java.util.EnumSet;
21  import java.util.List;
22  
23  import mockit.asm.annotations.AnnotationVisitor;
24  import mockit.asm.classes.ClassInfo;
25  import mockit.asm.classes.ClassReader;
26  import mockit.asm.fields.FieldVisitor;
27  import mockit.asm.metadata.ClassMetadataReader;
28  import mockit.asm.metadata.ClassMetadataReader.Attribute;
29  import mockit.asm.metadata.ClassMetadataReader.MethodInfo;
30  import mockit.asm.methods.MethodVisitor;
31  import mockit.internal.BaseClassModifier;
32  import mockit.internal.ClassFile;
33  import mockit.internal.classGeneration.MockedTypeInfo;
34  import mockit.internal.reflection.GenericTypeReflection;
35  import mockit.internal.reflection.GenericTypeReflection.GenericSignature;
36  
37  public final class InterfaceImplementationGenerator extends BaseClassModifier {
38      private static final int CLASS_ACCESS = PUBLIC + FINAL;
39      private static final EnumSet<Attribute> SIGNATURE = EnumSet.of(Attribute.Signature);
40  
41      @NonNull
42      private final MockedTypeInfo mockedTypeInfo;
43      @NonNull
44      private final String implementationClassDesc;
45      @NonNull
46      private final List<String> implementedMethods;
47      private String interfaceName;
48      private String methodOwner;
49      @Nullable
50      private String[] initialSuperInterfaces;
51  
52      public InterfaceImplementationGenerator(@NonNull ClassReader cr, @NonNull Type mockedType,
53              @NonNull String implementationClassName) {
54          super(cr);
55          mockedTypeInfo = new MockedTypeInfo(mockedType);
56          implementationClassDesc = implementationClassName.replace('.', '/');
57          implementedMethods = new ArrayList<>();
58      }
59  
60      @Override
61      public void visit(int version, int access, @NonNull String name, @NonNull ClassInfo additionalInfo) {
62          interfaceName = name;
63          methodOwner = name;
64          initialSuperInterfaces = additionalInfo.interfaces;
65  
66          ClassInfo implementationClassInfo = new ClassInfo();
67          String signature = additionalInfo.signature;
68          implementationClassInfo.signature = signature == null ? null
69                  : signature + mockedTypeInfo.implementationSignature;
70          implementationClassInfo.interfaces = new String[] { name };
71          implementationClassInfo.superName = additionalInfo.superName;
72  
73          super.visit(version, CLASS_ACCESS, implementationClassDesc, implementationClassInfo);
74  
75          generateNoArgsConstructor();
76      }
77  
78      private void generateNoArgsConstructor() {
79          mw = cw.visitMethod(PUBLIC, "<init>", "()V", null, null);
80          mw.visitVarInsn(ALOAD, 0);
81          mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
82          generateEmptyImplementation();
83      }
84  
85      @Override
86      public AnnotationVisitor visitAnnotation(@NonNull String desc) {
87          return null;
88      }
89  
90      @Override
91      public void visitInnerClass(@NonNull String name, String outerName, String innerName, int access) {
92      }
93  
94      @Nullable
95      @Override
96      public FieldVisitor visitField(int access, @NonNull String name, @NonNull String desc, @Nullable String signature,
97              @Nullable Object value) {
98          return null;
99      }
100 
101     @Nullable
102     @Override
103     public MethodVisitor visitMethod(int access, @NonNull String name, @NonNull String desc, @Nullable String signature,
104             @Nullable String[] exceptions) {
105         if (!isSynthetic(access)) {
106             generateMethodImplementation(access, name, desc, signature, exceptions);
107         }
108 
109         return null;
110     }
111 
112     private void generateMethodImplementation(int access, @NonNull String name, @NonNull String desc,
113             @Nullable String signature, @Nullable String[] exceptions) {
114         if (!isStatic(access)) {
115             String methodNameAndDesc = name + desc;
116 
117             if (!implementedMethods.contains(methodNameAndDesc)) {
118                 generateMethodBody(access, name, desc, signature, exceptions);
119                 implementedMethods.add(methodNameAndDesc);
120             }
121         }
122     }
123 
124     private void generateMethodBody(int access, @NonNull String name, @NonNull String desc, @Nullable String signature,
125             @Nullable String[] exceptions) {
126         mw = cw.visitMethod(PUBLIC, name, desc, signature, exceptions);
127 
128         String className = null;
129 
130         if (signature != null) {
131             String subInterfaceOverride = getSubInterfaceOverride(mockedTypeInfo.genericTypeMap, name, signature);
132 
133             if (subInterfaceOverride != null) {
134                 className = interfaceName;
135                 // noinspection AssignmentToMethodParameter
136                 desc = subInterfaceOverride.substring(name.length());
137                 // noinspection AssignmentToMethodParameter
138                 signature = null;
139             }
140         }
141 
142         if (className == null) {
143             className = isOverrideOfMethodFromSuperInterface(name, desc) ? interfaceName : methodOwner;
144         }
145 
146         generateDirectCallToHandler(className, access, name, desc, signature);
147         generateReturnWithObjectAtTopOfTheStack(desc);
148         mw.visitMaxStack(1);
149     }
150 
151     @Nullable
152     private String getSubInterfaceOverride(@NonNull GenericTypeReflection genericTypeMap, @NonNull String name,
153             @NonNull String genericSignature) {
154         if (!implementedMethods.isEmpty()) {
155             GenericSignature parsedSignature = genericTypeMap.parseSignature(genericSignature);
156 
157             for (String implementedMethod : implementedMethods) {
158                 if (sameMethodName(implementedMethod, name) && parsedSignature.satisfiesSignature(implementedMethod)) {
159                     return implementedMethod;
160                 }
161             }
162         }
163 
164         return null;
165     }
166 
167     private static boolean sameMethodName(@NonNull String implementedMethod, @NonNull String name) {
168         return implementedMethod.startsWith(name) && implementedMethod.charAt(name.length()) == '(';
169     }
170 
171     private boolean isOverrideOfMethodFromSuperInterface(@NonNull String name, @NonNull String desc) {
172         if (!implementedMethods.isEmpty()) {
173             int p = desc.lastIndexOf(')');
174             String descNoReturnType = desc.substring(0, p + 1);
175 
176             for (String implementedMethod : implementedMethods) {
177                 if (sameMethodName(implementedMethod, name) && implementedMethod.contains(descNoReturnType)) {
178                     return true;
179                 }
180             }
181         }
182 
183         return false;
184     }
185 
186     @Override
187     public void visitEnd() {
188         assert initialSuperInterfaces != null;
189 
190         for (String superInterface : initialSuperInterfaces) {
191             generateImplementationsForInterfaceMethodsRecurringToSuperInterfaces(superInterface);
192         }
193     }
194 
195     private void generateImplementationsForInterfaceMethodsRecurringToSuperInterfaces(@NonNull String anInterface) {
196         methodOwner = anInterface;
197 
198         byte[] interfaceBytecode = ClassFile.getClassFile(anInterface);
199         ClassMetadataReader cmr = new ClassMetadataReader(interfaceBytecode, SIGNATURE);
200         String[] superInterfaces = cmr.getInterfaces();
201 
202         for (MethodInfo method : cmr.getMethods()) {
203             generateMethodImplementation(method.accessFlags, method.name, method.desc, method.signature, null);
204         }
205 
206         if (superInterfaces != null) {
207             for (String superInterface : superInterfaces) {
208                 generateImplementationsForInterfaceMethodsRecurringToSuperInterfaces(superInterface);
209             }
210         }
211     }
212 }