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.constantpool.ClassEntry;
30 import java.lang.classfile.constantpool.ConstantPool;
31 import java.lang.classfile.constantpool.ConstantPoolBuilder;
32 import java.lang.classfile.constantpool.PoolEntry;
33 import java.lang.runtime.ExactConversionsSupport;
34 import java.util.Arrays;
35
36 import jdk.internal.access.JavaLangAccess;
37 import jdk.internal.access.SharedSecrets;
38 import jdk.internal.vm.annotation.ForceInline;
39
40 import static java.lang.classfile.constantpool.PoolEntry.TAG_UTF8;
41 import static jdk.internal.util.ModifiedUtf.putChar;
42 import static jdk.internal.util.ModifiedUtf.utfLen;
43
44 public final class BufWriterImpl implements BufWriter {
45 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
46
47 private final ConstantPoolBuilder constantPool;
48 private final ClassFileImpl context;
49 private LabelContext labelContext;
50 private boolean labelsMatch;
51 private final ClassEntry thisClass;
52 private final int majorVersion;
53 byte[] elems;
54 int offset = 0;
55
56 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context) {
57 this(constantPool, context, 64, null, 0);
58 }
59
60 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context, int initialSize) {
61 this(constantPool, context, initialSize, null, 0);
62 }
63
64 public BufWriterImpl(ConstantPoolBuilder constantPool, ClassFileImpl context, int initialSize, ClassEntry thisClass, int majorVersion) {
65 this.constantPool = constantPool;
66 this.context = context;
67 elems = new byte[initialSize];
68 this.thisClass = thisClass;
69 this.majorVersion = majorVersion;
70 }
71
72 @Override
73 public ConstantPoolBuilder constantPool() {
74 return constantPool;
75 }
76
77 public LabelContext labelContext() {
78 return labelContext;
79 }
80
81 public void setLabelContext(LabelContext labelContext, boolean labelsMatch) {
82 this.labelContext = labelContext;
83 this.labelsMatch = labelsMatch;
84 }
85
86 public boolean labelsMatch(LabelContext lc) {
87 return labelsMatch
88 && labelContext instanceof DirectCodeBuilder dcb
89 && dcb.original == lc;
90 }
91
92 @Override
93 public boolean canWriteDirect(ConstantPool other) {
94 return constantPool.canWriteDirect(other);
95 }
96
97 public ClassEntry thisClass() {
98 return thisClass;
99 }
100
101 public int getMajorVersion() {
102 return majorVersion;
103 }
104
105 public ClassFileImpl context() {
106 return context;
107 }
108
109 @Override
110 public void writeU1(int x) {
111 reserveSpace(1);
112 elems[offset++] = (byte) x;
113 }
114
115 @ForceInline
116 @Override
117 public void writeU2(int x) {
118 reserveSpace(2);
119 byte[] elems = this.elems;
120 int offset = this.offset;
121 elems[offset ] = (byte) (x >> 8);
122 elems[offset + 1] = (byte) x;
123 this.offset = offset + 2;
124 }
125
126 @ForceInline
127 public void writeU1U1(int x1, int x2) {
128 reserveSpace(2);
129 byte[] elems = this.elems;
130 int offset = this.offset;
131 elems[offset ] = (byte) x1;
132 elems[offset + 1] = (byte) x2;
133 this.offset = offset + 2;
134 }
135
136 public void writeU1U2(int u1, int u2) {
137 reserveSpace(3);
138 byte[] elems = this.elems;
139 int offset = this.offset;
140 elems[offset ] = (byte) u1;
141 elems[offset + 1] = (byte) (u2 >> 8);
142 elems[offset + 2] = (byte) u2;
143 this.offset = offset + 3;
144 }
145
146 public void writeU1U1U1(int x1, int x2, int x3) {
147 reserveSpace(3);
148 byte[] elems = this.elems;
149 int offset = this.offset;
150 elems[offset ] = (byte) x1;
151 elems[offset + 1] = (byte) x2;
152 elems[offset + 2] = (byte) x3;
153 this.offset = offset + 3;
154 }
155
156 public void writeU1U1U2(int x1, int x2, int x3) {
157 reserveSpace(4);
158 byte[] elems = this.elems;
159 int offset = this.offset;
160 elems[offset ] = (byte) x1;
161 elems[offset + 1] = (byte) x2;
162 elems[offset + 2] = (byte) (x3 >> 8);
163 elems[offset + 3] = (byte) x3;
164 this.offset = offset + 4;
165 }
166
167 public void writeU1U2U2(int x1, int x2, int x3) {
168 reserveSpace(5);
169 byte[] elems = this.elems;
170 int offset = this.offset;
171 elems[offset ] = (byte) x1;
172 elems[offset + 1] = (byte) (x2 >> 8);
173 elems[offset + 2] = (byte) x2;
174 elems[offset + 3] = (byte) (x3 >> 8);
175 elems[offset + 4] = (byte) x3;
176 this.offset = offset + 5;
177 }
178
179 public void writeU2U1(int x1, int x2) {
180 reserveSpace(3);
181 byte[] elems = this.elems;
182 int offset = this.offset;
183 elems[offset ] = (byte) (x1 >> 8);
184 elems[offset + 1] = (byte) x1;
185 elems[offset + 2] = (byte) x2;
186 this.offset = offset + 3;
187 }
188
189 public void writeU2U2(int x1, int x2) {
190 reserveSpace(4);
191 byte[] elems = this.elems;
192 int offset = this.offset;
193 elems[offset ] = (byte) (x1 >> 8);
194 elems[offset + 1] = (byte) x1;
195 elems[offset + 2] = (byte) (x2 >> 8);
196 elems[offset + 3] = (byte) x2;
197 this.offset = offset + 4;
198 }
199
200 public void writeU2U2U2(int x1, int x2, int x3) {
201 reserveSpace(6);
202 byte[] elems = this.elems;
203 int offset = this.offset;
204 elems[offset ] = (byte) (x1 >> 8);
205 elems[offset + 1] = (byte) x1;
206 elems[offset + 2] = (byte) (x2 >> 8);
207 elems[offset + 3] = (byte) x2;
208 elems[offset + 4] = (byte) (x3 >> 8);
209 elems[offset + 5] = (byte) x3;
210 this.offset = offset + 6;
211 }
212
213 @Override
214 public void writeInt(int x) {
215 reserveSpace(4);
216 byte[] elems = this.elems;
217 int offset = this.offset;
218 elems[offset ] = (byte) (x >> 24);
219 elems[offset + 1] = (byte) (x >> 16);
220 elems[offset + 2] = (byte) (x >> 8);
221 elems[offset + 3] = (byte) x;
222 this.offset = offset + 4;
223 }
224
225 public void writeIntInt(int x1, int x2) {
226 reserveSpace(8);
227 byte[] elems = this.elems;
228 int offset = this.offset;
229 elems[offset ] = (byte) (x1 >> 24);
230 elems[offset + 1] = (byte) (x1 >> 16);
231 elems[offset + 2] = (byte) (x1 >> 8);
232 elems[offset + 3] = (byte) x1;
233 elems[offset + 4] = (byte) (x2 >> 24);
234 elems[offset + 5] = (byte) (x2 >> 16);
235 elems[offset + 6] = (byte) (x2 >> 8);
236 elems[offset + 7] = (byte) x2;
237 this.offset = offset + 8;
238 }
239
240 @Override
241 public void writeFloat(float x) {
242 writeInt(Float.floatToIntBits(x));
243 }
244
245 @Override
246 public void writeLong(long x) {
247 reserveSpace(8);
248 byte[] elems = this.elems;
249 int offset = this.offset;
250 elems[offset ] = (byte) (x >> 56);
251 elems[offset + 1] = (byte) (x >> 48);
252 elems[offset + 2] = (byte) (x >> 40);
253 elems[offset + 3] = (byte) (x >> 32);
254 elems[offset + 4] = (byte) (x >> 24);
255 elems[offset + 5] = (byte) (x >> 16);
256 elems[offset + 6] = (byte) (x >> 8);
257 elems[offset + 7] = (byte) x;
258 this.offset = offset + 8;
259 }
260
261 @Override
262 public void writeDouble(double x) {
263 writeLong(Double.doubleToLongBits(x));
264 }
265
266 @Override
267 public void writeBytes(byte[] arr) {
268 writeBytes(arr, 0, arr.length);
269 }
270
271 public void writeBytes(BufWriterImpl other) {
272 writeBytes(other.elems, 0, other.offset);
273 }
274
275 @SuppressWarnings("deprecation")
276 void writeUtfEntry(String str) {
277 int strlen = str.length();
278 int countNonZeroAscii = JLA.countNonZeroAscii(str);
279 long utflenLong = utfLen(str, countNonZeroAscii);
280 if (!ExactConversionsSupport.isLongToCharExact(utflenLong)) {
281 throw new IllegalArgumentException("utf8 length out of range of u2: " + utflenLong);
282 }
283 int utflen = (int)utflenLong;
284 reserveSpace(utflen + 3);
285
286 int offset = this.offset;
287 byte[] elems = this.elems;
288
289 elems[offset ] = (byte) TAG_UTF8;
290 elems[offset + 1] = (byte) (utflen >> 8);
291 elems[offset + 2] = (byte) utflen;
292 offset += 3;
293
294 str.getBytes(0, countNonZeroAscii, elems, offset);
295 offset += countNonZeroAscii;
296
297 for (int i = countNonZeroAscii; i < strlen; i++) {
298 offset = putChar(elems, offset, str.charAt(i));
299 }
300
301 this.offset = offset;
302 }
303
304 @Override
305 public void writeBytes(byte[] arr, int start, int length) {
306 reserveSpace(length);
307 System.arraycopy(arr, start, elems, offset, length);
308 offset += length;
309 }
310
311 @Override
312 public void patchInt(int offset, int size, int value) {
313 int prevOffset = this.offset;
314 this.offset = offset;
315 writeIntBytes(size, value);
316 this.offset = prevOffset;
317 }
318
319 public void patchU2(int offset, int x) {
320 byte[] elems = this.elems;
321 elems[offset ] = (byte) (x >> 8);
322 elems[offset + 1] = (byte) x;
323 }
324
325 public void patchInt(int offset, int x) {
326 byte[] elems = this.elems;
327 elems[offset ] = (byte) (x >> 24);
328 elems[offset + 1] = (byte) (x >> 16);
329 elems[offset + 2] = (byte) (x >> 8);
330 elems[offset + 3] = (byte) x;
331 }
332
333 @Override
334 public void writeIntBytes(int intSize, long intValue) {
335 reserveSpace(intSize);
336 for (int i = 0; i < intSize; i++) {
337 elems[offset++] = (byte) ((intValue >> 8 * (intSize - i - 1)) & 0xFF);
338 }
339 }
340
341 /**
342 * Skip a few bytes in the output buffer. The skipped area has undefined value.
343 * @param bytes number of bytes to skip
344 * @return the index, for later patching
345 */
346 public int skip(int bytes) {
347 int now = offset;
348 reserveSpace(bytes);
349 offset += bytes;
350 return now;
351 }
352
353 @Override
354 public void reserveSpace(int freeBytes) {
355 int minCapacity = offset + freeBytes;
356 if (minCapacity > elems.length) {
357 grow(minCapacity);
358 }
359 }
360
361 private void grow(int minCapacity) {
362 int newsize = elems.length * 2;
363 while (minCapacity > newsize) {
364 newsize *= 2;
365 }
366 elems = Arrays.copyOf(elems, newsize);
367 }
368
369 @Override
370 public int size() {
371 return offset;
372 }
373
374 public RawBytecodeHelper.CodeRange bytecodeView() {
375 return RawBytecodeHelper.of(elems, offset);
376 }
377
378 public void copyTo(byte[] array, int bufferOffset) {
379 System.arraycopy(elems, 0, array, bufferOffset, size());
380 }
381
382 // writeIndex methods ensure that any CP info written
383 // is relative to the correct constant pool
384
385 public int cpIndex(PoolEntry entry) {
386 int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index();
387 if (idx < 1 || idx > Character.MAX_VALUE)
388 throw invalidIndex(idx, entry);
389 return idx;
390 }
391
392 public int cpIndexOrZero(PoolEntry entry) {
393 if (entry == null || entry.index() == 0)
394 return 0;
395 return cpIndex(entry);
396 }
397
398 @ForceInline
399 @Override
400 public void writeIndex(PoolEntry entry) {
401 writeU2(cpIndex(entry));
402 }
403
404 // Null checks entry
405 public void writeIndex(int bytecode, PoolEntry entry) {
406 writeU1U2(bytecode, cpIndex(entry));
407 }
408
409 static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) {
410 return new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry);
411 }
412
413 @Override
414 public void writeIndexOrZero(PoolEntry entry) {
415 writeU2(cpIndexOrZero(entry));
416 }
417
418 /**
419 * Join head and tail into an exact-size buffer
420 */
421 static byte[] join(BufWriterImpl head, BufWriterImpl tail) {
422 byte[] result = new byte[head.size() + tail.size()];
423 head.copyTo(result, 0);
424 tail.copyTo(result, head.size());
425 return result;
426 }
427 }