1 /*
  2  * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package jdk.internal.classfile.impl;
 26 
 27 import java.lang.classfile.*;
 28 import java.lang.classfile.attribute.CodeAttribute;
 29 import jdk.internal.classfile.components.ClassPrinter;
 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.ModuleEntry;
 34 import java.lang.classfile.constantpool.PoolEntry;
 35 import java.lang.classfile.constantpool.Utf8Entry;
 36 import java.lang.constant.ClassDesc;
 37 import java.lang.constant.MethodTypeDesc;
 38 import java.lang.constant.ModuleDesc;
 39 import java.lang.reflect.AccessFlag;
 40 import java.lang.reflect.ClassFileFormatVersion;
 41 import java.util.AbstractList;
 42 import java.util.Collection;
 43 import java.util.List;
 44 import java.util.function.Consumer;
 45 import java.util.function.Function;
 46 
 47 import jdk.internal.access.SharedSecrets;
 48 import jdk.internal.constant.ClassOrInterfaceDescImpl;
 49 import jdk.internal.vm.annotation.ForceInline;
 50 import jdk.internal.vm.annotation.Stable;
 51 
 52 import static java.lang.classfile.ClassFile.ACC_STATIC;
 53 import static java.lang.constant.ConstantDescs.INIT_NAME;
 54 import static jdk.internal.constant.PrimitiveClassDescImpl.CD_double;
 55 import static jdk.internal.constant.PrimitiveClassDescImpl.CD_long;
 56 import static jdk.internal.constant.PrimitiveClassDescImpl.CD_void;
 57 
 58 /**
 59  * Helper to create and manipulate type descriptors, where type descriptors are
 60  * represented as JVM type descriptor strings and symbols are represented as
 61  * name strings
 62  */
 63 public class Util {
 64 
 65     private Util() {
 66     }
 67 
 68     public static <T> Consumer<Consumer<T>> writingAll(Iterable<T> container) {
 69         record ForEachConsumer<T>(Iterable<T> container) implements Consumer<Consumer<T>> {
 70             @Override
 71             public void accept(Consumer<T> consumer) {
 72                 container.forEach(consumer);
 73             }
 74         }
 75         return new ForEachConsumer<>(container);
 76     }
 77 
 78     public static Consumer<MethodBuilder> buildingCode(Consumer<? super CodeBuilder> codeHandler) {
 79         record WithCodeMethodHandler(Consumer<? super CodeBuilder> codeHandler) implements Consumer<MethodBuilder> {
 80             @Override
 81             public void accept(MethodBuilder builder) {
 82                 builder.withCode(codeHandler);
 83             }
 84         }
 85         return new WithCodeMethodHandler(codeHandler);
 86     }
 87 
 88     public static Consumer<FieldBuilder> buildingFlags(int flags) {
 89         record WithFlagFieldHandler(int flags) implements Consumer<FieldBuilder> {
 90             @Override
 91             public void accept(FieldBuilder builder) {
 92                 builder.withFlags(flags);
 93             }
 94         }
 95         return new WithFlagFieldHandler(flags);
 96     }
 97 
 98     private static final int ATTRIBUTE_STABILITY_COUNT = AttributeMapper.AttributeStability.values().length;
 99 
100     public static boolean isAttributeAllowed(final Attribute<?> attr,
101                                              final ClassFileImpl context) {
102         return attr instanceof BoundAttribute
103                 ? ATTRIBUTE_STABILITY_COUNT - attr.attributeMapper().stability().ordinal() > context.attributesProcessingOption().ordinal()
104                 : true;
105     }
106 
107     public static int parameterSlots(MethodTypeDesc mDesc) {
108         int count = mDesc.parameterCount();
109         for (int i = count - 1; i >= 0; i--) {
110             if (isDoubleSlot(mDesc.parameterType(i))) {
111                 count++;
112             }
113         }
114         return count;
115     }
116 
117     public static int[] parseParameterSlots(int flags, MethodTypeDesc mDesc) {
118         int[] result = new int[mDesc.parameterCount()];
119         int count = ((flags & ACC_STATIC) != 0) ? 0 : 1;
120         for (int i = 0; i < result.length; i++) {
121             result[i] = count;
122             count += paramSlotSize(mDesc.parameterType(i));
123         }
124         return result;
125     }
126 
127     public static int maxLocals(int flags, MethodTypeDesc mDesc) {
128         return parameterSlots(mDesc) + ((flags & ACC_STATIC) == 0 ? 1 : 0) ;
129     }
130 
131     /**
132      * Converts a descriptor of classes or interfaces into
133      * a binary name. Rejects primitive types or arrays.
134      * This is an inverse of {@link ClassDesc#of(String)}.
135      */
136     public static String toBinaryName(ClassDesc cd) {
137         return toInternalName(cd).replace('/', '.');
138     }
139 
140     public static String toInternalName(ClassDesc cd) {
141         if (cd instanceof ClassOrInterfaceDescImpl coi) {
142             return coi.internalName();
143         }
144         throw new IllegalArgumentException(cd.descriptorString());
145     }
146 
147     public static ClassDesc toClassDesc(String classInternalNameOrArrayDesc) {
148         return classInternalNameOrArrayDesc.charAt(0) == '['
149                 ? ClassDesc.ofDescriptor(classInternalNameOrArrayDesc)
150                 : ClassDesc.ofInternalName(classInternalNameOrArrayDesc);
151     }
152 
153     public static<T, U> List<U> mappedList(List<? extends T> list, Function<T, U> mapper) {
154         return new AbstractList<>() {
155             @Override
156             public U get(int index) {
157                 return mapper.apply(list.get(index));
158             }
159 
160             @Override
161             public int size() {
162                 return list.size();
163             }
164         };
165     }
166 
167     public static List<ClassEntry> entryList(List<? extends ClassDesc> list) {
168         var result = new Object[list.size()]; // null check
169         for (int i = 0; i < result.length; i++) {
170             result[i] = TemporaryConstantPool.INSTANCE.classEntry(list.get(i));
171         }
172         return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result);
173     }
174 
175     public static List<ModuleEntry> moduleEntryList(List<? extends ModuleDesc> list) {
176         var result = new Object[list.size()]; // null check
177         for (int i = 0; i < result.length; i++) {
178             result[i] = TemporaryConstantPool.INSTANCE.moduleEntry(TemporaryConstantPool.INSTANCE.utf8Entry(list.get(i).name()));
179         }
180         return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result);
181     }
182 
183     public static void checkKind(Opcode op, Opcode.Kind k) {
184         if (op.kind() != k)
185             throw badOpcodeKindException(op, k);
186     }
187 
188     public static IllegalArgumentException badOpcodeKindException(Opcode op, Opcode.Kind k) {
189         return new IllegalArgumentException(
190                 String.format("Wrong opcode kind specified; found %s(%s), expected %s", op, op.kind(), k));
191     }
192 
193     public static int flagsToBits(AccessFlag.Location location, Collection<AccessFlag> flags) {
194         int i = 0;
195         for (AccessFlag f : flags) {
196             if (!f.locations().contains(location)) {
197                 throw new IllegalArgumentException("unexpected flag: " + f + " use in target location: " + location);
198             }
199             i |= f.mask();
200         }
201         return i;
202     }
203 
204     public static int flagsToBits(AccessFlag.Location location, AccessFlag... flags) {
205         int i = 0;
206         for (AccessFlag f : flags) {
207             if (!f.locations().contains(location) && !f.locations(ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES).contains(location)) {
208                 throw new IllegalArgumentException("unexpected flag: " + f + " use in target location: " + location);
209             }
210             i |= f.mask();
211         }
212         return i;
213     }
214 
215     public static boolean has(AccessFlag.Location location, int flagsMask, AccessFlag flag) {
216         return (flag.mask() & flagsMask) == flag.mask() && (flag.locations().contains(location)
217                 || flag.locations(ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES).contains(location));
218     }
219 
220     public static ClassDesc fieldTypeSymbol(Utf8Entry utf8) {
221         return ((AbstractPoolEntry.Utf8EntryImpl) utf8).fieldTypeSymbol();
222     }
223 
224     public static MethodTypeDesc methodTypeSymbol(Utf8Entry utf8) {
225         return ((AbstractPoolEntry.Utf8EntryImpl) utf8).methodTypeSymbol();
226     }
227 
228     @SuppressWarnings("unchecked")
229     public static <T extends Attribute<T>> void writeAttribute(BufWriterImpl writer, Attribute<?> attr) {
230         if (attr instanceof CustomAttribute<?> ca) {
231             var mapper = (AttributeMapper<T>) ca.attributeMapper();
232             mapper.writeAttribute(writer, (T) ca);
233         } else {
234             assert attr instanceof BoundAttribute || attr instanceof UnboundAttribute;
235             ((Writable) attr).writeTo(writer);
236         }
237     }
238 
239     @ForceInline
240     public static void writeAttributes(BufWriterImpl buf, List<? extends Attribute<?>> list) {
241         int size = list.size();
242         buf.writeU2(size);
243         for (int i = 0; i < size; i++) {
244             writeAttribute(buf, list.get(i));
245         }
246     }
247 
248     @ForceInline
249     static void writeList(BufWriterImpl buf, Writable[] array, int size) {
250         buf.writeU2(size);
251         for (int i = 0; i < size; i++) {
252             array[i].writeTo(buf);
253         }
254     }
255 
256     public static int slotSize(ClassDesc desc) {
257         return desc == CD_void ? 0 : isDoubleSlot(desc) ? 2 : 1;
258     }
259 
260     public static int paramSlotSize(ClassDesc desc) {
261         return isDoubleSlot(desc) ? 2 : 1;
262     }
263 
264     public static boolean isDoubleSlot(ClassDesc desc) {
265         return desc == CD_double || desc == CD_long;
266     }
267 
268     public static boolean checkConstantPoolsCompatible(ConstantPool one, ConstantPool two) {
269         if (one.equals(two))
270             return true;
271         if (one instanceof ConstantPoolBuilder cpb && cpb.canWriteDirect(two))
272             return true;
273         return two instanceof ConstantPoolBuilder cpb && cpb.canWriteDirect(one);
274     }
275 
276     public static void dumpMethod(SplitConstantPool cp,
277                                   ClassDesc cls,
278                                   String methodName,
279                                   MethodTypeDesc methodDesc,
280                                   int acc,
281                                   RawBytecodeHelper.CodeRange bytecode,
282                                   Consumer<String> dump) {
283 
284         // try to dump debug info about corrupted bytecode
285         try {
286             var cc = ClassFile.of();
287             var clm = cc.parse(cc.build(cp.classEntry(cls), cp, clb ->
288                     clb.withMethod(methodName, methodDesc, acc, mb ->
289                             ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute<CodeAttribute>(Attributes.code()) {
290                                 @Override
291                                 public void writeBody(BufWriterImpl b) {
292                                     b.writeU2U2(-1, -1);//max stack & locals
293                                     b.writeInt(bytecode.length());
294                                     b.writeBytes(bytecode.array(), 0, bytecode.length());
295                                     b.writeU2U2(0, 0);//exception handlers & attributes
296                                 }
297 
298                                 @Override
299                                 public Utf8Entry attributeName() {
300                                     return cp.utf8Entry(Attributes.NAME_CODE);
301                                 }
302                     }))));
303             ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump);
304         } catch (Error | Exception _) {
305             // fallback to bytecode hex dump
306             dumpBytesHex(dump, bytecode.array(), bytecode.length());
307         }
308     }
309 
310     public static void dumpBytesHex(Consumer<String> dump, byte[] bytes, int length) {
311         for (int i = 0; i < length; i++) {
312             if (i % 16 == 0) {
313                 dump.accept("%n%04x:".formatted(i));
314             }
315             dump.accept(" %02x".formatted(bytes[i]));
316         }
317     }
318 
319     public static boolean canSkipMethodInflation(ClassReader cr, MethodInfo method, BufWriterImpl buf) {
320         if (!buf.canWriteDirect(cr)) {
321             return false;
322         }
323         if (method.methodName().equalsString(INIT_NAME) &&
324                 !buf.strictFieldsMatch(((ClassReaderImpl) cr).getContainedClass())) {
325             return false;
326         }
327         return true;
328     }
329 
330     public static void writeListIndices(BufWriter writer, List<? extends PoolEntry> list) {
331         writer.writeU2(list.size());
332         for (PoolEntry info : list) {
333             writer.writeIndex(info);
334         }
335     }
336 
337     public static boolean writeLocalVariable(BufWriterImpl buf, PseudoInstruction lvOrLvt) {
338         return ((WritableLocalVariable) lvOrLvt).writeLocalTo(buf);
339     }
340 
341     /**
342      * A generic interface for objects to write to a
343      * buf writer. Do not implement unless necessary,
344      * as this writeTo is public, which can be troublesome.
345      */
346     interface Writable {
347         void writeTo(BufWriterImpl writer);
348     }
349 
350     interface WritableLocalVariable {
351         boolean writeLocalTo(BufWriterImpl buf);
352     }
353 
354     /**
355      * Returns the hash code of a class or interface L descriptor given the internal name.
356      */
357     public static int descriptorStringHash(int length, int hash) {
358         if (length > 0xffff)
359             throw new IllegalArgumentException("String too long: ".concat(Integer.toString(length)));
360         return 'L' * pow31(length + 1) + hash * 31 + ';';
361     }
362 
363     // k is at most 65536, length of Utf8 entry + 1
364     public static int pow31(int k) {
365         int r = 1;
366         // calculate the power contribution from index-th octal digit
367         // from least to most significant (right to left)
368         // e.g. decimal 26=octal 32, power(26)=powerOctal(2,0)*powerOctal(3,1)
369         for (int i = 0; i < SIGNIFICANT_OCTAL_DIGITS; i++) {
370             r *= powerOctal(k & 7, i);
371             k >>= 3;
372         }
373         return r;
374     }
375 
376     // The inverse of 31 in Z/2^32Z* modulo group, a * INVERSE_31 * 31 = a
377     static final int INVERSE_31 = 0xbdef7bdf;
378 
379     // k is at most 65536 = octal 200000, only consider 6 octal digits
380     // Note: 31 powers repeat beyond 1 << 27, only 9 octal digits matter
381     static final int SIGNIFICANT_OCTAL_DIGITS = 6;
382 
383     // for base k, storage is k * log_k(N)=k/ln(k) * ln(N)
384     // k = 2 or 4 is better for space at the cost of more multiplications
385     /**
386      * The code below is as if:
387      * {@snippet lang=java :
388      * int[] powers = new int[7 * SIGNIFICANT_OCTAL_DIGITS];
389      *
390      * for (int i = 1, k = 31; i <= 7; i++, k *= 31) {
391      *    int t = powers[powersIndex(i, 0)] = k;
392      *    for (int j = 1; j < SIGNIFICANT_OCTAL_DIGITS; j++) {
393      *        t *= t;
394      *        t *= t;
395      *        t *= t;
396      *        powers[powersIndex(i, j)] = t;
397      *    }
398      * }
399      * }
400      * This is converted to explicit initialization to avoid bootstrap overhead.
401      * Validated in UtilTest.
402      */
403     static final @Stable int[] powers = new int[] {
404             0x0000001f, 0x000003c1, 0x0000745f, 0x000e1781, 0x01b4d89f, 0x34e63b41, 0x67e12cdf,
405             0x94446f01, 0x50a9de01, 0x84304d01, 0x7dd7bc01, 0x8ca02b01, 0xff899a01, 0x25940901,
406             0x4dbf7801, 0xe3bef001, 0xc1fe6801, 0xe87de001, 0x573d5801, 0x0e3cd001, 0x0d7c4801,
407             0x54fbc001, 0xb9f78001, 0x2ef34001, 0xb3ef0001, 0x48eac001, 0xede68001, 0xa2e24001,
408             0x67de0001, 0xcfbc0001, 0x379a0001, 0x9f780001, 0x07560001, 0x6f340001, 0xd7120001,
409             0x3ef00001, 0x7de00001, 0xbcd00001, 0xfbc00001, 0x3ab00001, 0x79a00001, 0xb8900001,
410             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
411     };
412 
413     static int powersIndex(int digit, int index) {
414         return (digit - 1) + index * 7;
415     }
416 
417     // (31 ^ digit) ^ (8 * index) = 31 ^ (digit * (8 ^ index))
418     // digit: 0 - 7
419     // index: 0 - SIGNIFICANT_OCTAL_DIGITS - 1
420     private static int powerOctal(int digit, int index) {
421         return digit == 0 ? 1 : powers[powersIndex(digit, index) & 0x3F]; // & 0x3F eliminates bound check
422     }
423 }