1
2
3
4
5
6 package mockit.asm.classes;
7
8 import edu.umd.cs.findbugs.annotations.NonNull;
9 import edu.umd.cs.findbugs.annotations.Nullable;
10
11 import java.util.ArrayList;
12 import java.util.List;
13
14 import mockit.asm.BaseWriter;
15 import mockit.asm.SignatureWriter;
16 import mockit.asm.constantPool.AttributeWriter;
17 import mockit.asm.constantPool.ConstantPoolGeneration;
18 import mockit.asm.constantPool.DynamicItem;
19 import mockit.asm.fields.FieldVisitor;
20 import mockit.asm.jvmConstants.ClassVersion;
21 import mockit.asm.methods.MethodWriter;
22 import mockit.asm.util.ByteVector;
23 import mockit.asm.util.MethodHandle;
24 import mockit.internal.util.ClassLoad;
25
26 import org.checkerframework.checker.index.qual.NonNegative;
27
28
29
30
31
32
33
34
35 @SuppressWarnings({ "OverlyCoupledClass", "ClassWithTooManyFields" })
36 public final class ClassWriter extends ClassVisitor {
37
38
39
40 @NonNull
41 public final byte[] code;
42
43
44
45
46 private int classVersion;
47
48
49
50
51 @NonNegative
52 private int nameItemIndex;
53
54
55
56
57 private String thisName;
58
59
60
61
62 @NonNegative
63 private int superNameItemIndex;
64
65 @NonNull
66 private final List<AttributeWriter> attributeWriters;
67 @Nullable
68 final BootstrapMethodsWriter bootstrapMethodsWriter;
69 @Nullable
70 private InterfaceWriter interfaceWriter;
71 @Nullable
72 private InnerClassesWriter innerClassesWriter;
73 @NonNull
74 private final List<FieldVisitor> fields;
75 @NonNull
76 private final List<MethodWriter> methods;
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 public ClassWriter(@NonNull ClassReader classReader) {
96 code = classReader.code;
97 classVersion = classReader.getVersion();
98
99 cp = new ConstantPoolGeneration();
100
101 bootstrapMethodsWriter = classReader.positionAtBootstrapMethodsAttribute()
102 ? new BootstrapMethodsWriter(cp, classReader)
103 : null;
104 new ConstantPoolCopying(classReader, this).copyPool(bootstrapMethodsWriter);
105
106 attributeWriters = new ArrayList<>(5);
107
108 if (bootstrapMethodsWriter != null) {
109 attributeWriters.add(bootstrapMethodsWriter);
110 }
111
112 fields = new ArrayList<>();
113 methods = new ArrayList<>();
114 }
115
116 public int getClassVersion() {
117 return classVersion;
118 }
119
120 public String getInternalClassName() {
121 return thisName;
122 }
123
124 @Override
125 public void visit(int version, int access, @NonNull String name, @NonNull ClassInfo additionalInfo) {
126 classVersion = version;
127 classOrMemberAccess = access;
128 nameItemIndex = cp.newClass(name);
129 thisName = name;
130
131 createMarkerAttributes(version);
132
133 String superName = additionalInfo.superName;
134 superNameItemIndex = superName == null ? 0 : cp.newClass(superName);
135
136 createInterfaceWriterIfApplicable(additionalInfo.interfaces);
137 createSignatureWriterIfApplicable(additionalInfo.signature);
138 createSourceFileWriterIfApplicable(additionalInfo.sourceFileName);
139 createNestWritersIfApplicable(additionalInfo.hostClassName, additionalInfo.nestMembers);
140
141 if (superName != null) {
142 ClassLoad.addSuperClass(name, superName);
143 }
144 }
145
146 private void createInterfaceWriterIfApplicable(@NonNull String[] interfaces) {
147 if (interfaces.length > 0) {
148 interfaceWriter = new InterfaceWriter(cp, interfaces);
149 }
150 }
151
152 private void createSignatureWriterIfApplicable(@Nullable String signature) {
153 if (signature != null) {
154 attributeWriters.add(new SignatureWriter(cp, signature));
155 }
156 }
157
158 private void createSourceFileWriterIfApplicable(@Nullable String sourceFileName) {
159 if (sourceFileName != null) {
160 attributeWriters.add(new SourceFileWriter(cp, sourceFileName));
161 }
162 }
163
164 private void createNestWritersIfApplicable(@Nullable String hostClassName, @Nullable String[] memberClassNames) {
165 if (hostClassName != null) {
166 attributeWriters.add(new NestHostWriter(cp, hostClassName));
167 } else if (memberClassNames != null) {
168 attributeWriters.add(new NestMembersWriter(cp, memberClassNames));
169 }
170 }
171
172 @Override
173 public void visitInnerClass(@NonNull String name, @Nullable String outerName, @Nullable String innerName,
174 int access) {
175 if (innerClassesWriter == null) {
176 innerClassesWriter = new InnerClassesWriter(cp);
177 attributeWriters.add(innerClassesWriter);
178 }
179
180 innerClassesWriter.add(name, outerName, innerName, access);
181 }
182
183 @NonNull
184 @Override
185 public FieldVisitor visitField(int access, @NonNull String name, @NonNull String desc, @Nullable String signature,
186 @Nullable Object value) {
187 FieldVisitor field = new FieldVisitor(this, access, name, desc, signature, value);
188 fields.add(field);
189 return field;
190 }
191
192 @NonNull
193 @Override
194 public MethodWriter visitMethod(int access, @NonNull String name, @NonNull String desc, @Nullable String signature,
195 @Nullable String[] exceptions) {
196 boolean computeFrames = classVersion >= ClassVersion.V7;
197 MethodWriter method = new MethodWriter(this, access, name, desc, signature, exceptions, computeFrames);
198 methods.add(method);
199 return method;
200 }
201
202
203
204
205 @NonNull
206 @Override
207 public byte[] toByteArray() {
208 cp.checkConstantPoolMaxSize();
209
210 int size = getBytecodeSize();
211
212
213
214 ByteVector out = new ByteVector(size);
215
216 putClassAttributes(out);
217 putAnnotations(out);
218 return out.getData();
219 }
220
221 @NonNegative
222 private int getBytecodeSize() {
223 int size = 24 + getMarkerAttributesSize() + getFieldsSize() + getMethodsSize();
224
225 if (interfaceWriter != null) {
226 size += interfaceWriter.getSize();
227 }
228
229 for (AttributeWriter attributeWriter : attributeWriters) {
230 size += attributeWriter.getSize();
231 }
232
233 return size + getAnnotationsSize() + cp.getSize();
234 }
235
236 @NonNegative
237 private int getFieldsSize() {
238 int size = 0;
239
240 for (FieldVisitor fv : fields) {
241 size += fv.getSize();
242 }
243
244 return size;
245 }
246
247 @NonNegative
248 private int getMethodsSize() {
249 int size = 0;
250
251 for (MethodWriter mb : methods) {
252 size += mb.getSize();
253 }
254
255 return size;
256 }
257
258 private void putClassAttributes(@NonNull ByteVector out) {
259 out.putInt(0xCAFEBABE).putInt(classVersion);
260 cp.put(out);
261
262 putAccess(out, 0);
263 out.putShort(nameItemIndex).putShort(superNameItemIndex);
264
265 if (interfaceWriter == null) {
266 out.putShort(0);
267 } else {
268 interfaceWriter.put(out);
269 }
270
271 BaseWriter.put(out, fields);
272 BaseWriter.put(out, methods);
273
274 int attributeCount = getAttributeCount();
275 out.putShort(attributeCount);
276
277 for (AttributeWriter attributeWriter : attributeWriters) {
278 attributeWriter.put(out);
279 }
280
281 putMarkerAttributes(out);
282 }
283
284 @NonNegative
285 private int getAttributeCount() {
286 int attributeCount = getMarkerAttributeCount() + attributeWriters.size();
287
288 if (annotations != null) {
289 attributeCount++;
290 }
291
292 return attributeCount;
293 }
294
295 @NonNull
296 public DynamicItem addInvokeDynamicReference(@NonNull String name, @NonNull String desc, @NonNull MethodHandle bsm,
297 @NonNull Object... bsmArgs) {
298 assert bootstrapMethodsWriter != null;
299 return bootstrapMethodsWriter.addInvokeDynamicReference(name, desc, bsm, bsmArgs);
300 }
301
302 public boolean isJava6OrNewer() {
303 return classVersion >= ClassVersion.V6;
304 }
305 }