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