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 }