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 }
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) {
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 }
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
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
|
1 /*
2 * Copyright (c) 2022, 2026, 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.util.AbstractList;
41 import java.util.Collection;
42 import java.util.List;
43 import java.util.function.Consumer;
44 import java.util.function.Function;
45
46 import jdk.internal.access.SharedSecrets;
47 import jdk.internal.constant.ClassOrInterfaceDescImpl;
48 import jdk.internal.reflect.PreviewAccessFlags;
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 }
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<Utf8Entry> fieldDescriptorList(List<? extends ClassDesc> list) {
205 var result = new Object[list.size()]; // null check
206 for (int i = 0; i < result.length; i++) {
207 result[i] = TemporaryConstantPool.INSTANCE.utf8Entry(list.get(i));
208 }
209 return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result);
210 }
211
212 public static List<ModuleEntry> moduleEntryList(List<? extends ModuleDesc> list) {
213 var result = new Object[list.size()]; // null check
214 for (int i = 0; i < result.length; i++) {
215 result[i] = TemporaryConstantPool.INSTANCE.moduleEntry(TemporaryConstantPool.INSTANCE.utf8Entry(list.get(i).name()));
216 }
217 return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(result);
218 }
219
220 public static void checkKind(Opcode op, Opcode.Kind k) {
221 if (op.kind() != k)
222 throw badOpcodeKindException(op, k);
223 }
224
225 public static IllegalArgumentException badOpcodeKindException(Opcode op, Opcode.Kind k) {
226 return new IllegalArgumentException(
227 String.format("Wrong opcode kind specified; found %s(%s), expected %s", op, op.kind(), k));
228 }
229
230 /// Ensures the given value won't be truncated when written as a u1
231 public static int checkU1(int incoming, String valueName) {
249
250 /// Ensures the given mask won't be truncated when written as an access flag
251 public static char checkFlags(int mask) {
252 return checkU2(mask, "access flags");
253 }
254
255 public static int flagsToBits(AccessFlag.Location location, Collection<AccessFlag> flags) {
256 int i = 0;
257 for (AccessFlag f : flags) {
258 if (!f.locations().contains(location)) {
259 throw new IllegalArgumentException("unexpected flag: " + f + " use in target location: " + location);
260 }
261 i |= f.mask();
262 }
263 return i;
264 }
265
266 public static int flagsToBits(AccessFlag.Location location, AccessFlag... flags) {
267 int i = 0;
268 for (AccessFlag f : flags) {
269 if (!f.locations().contains(location) && !PreviewAccessFlags.locations(f).contains(location)) {
270 throw new IllegalArgumentException("unexpected flag: " + f + " use in target location: " + location);
271 }
272 i |= f.mask();
273 }
274 return i;
275 }
276
277 public static boolean has(AccessFlag.Location location, int flagsMask, AccessFlag flag) {
278 return (flag.mask() & flagsMask) == flag.mask() && (flag.locations().contains(location)
279 || PreviewAccessFlags.locations(flag).contains(location));
280 }
281
282 public static ClassDesc fieldTypeSymbol(Utf8Entry utf8) {
283 return ((AbstractPoolEntry.Utf8EntryImpl) utf8).fieldTypeSymbol();
284 }
285
286 public static MethodTypeDesc methodTypeSymbol(Utf8Entry utf8) {
287 return ((AbstractPoolEntry.Utf8EntryImpl) utf8).methodTypeSymbol();
288 }
289
290 @SuppressWarnings("unchecked")
291 public static <T extends Attribute<T>> void writeAttribute(BufWriterImpl writer, Attribute<?> attr) {
292 if (attr instanceof CustomAttribute<?> ca) {
293 var mapper = (AttributeMapper<T>) ca.attributeMapper();
294 mapper.writeAttribute(writer, (T) ca);
295 } else {
296 assert attr instanceof BoundAttribute || attr instanceof UnboundAttribute;
297 ((Writable) attr).writeTo(writer);
298 }
299 }
312 static void writeList(BufWriterImpl buf, Writable[] array, int size) {
313 Util.checkU2(size, "member count");
314 buf.writeU2(size);
315 for (int i = 0; i < size; i++) {
316 array[i].writeTo(buf);
317 }
318 }
319
320 public static int slotSize(ClassDesc desc) {
321 return desc == CD_void ? 0 : isDoubleSlot(desc) ? 2 : 1;
322 }
323
324 public static int paramSlotSize(ClassDesc desc) {
325 return isDoubleSlot(desc) ? 2 : 1;
326 }
327
328 public static boolean isDoubleSlot(ClassDesc desc) {
329 return desc == CD_double || desc == CD_long;
330 }
331
332 public static boolean checkConstantPoolsCompatible(ConstantPool one, ConstantPool two) {
333 if (one.equals(two))
334 return true;
335 if (one instanceof ConstantPoolBuilder cpb && cpb.canWriteDirect(two))
336 return true;
337 return two instanceof ConstantPoolBuilder cpb && cpb.canWriteDirect(one);
338 }
339
340 public static void dumpMethod(SplitConstantPool cp,
341 ClassDesc cls,
342 String methodName,
343 MethodTypeDesc methodDesc,
344 int acc,
345 RawBytecodeHelper.CodeRange bytecode,
346 Consumer<String> dump) {
347
348 // try to dump debug info about corrupted bytecode
349 try {
350 var cc = ClassFile.of();
351 var clm = cc.parse(cc.build(cp.classEntry(cls), cp, clb ->
352 clb.withMethod(methodName, methodDesc, acc, mb ->
353 ((DirectMethodBuilder)mb).writeAttribute(new UnboundAttribute.AdHocAttribute<CodeAttribute>(Attributes.code()) {
354 @Override
355 public void writeBody(BufWriterImpl b) {
356 b.writeU2U2(-1, -1);//max stack & locals
357 b.writeInt(bytecode.length());
358 b.writeBytes(bytecode.array(), 0, bytecode.length());
359 b.writeU2U2(0, 0);//exception handlers & attributes
363 public Utf8Entry attributeName() {
364 return cp.utf8Entry(Attributes.NAME_CODE);
365 }
366 }))));
367 ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump);
368 } catch (Error | Exception _) {
369 // fallback to bytecode hex dump
370 dumpBytesHex(dump, bytecode.array(), bytecode.length());
371 }
372 }
373
374 public static void dumpBytesHex(Consumer<String> dump, byte[] bytes, int length) {
375 for (int i = 0; i < length; i++) {
376 if (i % 16 == 0) {
377 dump.accept("%n%04x:".formatted(i));
378 }
379 dump.accept(" %02x".formatted(bytes[i]));
380 }
381 }
382
383 public static boolean canSkipMethodInflation(ClassReader cr, MethodInfo method, BufWriterImpl buf) {
384 if (!buf.canWriteDirect(cr)) {
385 return false;
386 }
387 if (method.methodName().equalsString(INIT_NAME) &&
388 !buf.strictFieldsMatch(((ClassReaderImpl) cr).getContainedClass())) {
389 return false;
390 }
391 return true;
392 }
393
394 public static void writeListIndices(BufWriter writer, List<? extends PoolEntry> list) {
395 writer.writeU2(list.size());
396 for (PoolEntry info : list) {
397 writer.writeIndex(info);
398 }
399 }
400
401 public static boolean writeLocalVariable(BufWriterImpl buf, PseudoInstruction lvOrLvt) {
402 return ((WritableLocalVariable) lvOrLvt).writeLocalTo(buf);
403 }
404
405 /**
406 * A generic interface for objects to write to a
407 * buf writer. Do not implement unless necessary,
408 * as this writeTo is public, which can be troublesome.
409 */
410 interface Writable {
411 void writeTo(BufWriterImpl writer);
412 }
413
|