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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /*
25 * @test
26 * @summary Testing ClassFile Verifier.
27 * @bug 8333812 8361526
28 * @run junit VerifierSelfTest
29 * @run junit/othervm --enable-preview VerifierSelfTest
30 */
31 import java.io.IOException;
32 import java.lang.classfile.constantpool.PoolEntry;
33 import java.lang.constant.ClassDesc;
34
35 import static java.lang.classfile.ClassFile.ACC_STATIC;
36 import static java.lang.classfile.ClassFile.JAVA_8_VERSION;
37 import static java.lang.constant.ConstantDescs.*;
38
39 import java.lang.constant.MethodTypeDesc;
40 import java.lang.invoke.MethodHandleInfo;
41 import java.lang.invoke.MethodHandles;
42 import java.net.URI;
43 import java.nio.file.FileSystem;
44 import java.nio.file.FileSystems;
45 import java.nio.file.Files;
46 import java.nio.file.Path;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.LinkedList;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Optional;
53 import java.util.stream.Collectors;
54 import java.util.stream.Stream;
55 import java.lang.classfile.*;
56 import java.lang.classfile.attribute.*;
57 import jdk.internal.classfile.components.ClassPrinter;
58 import java.lang.classfile.constantpool.Utf8Entry;
59 import java.lang.constant.ModuleDesc;
60
61 import jdk.internal.classfile.impl.BufWriterImpl;
62 import jdk.internal.classfile.impl.DirectClassBuilder;
63 import jdk.internal.classfile.impl.UnboundAttribute;
64 import org.junit.jupiter.api.Test;
65 import org.junit.jupiter.params.ParameterizedTest;
66 import org.junit.jupiter.params.provider.Arguments;
67 import org.junit.jupiter.params.provider.MethodSource;
68
69 import static org.junit.jupiter.api.Assertions.*;
70
71 class VerifierSelfTest {
72
73 private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/"));
74
75 @Test
76 void testVerify() throws IOException {
77 Stream.of(
78 Files.walk(JRT.getPath("modules/java.base")),
79 Files.walk(JRT.getPath("modules"), 2).filter(p -> p.endsWith("module-info.class")))
80 .flatMap(p -> p)
81 .filter(p -> Files.isRegularFile(p) && p.toString().endsWith(".class")).forEach(path -> {
82 try {
83 ClassFile.of().verify(path);
84 } catch (IOException e) {
85 throw new AssertionError(e);
86 }
87 });
88 }
89
90 @Test
91 void testFailed() throws IOException {
92 Path path = FileSystems.getFileSystem(URI.create("jrt:/")).getPath("modules/java.base/java/util/HashMap.class");
93 var cc = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(
94 className -> ClassHierarchyResolver.ClassHierarchyInfo.ofClass(null)));
95 var classModel = cc.parse(path);
96 byte[] brokenClassBytes = cc.transformClass(classModel,
97 (clb, cle) -> {
98 if (cle instanceof MethodModel mm) {
99 clb.transformMethod(mm, (mb, me) -> {
100 if (me instanceof CodeModel cm) {
101 mb.withCode(cob -> cm.forEach(cob));
102 }
103 else
104 mb.with(me);
105 });
106 }
107 else
108 clb.with(cle);
109 });
110 StringBuilder sb = new StringBuilder();
111 if (ClassFile.of().verify(brokenClassBytes).isEmpty()) {
112 throw new AssertionError("expected verification failure");
113 }
114 }
115
116 @Test
117 void testInvalidAttrLocation() {
118 var cc = ClassFile.of();
119 var bytes = cc.build(ClassDesc.of("InvalidAttrLocationClass"), cb ->
120 ((DirectClassBuilder)cb).writeAttribute(new UnboundAttribute.AdHocAttribute<LocalVariableTableAttribute>(Attributes.localVariableTable()) {
121 @Override
122 public void writeBody(BufWriterImpl b) {
123 b.writeU2(0);
124 }
125
126 @Override
127 public Utf8Entry attributeName() {
128 return cb.constantPool().utf8Entry(Attributes.NAME_LOCAL_VARIABLE_TABLE);
129 }
130 }));
131 assertTrue(cc.verify(bytes).stream().anyMatch(e -> e.getMessage().contains("Invalid LocalVariableTable attribute location")));
132 }
133
134 @Test
135 void testInvalidClassNameEntry() {
136 var cc = ClassFile.of();
137 var bytes = cc.parse(new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE,
138 0, 0, 0, 0, 0, 2, PoolEntry.TAG_INTEGER, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
139 assertTrue(cc.verify(bytes).stream().anyMatch(e -> e.getMessage().contains("expected ClassEntry")));
140 }
141
142 @Test
143 void testParserVerification() {
144 var cc = ClassFile.of();
145 var cd_test = ClassDesc.of("ParserVerificationTestClass");
146 var indexes = new Object[9];
147 var clm = cc.parse(cc.build(cd_test, clb -> {
148 clb.withFlags(ClassFile.ACC_INTERFACE | ClassFile.ACC_FINAL);
149 var cp = clb.constantPool();
150 var ce_valid = cp.classEntry(cd_test);
151 var ce_invalid = cp.classEntry(cp.utf8Entry("invalid.class.name"));
152 indexes[0] = ce_invalid.index();
153 var nate_invalid_field = cp.nameAndTypeEntry("field;", CD_int);
154 var nate_invalid_method = cp.nameAndTypeEntry("method;", MTD_void);
155 var bsme = cp.bsmEntry(BSM_INVOKE, List.of());
156 indexes[1] = cp.methodTypeEntry(cp.utf8Entry("invalid method type")).index();
157 indexes[2] = cp.constantDynamicEntry(bsme, nate_invalid_method).index();
158 indexes[3] = cp.invokeDynamicEntry(bsme, nate_invalid_field).index();
159 indexes[4] = cp.fieldRefEntry(ce_invalid, nate_invalid_method).index();
160 indexes[5] = cp.methodRefEntry(ce_invalid, nate_invalid_field).index();
161 indexes[6] = cp.interfaceMethodRefEntry(ce_invalid, nate_invalid_field).index();
162 indexes[7] = cp.methodHandleEntry(MethodHandleInfo.REF_getField, cp.methodRefEntry(cd_test, "method", MTD_void)).index();
163 indexes[8] = cp.methodHandleEntry(MethodHandleInfo.REF_invokeVirtual, cp.fieldRefEntry(cd_test, "field", CD_int)).index();
164 patch(clb,
165 CompilationIDAttribute.of("12345"),
166 DeprecatedAttribute.of(),
167 EnclosingMethodAttribute.of(cd_test, Optional.empty(), Optional.empty()),
168 InnerClassesAttribute.of(InnerClassInfo.of(cd_test, Optional.of(cd_test), Optional.of("inner"), 0)),
169 ModuleAttribute.of(ModuleDesc.of("m"), mab -> {}),
170 ModuleHashesAttribute.of("alg", List.of()),
171 ModuleMainClassAttribute.of(cd_test),
172 ModulePackagesAttribute.of(),
173 ModuleResolutionAttribute.of(0),
174 ModuleTargetAttribute.of("t"),
175 NestHostAttribute.of(cd_test),
176 NestMembersAttribute.ofSymbols(cd_test),
177 PermittedSubclassesAttribute.ofSymbols(cd_test),
178 RecordAttribute.of(RecordComponentInfo.of("c", CD_String, patch(
179 SignatureAttribute.of(Signature.of(CD_String)),
180 RuntimeVisibleAnnotationsAttribute.of(),
181 RuntimeInvisibleAnnotationsAttribute.of(),
182 RuntimeVisibleTypeAnnotationsAttribute.of(),
183 RuntimeInvisibleTypeAnnotationsAttribute.of()))),
184 RuntimeVisibleAnnotationsAttribute.of(),
185 RuntimeInvisibleAnnotationsAttribute.of(),
186 RuntimeVisibleTypeAnnotationsAttribute.of(),
187 RuntimeInvisibleTypeAnnotationsAttribute.of(),
188 SignatureAttribute.of(ClassSignature.of(Signature.ClassTypeSig.of(cd_test))),
189 SourceDebugExtensionAttribute.of("sde".getBytes()),
190 SourceFileAttribute.of("ParserVerificationTestClass.java"),
191 SourceIDAttribute.of("sID"),
192 SyntheticAttribute.of())
193 .withInterfaceSymbols(CD_List, CD_List)
194 .withField("f", CD_String, fb -> patch(fb,
195 ConstantValueAttribute.of(0),
196 DeprecatedAttribute.of(),
197 RuntimeVisibleAnnotationsAttribute.of(),
198 RuntimeInvisibleAnnotationsAttribute.of(),
199 RuntimeVisibleTypeAnnotationsAttribute.of(),
200 RuntimeInvisibleTypeAnnotationsAttribute.of(),
201 SignatureAttribute.of(Signature.of(CD_String)),
202 SyntheticAttribute.of()))
203 .withField("/", CD_int, 0)
204 .withField("/", CD_int, 0)
205 .withMethod("m", MTD_void, ClassFile.ACC_ABSTRACT | ClassFile.ACC_STATIC, mb -> patch(mb,
206 AnnotationDefaultAttribute.of(AnnotationValue.ofInt(0)),
207 DeprecatedAttribute.of(),
208 ExceptionsAttribute.ofSymbols(CD_Exception),
209 MethodParametersAttribute.of(MethodParameterInfo.ofParameter(Optional.empty(), 0)),
210 RuntimeVisibleAnnotationsAttribute.of(),
211 RuntimeInvisibleAnnotationsAttribute.of(),
212 RuntimeVisibleParameterAnnotationsAttribute.of(List.of()),
213 RuntimeInvisibleParameterAnnotationsAttribute.of(List.of()),
214 SignatureAttribute.of(MethodSignature.of(MTD_void)),
215 SyntheticAttribute.of())
216 .withCode(cob ->
217 cob.iconst_0()
218 .ifThen(CodeBuilder::nop)
219 .return_()
220 .with(new CloneAttribute(CharacterRangeTableAttribute.of(List.of())))
221 .with(new CloneAttribute(LineNumberTableAttribute.of(List.of())))
222 .with(new CloneAttribute(LocalVariableTableAttribute.of(List.of())))
223 .with(new CloneAttribute(LocalVariableTypeTableAttribute.of(List.of())))))
224 .withMethod("<>", MTD_void, ClassFile.ACC_NATIVE, mb -> {})
225 .withMethod("<>", MTD_void, ClassFile.ACC_NATIVE, mb -> {})
226 .withMethod(INIT_NAME, MTD_void, 0, mb -> {})
227 .withMethod(CLASS_INIT_NAME, MTD_void, 0, mb -> {});
228 }));
229 var found = cc.verify(clm).stream().map(VerifyError::getMessage).collect(Collectors.toCollection(LinkedList::new));
230 var expected = """
231 Invalid class name: invalid.class.name at constant pool index %1$d in class ParserVerificationTestClass
232 Bad method descriptor: invalid method type at constant pool index %2$d in class ParserVerificationTestClass
233 not a valid reference type descriptor: ()V at constant pool index %3$d in class ParserVerificationTestClass
234 Bad method descriptor: I at constant pool index %4$d in class ParserVerificationTestClass
235 not a valid reference type descriptor: ()V at constant pool index %5$d in class ParserVerificationTestClass
236 Invalid class name: invalid.class.name at constant pool index %5$d in class ParserVerificationTestClass
237 Illegal field name method; in class ParserVerificationTestClass at constant pool index %5$d in class ParserVerificationTestClass
238 Bad method descriptor: I at constant pool index %6$d in class ParserVerificationTestClass
239 Invalid class name: invalid.class.name at constant pool index %6$d in class ParserVerificationTestClass
240 Illegal method name field; in class ParserVerificationTestClass at constant pool index %6$d in class ParserVerificationTestClass
241 Bad method descriptor: I at constant pool index %7$d in class ParserVerificationTestClass
242 Invalid class name: invalid.class.name at constant pool index %7$d in class ParserVerificationTestClass
243 Illegal method name field; in class ParserVerificationTestClass at constant pool index %7$d in class ParserVerificationTestClass
244 not a valid reference type descriptor: ()V at constant pool index %8$d in class ParserVerificationTestClass
245 Bad method descriptor: I at constant pool index %9$d in class ParserVerificationTestClass
246 Duplicate interface List in class ParserVerificationTestClass
247 Illegal field name / in class ParserVerificationTestClass
248 Duplicate field name / with signature I in class ParserVerificationTestClass
249 Illegal field name / in class ParserVerificationTestClass
250 Illegal method name <> in class ParserVerificationTestClass
251 Duplicate method name <> with signature ()V in class ParserVerificationTestClass
252 Illegal method name <> in class ParserVerificationTestClass
253 Interface cannot have a method named <init> in class ParserVerificationTestClass
254 Method <clinit> is not static in class ParserVerificationTestClass
255 Multiple CompilationID attributes in class ParserVerificationTestClass
256 Wrong CompilationID attribute length in class ParserVerificationTestClass
257 Wrong Deprecated attribute length in class ParserVerificationTestClass
258 Multiple EnclosingMethod attributes in class ParserVerificationTestClass
259 Wrong EnclosingMethod attribute length in class ParserVerificationTestClass
260 Class is both outer and inner class in class ParserVerificationTestClass
261 Multiple InnerClasses attributes in class ParserVerificationTestClass
262 Class is both outer and inner class in class ParserVerificationTestClass
263 Wrong InnerClasses attribute length in class ParserVerificationTestClass
264 Multiple Module attributes in class ParserVerificationTestClass
265 Wrong Module attribute length in class ParserVerificationTestClass
266 Multiple ModuleHashes attributes in class ParserVerificationTestClass
267 Wrong ModuleHashes attribute length in class ParserVerificationTestClass
268 Multiple ModuleMainClass attributes in class ParserVerificationTestClass
269 Wrong ModuleMainClass attribute length in class ParserVerificationTestClass
270 Multiple ModulePackages attributes in class ParserVerificationTestClass
271 Wrong ModulePackages attribute length in class ParserVerificationTestClass
272 Multiple ModuleResolution attributes in class ParserVerificationTestClass
273 Wrong ModuleResolution attribute length in class ParserVerificationTestClass
274 Multiple ModuleTarget attributes in class ParserVerificationTestClass
275 Wrong ModuleTarget attribute length in class ParserVerificationTestClass
276 Multiple NestHost attributes in class ParserVerificationTestClass
277 Wrong NestHost attribute length in class ParserVerificationTestClass
278 Conflicting NestHost and NestMembers attributes in class ParserVerificationTestClass
279 Multiple NestMembers attributes in class ParserVerificationTestClass
280 Conflicting NestHost and NestMembers attributes in class ParserVerificationTestClass
281 Wrong NestMembers attribute length in class ParserVerificationTestClass
282 PermittedSubclasses attribute in final class ParserVerificationTestClass
283 Multiple PermittedSubclasses attributes in class ParserVerificationTestClass
284 PermittedSubclasses attribute in final class ParserVerificationTestClass
285 Wrong PermittedSubclasses attribute length in class ParserVerificationTestClass
286 Multiple Record attributes in class ParserVerificationTestClass
287 Wrong Record attribute length in class ParserVerificationTestClass
288 Multiple RuntimeVisibleAnnotations attributes in class ParserVerificationTestClass
289 Wrong RuntimeVisibleAnnotations attribute length in class ParserVerificationTestClass
290 Multiple RuntimeInvisibleAnnotations attributes in class ParserVerificationTestClass
291 Wrong RuntimeInvisibleAnnotations attribute length in class ParserVerificationTestClass
292 Multiple RuntimeVisibleTypeAnnotations attributes in class ParserVerificationTestClass
293 Wrong RuntimeVisibleTypeAnnotations attribute length in class ParserVerificationTestClass
294 Multiple RuntimeInvisibleTypeAnnotations attributes in class ParserVerificationTestClass
295 Wrong RuntimeInvisibleTypeAnnotations attribute length in class ParserVerificationTestClass
296 Multiple Signature attributes in class ParserVerificationTestClass
297 Wrong Signature attribute length in class ParserVerificationTestClass
298 Multiple SourceDebugExtension attributes in class ParserVerificationTestClass
299 Multiple SourceFile attributes in class ParserVerificationTestClass
300 Wrong SourceFile attribute length in class ParserVerificationTestClass
301 Multiple SourceID attributes in class ParserVerificationTestClass
302 Wrong SourceID attribute length in class ParserVerificationTestClass
303 Wrong Synthetic attribute length in class ParserVerificationTestClass
304 Bad constant value type in field ParserVerificationTestClass.f
305 Multiple ConstantValue attributes in field ParserVerificationTestClass.f
306 Bad constant value type in field ParserVerificationTestClass.f
307 Wrong ConstantValue attribute length in field ParserVerificationTestClass.f
308 Wrong Deprecated attribute length in field ParserVerificationTestClass.f
309 Multiple RuntimeVisibleAnnotations attributes in field ParserVerificationTestClass.f
310 Wrong RuntimeVisibleAnnotations attribute length in field ParserVerificationTestClass.f
311 Multiple RuntimeInvisibleAnnotations attributes in field ParserVerificationTestClass.f
312 Wrong RuntimeInvisibleAnnotations attribute length in field ParserVerificationTestClass.f
313 Multiple RuntimeVisibleTypeAnnotations attributes in field ParserVerificationTestClass.f
314 Wrong RuntimeVisibleTypeAnnotations attribute length in field ParserVerificationTestClass.f
315 Multiple RuntimeInvisibleTypeAnnotations attributes in field ParserVerificationTestClass.f
316 Wrong RuntimeInvisibleTypeAnnotations attribute length in field ParserVerificationTestClass.f
317 Multiple Signature attributes in field ParserVerificationTestClass.f
318 Wrong Signature attribute length in field ParserVerificationTestClass.f
319 Wrong Synthetic attribute length in field ParserVerificationTestClass.f
320 Multiple AnnotationDefault attributes in method ParserVerificationTestClass::m()
321 Wrong AnnotationDefault attribute length in method ParserVerificationTestClass::m()
322 Wrong Deprecated attribute length in method ParserVerificationTestClass::m()
323 Multiple Exceptions attributes in method ParserVerificationTestClass::m()
324 Wrong Exceptions attribute length in method ParserVerificationTestClass::m()
325 Multiple MethodParameters attributes in method ParserVerificationTestClass::m()
326 Wrong MethodParameters attribute length in method ParserVerificationTestClass::m()
327 Multiple RuntimeVisibleAnnotations attributes in method ParserVerificationTestClass::m()
328 Wrong RuntimeVisibleAnnotations attribute length in method ParserVerificationTestClass::m()
329 Multiple RuntimeInvisibleAnnotations attributes in method ParserVerificationTestClass::m()
330 Wrong RuntimeInvisibleAnnotations attribute length in method ParserVerificationTestClass::m()
331 Multiple RuntimeVisibleParameterAnnotations attributes in method ParserVerificationTestClass::m()
332 Wrong RuntimeVisibleParameterAnnotations attribute length in method ParserVerificationTestClass::m()
333 Multiple RuntimeInvisibleParameterAnnotations attributes in method ParserVerificationTestClass::m()
334 Wrong RuntimeInvisibleParameterAnnotations attribute length in method ParserVerificationTestClass::m()
335 Multiple Signature attributes in method ParserVerificationTestClass::m()
336 Wrong Signature attribute length in method ParserVerificationTestClass::m()
337 Wrong Synthetic attribute length in method ParserVerificationTestClass::m()
338 Code attribute in native or abstract method ParserVerificationTestClass::m()
339 Wrong CharacterRangeTable attribute length in Code attribute for method ParserVerificationTestClass::m()
340 Wrong LineNumberTable attribute length in Code attribute for method ParserVerificationTestClass::m()
341 Wrong LocalVariableTable attribute length in Code attribute for method ParserVerificationTestClass::m()
342 Wrong LocalVariableTypeTable attribute length in Code attribute for method ParserVerificationTestClass::m()
343 Multiple Signature attributes in Record component c of class ParserVerificationTestClass
344 Wrong Signature attribute length in Record component c of class ParserVerificationTestClass
345 Multiple RuntimeVisibleAnnotations attributes in Record component c of class ParserVerificationTestClass
346 Wrong RuntimeVisibleAnnotations attribute length in Record component c of class ParserVerificationTestClass
347 Multiple RuntimeInvisibleAnnotations attributes in Record component c of class ParserVerificationTestClass
348 Wrong RuntimeInvisibleAnnotations attribute length in Record component c of class ParserVerificationTestClass
349 Multiple RuntimeVisibleTypeAnnotations attributes in Record component c of class ParserVerificationTestClass
350 Wrong RuntimeVisibleTypeAnnotations attribute length in Record component c of class ParserVerificationTestClass
351 Multiple RuntimeInvisibleTypeAnnotations attributes in Record component c of class ParserVerificationTestClass
352 Wrong RuntimeInvisibleTypeAnnotations attribute length in Record component c of class ParserVerificationTestClass
353 Multiple Signature attributes in Record component c of class ParserVerificationTestClass
354 Wrong Signature attribute length in Record component c of class ParserVerificationTestClass
355 Multiple RuntimeVisibleAnnotations attributes in Record component c of class ParserVerificationTestClass
356 Wrong RuntimeVisibleAnnotations attribute length in Record component c of class ParserVerificationTestClass
357 Multiple RuntimeInvisibleAnnotations attributes in Record component c of class ParserVerificationTestClass
358 Wrong RuntimeInvisibleAnnotations attribute length in Record component c of class ParserVerificationTestClass
359 Multiple RuntimeVisibleTypeAnnotations attributes in Record component c of class ParserVerificationTestClass
360 Wrong RuntimeVisibleTypeAnnotations attribute length in Record component c of class ParserVerificationTestClass
361 Multiple RuntimeInvisibleTypeAnnotations attributes in Record component c of class ParserVerificationTestClass
362 Wrong RuntimeInvisibleTypeAnnotations attribute length in Record component c of class ParserVerificationTestClass
363 Missing Code attribute in ParserVerificationTestClass::<init>() @0
364 Missing Code attribute in ParserVerificationTestClass::<clinit>() @0
365 """.formatted(indexes).lines().filter(exp -> !found.remove(exp)).toList();
366 if (!found.isEmpty() || !expected.isEmpty()) {
367 ClassPrinter.toYaml(clm, ClassPrinter.Verbosity.TRACE_ALL, System.out::print);
368 fail("""
369
370 Expected:
371 %s
372
373 Found:
374 %s
375 """.formatted(expected.stream().collect(Collectors.joining("\n ")), found.stream().collect(Collectors.joining("\n "))));
376 }
377 }
378
379 private static class CloneAttribute extends CustomAttribute<CloneAttribute> {
380 CloneAttribute(Attribute a) {
381 super(new AttributeMapper<CloneAttribute>(){
382 @Override
383 public String name() {
384 return a.attributeName().stringValue();
385 }
386
387 @Override
388 public CloneAttribute readAttribute(AttributedElement enclosing, ClassReader cf, int pos) {
389 throw new UnsupportedOperationException();
390 }
391
392 @Override
393 public void writeAttribute(BufWriter buf, CloneAttribute attr) {
394 int start = buf.size();
395 a.attributeMapper().writeAttribute(buf, a);
396 buf.writeU1(0); //writes additional byte to the attribute payload
397 buf.patchInt(start + 2, 4, buf.size() - start - 6);
398 }
399
400 @Override
401 public AttributeMapper.AttributeStability stability() {
402 return a.attributeMapper().stability();
403 }
404 });
405 }
406 }
407
408 private static <B extends ClassFileBuilder> B patch(B b, Attribute... attrs) {
409 for (var a : attrs) {
410 b.with(a).with(new CloneAttribute(a));
411 }
412 return b;
413 }
414
415 private static List<Attribute<?>> patch(Attribute... attrs) {
416 var lst = new ArrayList<Attribute<?>>(attrs.length * 2);
417 for (var a : attrs) {
418 lst.add(a);
419 lst.add(new CloneAttribute(a));
420 }
421 return lst;
422 }
423
424 @Test // JDK-8350029
425 void testInvokeSpecialInterfacePatch() {
426 var runClass = ClassDesc.of("Run");
427 var testClass = ClassDesc.of("Test");
428 var runnableClass = Runnable.class.describeConstable().orElseThrow();
429 var chr = ClassHierarchyResolver.of(List.of(), Map.of(runClass, CD_Object))
430 .orElse(ClassHierarchyResolver.defaultResolver()).cached();
431 var context = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(chr));
432
433 for (var isInterface : new boolean[] {true, false}) {
434 var bytes = context.build(testClass, clb -> clb
435 .withVersion(JAVA_8_VERSION, 0)
436 .withSuperclass(runClass)
437 .withMethodBody("test", MethodTypeDesc.of(CD_void, testClass), ACC_STATIC, cob -> cob
438 .aload(0)
439 .invokespecial(runnableClass, "run", MTD_void, isInterface)
440 .return_()));
441 var errors = context.verify(bytes);
442 assertNotEquals(List.of(), errors, "invokespecial, isInterface = " + isInterface);
443 assertTrue(errors.getFirst().getMessage().contains("interface method to invoke is not in a direct superinterface"), errors.getFirst().getMessage());
444 }
445 }
446
447 enum ComparisonInstruction {
448 IF_ACMPEQ(Opcode.IF_ACMPEQ, 2),
449 IF_ACMPNE(Opcode.IF_ACMPNE, 2),
450 IFNONNULL(Opcode.IFNONNULL, 1),
451 IFNULL(Opcode.IFNULL, 1);
452 final Opcode opcode;
453 final int argCount;
454 ComparisonInstruction(Opcode opcode, int argCount) {
455 this.opcode = opcode;
456 this.argCount = argCount;
457 }
458 }
459
460 enum UninitializeKind {
461 UNINITIALIZED, UNINITIALIZED_THIS
462 }
463
464 @ParameterizedTest
465 @MethodSource("uninitializedInBytecodeClasses")
466 public void testUninitializedInComparisons(ComparisonInstruction inst, UninitializeKind kind) throws Throwable {
467 var bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(ClassDesc.of("Test"), clb -> clb
468 .withMethodBody(INIT_NAME, MTD_void, 0, cob -> {
469 StackMapFrameInfo.VerificationTypeInfo uninitializeInfo;
470 if (kind == UninitializeKind.UNINITIALIZED) {
471 uninitializeInfo = StackMapFrameInfo.UninitializedVerificationTypeInfo.of(cob.newBoundLabel());
472 cob.new_(CD_Object);
473 } else {
474 uninitializeInfo = StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
475 cob.aload(0);
476 }
477
478 // Stack: uninitializeInfo
479 for (int i = 0; i < inst.argCount; i++) {
480 cob.dup();
481 }
482 var dest = cob.newLabel();
483 cob.branch(inst.opcode, dest)
484 .nop()
485 .labelBinding(dest)
486 .with(StackMapTableAttribute.of(List.of(StackMapFrameInfo.of(dest,
487 List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS),
488 List.of(uninitializeInfo)))))
489 .invokespecial(CD_Object, INIT_NAME, MTD_void);
490 if (kind == UninitializeKind.UNINITIALIZED) {
491 // still need to call super constructor
492 cob.aload(0)
493 .invokespecial(CD_Object, INIT_NAME, MTD_void);
494 }
495 cob.return_();
496 }));
497 var errors = ClassFile.of().verify(bytes);
498 assertNotEquals(List.of(), errors, () -> errors + " : " + ClassFile.of().parse(bytes).toDebugString());
499 var lookup = MethodHandles.lookup();
500 assertThrows(VerifyError.class, () -> lookup.defineHiddenClass(bytes, true)); // force JVM verification
501 }
502
503 public static Stream<Arguments> uninitializedInBytecodeClasses() {
504 return Arrays.stream(ComparisonInstruction.values())
505 .mapMulti((inst, sink) -> {
506 for (var kind : UninitializeKind.values()) {
507 sink.accept(Arguments.of(inst, kind));
508 }
509 });
510 }
511 }