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