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  * ValueObjectCompilationTests
  26  *
  27  * @test
  28  * @bug 8287136 8292630 8279368 8287136 8287770 8279840 8279672 8292753 8287763 8279901 8287767 8293183 8293120
  29  *      8329345 8341061 8340984 8334484
  30  * @summary Negative compilation tests, and positive compilation (smoke) tests for Value Objects
  31  * @enablePreview
  32  * @library /lib/combo /tools/lib
  33  * @modules
  34  *     jdk.compiler/com.sun.tools.javac.util
  35  *     jdk.compiler/com.sun.tools.javac.api
  36  *     jdk.compiler/com.sun.tools.javac.main
  37  *     jdk.compiler/com.sun.tools.javac.code
  38  * @build toolbox.ToolBox toolbox.JavacTask
  39  * @run junit ValueObjectCompilationTests
  40  */
  41 
  42 import java.io.File;
  43 
  44 import java.lang.classfile.Attributes;
  45 import java.lang.classfile.ClassFile;
  46 import java.lang.classfile.Instruction;
  47 import java.lang.classfile.Opcode;
  48 import java.lang.classfile.instruction.FieldInstruction;
  49 import java.lang.constant.ConstantDescs;
  50 import java.lang.reflect.AccessFlag;
  51 import java.util.ArrayList;
  52 import java.util.List;
  53 import java.util.Locale;
  54 import java.util.Set;
  55 
  56 import com.sun.tools.javac.util.Assert;
  57 
  58 import com.sun.tools.javac.code.Flags;
  59 
  60 import org.junit.jupiter.api.Test;
  61 import tools.javac.combo.CompilationTestCase;
  62 import toolbox.ToolBox;
  63 
  64 class ValueObjectCompilationTests extends CompilationTestCase {
  65 
  66     private static String[] PREVIEW_OPTIONS = {
  67             "--enable-preview",
  68             "-source", Integer.toString(Runtime.version().feature())
  69     };
  70 
  71     private static String[] PREVIEW_OPTIONS_PLUS_VM_ANNO = {
  72             "--enable-preview",
  73             "-source", Integer.toString(Runtime.version().feature()),
  74             "--add-exports=java.base/jdk.internal.vm.annotation=ALL-UNNAMED"
  75     };
  76 
  77     public ValueObjectCompilationTests() {
  78         setDefaultFilename("ValueObjectsTest.java");
  79         setCompileOptions(PREVIEW_OPTIONS);
  80     }
  81 
  82     @Test
  83     void testValueModifierConstraints() {
  84         assertFail("compiler.err.illegal.combination.of.modifiers",
  85                 """
  86                 value @interface IA {}
  87                 """);
  88         assertFail("compiler.err.illegal.combination.of.modifiers",
  89                 """
  90                 value interface I {}
  91                 """);
  92         assertFail("compiler.err.mod.not.allowed.here",
  93                 """
  94                 class Test {
  95                     value int x;
  96                 }
  97                 """);
  98         assertFail("compiler.err.mod.not.allowed.here",
  99                 """
 100                 class Test {
 101                     value int foo();
 102                 }
 103                 """);
 104         assertFail("compiler.err.mod.not.allowed.here",
 105                 """
 106                 value enum Enum {}
 107                 """);
 108     }
 109 
 110     record TestData(String message, String snippet, String[] compilerOptions, boolean testLocalToo) {
 111         TestData(String snippet) {
 112             this("", snippet, null, true);
 113         }
 114 
 115         TestData(String snippet, boolean testLocalToo) {
 116             this("", snippet, null, testLocalToo);
 117         }
 118 
 119         TestData(String message, String snippet) {
 120             this(message, snippet, null, true);
 121         }
 122 
 123         TestData(String snippet, String[] compilerOptions) {
 124             this("", snippet, compilerOptions, true);
 125         }
 126 
 127         TestData(String message, String snippet, String[] compilerOptions) {
 128             this(message, snippet, compilerOptions, true);
 129         }
 130 
 131         TestData(String message, String snippet, boolean testLocalToo) {
 132             this(message, snippet, null, testLocalToo);
 133         }
 134     }
 135 
 136     private void testHelper(List<TestData> testDataList) {
 137         String ttt =
 138                 """
 139                     class TTT {
 140                         void m() {
 141                             #LOCAL
 142                         }
 143                     }
 144                 """;
 145         for (TestData td : testDataList) {
 146             String localSnippet = ttt.replace("#LOCAL", td.snippet);
 147             String[] previousOptions = getCompileOptions();
 148             try {
 149                 if (td.compilerOptions != null) {
 150                     setCompileOptions(td.compilerOptions);
 151                 }
 152                 if (td.message == "") {
 153                     assertOK(td.snippet);
 154                     if (td.testLocalToo) {
 155                         assertOK(localSnippet);
 156                     }
 157                 } else if (td.message.startsWith("compiler.err")) {
 158                     assertFail(td.message, td.snippet);
 159                     if (td.testLocalToo) {
 160                         assertFail(td.message, localSnippet);
 161                     }
 162                 } else {
 163                     assertOKWithWarning(td.message, td.snippet);
 164                     if (td.testLocalToo) {
 165                         assertOKWithWarning(td.message, localSnippet);
 166                     }
 167                 }
 168             } finally {
 169                 setCompileOptions(previousOptions);
 170             }
 171         }
 172     }
 173 
 174     private static final List<TestData> superClassConstraints = List.of(
 175             new TestData(
 176                     "compiler.err.super.class.method.cannot.be.synchronized",
 177                     """
 178                     abstract class I {
 179                         synchronized void foo() {}
 180                     }
 181                     value class V extends I {}
 182                     """
 183             ),
 184             new TestData(
 185                     "compiler.err.concrete.supertype.for.value.class",
 186                     """
 187                     class ConcreteSuperType {
 188                         static abstract value class V extends ConcreteSuperType {}  // Error: concrete super.
 189                     }
 190                     """
 191             ),
 192             new TestData(
 193                     """
 194                     value record Point(int x, int y) {}
 195                     """
 196             ),
 197             new TestData(
 198                     """
 199                     value class One extends Number {
 200                         public int intValue() { return 0; }
 201                         public long longValue() { return 0; }
 202                         public float floatValue() { return 0; }
 203                         public double doubleValue() { return 0; }
 204                     }
 205                     """
 206             ),
 207             new TestData(
 208                     """
 209                     value class V extends Object {}
 210                     """
 211             ),
 212             new TestData(
 213                     "compiler.err.value.type.has.identity.super.type",
 214                     """
 215                     abstract class A {}
 216                     value class V extends A {}
 217                     """
 218             )
 219     );
 220 
 221     @Test
 222     void testSuperClassConstraints() {
 223         testHelper(superClassConstraints);
 224     }
 225 
 226     @Test
 227     void testRepeatedModifiers() {
 228         assertFail("compiler.err.repeated.modifier", "value value class ValueTest {}");
 229     }
 230 
 231     @Test
 232     void testParserTest() {
 233         assertOK(
 234                 """
 235                 value class Substring implements CharSequence {
 236                     private String str;
 237                     private int start;
 238                     private int end;
 239 
 240                     public Substring(String str, int start, int end) {
 241                         checkBounds(start, end, str.length());
 242                         this.str = str;
 243                         this.start = start;
 244                         this.end = end;
 245                     }
 246 
 247                     public int length() {
 248                         return end - start;
 249                     }
 250 
 251                     public char charAt(int i) {
 252                         checkBounds(0, i, length());
 253                         return str.charAt(start + i);
 254                     }
 255 
 256                     public Substring subSequence(int s, int e) {
 257                         checkBounds(s, e, length());
 258                         return new Substring(str, start + s, start + e);
 259                     }
 260 
 261                     public String toString() {
 262                         return str.substring(start, end);
 263                     }
 264 
 265                     private static void checkBounds(int start, int end, int length) {
 266                         if (start < 0 || end < start || length < end)
 267                             throw new IndexOutOfBoundsException();
 268                     }
 269                 }
 270                 """
 271         );
 272     }
 273 
 274     private static final List<TestData> semanticsViolations = List.of(
 275             new TestData(
 276                     "compiler.err.cant.inherit.from.final",
 277                     """
 278                     value class Base {}
 279                     class Subclass extends Base {}
 280                     """
 281             ),
 282             new TestData(
 283                     "compiler.err.cant.assign.val.to.var",
 284                     """
 285                     value class Point {
 286                         int x = 10;
 287                         int y;
 288                         Point (int x, int y) {
 289                             this.x = x; // Error, final field 'x' is already assigned to.
 290                             this.y = y; // OK.
 291                         }
 292                     }
 293                     """
 294             ),
 295             new TestData(
 296                     "compiler.err.cant.assign.val.to.var",
 297                     """
 298                     value class Point {
 299                         int x;
 300                         int y;
 301                         Point (int x, int y) {
 302                             this.x = x;
 303                             this.y = y;
 304                         }
 305                         void foo(Point p) {
 306                             this.y = p.y; // Error, y is final and can't be written outside of ctor.
 307                         }
 308                     }
 309                     """
 310             ),
 311             new TestData(
 312                     "compiler.err.cant.assign.val.to.var",
 313                     """
 314                     abstract value class Point {
 315                         int x;
 316                         int y;
 317                         Point (int x, int y) {
 318                             this.x = x;
 319                             this.y = y;
 320                         }
 321                         void foo(Point p) {
 322                             this.y = p.y; // Error, y is final and can't be written outside of ctor.
 323                         }
 324                     }
 325                     """
 326             ),
 327             new TestData(
 328                     "compiler.err.strict.field.not.have.been.initialized.before.super",
 329                     """
 330                     value class Point {
 331                         int x;
 332                         int y;
 333                         Point (int x, int y) {
 334                             this.x = x;
 335                             // y hasn't been initialized
 336                         }
 337                     }
 338                     """
 339             ),
 340             new TestData(
 341                     "compiler.err.mod.not.allowed.here",
 342                     """
 343                     abstract value class V {
 344                         synchronized void foo() {
 345                          // Error, abstract value class may not declare a synchronized instance method.
 346                         }
 347                     }
 348                     """
 349             ),
 350             new TestData(
 351                     """
 352                     abstract value class V {
 353                         static synchronized void foo() {} // OK static
 354                     }
 355                     """
 356             ),
 357             new TestData(
 358                     "compiler.err.mod.not.allowed.here",
 359                     """
 360                     value class V {
 361                         synchronized void foo() {}
 362                     }
 363                     """
 364             ),
 365             new TestData(
 366                     """
 367                     value class V {
 368                         synchronized static void soo() {} // OK static
 369                     }
 370                     """
 371             ),
 372             new TestData(
 373                     "compiler.err.type.found.req",
 374                     """
 375                     value class V {
 376                         { synchronized(this) {} }
 377                     }
 378                     """
 379             ),
 380             new TestData(
 381                     "compiler.err.mod.not.allowed.here",
 382                     """
 383                     value record R() {
 384                         synchronized void foo() { } // Error;
 385                         synchronized static void soo() {} // OK.
 386                     }
 387                     """
 388             ),
 389             new TestData(
 390                     "compiler.err.cant.ref.before.ctor.called",
 391                     """
 392                     value class V {
 393                         int x;
 394                         V() {
 395                             foo(this); // Error.
 396                             x = 10;
 397                         }
 398                         void foo(V v) {}
 399                     }
 400                     """
 401             ),
 402             new TestData(
 403                     "compiler.err.cant.ref.before.ctor.called",
 404                     """
 405                     value class V {
 406                         int x;
 407                         V() {
 408                             x = 10;
 409                             foo(this); // error
 410                         }
 411                         void foo(V v) {}
 412                     }
 413                     """
 414             ),
 415             new TestData(
 416                     "compiler.err.type.found.req",
 417                     """
 418                     interface I {}
 419                     interface VI extends I {}
 420                     class C {}
 421                     value class VC<T extends VC> {
 422                         void m(T t) {
 423                             synchronized(t) {} // error
 424                         }
 425                     }
 426                     """
 427             ),
 428             new TestData(
 429                     "compiler.err.type.found.req",
 430                     """
 431                     interface I {}
 432                     interface VI extends I {}
 433                     class C {}
 434                     value class VC<T extends VC> {
 435                         void foo(Object o) {
 436                             synchronized ((VC & I)o) {} // error
 437                         }
 438                     }
 439                     """
 440             ),
 441             new TestData(
 442                     // OK if the value class is abstract
 443                     """
 444                     interface I {}
 445                     abstract value class VI implements I {}
 446                     class C {}
 447                     value class VC<T extends VC> {
 448                         void bar(Object o) {
 449                             synchronized ((VI & I)o) {} // error
 450                         }
 451                     }
 452                     """
 453             ),
 454             new TestData(
 455                     "compiler.err.type.found.req", // --enable-preview -source"
 456                     """
 457                     class V {
 458                         final Integer val = Integer.valueOf(42);
 459                         void test() {
 460                             synchronized (val) { // error
 461                             }
 462                         }
 463                     }
 464                     """
 465             ),
 466             new TestData(
 467                     "compiler.err.type.found.req", // --enable-preview -source"
 468                     """
 469                     import java.time.*;
 470                     class V {
 471                         final Duration val = Duration.ZERO;
 472                         void test() {
 473                             synchronized (val) { // warn
 474                             }
 475                         }
 476                     }
 477                     """,
 478                     false // cant do local as there is an import statement
 479             ),
 480             new TestData(
 481                     "compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class", // empty options
 482                     """
 483                     class V {
 484                         final Integer val = Integer.valueOf(42);
 485                         void test() {
 486                             synchronized (val) { // warn
 487                             }
 488                         }
 489                     }
 490                     """,
 491                     new String[] {}
 492             ),
 493             new TestData(
 494                     "compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class", // --source
 495                     """
 496                     class V {
 497                         final Integer val = Integer.valueOf(42);
 498                         void test() {
 499                             synchronized (val) { // warn
 500                             }
 501                         }
 502                     }
 503                     """,
 504                     new String[] {"--source", Integer.toString(Runtime.version().feature())}
 505             ),
 506             new TestData(
 507                     "compiler.warn.attempt.to.synchronize.on.instance.of.value.based.class", // --source
 508                     """
 509                     class V {
 510                         final Integer val = Integer.valueOf(42);
 511                         void test() {
 512                             synchronized (val) { // warn
 513                             }
 514                         }
 515                     }
 516                     """,
 517                     new String[] {"--source", Integer.toString(Runtime.version().feature())}
 518             ),
 519             new TestData(
 520                     "compiler.err.illegal.combination.of.modifiers", // --enable-preview -source"
 521                     """
 522                     value class V {
 523                         volatile int f = 1;
 524                     }
 525                     """
 526             )
 527     );
 528 
 529     @Test
 530     void testSemanticsViolations() {
 531         testHelper(semanticsViolations);
 532     }
 533 
 534     private static final List<TestData> sealedClassesData = List.of(
 535             new TestData(
 536                     """
 537                     abstract sealed value class SC {}
 538                     value class VC extends SC {}
 539                     """,
 540                     false // local sealed classes are not allowed
 541             ),
 542             new TestData(
 543                     """
 544                     abstract sealed interface SI {}
 545                     value class VC implements SI {}
 546                     """,
 547                     false // local sealed classes are not allowed
 548             ),
 549             new TestData(
 550                     """
 551                     abstract sealed class SC {}
 552                     final class IC extends SC {}
 553                     non-sealed class IC2 extends SC {}
 554                     final class IC3 extends IC2 {}
 555                     """,
 556                     false
 557             ),
 558             new TestData(
 559                     """
 560                     abstract sealed interface SI {}
 561                     final class IC implements SI {}
 562                     non-sealed class IC2 implements SI {}
 563                     final class IC3 extends IC2 {}
 564                     """,
 565                     false // local sealed classes are not allowed
 566             ),
 567             new TestData(
 568                     "compiler.err.non.abstract.value.class.cant.be.sealed.or.non.sealed",
 569                     """
 570                     abstract sealed value class SC {}
 571                     non-sealed value class VC extends SC {}
 572                     """,
 573                     false
 574             ),
 575             new TestData(
 576                     "compiler.err.non.abstract.value.class.cant.be.sealed.or.non.sealed",
 577                     """
 578                     sealed value class SI {}
 579                     """,
 580                     false
 581             ),
 582             new TestData(
 583                     """
 584                     sealed abstract value class SI {}
 585                     value class V extends SI {}
 586                     """,
 587                     false
 588             ),
 589             new TestData(
 590                     """
 591                     sealed abstract value class SI permits V {}
 592                     value class V extends SI {}
 593                     """,
 594                     false
 595             ),
 596             new TestData(
 597                     """
 598                     sealed interface I {}
 599                     non-sealed abstract value class V implements I {}
 600                     """,
 601                     false
 602             ),
 603             new TestData(
 604                     """
 605                     sealed interface I permits V {}
 606                     non-sealed abstract value class V implements I {}
 607                     """,
 608                     false
 609             )
 610     );
 611 
 612     @Test
 613     void testInteractionWithSealedClasses() {
 614         testHelper(sealedClassesData);
 615     }
 616 
 617     @Test
 618     void testCheckClassFileFlags() throws Exception {
 619         for (String source : List.of(
 620                 """
 621                 interface I {}
 622                 class Test {
 623                     I i = new I() {};
 624                 }
 625                 """,
 626                 """
 627                 class C {}
 628                 class Test {
 629                     C c = new C() {};
 630                 }
 631                 """,
 632                 """
 633                 class Test {
 634                     Object o = new Object() {};
 635                 }
 636                 """,
 637                 """
 638                 class Test {
 639                     abstract class Inner {}
 640                 }
 641                 """
 642         )) {
 643             File dir = assertOK(true, source);
 644             for (final File fileEntry : dir.listFiles()) {
 645                 if (fileEntry.getName().contains("$")) {
 646                     var classFile = ClassFile.of().parse(fileEntry.toPath());
 647                     Assert.check(classFile.flags().has(AccessFlag.IDENTITY));
 648                 }
 649             }
 650         }
 651 
 652         for (String source : List.of(
 653                 """
 654                 class C {}
 655                 """,
 656                 """
 657                 abstract class A {
 658                     int i;
 659                 }
 660                 """,
 661                 """
 662                 abstract class A {
 663                     synchronized void m() {}
 664                 }
 665                 """,
 666                 """
 667                 class C {
 668                     synchronized void m() {}
 669                 }
 670                 """,
 671                 """
 672                 abstract class A {
 673                     int i;
 674                     { i = 0; }
 675                 }
 676                 """,
 677                 """
 678                 abstract class A {
 679                     A(int i) {}
 680                 }
 681                 """,
 682                 """
 683                     enum E {}
 684                 """,
 685                 """
 686                     record R() {}
 687                 """
 688         )) {
 689             File dir = assertOK(true, source);
 690             for (final File fileEntry : dir.listFiles()) {
 691                 var classFile = ClassFile.of().parse(fileEntry.toPath());
 692                 Assert.check(classFile.flags().has(AccessFlag.IDENTITY));
 693             }
 694         }
 695 
 696         {
 697             String source =
 698                     """
 699                     abstract value class A {}
 700                     value class Sub extends A {} //implicitly final
 701                     """;
 702             File dir = assertOK(true, source);
 703             for (final File fileEntry : dir.listFiles()) {
 704                 var classFile = ClassFile.of().parse(fileEntry.toPath());
 705                 switch (classFile.thisClass().asInternalName()) {
 706                     case "Sub":
 707                         Assert.check((classFile.flags().flagsMask() & (Flags.FINAL)) != 0);
 708                         break;
 709                     case "A":
 710                         Assert.check((classFile.flags().flagsMask() & (Flags.ABSTRACT)) != 0);
 711                         break;
 712                     default:
 713                         throw new AssertionError("you shoulnd't be here");
 714                 }
 715             }
 716         }
 717 
 718         for (String source : List.of(
 719                 """
 720                 value class V {
 721                     int i = 0;
 722                     static int j;
 723                 }
 724                 """,
 725                 """
 726                 abstract value class A {
 727                     static int j;
 728                 }
 729 
 730                 value class V extends A {
 731                     int i = 0;
 732                 }
 733                 """
 734         )) {
 735             File dir = assertOK(true, source);
 736             for (final File fileEntry : dir.listFiles()) {
 737                 var classFile = ClassFile.of().parse(fileEntry.toPath());
 738                 for (var field : classFile.fields()) {
 739                     if (!field.flags().has(AccessFlag.STATIC)) {
 740                         Set<AccessFlag> fieldFlags = field.flags().flags();
 741                         Assert.check(fieldFlags.size() == 2 && fieldFlags.contains(AccessFlag.FINAL) && fieldFlags.contains(AccessFlag.STRICT_INIT));
 742                     }
 743                 }
 744             }
 745         }
 746 
 747         // testing experimental @Strict annotation
 748         String[] previousOptions = getCompileOptions();
 749         try {
 750             setCompileOptions(PREVIEW_OPTIONS_PLUS_VM_ANNO);
 751             for (String source : List.of(
 752                     """
 753                     import jdk.internal.vm.annotation.Strict;
 754                     class Test {
 755                         @Strict int i = 0;
 756                     }
 757                     """,
 758                     """
 759                     import jdk.internal.vm.annotation.Strict;
 760                     class Test {
 761                         @Strict final int i = 0;
 762                     }
 763                     """
 764             )) {
 765                 File dir = assertOK(true, source);
 766                 for (final File fileEntry : dir.listFiles()) {
 767                     var classFile = ClassFile.of().parse(fileEntry.toPath());
 768                     Assert.check(classFile.flags().has(AccessFlag.IDENTITY));
 769                     for (var field : classFile.fields()) {
 770                         if (!field.flags().has(AccessFlag.STATIC)) {
 771                             Set<AccessFlag> fieldFlags = field.flags().flags();
 772                             Assert.check(fieldFlags.contains(AccessFlag.STRICT_INIT));
 773                         }
 774                     }
 775                 }
 776             }
 777         } finally {
 778             setCompileOptions(previousOptions);
 779         }
 780     }
 781 
 782     @Test
 783     void testConstruction() throws Exception {
 784         record Data(String src, boolean isRecord) {
 785             Data(String src) {
 786                 this(src, false);
 787             }
 788         }
 789         for (Data data : List.of(
 790                 new Data(
 791                     """
 792                     value class Test {
 793                         int i = 100;
 794                     }
 795                     """),
 796                 new Data(
 797                     """
 798                     value class Test {
 799                         int i;
 800                         Test() {
 801                             i = 100;
 802                         }
 803                     }
 804                     """),
 805                 new Data(
 806                     """
 807                     value class Test {
 808                         int i;
 809                         Test() {
 810                             i = 100;
 811                             super();
 812                         }
 813                     }
 814                     """),
 815                 new Data(
 816                     """
 817                     value class Test {
 818                         int i;
 819                         Test() {
 820                             this.i = 100;
 821                             super();
 822                         }
 823                     }
 824                     """),
 825                 new Data(
 826                     """
 827                     value record Test(int i) {}
 828                     """, true)
 829         )) {
 830             String expectedCodeSequence = "aload_0,bipush,putfield,aload_0,invokespecial,return";
 831             String expectedCodeSequenceRecord = "aload_0,iload_1,putfield,aload_0,invokespecial,return";
 832             File dir = assertOK(true, data.src);
 833             for (final File fileEntry : dir.listFiles()) {
 834                 var classFile = ClassFile.of().parse(fileEntry.toPath());
 835                 classFile.methods().stream()
 836                         .filter(mm -> mm.methodName().equalsString(ConstantDescs.INIT_NAME))
 837                         .map(mm -> mm.findAttribute(Attributes.code()).orElseThrow())
 838                         .forEach(code -> {
 839                             List<String> mnemonics = new ArrayList<>();
 840                             for (var coe : code) {
 841                                 if (coe instanceof Instruction inst) {
 842                                     mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT));
 843                                 }
 844                             }
 845                             var foundCodeSequence = String.join(",", mnemonics);
 846                             if (!data.isRecord) {
 847                                 Assert.check(expectedCodeSequence.equals(foundCodeSequence));
 848                             } else {
 849                                 Assert.check(expectedCodeSequenceRecord.equals(foundCodeSequence));
 850                             }
 851                         });
 852             }
 853         }
 854 
 855         String source =
 856                 """
 857                 value class Test {
 858                     int i = 100;
 859                     int j = 0;
 860                     {
 861                         System.out.println(j);
 862                     }
 863                 }
 864                 """;
 865         {
 866             String expectedCodeSequence = "aload_0,bipush,putfield,aload_0,iconst_0,putfield,aload_0,invokespecial,getstatic,iconst_0,invokevirtual,return";
 867             File dir = assertOK(true, source);
 868             for (final File fileEntry : dir.listFiles()) {
 869                 var classFile = ClassFile.of().parse(fileEntry.toPath());
 870                 classFile.methods().stream()
 871                         .filter(mm -> mm.methodName().equalsString(ConstantDescs.INIT_NAME))
 872                         .map(mm -> mm.findAttribute(Attributes.code()).orElseThrow())
 873                         .forEach(code -> {
 874                             List<String> mnemonics = new ArrayList<>();
 875                             for (var coe : code) {
 876                                 if (coe instanceof Instruction inst) {
 877                                     mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT));
 878                                 }
 879                             }
 880                             var foundCodeSequence = String.join(",", mnemonics);
 881                             Assert.check(expectedCodeSequence.equals(foundCodeSequence), "found " + foundCodeSequence);
 882                         });
 883             }
 884         }
 885 
 886         assertFail("compiler.err.cant.ref.before.ctor.called",
 887                 """
 888                 value class Test {
 889                     Test() {
 890                         m();
 891                     }
 892                     void m() {}
 893                 }
 894                 """
 895         );
 896         assertFail("compiler.err.strict.field.not.have.been.initialized.before.super",
 897                 """
 898                 value class Test {
 899                     int i;
 900                     Test() {
 901                         super();
 902                         this.i = i;
 903                     }
 904                 }
 905                 """
 906         );
 907         assertOK(
 908                 """
 909                 class UnrelatedThisLeak {
 910                     value class V {
 911                         int f;
 912                         V() {
 913                             UnrelatedThisLeak x = UnrelatedThisLeak.this;
 914                             f = 10;
 915                             x = UnrelatedThisLeak.this;
 916                         }
 917                     }
 918                 }
 919                 """
 920         );
 921         assertFail("compiler.err.cant.ref.before.ctor.called",
 922                 """
 923                 value class Test {
 924                     Test t = null;
 925                     Runnable r = () -> { System.err.println(t); }; // cant reference `t` from a lambda expression in the prologue
 926                 }
 927                 """
 928         );
 929         assertFail("compiler.err.strict.field.not.have.been.initialized.before.super",
 930                 """
 931                 value class Test {
 932                     int f;
 933                     {
 934                         f = 1;
 935                     }
 936                 }
 937                 """
 938         );
 939         assertOK(
 940                 """
 941                 value class V {
 942                     int x;
 943                     int y = x + 1; // allowed
 944                     V() {
 945                         x = 12;
 946                         // super();
 947                     }
 948                 }
 949                 """
 950         );
 951         assertFail("compiler.err.var.might.already.be.assigned",
 952                 """
 953                 value class V2 {
 954                     int x;
 955                     V2() { this(x = 3); } // error
 956                     V2(int i) { x = 4; }
 957                 }
 958                 """
 959         );
 960         assertOK(
 961                 """
 962                 abstract value class AV1 {
 963                     AV1(int i) {}
 964                 }
 965                 value class V3 extends AV1 {
 966                     int x;
 967                     V3() {
 968                         super(x = 3); // ok
 969                     }
 970                 }
 971                 """
 972         );
 973         assertOK(
 974                 """
 975                 value class V4 {
 976                     int x;
 977                     int y = x + 1;
 978                     V4() {
 979                         x = 12;
 980                     }
 981                     V4(int i) {
 982                         x = i;
 983                     }
 984                 }
 985                 """
 986         );
 987         assertOK(
 988                 """
 989                 value class V {
 990                     final int x = "abc".length();
 991                     { System.out.println(x); }
 992                 }
 993                 """
 994         );
 995         assertFail("compiler.err.illegal.forward.ref",
 996                 """
 997                 value class V {
 998                     { System.out.println(x); }
 999                     final int x = "abc".length();
1000                 }
1001                 """
1002         );
1003         assertOK(
1004                 """
1005                 value class V {
1006                     int x = "abc".length();
1007                     int y = x;
1008                 }
1009                 """
1010         );
1011         assertOK(
1012                 """
1013                 value class V {
1014                     int x = "abc".length();
1015                     { int y = x; }
1016                 }
1017                 """
1018         );
1019         assertOK(
1020                 """
1021                 value class V {
1022                     String s1;
1023                     { System.out.println(s1); }
1024                     String s2 = (s1 = "abc");
1025                 }
1026                 """
1027         );
1028 
1029         String[] previousOptions = getCompileOptions();
1030         try {
1031             setCompileOptions(PREVIEW_OPTIONS_PLUS_VM_ANNO);
1032             String[] sources = new String[]{
1033                     """
1034                     import jdk.internal.vm.annotation.Strict;
1035                     class Test {
1036                         static value class IValue {
1037                             int i = 0;
1038                         }
1039                         @Strict
1040                         final IValue val = new IValue();
1041                     }
1042                     """,
1043                     """
1044                     import jdk.internal.vm.annotation.Strict;
1045                     class Test {
1046                         static value class IValue {
1047                             int i = 0;
1048                         }
1049                         @Strict
1050                         final IValue val;
1051                         Test() {
1052                             val = new IValue();
1053                         }
1054                     }
1055                     """
1056             };
1057             var expectedCodeSequence = "aload_0,new,dup,invokespecial,putfield,aload_0,invokespecial,return";
1058             for (String src : sources) {
1059                 File dir = assertOK(true, src);
1060                 for (final File fileEntry : dir.listFiles()) {
1061                     var classFile = ClassFile.of().parse(fileEntry.toPath());
1062                     if (classFile.thisClass().name().equalsString("Test")) {
1063                         for (var method : classFile.methods()) {
1064                             if (method.methodName().equalsString("<init>")) {
1065                                 var code = method.findAttribute(Attributes.code()).orElseThrow();
1066                                 List<String> mnemonics = new ArrayList<>();
1067                                 for (var coe : code) {
1068                                     if (coe instanceof Instruction inst) {
1069                                         mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT));
1070                                     }
1071                                 }
1072                                 var foundCodeSequence = String.join(",", mnemonics);
1073                                 Assert.check(expectedCodeSequence.equals(foundCodeSequence), "found " + foundCodeSequence);
1074                             }
1075                         }
1076                     }
1077                 }
1078             }
1079 
1080             assertFail("compiler.err.cant.ref.before.ctor.called",
1081                     """
1082                     import jdk.internal.vm.annotation.NullRestricted;
1083                     import jdk.internal.vm.annotation.Strict;
1084                     class StrictNR {
1085                         static value class IValue {
1086                             int i = 0;
1087                         }
1088                         value class SValue {
1089                             short s = 0;
1090                         }
1091                         @Strict
1092                         @NullRestricted
1093                         IValue val = new IValue();
1094                         @Strict
1095                         @NullRestricted
1096                         final IValue val2;
1097                         @Strict
1098                         @NullRestricted
1099                         SValue val3 = new SValue();
1100                     }
1101                     """
1102             );
1103             assertFail("compiler.err.strict.field.not.have.been.initialized.before.super",
1104                     """
1105                     import jdk.internal.vm.annotation.Strict;
1106                     class Test {
1107                         @Strict int i;
1108                     }
1109                     """
1110             );
1111             assertFail("compiler.err.strict.field.not.have.been.initialized.before.super",
1112                     """
1113                     import jdk.internal.vm.annotation.Strict;
1114                     class Test {
1115                         @Strict int i;
1116                         Test() {
1117                             super();
1118                             i = 0;
1119                         }
1120                     }
1121                     """
1122             );
1123             assertFail("compiler.err.cant.ref.before.ctor.called",
1124                     """
1125                     import jdk.internal.vm.annotation.NullRestricted;
1126                     import jdk.internal.vm.annotation.Strict;
1127                     class StrictNR {
1128                         static value class IValue {
1129                             int i = 0;
1130                         }
1131                         value class SValue {
1132                             short s = 0;
1133                         }
1134                         @Strict
1135                         @NullRestricted
1136                         IValue val = new IValue();
1137                             @Strict
1138                             @NullRestricted
1139                             SValue val4;
1140                         public StrictNR() {
1141                             val4 = new SValue();
1142                         }
1143                     }
1144                     """
1145             );
1146         } finally {
1147             setCompileOptions(previousOptions);
1148         }
1149 
1150         source =
1151             """
1152             value class V {
1153                 int i = 1;
1154                 int y;
1155                 V() {
1156                     y = 2;
1157                 }
1158             }
1159             """;
1160         {
1161             File dir = assertOK(true, source);
1162             File fileEntry = dir.listFiles()[0];
1163             var expectedCodeSequence = "putfield i,putfield y";
1164             var classFile = ClassFile.of().parse(fileEntry.toPath());
1165             for (var method : classFile.methods()) {
1166                 if (method.methodName().equalsString("<init>")) {
1167                     var code = method.findAttribute(Attributes.code()).orElseThrow();
1168                     List<String> mnemonics = new ArrayList<>();
1169                     for (var coe : code) {
1170                         if (coe instanceof FieldInstruction inst && inst.opcode() == Opcode.PUTFIELD) {
1171                             mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT) + " " + inst.name());
1172                         }
1173                     }
1174                     var foundCodeSequence = String.join(",", mnemonics);
1175                     Assert.check(expectedCodeSequence.equals(foundCodeSequence), "found " + foundCodeSequence);
1176                 }
1177             }
1178         }
1179     }
1180 
1181     @Test
1182     void testThisCallingConstructor() throws Exception {
1183         // make sure that this() calling constructors doesn't initialize final fields
1184         String source =
1185                 """
1186                 value class Test {
1187                     int i;
1188                     Test() {
1189                         this(0);
1190                     }
1191 
1192                     Test(int i) {
1193                         this.i = i;
1194                     }
1195                 }
1196                 """;
1197         File dir = assertOK(true, source);
1198         File fileEntry = dir.listFiles()[0];
1199         String expectedCodeSequenceThisCallingConst = "aload_0,iconst_0,invokespecial,return";
1200         String expectedCodeSequenceNonThisCallingConst = "aload_0,iload_1,putfield,aload_0,invokespecial,return";
1201         var classFile = ClassFile.of().parse(fileEntry.toPath());
1202         for (var method : classFile.methods()) {
1203             if (method.methodName().equalsString("<init>")) {
1204                 var code = method.findAttribute(Attributes.code()).orElseThrow();
1205                 List<String> mnemonics = new ArrayList<>();
1206                 for (var coe : code) {
1207                     if (coe instanceof Instruction inst) {
1208                         mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT));
1209                     }
1210                 }
1211                 var foundCodeSequence = String.join(",", mnemonics);
1212                 var expected = method.methodTypeSymbol().parameterCount() == 0 ?
1213                         expectedCodeSequenceThisCallingConst : expectedCodeSequenceNonThisCallingConst;
1214                 Assert.check(expected.equals(foundCodeSequence), "found " + foundCodeSequence);
1215             }
1216         }
1217     }
1218 
1219     @Test
1220     void testSelectors() throws Exception {
1221         assertOK(
1222                 """
1223                 value class V {
1224                     void selector() {
1225                         Class<?> c = int.class;
1226                     }
1227                 }
1228                 """
1229         );
1230         assertFail("compiler.err.expected",
1231                 """
1232                 value class V {
1233                     void selector() {
1234                         int i = int.some_selector;
1235                     }
1236                 }
1237                 """
1238         );
1239     }
1240 
1241     @Test
1242     void testNullAssigment() throws Exception {
1243         assertOK(
1244                 """
1245                 value final class V {
1246                     final int x = 10;
1247 
1248                     value final class X {
1249                         final V v;
1250                         final V v2;
1251 
1252                         X() {
1253                             this.v = null;
1254                             this.v2 = null;
1255                         }
1256 
1257                         X(V v) {
1258                             this.v = v;
1259                             this.v2 = v;
1260                         }
1261 
1262                         V foo(X x) {
1263                             x = new X(null);  // OK
1264                             return x.v;
1265                         }
1266                     }
1267                     V bar(X x) {
1268                         x = new X(null); // OK
1269                         return x.v;
1270                     }
1271 
1272                     class Y {
1273                         V v;
1274                         V [] va = { null }; // OK: array initialization
1275                         V [] va2 = new V[] { null }; // OK: array initialization
1276                         void ooo(X x) {
1277                             x = new X(null); // OK
1278                             v = null; // legal assignment.
1279                             va[0] = null; // legal.
1280                             va = new V[] { null }; // legal
1281                         }
1282                     }
1283                 }
1284                 """
1285         );
1286     }
1287 
1288     @Test
1289     void testSerializationWarnings() throws Exception {
1290         String[] previousOptions = getCompileOptions();
1291         try {
1292             setCompileOptions(new String[] {"-Xlint:serial", "--enable-preview", "--source",
1293                     Integer.toString(Runtime.version().feature())});
1294             assertOK(
1295                     """
1296                     import java.io.*;
1297                     abstract value class AVC implements Serializable {}
1298                     """);
1299             assertOKWithWarning("compiler.warn.serializable.value.class.without.write.replace.1",
1300                     """
1301                     import java.io.*;
1302                     value class VC implements Serializable {
1303                         private static final long serialVersionUID = 0;
1304                     }
1305                     """);
1306             assertOK(
1307                     """
1308                     import java.io.*;
1309                     class C implements Serializable {
1310                         private static final long serialVersionUID = 0;
1311                     }
1312                     """);
1313             assertOK(
1314                     """
1315                     import java.io.*;
1316                     abstract value class Super implements Serializable {
1317                         private static final long serialVersionUID = 0;
1318                         protected Object writeReplace() throws ObjectStreamException {
1319                             return null;
1320                         }
1321                     }
1322                     value class ValueSerializable extends Super {
1323                         private static final long serialVersionUID = 1;
1324                     }
1325                     """);
1326             assertOK(
1327                     """
1328                     import java.io.*;
1329                     abstract value class Super implements Serializable {
1330                         private static final long serialVersionUID = 0;
1331                         Object writeReplace() throws ObjectStreamException {
1332                             return null;
1333                         }
1334                     }
1335                     value class ValueSerializable extends Super {
1336                         private static final long serialVersionUID = 1;
1337                     }
1338                     """);
1339             assertOK(
1340                     """
1341                     import java.io.*;
1342                     abstract value class Super implements Serializable {
1343                         private static final long serialVersionUID = 0;
1344                         public Object writeReplace() throws ObjectStreamException {
1345                             return null;
1346                         }
1347                     }
1348                     value class ValueSerializable extends Super {
1349                         private static final long serialVersionUID = 1;
1350                     }
1351                     """);
1352             assertOKWithWarning("compiler.warn.serializable.value.class.without.write.replace.1",
1353                     """
1354                     import java.io.*;
1355                     abstract value class Super implements Serializable {
1356                         private static final long serialVersionUID = 0;
1357                         private Object writeReplace() throws ObjectStreamException {
1358                             return null;
1359                         }
1360                     }
1361                     value class ValueSerializable extends Super {
1362                         private static final long serialVersionUID = 1;
1363                     }
1364                     """);
1365             assertOKWithWarning("compiler.warn.serializable.value.class.without.write.replace.2",
1366                     """
1367                     import java.io.*;
1368                     abstract value class Super implements Serializable {
1369                         private static final long serialVersionUID = 0;
1370                         private Object writeReplace() throws ObjectStreamException {
1371                             return null;
1372                         }
1373                     }
1374                     class Serializable1 extends Super {
1375                         private static final long serialVersionUID = 1;
1376                     }
1377                     class Serializable2 extends Serializable1 {
1378                         private static final long serialVersionUID = 1;
1379                     }
1380                     """);
1381             assertOK(
1382                     """
1383                     import java.io.*;
1384                     abstract value class Super implements Serializable {
1385                         private static final long serialVersionUID = 0;
1386                         Object writeReplace() throws ObjectStreamException {
1387                             return null;
1388                         }
1389                     }
1390                     class ValueSerializable extends Super {
1391                         private static final long serialVersionUID = 1;
1392                     }
1393                     """);
1394             assertOK(
1395                     """
1396                     import java.io.*;
1397                     abstract value class Super implements Serializable {
1398                         private static final long serialVersionUID = 0;
1399                         public Object writeReplace() throws ObjectStreamException {
1400                             return null;
1401                         }
1402                     }
1403                     class ValueSerializable extends Super {
1404                         private static final long serialVersionUID = 1;
1405                     }
1406                     """);
1407             assertOK(
1408                     """
1409                     import java.io.*;
1410                     abstract value class Super implements Serializable {
1411                         private static final long serialVersionUID = 0;
1412                         protected Object writeReplace() throws ObjectStreamException {
1413                             return null;
1414                         }
1415                     }
1416                     class ValueSerializable extends Super {
1417                         private static final long serialVersionUID = 1;
1418                     }
1419                     """);
1420             assertOK(
1421                     """
1422                     import java.io.*;
1423                     value record ValueRecord() implements Serializable {
1424                         private static final long serialVersionUID = 1;
1425                     }
1426                     """);
1427             assertOK(
1428                     // Number is a special case, no warning for identity classes extending it
1429                     """
1430                     class NumberSubClass extends Number {
1431                         private static final long serialVersionUID = 0L;
1432                         @Override
1433                         public double doubleValue() { return 0; }
1434                         @Override
1435                         public int intValue() { return 0; }
1436                         @Override
1437                         public long longValue() { return 0; }
1438                         @Override
1439                         public float floatValue() { return 0; }
1440                     }
1441                     """
1442             );
1443         } finally {
1444             setCompileOptions(previousOptions);
1445         }
1446     }
1447 
1448     @Test
1449     void testAssertUnsetFieldsSMEntry() throws Exception {
1450         String[] previousOptions = getCompileOptions();
1451         try {
1452             String[] testOptions = {
1453                     "--enable-preview",
1454                     "-source", Integer.toString(Runtime.version().feature()),
1455                     "-XDnoLocalProxyVars",
1456                     "-XDdebug.stackmap",
1457                     "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED"
1458             };
1459             setCompileOptions(testOptions);
1460 
1461             record Data(String src, int[] expectedFrameTypes, String[][] expectedUnsetFields) {}
1462             for (Data data : List.of(
1463                     new Data(
1464                             """
1465                             import jdk.internal.vm.annotation.Strict;
1466                             class Test {
1467                                 @Strict
1468                                 final int x;
1469                                 @Strict
1470                                 final int y;
1471                                 Test(boolean a, boolean b) {
1472                                     if (a) { // early_larval {x, y}
1473                                         x = 1;
1474                                         if (b) { // early_larval {y}
1475                                             y = 1;
1476                                         } else { // early_larval {y}
1477                                             y = 2;
1478                                         }
1479                                     } else { // early_larval {x, y}
1480                                         x = y = 3;
1481                                     }
1482                                     super();
1483                                 }
1484                             }
1485                             """,
1486                             // three unset_fields entries, entry type 246, are expected in the stackmap table
1487                             new int[] {246, 246, 246},
1488                             // expected fields for each of them:
1489                             new String[][] { new String[] { "y:I" }, new String[] { "x:I", "y:I" }, new String[] {} }
1490                     ),
1491                     new Data(
1492                             """
1493                             import jdk.internal.vm.annotation.Strict;
1494                             class Test {
1495                                 @Strict
1496                                 final int x;
1497                                 @Strict
1498                                 final int y;
1499                                 Test(int n) {
1500                                     switch(n) {
1501                                         case 2:
1502                                             x = y = 2;
1503                                             break;
1504                                         default:
1505                                             x = y = 100;
1506                                             break;
1507                                     }
1508                                     super();
1509                                 }
1510                             }
1511                             """,
1512                             // here we expect only one
1513                             new int[] {20, 12, 246},
1514                             // stating that no field is unset
1515                             new String[][] { new String[] {} }
1516                     ),
1517                     new Data(
1518                             """
1519                             import jdk.internal.vm.annotation.Strict;
1520                             class Test {
1521                                 @Strict
1522                                 final int x;
1523                                 @Strict
1524                                 final int y;
1525                                 Test(int n) {
1526                                     if (n % 3 == 0) {
1527                                         x = n / 3;
1528                                     } else { // no unset change
1529                                         x = n + 2;
1530                                     } // early_larval {y}
1531                                     y = n >>> 3;
1532                                     super();
1533                                     if ((char) n != n) {
1534                                         n -= 5;
1535                                     } // no uninitializedThis - automatically cleared unsets
1536                                     Math.abs(n);
1537                                 }
1538                             }
1539                             """,
1540                             // here we expect only one, none for the post-larval frame
1541                             new int[] {16, 246, 255},
1542                             // stating that y is unset when if-else finishes
1543                             new String[][] { new String[] {"y:I"} }
1544                     )
1545             )) {
1546                 File dir = assertOK(true, data.src());
1547                 for (final File fileEntry : dir.listFiles()) {
1548                     var classFile = ClassFile.of().parse(fileEntry.toPath());
1549                     for (var method : classFile.methods()) {
1550                         if (method.methodName().equalsString(ConstantDescs.INIT_NAME)) {
1551                             var code = method.findAttribute(Attributes.code()).orElseThrow();
1552                             var stackMapTable = code.findAttribute(Attributes.stackMapTable()).orElseThrow();
1553                             Assert.check(data.expectedFrameTypes().length == stackMapTable.entries().size(), "unexpected stackmap length");
1554                             int entryIndex = 0;
1555                             int expectedUnsetFieldsIndex = 0;
1556                             for (var entry : stackMapTable.entries()) {
1557                                 Assert.check(data.expectedFrameTypes()[entryIndex++] == entry.frameType(), "expected " + data.expectedFrameTypes()[entryIndex - 1] + " found " + entry.frameType());
1558                                 if (entry.frameType() == 246) {
1559                                     Assert.check(data.expectedUnsetFields()[expectedUnsetFieldsIndex].length == entry.unsetFields().size());
1560                                     int index = 0;
1561                                     for (var nat : entry.unsetFields()) {
1562                                         String unsetStr = nat.name() + ":" + nat.type();
1563                                         Assert.check(data.expectedUnsetFields()[expectedUnsetFieldsIndex][index++].equals(unsetStr));
1564                                     }
1565                                     expectedUnsetFieldsIndex++;
1566                                 }
1567                             }
1568                         }
1569                     }
1570                 }
1571             }
1572         } finally {
1573             setCompileOptions(previousOptions);
1574         }
1575     }
1576 
1577     @Test
1578     void testLocalProxyVars() throws Exception {
1579         String[] previousOptions = getCompileOptions();
1580         try {
1581             String[] testOptions = {
1582                     "--enable-preview",
1583                     "-source", Integer.toString(Runtime.version().feature()),
1584                     "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED"
1585             };
1586             setCompileOptions(testOptions);
1587             String[] sources = new String[] {
1588                     """
1589                     value class Test {
1590                         int i;
1591                         int j;
1592                         Test() {// javac should generate a proxy local var for `i`
1593                             i = 1;
1594                             j = i; // as here `i` is being read during the early construction phase, use the local var instead
1595                             super();
1596                             System.err.println(i);
1597                         }
1598                     }
1599                     """,
1600                     """
1601                     import jdk.internal.vm.annotation.Strict;
1602                     class Test {
1603                         @Strict
1604                         int i;
1605                         @Strict
1606                         int j;
1607                         Test() {
1608                             i = 1;
1609                             j = i;
1610                             super();
1611                             System.err.println(i);
1612                         }
1613                     }
1614                     """
1615             };
1616             for (String source : sources) {
1617                 File dir = assertOK(true, source);
1618                 File fileEntry = dir.listFiles()[0];
1619                 String expectedCodeSequence = "iconst_1,istore_1,aload_0,iload_1,putfield,aload_0,iload_1,putfield," +
1620                         "aload_0,invokespecial,getstatic,aload_0,getfield,invokevirtual,return";
1621                 var classFile = ClassFile.of().parse(fileEntry.toPath());
1622                 for (var method : classFile.methods()) {
1623                     if (method.methodName().equalsString("<init>")) {
1624                         var code = method.findAttribute(Attributes.code()).orElseThrow();
1625                         List<String> mnemonics = new ArrayList<>();
1626                         for (var coe : code) {
1627                             if (coe instanceof Instruction inst) {
1628                                 mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT));
1629                             }
1630                         }
1631                         var foundCodeSequence = String.join(",", mnemonics);
1632                         Assert.check(expectedCodeSequence.equals(foundCodeSequence), "found " + foundCodeSequence);
1633                     }
1634                 }
1635             }
1636         } finally {
1637             setCompileOptions(previousOptions);
1638         }
1639     }
1640 }