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