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