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 }