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 if (!ExactConversionsSupport.isLongToCharExact(utflenLong)) {
324 throw new IllegalArgumentException("utf8 length out of range of u2: " + utflenLong);
325 }
326 int utflen = (int)utflenLong;
327 reserveSpace(utflen + 3);
328
329 int offset = this.offset;
330 byte[] elems = this.elems;
331
332 elems[offset ] = (byte) TAG_UTF8;
333 elems[offset + 1] = (byte) (utflen >> 8);
334 elems[offset + 2] = (byte) utflen;
335 offset += 3;
336
337 str.getBytes(0, countNonZeroAscii, elems, offset);
338 offset += countNonZeroAscii;
339
340 for (int i = countNonZeroAscii; i < strlen; i++) {
341 offset = putChar(elems, offset, str.charAt(i));
342 }
343
344 this.offset = offset;
345 }
346
347 @Override
348 public void writeBytes(byte[] arr, int start, int length) {
349 reserveSpace(length);
350 System.arraycopy(arr, start, elems, offset, length);
351 offset += length;
352 }
353
354 @Override
355 public void patchInt(int offset, int size, int value) {
356 int prevOffset = this.offset;
357 this.offset = offset;
358 writeIntBytes(size, value);
359 this.offset = prevOffset;
360 }
361
362 public void patchU2(int offset, int x) {
363 byte[] elems = this.elems;
364 elems[offset ] = (byte) (x >> 8);
365 elems[offset + 1] = (byte) x;
366 }
367
368 public void patchInt(int offset, int x) {
369 byte[] elems = this.elems;
370 elems[offset ] = (byte) (x >> 24);
371 elems[offset + 1] = (byte) (x >> 16);
372 elems[offset + 2] = (byte) (x >> 8);
373 elems[offset + 3] = (byte) x;
374 }
375
376 @Override
377 public void writeIntBytes(int intSize, long intValue) {
378 reserveSpace(intSize);
379 for (int i = 0; i < intSize; i++) {
380 elems[offset++] = (byte) ((intValue >> 8 * (intSize - i - 1)) & 0xFF);
381 }
382 }
383
384 /**
385 * Skip a few bytes in the output buffer. The skipped area has undefined value.
386 * @param bytes number of bytes to skip
387 * @return the index, for later patching
388 */
389 public int skip(int bytes) {
390 int now = offset;
391 reserveSpace(bytes);
392 offset += bytes;
393 return now;
394 }
395
396 @Override
397 public void reserveSpace(int freeBytes) {
398 int minCapacity = offset + freeBytes;
399 if (minCapacity > elems.length) {
400 grow(minCapacity);
401 }
402 }
403
404 private void grow(int minCapacity) {
405 int newsize = elems.length * 2;
406 while (minCapacity > newsize) {
407 newsize *= 2;
408 }
409 elems = Arrays.copyOf(elems, newsize);
410 }
411
412 @Override
413 public int size() {
414 return offset;
415 }
416
417 public RawBytecodeHelper.CodeRange bytecodeView() {
418 return RawBytecodeHelper.of(elems, offset);
419 }
420
421 public void copyTo(byte[] array, int bufferOffset) {
422 System.arraycopy(elems, 0, array, bufferOffset, size());
423 }
424
425 // writeIndex methods ensure that any CP info written
426 // is relative to the correct constant pool
427
428 public int cpIndex(PoolEntry entry) {
429 int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index();
430 if (idx < 1 || idx > Character.MAX_VALUE)
431 throw invalidIndex(idx, entry);
432 return idx;
433 }
434
435 public int cpIndexOrZero(PoolEntry entry) {
436 if (entry == null || entry.index() == 0)
437 return 0;
438 return cpIndex(entry);
439 }
440
441 @ForceInline
442 @Override
443 public void writeIndex(PoolEntry entry) {
444 writeU2(cpIndex(entry));
445 }
446
447 // Null checks entry
448 public void writeIndex(int bytecode, PoolEntry entry) {
449 writeU1U2(bytecode, cpIndex(entry));
450 }
451
452 static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) {
453 return new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry);
454 }
455
456 @Override
457 public void writeIndexOrZero(PoolEntry entry) {
458 writeU2(cpIndexOrZero(entry));
459 }
460
461 /**
462 * Join head and tail into an exact-size buffer
463 */
464 static byte[] join(BufWriterImpl head, BufWriterImpl tail) {
465 byte[] result = new byte[head.size() + tail.size()];
466 head.copyTo(result, 0);
467 tail.copyTo(result, head.size());
468 return result;
469 }
470 }