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