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                             if (field.attributes().size() != 0) {
 774                                 for (var attr : field.attributes()) {
 775                                     Assert.check(!attr.attributeName().stringValue().equals("RuntimeInvisibleAnnotations"));
 776                                 }
 777                             }
 778                         }
 779                     }
 780                 }
 781             }
 782         } finally {
 783             setCompileOptions(previousOptions);
 784         }
 785     }
 786 
 787     @Test
 788     void testConstruction() throws Exception {
 789         record Data(String src, boolean isRecord) {
 790             Data(String src) {
 791                 this(src, false);
 792             }
 793         }
 794         for (Data data : List.of(
 795                 new Data(
 796                     """
 797                     value class Test {
 798                         int i = 100;
 799                     }
 800                     """),
 801                 new Data(
 802                     """
 803                     value class Test {
 804                         int i;
 805                         Test() {
 806                             i = 100;
 807                         }
 808                     }
 809                     """),
 810                 new Data(
 811                     """
 812                     value class Test {
 813                         int i;
 814                         Test() {
 815                             i = 100;
 816                             super();
 817                         }
 818                     }
 819                     """),
 820                 new Data(
 821                     """
 822                     value class Test {
 823                         int i;
 824                         Test() {
 825                             this.i = 100;
 826                             super();
 827                         }
 828                     }
 829                     """),
 830                 new Data(
 831                     """
 832                     value record Test(int i) {}
 833                     """, true)
 834         )) {
 835             if (!data.isRecord()) {
 836                 checkMnemonicsFor(data.src, "aload_0,bipush,putfield,aload_0,invokespecial,return");
 837             } else {
 838                 checkMnemonicsFor(data.src, "aload_0,iload_1,putfield,aload_0,invokespecial,return");
 839             }
 840         }
 841 
 842         String source =
 843                 """
 844                 value class Test {
 845                     int i = 100;
 846                     int j = 0;
 847                     {
 848                         System.out.println(j);
 849                     }
 850                 }
 851                 """;
 852         checkMnemonicsFor(
 853                 """
 854                 value class Test {
 855                     int i = 100;
 856                     int j = 0;
 857                     {
 858                         System.out.println(j);
 859                     }
 860                 }
 861                 """,
 862                 "aload_0,bipush,putfield,aload_0,iconst_0,putfield,aload_0,invokespecial,getstatic,iconst_0,invokevirtual,return"
 863         );
 864 
 865         assertFail("compiler.err.cant.ref.before.ctor.called",
 866                 """
 867                 value class Test {
 868                     Test() {
 869                         m();
 870                     }
 871                     void m() {}
 872                 }
 873                 """
 874         );
 875         assertFail("compiler.err.strict.field.not.have.been.initialized.before.super",
 876                 """
 877                 value class Test {
 878                     int i;
 879                     Test() {
 880                         super();
 881                         this.i = i;
 882                     }
 883                 }
 884                 """
 885         );
 886         assertOK(
 887                 """
 888                 class UnrelatedThisLeak {
 889                     value class V {
 890                         int f;
 891                         V() {
 892                             UnrelatedThisLeak x = UnrelatedThisLeak.this;
 893                             f = 10;
 894                             x = UnrelatedThisLeak.this;
 895                         }
 896                     }
 897                 }
 898                 """
 899         );
 900         assertFail("compiler.err.cant.ref.before.ctor.called",
 901                 """
 902                 value class Test {
 903                     Test t = null;
 904                     Runnable r = () -> { System.err.println(t); }; // cant reference `t` from a lambda expression in the prologue
 905                 }
 906                 """
 907         );
 908         assertFail("compiler.err.strict.field.not.have.been.initialized.before.super",
 909                 """
 910                 value class Test {
 911                     int f;
 912                     {
 913                         f = 1;
 914                     }
 915                 }
 916                 """
 917         );
 918         assertOK(
 919                 """
 920                 value class V {
 921                     int x;
 922                     int y = x + 1; // allowed
 923                     V() {
 924                         x = 12;
 925                         // super();
 926                     }
 927                 }
 928                 """
 929         );
 930         assertFail("compiler.err.cant.ref.before.ctor.called",
 931                 """
 932                 value class V2 {
 933                     int x;
 934                     V2() { this(x = 3); } // error
 935                     V2(int i) { x = 4; }
 936                 }
 937                 """
 938         );
 939         assertOK(
 940                 """
 941                 abstract value class AV1 {
 942                     AV1(int i) {}
 943                 }
 944                 value class V3 extends AV1 {
 945                     int x;
 946                     V3() {
 947                         super(x = 3); // ok
 948                     }
 949                 }
 950                 """
 951         );
 952         assertOK(
 953                 """
 954                 value class V4 {
 955                     int x;
 956                     int y = x + 1;
 957                     V4() {
 958                         x = 12;
 959                     }
 960                     V4(int i) {
 961                         x = i;
 962                     }
 963                 }
 964                 """
 965         );
 966         assertOK(
 967                 """
 968                 value class V {
 969                     final int x = "abc".length();
 970                     { System.out.println(x); }
 971                 }
 972                 """
 973         );
 974         assertFail("compiler.err.illegal.forward.ref",
 975                 """
 976                 value class V {
 977                     { System.out.println(x); }
 978                     final int x = "abc".length();
 979                 }
 980                 """
 981         );
 982         assertOK(
 983                 """
 984                 value class V {
 985                     int x = "abc".length();
 986                     int y = x;
 987                 }
 988                 """
 989         );
 990         assertOK(
 991                 """
 992                 value class V {
 993                     int x = "abc".length();
 994                     { int y = x; }
 995                 }
 996                 """
 997         );
 998         assertOK(
 999                 """
1000                 value class V {
1001                     String s1;
1002                     { System.out.println(s1); }
1003                     String s2 = (s1 = "abc");
1004                 }
1005                 """
1006         );
1007 
1008         String[] previousOptions = getCompileOptions();
1009         try {
1010             setCompileOptions(PREVIEW_OPTIONS_PLUS_VM_ANNO);
1011             String[] sources = new String[]{
1012                     """
1013                     import jdk.internal.vm.annotation.Strict;
1014                     class Test {
1015                         static value class IValue {
1016                             int i = 0;
1017                         }
1018                         @Strict
1019                         final IValue val = new IValue();
1020                     }
1021                     """,
1022                     """
1023                     import jdk.internal.vm.annotation.Strict;
1024                     class Test {
1025                         static value class IValue {
1026                             int i = 0;
1027                         }
1028                         @Strict
1029                         final IValue val;
1030                         Test() {
1031                             val = new IValue();
1032                         }
1033                     }
1034                     """
1035             };
1036             String expectedCodeSequence = "aload_0,new,dup,invokespecial,putfield,aload_0,invokespecial,return";
1037             for (String src : sources) {
1038                 checkMnemonicsFor(src, "aload_0,new,dup,invokespecial,putfield,aload_0,invokespecial,return");
1039             }
1040 
1041             assertFail("compiler.err.cant.ref.before.ctor.called",
1042                     """
1043                     import jdk.internal.vm.annotation.NullRestricted;
1044                     import jdk.internal.vm.annotation.Strict;
1045                     class StrictNR {
1046                         static value class IValue {
1047                             int i = 0;
1048                         }
1049                         value class SValue {
1050                             short s = 0;
1051                         }
1052                         @Strict
1053                         @NullRestricted
1054                         IValue val = new IValue();
1055                         @Strict
1056                         @NullRestricted
1057                         final IValue val2;
1058                         @Strict
1059                         @NullRestricted
1060                         SValue val3 = new SValue();
1061                     }
1062                     """
1063             );
1064             assertFail("compiler.err.strict.field.not.have.been.initialized.before.super",
1065                     """
1066                     import jdk.internal.vm.annotation.Strict;
1067                     class Test {
1068                         @Strict int i;
1069                     }
1070                     """
1071             );
1072             assertFail("compiler.err.strict.field.not.have.been.initialized.before.super",
1073                     """
1074                     import jdk.internal.vm.annotation.Strict;
1075                     class Test {
1076                         @Strict int i;
1077                         Test() {
1078                             super();
1079                             i = 0;
1080                         }
1081                     }
1082                     """
1083             );
1084             assertFail("compiler.err.cant.ref.before.ctor.called",
1085                     """
1086                     import jdk.internal.vm.annotation.NullRestricted;
1087                     import jdk.internal.vm.annotation.Strict;
1088                     class StrictNR {
1089                         static value class IValue {
1090                             int i = 0;
1091                         }
1092                         value class SValue {
1093                             short s = 0;
1094                         }
1095                         @Strict
1096                         @NullRestricted
1097                         IValue val = new IValue();
1098                             @Strict
1099                             @NullRestricted
1100                             SValue val4;
1101                         public StrictNR() {
1102                             val4 = new SValue();
1103                         }
1104                     }
1105                     """
1106             );
1107         } finally {
1108             setCompileOptions(previousOptions);
1109         }
1110 
1111         source =
1112             """
1113             value class V {
1114                 int i = 1;
1115                 int y;
1116                 V() {
1117                     y = 2;
1118                 }
1119             }
1120             """;
1121         {
1122             File dir = assertOK(true, source);
1123             File fileEntry = dir.listFiles()[0];
1124             var expectedCodeSequence = "putfield i,putfield y";
1125             var classFile = ClassFile.of().parse(fileEntry.toPath());
1126             for (var method : classFile.methods()) {
1127                 if (method.methodName().equalsString("<init>")) {
1128                     var code = method.findAttribute(Attributes.code()).orElseThrow();
1129                     List<String> mnemonics = new ArrayList<>();
1130                     for (var coe : code) {
1131                         if (coe instanceof FieldInstruction inst && inst.opcode() == Opcode.PUTFIELD) {
1132                             mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT) + " " + inst.name());
1133                         }
1134                     }
1135                     var foundCodeSequence = String.join(",", mnemonics);
1136                     Assert.check(expectedCodeSequence.equals(foundCodeSequence), "found " + foundCodeSequence);
1137                 }
1138             }
1139         }
1140 
1141         // check that javac doesn't generate duplicate initializer code
1142         checkMnemonicsFor(
1143                 """
1144                 value class Test {
1145                     static class Foo {
1146                         int x;
1147                         int getX() { return x; }
1148                     }
1149                     Foo data = new Foo();
1150                     Test() { // we will check that: `data = new Foo();` is generated only once
1151                         data.getX();
1152                         super();
1153                     }
1154                 }
1155                 """,
1156                 "new,dup,invokespecial,astore_1,aload_1,invokevirtual,pop,aload_0,aload_1,putfield,aload_0,invokespecial,return"
1157         );
1158 
1159         assertFail("compiler.err.invalid.canonical.constructor.in.record",
1160                 """
1161                 record R(int x) {
1162                     public R {
1163                         super();
1164                     }
1165                 }
1166                 """
1167         );
1168 
1169         assertFail("compiler.err.invalid.canonical.constructor.in.record",
1170                 """
1171                 record R(int x) {
1172                     public R {
1173                         this();
1174                     }
1175                     public R() {
1176                         this(1);
1177                     }
1178                 }
1179                 """
1180         );
1181 
1182         assertOK(
1183                 """
1184                 record R(int x) {
1185                     public R(int x) {
1186                         this.x = x;
1187                         super();
1188                     }
1189                 }
1190                 """
1191         );
1192     }
1193 
1194     void checkMnemonicsFor(String source, String expectedMnemonics) throws Exception {
1195         File dir = assertOK(true, source);
1196         for (final File fileEntry : dir.listFiles()) {
1197             var classFile = ClassFile.of().parse(fileEntry.toPath());
1198             if (classFile.thisClass().name().equalsString("Test")) {
1199                 for (var method : classFile.methods()) {
1200                     if (method.methodName().equalsString("<init>")) {
1201                         var code = method.findAttribute(Attributes.code()).orElseThrow();
1202                         List<String> mnemonics = new ArrayList<>();
1203                         for (var coe : code) {
1204                             if (coe instanceof Instruction inst) {
1205                                 mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT));
1206                             }
1207                         }
1208                         var foundCodeSequence = String.join(",", mnemonics);
1209                         Assert.check(expectedMnemonics.equals(foundCodeSequence), "found " + foundCodeSequence);
1210                     }
1211                 }
1212             }
1213         }
1214     }
1215 
1216     @Test
1217     void testThisCallingConstructor() throws Exception {
1218         // make sure that this() calling constructors doesn't initialize final fields
1219         String source =
1220                 """
1221                 value class Test {
1222                     int i;
1223                     Test() {
1224                         this(0);
1225                     }
1226 
1227                     Test(int i) {
1228                         this.i = i;
1229                     }
1230                 }
1231                 """;
1232         File dir = assertOK(true, source);
1233         File fileEntry = dir.listFiles()[0];
1234         String expectedCodeSequenceThisCallingConst = "aload_0,iconst_0,invokespecial,return";
1235         String expectedCodeSequenceNonThisCallingConst = "aload_0,iload_1,putfield,aload_0,invokespecial,return";
1236         var classFile = ClassFile.of().parse(fileEntry.toPath());
1237         for (var method : classFile.methods()) {
1238             if (method.methodName().equalsString("<init>")) {
1239                 var code = method.findAttribute(Attributes.code()).orElseThrow();
1240                 List<String> mnemonics = new ArrayList<>();
1241                 for (var coe : code) {
1242                     if (coe instanceof Instruction inst) {
1243                         mnemonics.add(inst.opcode().name().toLowerCase(Locale.ROOT));
1244                     }
1245                 }
1246                 var foundCodeSequence = String.join(",", mnemonics);
1247                 var expected = method.methodTypeSymbol().parameterCount() == 0 ?
1248                         expectedCodeSequenceThisCallingConst : expectedCodeSequenceNonThisCallingConst;
1249                 Assert.check(expected.equals(foundCodeSequence), "found " + foundCodeSequence);
1250             }
1251         }
1252     }
1253 
1254     @Test
1255     void testSelectors() throws Exception {
1256         assertOK(
1257                 """
1258                 value class V {
1259                     void selector() {
1260                         Class<?> c = int.class;
1261                     }
1262                 }
1263                 """
1264         );
1265         assertFail("compiler.err.expected",
1266                 """
1267                 value class V {
1268                     void selector() {
1269                         int i = int.some_selector;
1270                     }
1271                 }
1272                 """
1273         );
1274     }
1275 
1276     @Test
1277     void testNullAssigment() throws Exception {
1278         assertOK(
1279                 """
1280                 value final class V {
1281                     final int x = 10;
1282 
1283                     value final class X {
1284                         final V v;
1285                         final V v2;
1286 
1287                         X() {
1288                             this.v = null;
1289                             this.v2 = null;
1290                         }
1291 
1292                         X(V v) {
1293                             this.v = v;
1294                             this.v2 = v;
1295                         }
1296 
1297                         V foo(X x) {
1298                             x = new X(null);  // OK
1299                             return x.v;
1300                         }
1301                     }
1302                     V bar(X x) {
1303                         x = new X(null); // OK
1304                         return x.v;
1305                     }
1306 
1307                     class Y {
1308                         V v;
1309                         V [] va = { null }; // OK: array initialization
1310                         V [] va2 = new V[] { null }; // OK: array initialization
1311                         void ooo(X x) {
1312                             x = new X(null); // OK
1313                             v = null; // legal assignment.
1314                             va[0] = null; // legal.
1315                             va = new V[] { null }; // legal
1316                         }
1317                     }
1318                 }
1319                 """
1320         );
1321     }
1322 
1323     @Test
1324     void testSerializationWarnings() throws Exception {
1325         String[] previousOptions = getCompileOptions();
1326         try {
1327             setCompileOptions(new String[] {"-Xlint:serial", "--enable-preview", "--source",
1328                     Integer.toString(Runtime.version().feature())});
1329             assertOK(
1330                     """
1331                     import java.io.*;
1332                     abstract value class AVC implements Serializable {}
1333                     """);
1334             assertOKWithWarning("compiler.warn.serializable.value.class.without.write.replace.1",
1335                     """
1336                     import java.io.*;
1337                     value class VC implements Serializable {
1338                         private static final long serialVersionUID = 0;
1339                     }
1340                     """);
1341             assertOK(
1342                     """
1343                     import java.io.*;
1344                     class C implements Serializable {
1345                         private static final long serialVersionUID = 0;
1346                     }
1347                     """);
1348             assertOK(
1349                     """
1350                     import java.io.*;
1351                     abstract value class Super implements Serializable {
1352                         private static final long serialVersionUID = 0;
1353                         protected Object writeReplace() throws ObjectStreamException {
1354                             return null;
1355                         }
1356                     }
1357                     value class ValueSerializable extends Super {
1358                         private static final long serialVersionUID = 1;
1359                     }
1360                     """);
1361             assertOK(
1362                     """
1363                     import java.io.*;
1364                     abstract value class Super implements Serializable {
1365                         private static final long serialVersionUID = 0;
1366                         Object writeReplace() throws ObjectStreamException {
1367                             return null;
1368                         }
1369                     }
1370                     value class ValueSerializable extends Super {
1371                         private static final long serialVersionUID = 1;
1372                     }
1373                     """);
1374             assertOK(
1375                     """
1376                     import java.io.*;
1377                     abstract value class Super implements Serializable {
1378                         private static final long serialVersionUID = 0;
1379                         public Object writeReplace() throws ObjectStreamException {
1380                             return null;
1381                         }
1382                     }
1383                     value class ValueSerializable extends Super {
1384                         private static final long serialVersionUID = 1;
1385                     }
1386                     """);
1387             assertOKWithWarning("compiler.warn.serializable.value.class.without.write.replace.1",
1388                     """
1389                     import java.io.*;
1390                     abstract value class Super implements Serializable {
1391                         private static final long serialVersionUID = 0;
1392                         private Object writeReplace() throws ObjectStreamException {
1393                             return null;
1394                         }
1395                     }
1396                     value class ValueSerializable extends Super {
1397                         private static final long serialVersionUID = 1;
1398                     }
1399                     """);
1400             assertOKWithWarning("compiler.warn.serializable.value.class.without.write.replace.2",
1401                     """
1402                     import java.io.*;
1403                     abstract value class Super implements Serializable {
1404                         private static final long serialVersionUID = 0;
1405                         private Object writeReplace() throws ObjectStreamException {
1406                             return null;
1407                         }
1408                     }
1409                     class Serializable1 extends Super {
1410                         private static final long serialVersionUID = 1;
1411                     }
1412                     class Serializable2 extends Serializable1 {
1413                         private static final long serialVersionUID = 1;
1414                     }
1415                     """);
1416             assertOK(
1417                     """
1418                     import java.io.*;
1419                     abstract value class Super implements Serializable {
1420                         private static final long serialVersionUID = 0;
1421                         Object writeReplace() throws ObjectStreamException {
1422                             return null;
1423                         }
1424                     }
1425                     class ValueSerializable extends Super {
1426                         private static final long serialVersionUID = 1;
1427                     }
1428                     """);
1429             assertOK(
1430                     """
1431                     import java.io.*;
1432                     abstract value class Super implements Serializable {
1433                         private static final long serialVersionUID = 0;
1434                         public Object writeReplace() throws ObjectStreamException {
1435                             return null;
1436                         }
1437                     }
1438                     class ValueSerializable extends Super {
1439                         private static final long serialVersionUID = 1;
1440                     }
1441                     """);
1442             assertOK(
1443                     """
1444                     import java.io.*;
1445                     abstract value class Super implements Serializable {
1446                         private static final long serialVersionUID = 0;
1447                         protected Object writeReplace() throws ObjectStreamException {
1448                             return null;
1449                         }
1450                     }
1451                     class ValueSerializable extends Super {
1452                         private static final long serialVersionUID = 1;
1453                     }
1454                     """);
1455             assertOK(
1456                     """
1457                     import java.io.*;
1458                     value record ValueRecord() implements Serializable {
1459                         private static final long serialVersionUID = 1;
1460                     }
1461                     """);
1462             assertOK(
1463                     // Number is a special case, no warning for identity classes extending it
1464                     """
1465                     class NumberSubClass extends Number {
1466                         private static final long serialVersionUID = 0L;
1467                         @Override
1468                         public double doubleValue() { return 0; }
1469                         @Override
1470                         public int intValue() { return 0; }
1471                         @Override
1472                         public long longValue() { return 0; }
1473                         @Override
1474                         public float floatValue() { return 0; }
1475                     }
1476                     """
1477             );
1478         } finally {
1479             setCompileOptions(previousOptions);
1480         }
1481     }
1482 
1483     @Test
1484     void testAssertUnsetFieldsSMEntry() throws Exception {
1485         String[] previousOptions = getCompileOptions();
1486         try {
1487             String[] testOptions = {
1488                     "--enable-preview",
1489                     "-source", Integer.toString(Runtime.version().feature()),
1490                     "-XDnoLocalProxyVars",
1491                     "-XDdebug.stackmap",
1492                     "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED"
1493             };
1494             setCompileOptions(testOptions);
1495 
1496             record Data(String src, int[] expectedFrameTypes, String[][] expectedUnsetFields) {}
1497             for (Data data : List.of(
1498                     new Data(
1499                             """
1500                             import jdk.internal.vm.annotation.Strict;
1501                             class Test {
1502                                 @Strict
1503                                 final int x;
1504                                 @Strict
1505                                 final int y;
1506                                 Test(boolean a, boolean b) {
1507                                     if (a) { // early_larval {x, y}
1508                                         x = 1;
1509                                         if (b) { // early_larval {y}
1510                                             y = 1;
1511                                         } else { // early_larval {y}
1512                                             y = 2;
1513                                         }
1514                                     } else { // early_larval {x, y}
1515                                         x = y = 3;
1516                                     }
1517                                     super();
1518                                 }
1519                             }
1520                             """,
1521                             // three unset_fields entries, entry type 246, are expected in the stackmap table
1522                             new int[] {246, 246, 246},
1523                             // expected fields for each of them:
1524                             new String[][] { new String[] { "y:I" }, new String[] { "x:I", "y:I" }, new String[] {} }
1525                     ),
1526                     new Data(
1527                             """
1528                             import jdk.internal.vm.annotation.Strict;
1529                             class Test {
1530                                 @Strict
1531                                 final int x;
1532                                 @Strict
1533                                 final int y;
1534                                 Test(int n) {
1535                                     switch(n) {
1536                                         case 2:
1537                                             x = y = 2;
1538                                             break;
1539                                         default:
1540                                             x = y = 100;
1541                                             break;
1542                                     }
1543                                     super();
1544                                 }
1545                             }
1546                             """,
1547                             // here we expect only one
1548                             new int[] {20, 12, 246},
1549                             // stating that no field is unset
1550                             new String[][] { new String[] {} }
1551                     ),
1552                     new Data(
1553                             """
1554                             import jdk.internal.vm.annotation.Strict;
1555                             class Test {
1556                                 @Strict
1557                                 final int x;
1558                                 @Strict
1559                                 final int y;
1560                                 Test(int n) {
1561                                     if (n % 3 == 0) {
1562                                         x = n / 3;
1563                                     } else { // no unset change
1564                                         x = n + 2;
1565                                     } // early_larval {y}
1566                                     y = n >>> 3;
1567                                     super();
1568                                     if ((char) n != n) {
1569                                         n -= 5;
1570                                     } // no uninitializedThis - automatically cleared unsets
1571                                     Math.abs(n);
1572                                 }
1573                             }
1574                             """,
1575                             // here we expect only one, none for the post-larval frame
1576                             new int[] {16, 246, 255},
1577                             // stating that y is unset when if-else finishes
1578                             new String[][] { new String[] {"y:I"} }
1579                     )
1580             )) {
1581                 File dir = assertOK(true, data.src());
1582                 for (final File fileEntry : dir.listFiles()) {
1583                     var classFile = ClassFile.of().parse(fileEntry.toPath());
1584                     for (var method : classFile.methods()) {
1585                         if (method.methodName().equalsString(ConstantDescs.INIT_NAME)) {
1586                             var code = method.findAttribute(Attributes.code()).orElseThrow();
1587                             var stackMapTable = code.findAttribute(Attributes.stackMapTable()).orElseThrow();
1588                             Assert.check(data.expectedFrameTypes().length == stackMapTable.entries().size(), "unexpected stackmap length");
1589                             int entryIndex = 0;
1590                             int expectedUnsetFieldsIndex = 0;
1591                             for (var entry : stackMapTable.entries()) {
1592                                 Assert.check(data.expectedFrameTypes()[entryIndex++] == entry.frameType(), "expected " + data.expectedFrameTypes()[entryIndex - 1] + " found " + entry.frameType());
1593                                 if (entry.frameType() == 246) {
1594                                     Assert.check(data.expectedUnsetFields()[expectedUnsetFieldsIndex].length == entry.unsetFields().size());
1595                                     int index = 0;
1596                                     for (var nat : entry.unsetFields()) {
1597                                         String unsetStr = nat.name() + ":" + nat.type();
1598                                         Assert.check(data.expectedUnsetFields()[expectedUnsetFieldsIndex][index++].equals(unsetStr));
1599                                     }
1600                                     expectedUnsetFieldsIndex++;
1601                                 }
1602                             }
1603                         }
1604                     }
1605                 }
1606             }
1607         } finally {
1608             setCompileOptions(previousOptions);
1609         }
1610     }
1611 
1612     @Test
1613     void testLocalProxyVars() throws Exception {
1614         String[] previousOptions = getCompileOptions();
1615         try {
1616             String[] testOptions = {
1617                     "--enable-preview",
1618                     "-source", Integer.toString(Runtime.version().feature()),
1619                     "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED"
1620             };
1621             setCompileOptions(testOptions);
1622             String[] sources = new String[] {
1623                     """
1624                     value class Test {
1625                         int i;
1626                         int j;
1627                         Test() {// javac should generate a proxy local var for `i`
1628                             i = 1;
1629                             j = i; // as here `i` is being read during the early construction phase, use the local var instead
1630                             super();
1631                             System.err.println(i);
1632                         }
1633                     }
1634                     """,
1635                     """
1636                     import jdk.internal.vm.annotation.Strict;
1637                     class Test {
1638                         @Strict
1639                         int i;
1640                         @Strict
1641                         int j;
1642                         Test() {
1643                             i = 1;
1644                             j = i;
1645                             super();
1646                             System.err.println(i);
1647                         }
1648                     }
1649                     """
1650             };
1651             for (String source : sources) {
1652                 checkMnemonicsFor(source, "iconst_1,istore_1,aload_0,iload_1,putfield,aload_0,iload_1,putfield," +
1653                         "aload_0,invokespecial,getstatic,aload_0,getfield,invokevirtual,return");
1654             }
1655         } finally {
1656             setCompileOptions(previousOptions);
1657         }
1658     }
1659 }