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 }