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