1 /*
2 * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation. Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26 package jdk.internal.classfile.impl;
27
28 import java.lang.classfile.BufWriter;
29 import java.lang.classfile.ClassModel;
30 import java.lang.classfile.constantpool.ClassEntry;
31 import java.lang.classfile.constantpool.ConstantPool;
32 import java.lang.classfile.constantpool.ConstantPoolBuilder;
33 import java.lang.classfile.constantpool.PoolEntry;
34 import java.lang.runtime.ExactConversionsSupport;
35 import java.util.Arrays;
36 import java.util.HashSet;
37
38 import jdk.internal.access.JavaLangAccess;
39 import jdk.internal.access.SharedSecrets;
40 import jdk.internal.vm.annotation.ForceInline;
41
42 import static java.lang.classfile.ClassFile.*;
43 import static java.lang.classfile.constantpool.PoolEntry.TAG_UTF8;
44 import static jdk.internal.util.ModifiedUtf.putChar;
45 import static jdk.internal.util.ModifiedUtf.utfLen;
46
47 public final class BufWriterImpl implements BufWriter {
48 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
49
50 private final ConstantPoolBuilder constantPool;
51 private final ClassFileImpl context;
52 private LabelContext labelContext;
53 private WritableField.UnsetField[] strictInstanceFields; // do not modify array contents
54 private ClassModel lastStrictCheckClass; // buf writer has short life, so do not need weak here
55 private boolean lastStrictCheckResult;
56 private boolean labelsMatch;
57 private final ClassEntry thisClass;
58 private final int majorVersion;
59 byte[] elems;
60 int offset = 0;
61
62 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context) {
63 this(constantPool, context, 64, null, 0);
64 }
65
66 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context, int initialSize) {
67 this(constantPool, context, initialSize, null, 0);
68 }
69
70 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context, int initialSize, ClassEntry thisClass, int majorVersion) {
71 this.constantPool = constantPool;
72 this.context = context;
73 elems = new byte[initialSize];
74 this.thisClass = thisClass;
75 this.majorVersion = majorVersion;
76 }
77
78 public boolean strictFieldsMatch(ClassModel cm) {
79 // We have a cache because this check will be called multiple times
80 // if a MethodModel is sent wholesale
81 if (lastStrictCheckClass == cm) {
82 return lastStrictCheckResult;
83 }
84
85 var result = doStrictFieldsMatchCheck(cm);
86 lastStrictCheckClass = cm;
87 lastStrictCheckResult = result;
88 return result;
89 }
90
91 private boolean doStrictFieldsMatchCheck(ClassModel cm) {
92 // TODO only check for preview class files?
93 // UTF8 Entry can be used as equality objects
94 var checks = new HashSet<>(Arrays.asList(getStrictInstanceFields()));
95 for (var f : cm.fields()) {
96 if ((f.flags().flagsMask() & (ACC_STATIC | ACC_STRICT_INIT)) == ACC_STRICT_INIT) {
97 if (!checks.remove(new WritableField.UnsetField(f.fieldName(), f.fieldType()))) {
98 return false; // Field mismatch!
99 }
100 }
101 }
102 return checks.isEmpty();
103 }
104
105 @Override
106 public ConstantPoolBuilder constantPool() {
107 return constantPool;
108 }
109
110 public LabelContext labelContext() {
111 return labelContext;
112 }
113
114 public void setLabelContext(LabelContext labelContext, boolean labelsMatch) {
115 this.labelContext = labelContext;
116 this.labelsMatch = labelsMatch;
117 }
118
119 public WritableField.UnsetField[] getStrictInstanceFields() {
120 assert strictInstanceFields != null : "should access only after setter call in DirectClassBuilder";
121 return strictInstanceFields;
122 }
123
124 public void setStrictInstanceFields(WritableField.UnsetField[] strictInstanceFields) {
125 this.strictInstanceFields = strictInstanceFields;
126 }
127
128
129 public boolean labelsMatch(LabelContext lc) {
130 return labelsMatch
131 && labelContext instanceof DirectCodeBuilder dcb
132 && dcb.original == lc;
133 }
134
135 @Override
136 public boolean canWriteDirect(ConstantPool other) {
137 return constantPool.canWriteDirect(other);
138 }
139
140 public ClassEntry thisClass() {
141 return thisClass;
142 }
143
144 public int getMajorVersion() {
145 return majorVersion;
146 }
147
148 public ClassFileImpl context() {
149 return context;
150 }
151
152 @Override
153 public void writeU1(int x) {
154 reserveSpace(1);
155 elems[offset++] = (byte) x;
156 }
157
158 @ForceInline
159 @Override
160 public void writeU2(int x) {
161 reserveSpace(2);
162 byte[] elems = this.elems;
163 int offset = this.offset;
164 elems[offset ] = (byte) (x >> 8);
165 elems[offset + 1] = (byte) x;
166 this.offset = offset + 2;
167 }
168
169 @ForceInline
170 public void writeU1U1(int x1, int x2) {
171 reserveSpace(2);
172 byte[] elems = this.elems;
173 int offset = this.offset;
174 elems[offset ] = (byte) x1;
175 elems[offset + 1] = (byte) x2;
176 this.offset = offset + 2;
177 }
178
179 public void writeU1U2(int u1, int u2) {
180 reserveSpace(3);
181 byte[] elems = this.elems;
182 int offset = this.offset;
183 elems[offset ] = (byte) u1;
184 elems[offset + 1] = (byte) (u2 >> 8);
185 elems[offset + 2] = (byte) u2;
186 this.offset = offset + 3;
187 }
188
189 public void writeU1U1U1(int x1, int x2, int x3) {
190 reserveSpace(3);
191 byte[] elems = this.elems;
192 int offset = this.offset;
193 elems[offset ] = (byte) x1;
194 elems[offset + 1] = (byte) x2;
195 elems[offset + 2] = (byte) x3;
196 this.offset = offset + 3;
197 }
198
199 public void writeU1U1U2(int x1, int x2, int x3) {
200 reserveSpace(4);
201 byte[] elems = this.elems;
202 int offset = this.offset;
203 elems[offset ] = (byte) x1;
204 elems[offset + 1] = (byte) x2;
205 elems[offset + 2] = (byte) (x3 >> 8);
206 elems[offset + 3] = (byte) x3;
207 this.offset = offset + 4;
208 }
209
210 public void writeU1U2U2(int x1, int x2, int x3) {
211 reserveSpace(5);
212 byte[] elems = this.elems;
213 int offset = this.offset;
214 elems[offset ] = (byte) x1;
215 elems[offset + 1] = (byte) (x2 >> 8);
216 elems[offset + 2] = (byte) x2;
217 elems[offset + 3] = (byte) (x3 >> 8);
218 elems[offset + 4] = (byte) x3;
219 this.offset = offset + 5;
220 }
221
222 public void writeU2U1(int x1, int x2) {
223 reserveSpace(3);
224 byte[] elems = this.elems;
225 int offset = this.offset;
226 elems[offset ] = (byte) (x1 >> 8);
227 elems[offset + 1] = (byte) x1;
228 elems[offset + 2] = (byte) x2;
229 this.offset = offset + 3;
230 }
231
232 public void writeU2U2(int x1, int x2) {
233 reserveSpace(4);
234 byte[] elems = this.elems;
235 int offset = this.offset;
236 elems[offset ] = (byte) (x1 >> 8);
237 elems[offset + 1] = (byte) x1;
238 elems[offset + 2] = (byte) (x2 >> 8);
239 elems[offset + 3] = (byte) x2;
240 this.offset = offset + 4;
241 }
242
243 public void writeU2U2U2(int x1, int x2, int x3) {
244 reserveSpace(6);
245 byte[] elems = this.elems;
246 int offset = this.offset;
247 elems[offset ] = (byte) (x1 >> 8);
248 elems[offset + 1] = (byte) x1;
249 elems[offset + 2] = (byte) (x2 >> 8);
250 elems[offset + 3] = (byte) x2;
251 elems[offset + 4] = (byte) (x3 >> 8);
252 elems[offset + 5] = (byte) x3;
253 this.offset = offset + 6;
254 }
255
256 @Override
257 public void writeInt(int x) {
258 reserveSpace(4);
259 byte[] elems = this.elems;
260 int offset = this.offset;
261 elems[offset ] = (byte) (x >> 24);
262 elems[offset + 1] = (byte) (x >> 16);
263 elems[offset + 2] = (byte) (x >> 8);
264 elems[offset + 3] = (byte) x;
265 this.offset = offset + 4;
266 }
267
268 public void writeIntInt(int x1, int x2) {
269 reserveSpace(8);
270 byte[] elems = this.elems;
271 int offset = this.offset;
272 elems[offset ] = (byte) (x1 >> 24);
273 elems[offset + 1] = (byte) (x1 >> 16);
274 elems[offset + 2] = (byte) (x1 >> 8);
275 elems[offset + 3] = (byte) x1;
276 elems[offset + 4] = (byte) (x2 >> 24);
277 elems[offset + 5] = (byte) (x2 >> 16);
278 elems[offset + 6] = (byte) (x2 >> 8);
279 elems[offset + 7] = (byte) x2;
280 this.offset = offset + 8;
281 }
282
283 @Override
284 public void writeFloat(float x) {
285 writeInt(Float.floatToIntBits(x));
286 }
287
288 @Override
289 public void writeLong(long x) {
290 reserveSpace(8);
291 byte[] elems = this.elems;
292 int offset = this.offset;
293 elems[offset ] = (byte) (x >> 56);
294 elems[offset + 1] = (byte) (x >> 48);
295 elems[offset + 2] = (byte) (x >> 40);
296 elems[offset + 3] = (byte) (x >> 32);
297 elems[offset + 4] = (byte) (x >> 24);
298 elems[offset + 5] = (byte) (x >> 16);
299 elems[offset + 6] = (byte) (x >> 8);
300 elems[offset + 7] = (byte) x;
301 this.offset = offset + 8;
302 }
303
304 @Override
305 public void writeDouble(double x) {
306 writeLong(Double.doubleToLongBits(x));
307 }
308
309 @Override
310 public void writeBytes(byte[] arr) {
311 writeBytes(arr, 0, arr.length);
312 }
313
314 public void writeBytes(BufWriterImpl other) {
315 writeBytes(other.elems, 0, other.offset);
316 }
317
318 @SuppressWarnings("deprecation")
319 void writeUtfEntry(String str) {
320 int strlen = str.length();
321 int countNonZeroAscii = JLA.countNonZeroAscii(str);
322 long utflenLong = utfLen(str, countNonZeroAscii);
323 // Utf8Entry should always be writable
324 assert ExactConversionsSupport.isLongToCharExact(utflenLong) : utflenLong;
325 int utflen = (int) utflenLong;
326 reserveSpace(utflen + 3);
327
328 int offset = this.offset;
329 byte[] elems = this.elems;
330
331 elems[offset ] = (byte) TAG_UTF8;
332 elems[offset + 1] = (byte) (utflen >> 8);
333 elems[offset + 2] = (byte) utflen;
334 offset += 3;
335
336 str.getBytes(0, countNonZeroAscii, elems, offset);
337 offset += countNonZeroAscii;
338
339 for (int i = countNonZeroAscii; i < strlen; i++) {
340 offset = putChar(elems, offset, str.charAt(i));
341 }
342
343 this.offset = offset;
344 }
345
346 @Override
347 public void writeBytes(byte[] arr, int start, int length) {
348 reserveSpace(length);
349 System.arraycopy(arr, start, elems, offset, length);
350 offset += length;
351 }
352
353 @Override
354 public void patchInt(int offset, int size, int value) {
355 int prevOffset = this.offset;
356 this.offset = offset;
357 writeIntBytes(size, value);
358 this.offset = prevOffset;
359 }
360
361 public void patchU2(int offset, int x) {
362 byte[] elems = this.elems;
363 elems[offset ] = (byte) (x >> 8);
364 elems[offset + 1] = (byte) x;
365 }
366
367 public void patchInt(int offset, int x) {
368 byte[] elems = this.elems;
369 elems[offset ] = (byte) (x >> 24);
370 elems[offset + 1] = (byte) (x >> 16);
371 elems[offset + 2] = (byte) (x >> 8);
372 elems[offset + 3] = (byte) x;
373 }
374
375 @Override
376 public void writeIntBytes(int intSize, long intValue) {
377 reserveSpace(intSize);
378 for (int i = 0; i < intSize; i++) {
379 elems[offset++] = (byte) ((intValue >> 8 * (intSize - i - 1)) & 0xFF);
380 }
381 }
382
383 /**
384 * Skip a few bytes in the output buffer. The skipped area has undefined value.
385 * @param bytes number of bytes to skip
386 * @return the index, for later patching
387 */
388 public int skip(int bytes) {
389 int now = offset;
390 reserveSpace(bytes);
391 offset += bytes;
392 return now;
393 }
394
395 @Override
396 public void reserveSpace(int freeBytes) {
397 int minCapacity = offset + freeBytes;
398 if (minCapacity > elems.length) {
399 grow(minCapacity);
400 }
401 }
402
403 private void grow(int minCapacity) {
404 int newsize = elems.length * 2;
405 while (minCapacity > newsize) {
406 newsize *= 2;
407 }
408 elems = Arrays.copyOf(elems, newsize);
409 }
410
411 @Override
412 public int size() {
413 return offset;
414 }
415
416 public RawBytecodeHelper.CodeRange bytecodeView() {
417 return RawBytecodeHelper.of(elems, offset);
418 }
419
420 public void copyTo(byte[] array, int bufferOffset) {
421 System.arraycopy(elems, 0, array, bufferOffset, size());
422 }
423
424 // writeIndex methods ensure that any CP info written
425 // is relative to the correct constant pool
426
427 public int cpIndex(PoolEntry entry) {
428 int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index();
429 if (idx < 1 || idx > Character.MAX_VALUE)
430 throw invalidIndex(idx, entry);
431 return idx;
432 }
433
434 public int cpIndexOrZero(PoolEntry entry) {
435 if (entry == null || entry.index() == 0)
436 return 0;
437 return cpIndex(entry);
438 }
439
440 @ForceInline
441 @Override
442 public void writeIndex(PoolEntry entry) {
443 writeU2(cpIndex(entry));
444 }
445
446 // Null checks entry
447 public void writeIndex(int bytecode, PoolEntry entry) {
448 writeU1U2(bytecode, cpIndex(entry));
449 }
450
451 static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) {
452 return new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry);
453 }
454
455 @Override
456 public void writeIndexOrZero(PoolEntry entry) {
457 writeU2(cpIndexOrZero(entry));
458 }
459
460 /**
461 * Join head and tail into an exact-size buffer
462 */
463 static byte[] join(BufWriterImpl head, BufWriterImpl tail) {
464 byte[] result = new byte[head.size() + tail.size()];
465 head.copyTo(result, 0);
466 tail.copyTo(result, head.size());
467 return result;
468 }
469 }