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