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