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 %s: %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 }