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