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.
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 enum ComparisonInstruction {
425 IF_ACMPEQ(Opcode.IF_ACMPEQ, 2),
426 IF_ACMPNE(Opcode.IF_ACMPNE, 2),
427 IFNONNULL(Opcode.IFNONNULL, 1),
428 IFNULL(Opcode.IFNULL, 1);
429 final Opcode opcode;
430 final int argCount;
431 ComparisonInstruction(Opcode opcode, int argCount) {
432 this.opcode = opcode;
433 this.argCount = argCount;
434 }
435 }
436
437 enum UninitializeKind {
438 UNINITIALIZED, UNINITIALIZED_THIS
439 }
440
441 @ParameterizedTest
442 @MethodSource("uninitializedInBytecodeClasses")
443 public void testUninitializedInComparisons(ComparisonInstruction inst, UninitializeKind kind) throws Throwable {
444 var bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(ClassDesc.of("Test"), clb -> clb
445 .withMethodBody(INIT_NAME, MTD_void, 0, cob -> {
446 StackMapFrameInfo.VerificationTypeInfo uninitializeInfo;
447 if (kind == UninitializeKind.UNINITIALIZED) {
448 uninitializeInfo = StackMapFrameInfo.UninitializedVerificationTypeInfo.of(cob.newBoundLabel());
449 cob.new_(CD_Object);
450 } else {
451 uninitializeInfo = StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS;
452 cob.aload(0);
453 }
454
455 // Stack: uninitializeInfo
456 for (int i = 0; i < inst.argCount; i++) {
457 cob.dup();
458 }
459 var dest = cob.newLabel();
460 cob.branch(inst.opcode, dest)
461 .nop()
462 .labelBinding(dest)
463 .with(StackMapTableAttribute.of(List.of(StackMapFrameInfo.of(dest,
464 List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS),
465 List.of(uninitializeInfo)))))
466 .invokespecial(CD_Object, INIT_NAME, MTD_void);
467 if (kind == UninitializeKind.UNINITIALIZED) {
468 // still need to call super constructor
469 cob.aload(0)
470 .invokespecial(CD_Object, INIT_NAME, MTD_void);
471 }
472 cob.return_();
473 }));
474 var errors = ClassFile.of().verify(bytes);
475 assertNotEquals(List.of(), errors, () -> errors + " : " + ClassFile.of().parse(bytes).toDebugString());
476 var lookup = MethodHandles.lookup();
477 assertThrows(VerifyError.class, () -> lookup.defineHiddenClass(bytes, true)); // force JVM verification
478 }
479
480 public static Stream<Arguments> uninitializedInBytecodeClasses() {
481 return Arrays.stream(ComparisonInstruction.values())
482 .mapMulti((inst, sink) -> {
483 for (var kind : UninitializeKind.values()) {
484 sink.accept(Arguments.of(inst, kind));
485 }
486 });
487 }
488
489 @Test // JDK-8350029
490 void testInvokeSpecialInterfacePatch() {
491 var runClass = ClassDesc.of("Run");
492 var testClass = ClassDesc.of("Test");
493 var runnableClass = Runnable.class.describeConstable().orElseThrow();
494 var chr = ClassHierarchyResolver.of(List.of(), Map.of(runClass, CD_Object))
495 .orElse(ClassHierarchyResolver.defaultResolver()).cached();
496 var context = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(chr));
497
498 for (var isInterface : new boolean[] {true, false}) {
499 var bytes = context.build(testClass, clb -> clb
500 .withVersion(JAVA_8_VERSION, 0)
501 .withSuperclass(runClass)
502 .withMethodBody("test", MethodTypeDesc.of(CD_void, testClass), ACC_STATIC, cob -> cob
503 .aload(0)
504 .invokespecial(runnableClass, "run", MTD_void, isInterface)
505 .return_()));
506 var errors = context.verify(bytes);
507 assertNotEquals(List.of(), errors, "invokespecial, isInterface = " + isInterface);
508 assertTrue(errors.getFirst().getMessage().contains("interface method to invoke is not in a direct superinterface"), errors.getFirst().getMessage());
509 }
510 }
511 }