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