1 /*
  2  * Copyright (c) 2022, 2024, 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 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     public static<T, U> List<U> mappedList(List<? extends T> list, Function<T, U> mapper) {
150         return new AbstractList<>() {
151             @Override
152             public U get(int index) {
153                 return mapper.apply(list.get(index));
154             }
155 
156             @Override
157             public int size() {
158                 return list.size();
159             }
160         };
161     }
162 
163     public static List<ClassEntry> entryList(List<? extends ClassDesc> list) {
164         var result = new Object[list.size()]; // null check
165         for (int i = 0; i < result.length; i++) {
166             result[i] = TemporaryConstantPool.INSTANCE.classEntry(list.get(i));
167         }
168         return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result);
169     }
170 
171     public static List<ModuleEntry> moduleEntryList(List<? extends ModuleDesc> list) {
172         var result = new Object[list.size()]; // null check
173         for (int i = 0; i < result.length; i++) {
174             result[i] = TemporaryConstantPool.INSTANCE.moduleEntry(TemporaryConstantPool.INSTANCE.utf8Entry(list.get(i).name()));
175         }
176         return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result);
177     }
178 
179     public static void checkKind(Opcode op, Opcode.Kind k) {
180         if (op.kind() != k)
181             throw badOpcodeKindException(op, k);
182     }
183 
184     public static IllegalArgumentException badOpcodeKindException(Opcode op, Opcode.Kind k) {
185         return new IllegalArgumentException(
186                 String.format("Wrong opcode kind specified; found %s(%s), expected %s", op, op.kind(), k));
187     }
188 
189     public static int flagsToBits(AccessFlag.Location location, Collection<AccessFlag> flags) {
190         int i = 0;
191         for (AccessFlag f : flags) {
192             if (!f.locations().contains(location)) {
193                 throw new IllegalArgumentException("unexpected flag: " + f + " use in target location: " + location);
194             }
195             i |= f.mask();
196         }
197         return i;
198     }
199 
200     public static int flagsToBits(AccessFlag.Location location, AccessFlag... flags) {
201         int i = 0;
202         for (AccessFlag f : flags) {
203             if (!f.locations().contains(location)) {
204                 throw new IllegalArgumentException("unexpected flag: " + f + " use in target location: " + location);
205             }
206             i |= f.mask();
207         }
208         return i;
209     }
210 
211     public static boolean has(AccessFlag.Location location, int flagsMask, AccessFlag flag) {
212         return (flag.mask() & flagsMask) == flag.mask() && flag.locations().contains(location);
213     }
214 
215     public static ClassDesc fieldTypeSymbol(Utf8Entry utf8) {
216         return ((AbstractPoolEntry.Utf8EntryImpl) utf8).fieldTypeSymbol();
217     }
218 
219     public static MethodTypeDesc methodTypeSymbol(Utf8Entry utf8) {
220         return ((AbstractPoolEntry.Utf8EntryImpl) utf8).methodTypeSymbol();
221     }
222 
223     @SuppressWarnings("unchecked")
224     public static <T extends Attribute<T>> void writeAttribute(BufWriterImpl writer, Attribute<?> attr) {
225         if (attr instanceof CustomAttribute<?> ca) {
226             var mapper = (AttributeMapper<T>) ca.attributeMapper();
227             mapper.writeAttribute(writer, (T) ca);
228         } else {
229             assert attr instanceof BoundAttribute || attr instanceof UnboundAttribute;
230             ((Writable) attr).writeTo(writer);
231         }
232     }
233 
234     @ForceInline
235     public static void writeAttributes(BufWriterImpl buf, List<? extends Attribute<?>> list) {
236         int size = list.size();
237         buf.writeU2(size);
238         for (int i = 0; i < size; i++) {
239             writeAttribute(buf, list.get(i));
240         }
241     }
242 
243     @ForceInline
244     static void writeList(BufWriterImpl buf, Writable[] array, int size) {
245         buf.writeU2(size);
246         for (int i = 0; i < size; i++) {
247             array[i].writeTo(buf);
248         }
249     }
250 
251     public static int slotSize(ClassDesc desc) {
252         return desc == CD_void ? 0 : isDoubleSlot(desc) ? 2 : 1;
253     }
254 
255     public static int paramSlotSize(ClassDesc desc) {
256         return isDoubleSlot(desc) ? 2 : 1;
257     }
258 
259     public static boolean isDoubleSlot(ClassDesc desc) {
260         return desc == CD_double || desc == CD_long;
261     }
262 
263     public static void dumpMethod(SplitConstantPool cp,
264                                   ClassDesc cls,
265                                   String methodName,
266                                   MethodTypeDesc methodDesc,
267                                   int acc,
268                                   RawBytecodeHelper.CodeRange bytecode,
269                                   Consumer<String> dump) {
270 
271         // try to dump debug info about corrupted bytecode
272         try {
273             var cc = ClassFile.of();
274             var clm = cc.parse(cc.build(cp.classEntry(cls), cp, clb ->
275                     clb.withMethod(methodName, methodDesc, acc, mb ->
276                             ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute<CodeAttribute>(Attributes.code()) {
277                                 @Override
278                                 public void writeBody(BufWriterImpl b) {
279                                     b.writeU2U2(-1, -1);//max stack & locals
280                                     b.writeInt(bytecode.length());
281                                     b.writeBytes(bytecode.array(), 0, bytecode.length());
282                                     b.writeU2U2(0, 0);//exception handlers & attributes
283                                 }
284 
285                                 @Override
286                                 public Utf8Entry attributeName() {
287                                     return cp.utf8Entry(Attributes.NAME_CODE);
288                                 }
289                     }))));
290             ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump);
291         } catch (Error | Exception _) {
292             // fallback to bytecode hex dump
293             dumpBytesHex(dump, bytecode.array(), bytecode.length());
294         }
295     }
296 
297     public static void dumpBytesHex(Consumer<String> dump, byte[] bytes, int length) {
298         for (int i = 0; i < length; i++) {
299             if (i % 16 == 0) {
300                 dump.accept("%n%04x:".formatted(i));
301             }
302             dump.accept(" %02x".formatted(bytes[i]));
303         }
304     }
305 
306     public static void writeListIndices(BufWriter writer, List<? extends PoolEntry> list) {
307         writer.writeU2(list.size());
308         for (PoolEntry info : list) {
309             writer.writeIndex(info);
310         }
311     }
312 
313     public static boolean writeLocalVariable(BufWriterImpl buf, PseudoInstruction lvOrLvt) {
314         return ((WritableLocalVariable) lvOrLvt).writeLocalTo(buf);
315     }
316 
317     /**
318      * A generic interface for objects to write to a
319      * buf writer. Do not implement unless necessary,
320      * as this writeTo is public, which can be troublesome.
321      */
322     interface Writable {
323         void writeTo(BufWriterImpl writer);
324     }
325 
326     interface WritableLocalVariable {
327         boolean writeLocalTo(BufWriterImpl buf);
328     }
329 
330     /**
331      * Returns the hash code of a class or interface L descriptor given the internal name.
332      */
333     public static int descriptorStringHash(int length, int hash) {
334         if (length > 0xffff)
335             throw new IllegalArgumentException("String too long: ".concat(Integer.toString(length)));
336         return 'L' * pow31(length + 1) + hash * 31 + ';';
337     }
338 
339     // k is at most 65536, length of Utf8 entry + 1
340     public static int pow31(int k) {
341         int r = 1;
342         // calculate the power contribution from index-th octal digit
343         // from least to most significant (right to left)
344         // e.g. decimal 26=octal 32, power(26)=powerOctal(2,0)*powerOctal(3,1)
345         for (int i = 0; i < SIGNIFICANT_OCTAL_DIGITS; i++) {
346             r *= powerOctal(k & 7, i);
347             k >>= 3;
348         }
349         return r;
350     }
351 
352     // The inverse of 31 in Z/2^32Z* modulo group, a * INVERSE_31 * 31 = a
353     static final int INVERSE_31 = 0xbdef7bdf;
354 
355     // k is at most 65536 = octal 200000, only consider 6 octal digits
356     // Note: 31 powers repeat beyond 1 << 27, only 9 octal digits matter
357     static final int SIGNIFICANT_OCTAL_DIGITS = 6;
358 
359     // for base k, storage is k * log_k(N)=k/ln(k) * ln(N)
360     // k = 2 or 4 is better for space at the cost of more multiplications
361     /**
362      * The code below is as if:
363      * {@snippet lang=java :
364      * int[] powers = new int[7 * SIGNIFICANT_OCTAL_DIGITS];
365      *
366      * for (int i = 1, k = 31; i <= 7; i++, k *= 31) {
367      *    int t = powers[powersIndex(i, 0)] = k;
368      *    for (int j = 1; j < SIGNIFICANT_OCTAL_DIGITS; j++) {
369      *        t *= t;
370      *        t *= t;
371      *        t *= t;
372      *        powers[powersIndex(i, j)] = t;
373      *    }
374      * }
375      * }
376      * This is converted to explicit initialization to avoid bootstrap overhead.
377      * Validated in UtilTest.
378      */
379     static final @Stable int[] powers = new int[] {
380             0x0000001f, 0x000003c1, 0x0000745f, 0x000e1781, 0x01b4d89f, 0x34e63b41, 0x67e12cdf,
381             0x94446f01, 0x50a9de01, 0x84304d01, 0x7dd7bc01, 0x8ca02b01, 0xff899a01, 0x25940901,
382             0x4dbf7801, 0xe3bef001, 0xc1fe6801, 0xe87de001, 0x573d5801, 0x0e3cd001, 0x0d7c4801,
383             0x54fbc001, 0xb9f78001, 0x2ef34001, 0xb3ef0001, 0x48eac001, 0xede68001, 0xa2e24001,
384             0x67de0001, 0xcfbc0001, 0x379a0001, 0x9f780001, 0x07560001, 0x6f340001, 0xd7120001,
385             0x3ef00001, 0x7de00001, 0xbcd00001, 0xfbc00001, 0x3ab00001, 0x79a00001, 0xb8900001,
386             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
387     };
388 
389     static int powersIndex(int digit, int index) {
390         return (digit - 1) + index * 7;
391     }
392 
393     // (31 ^ digit) ^ (8 * index) = 31 ^ (digit * (8 ^ index))
394     // digit: 0 - 7
395     // index: 0 - SIGNIFICANT_OCTAL_DIGITS - 1
396     private static int powerOctal(int digit, int index) {
397         return digit == 0 ? 1 : powers[powersIndex(digit, index) & 0x3F]; // & 0x3F eliminates bound check
398     }
399 }