1 /*
   2  * Copyright (c) 2024, 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 package compiler.valhalla.inlinetypes;
  25 
  26 import jdk.internal.value.ValueClass;
  27 import jdk.internal.vm.annotation.LooselyConsistentValue;
  28 import jdk.internal.vm.annotation.NullRestricted;
  29 import jdk.internal.vm.annotation.Strict;
  30 
  31 import jdk.test.lib.Asserts;
  32 
  33 /*
  34  * @test
  35  * @key randomness
  36  * @summary Test support for null markers in flat fields.
  37  * @library /test/lib /
  38  * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
  39  * @enablePreview
  40  * @modules java.base/jdk.internal.value
  41  *          java.base/jdk.internal.vm.annotation
  42  * @run main/othervm compiler.valhalla.inlinetypes.TestFieldNullMarkers
  43  * @run main/othervm -Xbatch -XX:-UseNullableValueFlattening -XX:-UseAtomicValueFlattening -XX:-UseNonAtomicValueFlattening
  44  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  45  * @run main/othervm -Xbatch -XX:-UseNullableValueFlattening -XX:-UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  46  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  47  * @run main/othervm -Xbatch -XX:-UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:-UseNonAtomicValueFlattening
  48  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  49  * @run main/othervm -Xbatch -XX:-UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  50  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  51  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:-UseAtomicValueFlattening -XX:-UseNonAtomicValueFlattening
  52  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  53  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:-UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  54  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  55  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:-UseNonAtomicValueFlattening
  56  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  57  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  58  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  59  *
  60  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  61  *                   -XX:CompileCommand=dontinline,*::testHelper*
  62  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  63  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  64  *                   -XX:+InlineTypeReturnedAsFields -XX:+InlineTypePassFieldsAsArgs
  65  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  66  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  67  *                   -XX:-InlineTypeReturnedAsFields -XX:-InlineTypePassFieldsAsArgs
  68  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  69  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  70  *                   -XX:+InlineTypeReturnedAsFields -XX:-InlineTypePassFieldsAsArgs
  71  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  72  * @run main/othervm -Xbatch -XX:+UseNullableValueFlattening -XX:+UseAtomicValueFlattening -XX:+UseNonAtomicValueFlattening
  73  *                   -XX:-InlineTypeReturnedAsFields -XX:+InlineTypePassFieldsAsArgs
  74  *                   compiler.valhalla.inlinetypes.TestFieldNullMarkers
  75  */
  76 
  77 public class TestFieldNullMarkers {
  78 
  79     // Value class with two nullable flat fields
  80     @LooselyConsistentValue
  81     static value class MyValue1 {
  82         byte x;
  83         MyValue2 val1;
  84         MyValue2 val2;
  85 
  86         public MyValue1(byte x, MyValue2 val1, MyValue2 val2) {
  87             this.x = x;
  88             this.val1 = val1;
  89             this.val2 = val2;
  90         }
  91 
  92         public String toString() {
  93             return "x = " + x + ", val1 = [" + val1 + "], val2 = [" + val2 + "]";
  94         }
  95     }
  96 
  97     @LooselyConsistentValue
  98     static abstract value class MyAbstract1 {
  99         byte x;
 100 
 101         public MyAbstract1(byte x) {
 102             this.x = x;
 103         }
 104     }
 105 
 106     // Empty value class inheriting single field from abstract super class
 107     @LooselyConsistentValue
 108     static value class MyValue2 extends MyAbstract1 {
 109         public MyValue2(byte x) {
 110             super(x);
 111         }
 112 
 113         public String toString() {
 114             return "x = " + x;
 115         }
 116     }
 117 
 118     // Value class with a hole in the payload that will be used for the null marker
 119     @LooselyConsistentValue
 120     static value class MyValue3 {
 121         byte x;
 122         // Hole that will be used by the null marker
 123         int i;
 124 
 125         public MyValue3(byte x) {
 126             this.x = x;
 127             this.i = x;
 128         }
 129 
 130         public String toString() {
 131             return "x = " + x + ", i = " + i;
 132         }
 133     }
 134 
 135     // Value class with two nullable flat fields that have their null markers *not* at the end of the payload
 136     @LooselyConsistentValue
 137     static value class MyValue4 {
 138         MyValue3 val1;
 139         MyValue3 val2;
 140 
 141         public MyValue4(MyValue3 val1, MyValue3 val2) {
 142             this.val1 = val1;
 143             this.val2 = val2;
 144         }
 145 
 146         public String toString() {
 147             return "val1 = [" + val1 + "], val2 = [" + val2 + "]";
 148         }
 149     }
 150 
 151     @LooselyConsistentValue
 152     static value class MyValue5_3 {
 153         byte x;
 154 
 155         public MyValue5_3(byte x) {
 156             this.x = x;
 157         }
 158     }
 159 
 160     @LooselyConsistentValue
 161     static value class MyValue5_2 {
 162         byte x;
 163         MyValue5_3 val;
 164 
 165         public MyValue5_2(byte x, MyValue5_3 val) {
 166             this.x = x;
 167             this.val = val;
 168         }
 169     }
 170 
 171     @LooselyConsistentValue
 172     static value class MyValue5_1 {
 173         byte x;
 174         MyValue5_2 val;
 175 
 176         public MyValue5_1(byte x, MyValue5_2 val) {
 177             this.x = x;
 178             this.val = val;
 179         }
 180     }
 181 
 182     // Value class with deep nesting of nullable flat fields
 183     @LooselyConsistentValue
 184     static value class MyValue5 {
 185         byte x;
 186         MyValue5_1 val;
 187 
 188         public MyValue5(byte x, MyValue5_1 val) {
 189             this.x = x;
 190             this.val = val;
 191         }
 192     }
 193 
 194     @LooselyConsistentValue
 195     static value class MyValueEmpty {
 196 
 197     }
 198 
 199     // Value class with flat field of empty value class
 200     @LooselyConsistentValue
 201     static value class MyValue6 {
 202         MyValueEmpty val;
 203 
 204         public MyValue6(MyValueEmpty val) {
 205             this.val = val;
 206         }
 207     }
 208 
 209     // Same as MyValue6 but one more level of nested flat fields
 210     @LooselyConsistentValue
 211     static value class MyValue7 {
 212         MyValue6 val;
 213 
 214         public MyValue7(MyValue6 val) {
 215             this.val = val;
 216         }
 217     }
 218 
 219     // Some more field types
 220 
 221     @LooselyConsistentValue
 222     static value class MyValue8 {
 223         byte b;
 224 
 225         public MyValue8(byte b) {
 226             this.b = b;
 227         }
 228     }
 229 
 230     @LooselyConsistentValue
 231     static value class MyValue9 {
 232         short s;
 233 
 234         public MyValue9(short s) {
 235             this.s = s;
 236         }
 237     }
 238 
 239     @LooselyConsistentValue
 240     static value class MyValue10 {
 241         int i;
 242 
 243         public MyValue10(int i) {
 244             this.i = i;
 245         }
 246     }
 247 
 248     @LooselyConsistentValue
 249     static value class MyValue11 {
 250         float f;
 251 
 252         public MyValue11(float f) {
 253             this.f = f;
 254         }
 255     }
 256 
 257     @LooselyConsistentValue
 258     static value class MyValue12 {
 259         char c;
 260 
 261         public MyValue12(char c) {
 262             this.c = c;
 263         }
 264     }
 265 
 266     @LooselyConsistentValue
 267     static value class MyValue13 {
 268         boolean b;
 269 
 270         public MyValue13(boolean b) {
 271             this.b = b;
 272         }
 273     }
 274 
 275     // Test value class with nullable and null-free fields
 276     @LooselyConsistentValue
 277     static value class MyValue14 {
 278         @Strict
 279         @NullRestricted
 280         MyValue8 nullfree;
 281         MyValue8 nullable;
 282 
 283         public MyValue14(MyValue8 nullfree, MyValue8 nullable) {
 284             this.nullfree = nullfree;
 285             this.nullable = nullable;
 286         }
 287 
 288         public static final MyValue14 DEFAULT = new MyValue14(new MyValue8((byte)0), null);
 289     }
 290 
 291     static class MyClass {
 292         int x;
 293 
 294         public MyClass(int x) {
 295             this.x = x;
 296         }
 297     }
 298 
 299     // Value class with oop field
 300     @LooselyConsistentValue
 301     static value class MyValue15 {
 302         MyClass obj;
 303 
 304         public MyValue15(MyClass obj) {
 305             this.obj = obj;
 306         }
 307     }
 308 
 309     // Value class with two oop fields
 310     @LooselyConsistentValue
 311     static value class MyValue16 {
 312         MyClass obj1;
 313         MyClass obj2;
 314 
 315         public MyValue16(MyClass obj1, MyClass obj2) {
 316             this.obj1 = obj1;
 317             this.obj2 = obj2;
 318         }
 319     }
 320 
 321     // Value class with oop field and primitive fields
 322     @LooselyConsistentValue
 323     static value class MyValue17 {
 324         byte b1;
 325         MyClass obj;
 326         byte b2;
 327 
 328         public MyValue17(MyClass obj, byte b1, byte b2) {
 329             this.obj = obj;
 330             this.b1 = b1;
 331             this.b2 = b2;
 332         }
 333     }
 334 
 335     MyValue1 field1; // Not flat
 336     MyValue4 field2; // Not flat
 337     MyValue5 field3; // Flat
 338     MyValue6 field4; // Flat
 339     MyValue7 field5; // Flat
 340     MyValue8 field6; // Flat
 341     MyValue9 field7; // Flat
 342     MyValue10 field8; // Flat
 343     MyValue11 field9; // Flat
 344     MyValue12 field10; // Flat
 345     MyValue13 field11; // Flat
 346 
 347     @Strict
 348     @NullRestricted
 349     volatile MyValue8 field12 = new MyValue8((byte)0);
 350 
 351     @Strict
 352     @NullRestricted
 353     MyValue14 field13 = MyValue14.DEFAULT; // Null-free, flat
 354     volatile MyValue14 field14; // Nullable, atomic, flat
 355     MyValue14 field15;          // Nullable, (atomic), flat
 356     @Strict
 357     @NullRestricted
 358     volatile MyValue14 field16 = MyValue14.DEFAULT; // Null-free, atomic, flat
 359 
 360     @Strict
 361     @NullRestricted
 362     volatile MyValue15 field17 = new MyValue15(null);
 363     MyValue15 field18;
 364     @Strict
 365     @NullRestricted
 366     volatile MyValue16 field19 = new MyValue16(null, null);
 367     @Strict
 368     @NullRestricted
 369     volatile MyValue17 field20 = new MyValue17(null, (byte)0, (byte)0);
 370     MyValue17 field21;
 371 
 372     // Combinations of strict fields
 373     static class StrictFieldHolder {
 374         @Strict
 375         MyValue8 strictField1;
 376         @Strict
 377         final MyValue8 strictField2;
 378         @Strict
 379         @NullRestricted
 380         MyValue8 strictField3;
 381         @Strict
 382         @NullRestricted
 383         final MyValue8 strictField4;
 384         @Strict
 385         volatile MyValue8 strictField5;
 386         @Strict
 387         @NullRestricted
 388         volatile MyValue8 strictField6;
 389 
 390         @Strict
 391         TwoBytes strictField7;
 392         @Strict
 393         final TwoBytes strictField8;
 394         @Strict
 395         @NullRestricted
 396         TwoBytes strictField9;
 397         @Strict
 398         @NullRestricted
 399         final TwoBytes strictField10;
 400         @Strict
 401         volatile TwoBytes strictField11;
 402         @Strict
 403         @NullRestricted
 404         volatile TwoBytes strictField12;
 405 
 406         public StrictFieldHolder(MyValue8 val8, MyValue8 val8NullFree, TwoBytes twoBytes, TwoBytes twoBytesNullFree) {
 407             strictField1 = val8;
 408             strictField2 = val8;
 409             strictField3 = val8NullFree;
 410             strictField4 = val8NullFree;
 411             strictField5 = val8NullFree;
 412             strictField6 = val8NullFree;
 413 
 414             strictField7 = twoBytes;
 415             strictField8 = twoBytes;
 416             strictField9 = twoBytesNullFree;
 417             strictField10 = twoBytesNullFree;
 418             strictField11 = twoBytesNullFree;
 419             strictField12 = twoBytesNullFree;
 420         }
 421     }
 422 
 423     @Strict
 424     @NullRestricted
 425     MyValueEmpty emptyField1 = new MyValueEmpty();
 426     @Strict
 427     @NullRestricted
 428     volatile MyValueEmpty emptyField2 = new MyValueEmpty();
 429     MyValueEmpty emptyField3;
 430     volatile MyValueEmpty emptyField4;
 431 
 432     static final MyValue1 VAL1 = new MyValue1((byte)42, new MyValue2((byte)43), null);
 433     static final MyValue4 VAL4 = new MyValue4(new MyValue3((byte)42), null);
 434     static final MyValue5 VAL5 = new MyValue5((byte)42, new MyValue5_1((byte)43, new MyValue5_2((byte)44, new MyValue5_3((byte)45))));
 435     static final MyValue6 VAL6 = new MyValue6(new MyValueEmpty());
 436     static final MyValue7 VAL7 = new MyValue7(new MyValue6(new MyValueEmpty()));
 437 
 438     // Using two bytes such that null-free fields will not be naturally atomic
 439     @LooselyConsistentValue
 440     static value class TwoBytes {
 441         byte b1;
 442         byte b2;
 443 
 444         public TwoBytes(byte b1, byte b2) {
 445             this.b1 = b1;
 446             this.b2 = b2;
 447         }
 448 
 449         public static final TwoBytes DEFAULT = new TwoBytes((byte)0, (byte)0);
 450     }
 451 
 452     static private final MyValue8 CANARY_VALUE = new MyValue8((byte)42);
 453 
 454     public static class Cage1 {
 455         MyValue8 canary1 = CANARY_VALUE;
 456 
 457         @Strict
 458         @NullRestricted
 459         volatile TwoBytes field = TwoBytes.DEFAULT;
 460 
 461         MyValue8 canary2 = CANARY_VALUE;
 462 
 463         public void verify(TwoBytes val) {
 464             Asserts.assertEQ(canary1, CANARY_VALUE);
 465             Asserts.assertEQ(field, val);
 466             Asserts.assertEQ(canary2, CANARY_VALUE);
 467         }
 468     }
 469 
 470     public static class Cage2 {
 471         @Strict
 472         @NullRestricted
 473         MyValue8 canary1 = CANARY_VALUE;
 474 
 475         @Strict
 476         @NullRestricted
 477         volatile TwoBytes field = TwoBytes.DEFAULT;
 478 
 479         @Strict
 480         @NullRestricted
 481         MyValue8 canary2 = CANARY_VALUE;
 482 
 483         public void verify(TwoBytes val) {
 484             Asserts.assertEQ(canary1, CANARY_VALUE);
 485             Asserts.assertEQ(field, val);
 486             Asserts.assertEQ(canary2, CANARY_VALUE);
 487         }
 488     }
 489 
 490     public static class Cage3 {
 491         @Strict
 492         @NullRestricted
 493         MyValue8 canary1 = CANARY_VALUE;
 494 
 495         volatile TwoBytes field;
 496 
 497         @Strict
 498         @NullRestricted
 499         MyValue8 canary2 = CANARY_VALUE;
 500 
 501         public void verify(TwoBytes val) {
 502             Asserts.assertEQ(canary1, CANARY_VALUE);
 503             Asserts.assertEQ(field, val);
 504             Asserts.assertEQ(canary2, CANARY_VALUE);
 505         }
 506     }
 507 
 508     public static class Cage4 {
 509         MyValue8 canary1 = CANARY_VALUE;
 510 
 511         volatile TwoBytes field;
 512 
 513         MyValue8 canary2 = CANARY_VALUE;
 514 
 515         public void verify(TwoBytes val) {
 516             Asserts.assertEQ(canary1, CANARY_VALUE);
 517             Asserts.assertEQ(field, val);
 518             Asserts.assertEQ(canary2, CANARY_VALUE);
 519         }
 520     }
 521 
 522     static final Cage1 canaryCage1 = new Cage1();
 523     static final Cage2 canaryCage2 = new Cage2();
 524     static final Cage3 canaryCage3 = new Cage3();
 525     static final Cage4 canaryCage4 = new Cage4();
 526 
 527     // Check that the canary values are not accidentally overwritten
 528     public void testOutOfBoundsAccess(int i) {
 529         TwoBytes val = new TwoBytes((byte)i, (byte)(i+1));
 530         canaryCage1.field = val;
 531         canaryCage1.verify(val);
 532 
 533         canaryCage2.field = val;
 534         canaryCage2.verify(val);
 535 
 536         canaryCage3.field = val;
 537         canaryCage3.verify(val);
 538 
 539         canaryCage3.field = null;
 540         canaryCage3.verify(null);
 541 
 542         canaryCage4.field = val;
 543         canaryCage4.verify(val);
 544 
 545         canaryCage4.field = null;
 546         canaryCage4.verify(null);
 547     }
 548 
 549     // Test that the calling convention is keeping track of the null marker
 550     public MyValue1 testHelper1(MyValue1 val) {
 551         return val;
 552     }
 553 
 554     public void testSet1(MyValue1 val) {
 555         field1 = testHelper1(val);
 556     }
 557 
 558     public MyValue1 testGet1() {
 559         return field1;
 560     }
 561 
 562     public void testDeopt1(byte x, MyValue1 neverNull, MyValue1 alwaysNull, boolean deopt) {
 563         MyValue2 val2 = new MyValue2(x);
 564         MyValue1 val1 = new MyValue1(x, val2, val2);
 565         if (deopt) {
 566             Asserts.assertEQ(val1.x, x);
 567             Asserts.assertEQ(val1.val1, val2);
 568             Asserts.assertEQ(val1.val2, val2);
 569             Asserts.assertEQ(neverNull.x, x);
 570             Asserts.assertEQ(neverNull.val1, val2);
 571             Asserts.assertEQ(neverNull.val2, val2);
 572             Asserts.assertEQ(alwaysNull.x, x);
 573             Asserts.assertEQ(alwaysNull.val1, null);
 574             Asserts.assertEQ(alwaysNull.val2, null);
 575         }
 576     }
 577 
 578     public void testOSR() {
 579         // Trigger OSR
 580         for (int i = 0; i < 100_000; ++i) {
 581             field1 = null;
 582             Asserts.assertEQ(field1, null);
 583             MyValue2 val2 = new MyValue2((byte)i);
 584             MyValue1 val = new MyValue1((byte)i, val2, null);
 585             field1 = val;
 586             Asserts.assertEQ(field1.x, (byte)i);
 587             Asserts.assertEQ(field1.val1, val2);
 588             Asserts.assertEQ(field1.val2, null);
 589         }
 590     }
 591 
 592     public boolean testACmp(MyValue2 val2) {
 593         return field1.val1 == val2;
 594     }
 595 
 596     // Test that the calling convention is keeping track of the null marker
 597     public MyValue4 testHelper2(MyValue4 val) {
 598         return val;
 599     }
 600 
 601     public void testSet2(MyValue4 val) {
 602         field2 = testHelper2(val);
 603     }
 604 
 605     public MyValue4 testGet2() {
 606         return field2;
 607     }
 608 
 609     public void testDeopt2(byte x, MyValue4 neverNull, MyValue4 alwaysNull, boolean deopt) {
 610         MyValue3 val3 = new MyValue3(x);
 611         MyValue4 val4 = new MyValue4(val3, null);
 612         if (deopt) {
 613             Asserts.assertEQ(val4.val1, val3);
 614             Asserts.assertEQ(val4.val2, null);
 615             Asserts.assertEQ(neverNull.val1, val3);
 616             Asserts.assertEQ(neverNull.val2, val3);
 617             Asserts.assertEQ(alwaysNull.val1, null);
 618             Asserts.assertEQ(alwaysNull.val2, null);
 619         }
 620     }
 621 
 622     // Test that the calling convention is keeping track of the null marker
 623     public MyValue5 testHelper3(MyValue5 val) {
 624         return val;
 625     }
 626 
 627     public void testSet3(MyValue5 val) {
 628         field3 = testHelper3(val);
 629     }
 630 
 631     public MyValue5 testGet3() {
 632         return field3;
 633     }
 634 
 635     public void testDeopt3(byte x, MyValue5 val6, MyValue5 val7, MyValue5 val8, MyValue5 val9, boolean deopt) {
 636         MyValue5 val1 = new MyValue5(x, new MyValue5_1(x, new MyValue5_2(x, new MyValue5_3(x))));
 637         MyValue5 val2 = new MyValue5(x, new MyValue5_1(x, new MyValue5_2(x, null)));
 638         MyValue5 val3 = new MyValue5(x, new MyValue5_1(x, null));
 639         MyValue5 val4 = new MyValue5(x, null);
 640         MyValue5 val5 = null;
 641         if (deopt) {
 642             Asserts.assertEQ(val1.x, x);
 643             Asserts.assertEQ(val1.val.x, x);
 644             Asserts.assertEQ(val1.val.val.x, x);
 645             Asserts.assertEQ(val1.val.val.val.x, x);
 646             Asserts.assertEQ(val2.x, x);
 647             Asserts.assertEQ(val2.val.x, x);
 648             Asserts.assertEQ(val2.val.val.x, x);
 649             Asserts.assertEQ(val2.val.val.val, null);
 650             Asserts.assertEQ(val3.x, x);
 651             Asserts.assertEQ(val3.val.x, x);
 652             Asserts.assertEQ(val3.val.val, null);
 653             Asserts.assertEQ(val4.x, x);
 654             Asserts.assertEQ(val4.val, null);
 655             Asserts.assertEQ(val5, null);
 656 
 657             Asserts.assertEQ(val6.x, x);
 658             Asserts.assertEQ(val6.val.x, x);
 659             Asserts.assertEQ(val6.val.val.x, x);
 660             Asserts.assertEQ(val6.val.val.val.x, x);
 661             Asserts.assertEQ(val7.x, x);
 662             Asserts.assertEQ(val7.val.x, x);
 663             Asserts.assertEQ(val7.val.val.x, x);
 664             Asserts.assertEQ(val7.val.val.val, null);
 665             Asserts.assertEQ(val8.x, x);
 666             Asserts.assertEQ(val8.val.x, x);
 667             Asserts.assertEQ(val8.val.val, null);
 668             Asserts.assertEQ(val9.x, x);
 669             Asserts.assertEQ(val9.val, null);
 670         }
 671     }
 672 
 673     // Test that the calling convention is keeping track of the null marker
 674     public MyValue6 testHelper4(MyValue6 val) {
 675         return val;
 676     }
 677 
 678     public void testSet4(MyValue6 val) {
 679         field4 = testHelper4(val);
 680     }
 681 
 682     public MyValue6 testGet4() {
 683         return field4;
 684     }
 685 
 686     public void testDeopt4(MyValue6 val4, MyValue6 val5, MyValue6 val6, boolean deopt) {
 687         MyValue6 val1 = new MyValue6(new MyValueEmpty());
 688         MyValue6 val2 = new MyValue6(null);
 689         MyValue6 val3 = null;
 690         if (deopt) {
 691             Asserts.assertEQ(val1.val, new MyValueEmpty());
 692             Asserts.assertEQ(val2.val, null);
 693             Asserts.assertEQ(val3, null);
 694 
 695             Asserts.assertEQ(val4.val, new MyValueEmpty());
 696             Asserts.assertEQ(val5.val, null);
 697             Asserts.assertEQ(val6, null);
 698         }
 699     }
 700 
 701     // Test that the calling convention is keeping track of the null marker
 702     public MyValue7 testHelper5(MyValue7 val) {
 703         return val;
 704     }
 705 
 706     public void testSet5(MyValue7 val) {
 707         field5 = testHelper5(val);
 708     }
 709 
 710     public MyValue7 testGet5() {
 711         return field5;
 712     }
 713 
 714     public void testDeopt5(MyValue7 val5, MyValue7 val6, MyValue7 val7, MyValue7 val8, boolean deopt) {
 715         MyValue7 val1 = new MyValue7(new MyValue6(new MyValueEmpty()));
 716         MyValue7 val2 = new MyValue7(new MyValue6(null));
 717         MyValue7 val3 = new MyValue7(null);
 718         MyValue7 val4 = null;
 719         if (deopt) {
 720             Asserts.assertEQ(val1.val, new MyValue6(new MyValueEmpty()));
 721             Asserts.assertEQ(val2.val, new MyValue6(null));
 722             Asserts.assertEQ(val3.val, null);
 723             Asserts.assertEQ(val4, null);
 724 
 725             Asserts.assertEQ(val5.val, new MyValue6(new MyValueEmpty()));
 726             Asserts.assertEQ(val6.val, new MyValue6(null));
 727             Asserts.assertEQ(val7.val, null);
 728             Asserts.assertEQ(val8, null);
 729         }
 730     }
 731 
 732     // Make sure that flat field accesses contain a (implicit) null check
 733     public static void testNPE1() {
 734         TestFieldNullMarkers t = null;
 735         try {
 736             MyValue8 v = t.field6;
 737             throw new RuntimeException("No NPE thrown!");
 738         } catch (NullPointerException e) {
 739             // Expected
 740         }
 741     }
 742 
 743     public static void testNPE2() {
 744         TestFieldNullMarkers t = null;
 745         try {
 746             t.field6 = null;
 747             throw new RuntimeException("No NPE thrown!");
 748         } catch (NullPointerException e) {
 749             // Expected
 750         }
 751     }
 752 
 753     public void checkFields(int i) {
 754         Asserts.assertEQ(field6.b, (byte)i);
 755         Asserts.assertEQ(field7.s, (short)i);
 756         Asserts.assertEQ(field8.i, i);
 757         Asserts.assertEQ(field9.f, (float)i);
 758         Asserts.assertEQ(field10.c, (char)i);
 759         Asserts.assertEQ(field11.b, (i % 2) == 0);
 760     }
 761 
 762     // Test that writing and reading a (signed) byte stays in bounds
 763     public void testBounds(int i) {
 764         MyValue8 val = new MyValue8((byte)i);
 765         field6 = val;
 766         int b = field6.b;
 767         if (b < -128 || b > 127) {
 768             throw new RuntimeException("Byte value out of bounds: " + b);
 769         }
 770     }
 771 
 772     static void produceGarbage() {
 773         for (int i = 0; i < 100; ++i) {
 774             Object[] arrays = new Object[1024];
 775             for (int j = 0; j < arrays.length; j++) {
 776                 arrays[j] = new int[1024];
 777             }
 778         }
 779         System.gc();
 780     }
 781 
 782     // Test that barriers are emitted when writing flat, atomic fields with oops
 783     public void testWriteOopFields1(MyValue15 val) {
 784         field17 = val;
 785         field18 = val;
 786     }
 787 
 788     public void testWriteOopFields2(MyValue16 val) {
 789         field19 = val;
 790     }
 791 
 792     public void testWriteOopFields3(MyValue17 val) {
 793         field20 = val;
 794         field21 = val;
 795     }
 796 
 797     public static class MyHolderClass9 {
 798         @Strict
 799         @NullRestricted
 800         TwoBytes field1 = TwoBytes.DEFAULT;
 801 
 802         TwoBytes field2;
 803 
 804         @Strict
 805         @NullRestricted
 806         volatile TwoBytes field3 = TwoBytes.DEFAULT;
 807 
 808         volatile TwoBytes field4;
 809     }
 810 
 811     static final MyHolderClass9 constantHolder = new MyHolderClass9();
 812 
 813     // Test loading a flat field from a constant container (should not be constant folded because fields are immutable)
 814     public void testLoadingFromConstantHolder(int i) {
 815         TwoBytes val = new TwoBytes((byte)i, (byte)(i + 1));
 816         constantHolder.field1 = val;
 817         Asserts.assertEQ(constantHolder.field1, val);
 818 
 819         constantHolder.field2 = val;
 820         Asserts.assertEQ(constantHolder.field2, val);
 821 
 822         constantHolder.field2 = null;
 823         Asserts.assertEQ(constantHolder.field2, null);
 824 
 825         constantHolder.field3 = val;
 826         Asserts.assertEQ(constantHolder.field3, val);
 827 
 828         constantHolder.field4 = val;
 829         Asserts.assertEQ(constantHolder.field4, val);
 830 
 831         constantHolder.field4 = null;
 832         Asserts.assertEQ(constantHolder.field4, null);
 833     }
 834 
 835     public void testStrictFields(StrictFieldHolder holder, MyValue8 val8, MyValue8 val8NullFree, TwoBytes twoBytes, TwoBytes twoBytesNullFree) {
 836         Asserts.assertEQ(holder.strictField1, val8);
 837         Asserts.assertEQ(holder.strictField2, val8);
 838         Asserts.assertEQ(holder.strictField3, val8NullFree);
 839         Asserts.assertEQ(holder.strictField4, val8NullFree);
 840         Asserts.assertEQ(holder.strictField5, val8NullFree);
 841         Asserts.assertEQ(holder.strictField6, val8NullFree);
 842 
 843         Asserts.assertEQ(holder.strictField7, twoBytes);
 844         Asserts.assertEQ(holder.strictField8, twoBytes);
 845         Asserts.assertEQ(holder.strictField9, twoBytesNullFree);
 846         Asserts.assertEQ(holder.strictField10, twoBytesNullFree);
 847         Asserts.assertEQ(holder.strictField11, twoBytesNullFree);
 848         Asserts.assertEQ(holder.strictField12, twoBytesNullFree);
 849     }
 850 
 851     public static void main(String[] args) {
 852         TestFieldNullMarkers t = new TestFieldNullMarkers();
 853         t.testOSR();
 854 
 855         final int LIMIT = 50_000;
 856         for (int i = -50_000; i < LIMIT; ++i) {
 857             t.field1 = null;
 858             Asserts.assertEQ(t.testGet1(), null);
 859 
 860             boolean useNull = (i % 2) == 0;
 861             MyValue2 val2 = useNull ? null : new MyValue2((byte)i);
 862             MyValue1 val = new MyValue1((byte)i, val2, val2);
 863             t.field1 = val;
 864             Asserts.assertEQ(t.testGet1().x, val.x);
 865             Asserts.assertEQ(t.testGet1().val1, val2);
 866             Asserts.assertEQ(t.testGet1().val2, val2);
 867 
 868             Asserts.assertTrue(t.testACmp(val2));
 869 
 870             t.testSet1(null);
 871             Asserts.assertEQ(t.field1, null);
 872 
 873             t.testSet1(val);
 874             Asserts.assertEQ(t.field1.x, val.x);
 875             Asserts.assertEQ(t.field1.val1, val2);
 876             Asserts.assertEQ(t.field1.val2, val2);
 877 
 878             t.testDeopt1((byte)i, null, null, false);
 879 
 880             t.field2 = null;
 881             Asserts.assertEQ(t.testGet2(), null);
 882 
 883             MyValue3 val3 = useNull ? null : new MyValue3((byte)i);
 884             MyValue4 val4 = new MyValue4(val3, val3);
 885             t.field2 = val4;
 886             Asserts.assertEQ(t.testGet2().val1, val3);
 887             Asserts.assertEQ(t.testGet2().val2, val3);
 888 
 889             t.testSet2(null);
 890             Asserts.assertEQ(t.testGet2(), null);
 891 
 892             t.testSet2(val4);
 893             Asserts.assertEQ(t.testGet2().val1, val3);
 894             Asserts.assertEQ(t.testGet2().val2, val3);
 895 
 896             t.testDeopt2((byte)i, null, null, false);
 897 
 898             t.field3 = null;
 899             Asserts.assertEQ(t.testGet3(), null);
 900 
 901             boolean useNull_1 = (i % 4) == 0;
 902             boolean useNull_2 = (i % 4) == 1;
 903             boolean useNull_3 = (i % 4) == 2;
 904             MyValue5_3 val5_3 = useNull_3 ? null : new MyValue5_3((byte)i);
 905             MyValue5_2 val5_2 = useNull_2 ? null : new MyValue5_2((byte)i, val5_3);
 906             MyValue5_1 val5_1 = useNull_1 ? null : new MyValue5_1((byte)i, val5_2);
 907             MyValue5 val5 = new MyValue5((byte)i, val5_1);
 908             t.field3 = val5;
 909             Asserts.assertEQ(t.testGet3().x, val5.x);
 910             if (useNull_1) {
 911                 Asserts.assertEQ(t.testGet3().val, null);
 912             } else {
 913                 Asserts.assertEQ(t.testGet3().val.x, val5_1.x);
 914                 if (useNull_2) {
 915                     Asserts.assertEQ(t.testGet3().val.val, null);
 916                 } else {
 917                     Asserts.assertEQ(t.testGet3().val.val.x, val5_2.x);
 918                     if (useNull_3) {
 919                         Asserts.assertEQ(t.testGet3().val.val.val, null);
 920                     } else {
 921                         Asserts.assertEQ(t.testGet3().val.val.val.x, val5_3.x);
 922                     }
 923                 }
 924             }
 925 
 926             t.testSet3(null);
 927             Asserts.assertEQ(t.field3, null);
 928 
 929             t.testSet3(val5);
 930             Asserts.assertEQ(t.testGet3().x, val5.x);
 931             if (useNull_1) {
 932                 Asserts.assertEQ(t.testGet3().val, null);
 933             } else {
 934                 Asserts.assertEQ(t.testGet3().val.x, val5_1.x);
 935                 if (useNull_2) {
 936                     Asserts.assertEQ(t.testGet3().val.val, null);
 937                 } else {
 938                     Asserts.assertEQ(t.testGet3().val.val.x, val5_2.x);
 939                     if (useNull_3) {
 940                         Asserts.assertEQ(t.testGet3().val.val.val, null);
 941                     } else {
 942                         Asserts.assertEQ(t.testGet3().val.val.val.x, val5_3.x);
 943                     }
 944                 }
 945             }
 946             t.testDeopt3((byte)i, null, null, null, null, false);
 947 
 948             t.field4 = null;
 949             Asserts.assertEQ(t.testGet4(), null);
 950 
 951             MyValueEmpty empty = useNull ? null : new MyValueEmpty();
 952             MyValue6 val6 = new MyValue6(empty);
 953             t.field4 = val6;
 954             Asserts.assertEQ(t.testGet4().val, empty);
 955 
 956             t.testSet4(null);
 957             Asserts.assertEQ(t.testGet4(), null);
 958 
 959             t.testSet4(val6);
 960             Asserts.assertEQ(t.testGet4().val, empty);
 961 
 962             t.testDeopt4(null, null, null, false);
 963 
 964             t.field5 = null;
 965             Asserts.assertEQ(t.testGet5(), null);
 966 
 967             empty = ((i % 3) == 0) ? null : new MyValueEmpty();
 968             val6 = ((i % 3) == 1) ? null : new MyValue6(empty);
 969             MyValue7 val7 = new MyValue7(val6);
 970             t.field5 = val7;
 971             Asserts.assertEQ(t.testGet5().val, val6);
 972 
 973             t.testSet5(null);
 974             Asserts.assertEQ(t.testGet5(), null);
 975 
 976             t.testSet5(val7);
 977             Asserts.assertEQ(t.testGet5().val, val6);
 978 
 979             t.testDeopt5(null, null, null, null, false);
 980 
 981             // Check accesses with constant value
 982             t.field1 = VAL1;
 983             Asserts.assertEQ(t.field1.x, VAL1.x);
 984             Asserts.assertEQ(t.field1.val1, VAL1.val1);
 985             Asserts.assertEQ(t.field1.val2, VAL1.val2);
 986 
 987             t.field2 = VAL4;
 988             Asserts.assertEQ(t.field2.val1, VAL4.val1);
 989             Asserts.assertEQ(t.field2.val2, VAL4.val2);
 990 
 991             t.field3 = VAL5;
 992             Asserts.assertEQ(t.field3.x, VAL5.x);
 993             Asserts.assertEQ(t.field3.val.x, VAL5.val.x);
 994             Asserts.assertEQ(t.field3.val.val.x, VAL5.val.val.x);
 995             Asserts.assertEQ(t.field3.val.val.val.x, VAL5.val.val.val.x);
 996 
 997             t.field4 = VAL6;
 998             Asserts.assertEQ(t.field4.val, VAL6.val);
 999 
1000             t.field5 = VAL7;
1001             Asserts.assertEQ(t.field5.val, VAL7.val);
1002 
1003             // Some more values classes with different flavors of primitive fields
1004             t.field6 = null;
1005             Asserts.assertEQ(t.field6, null);
1006             t.field6 = new MyValue8((byte)i);
1007             Asserts.assertEQ(t.field6.b, (byte)i);
1008             t.field7 = null;
1009             Asserts.assertEQ(t.field7, null);
1010             t.field7 = new MyValue9((short)i);
1011             Asserts.assertEQ(t.field7.s, (short)i);
1012             t.field8 = null;
1013             Asserts.assertEQ(t.field8, null);
1014             t.field8 = new MyValue10(i);
1015             Asserts.assertEQ(t.field8.i, i);
1016             t.field9 = null;
1017             Asserts.assertEQ(t.field9, null);
1018             t.field9 = new MyValue11((float)i);
1019             Asserts.assertEQ(t.field9.f, (float)i);
1020             t.field10 = null;
1021             Asserts.assertEQ(t.field10, null);
1022             t.field10 = new MyValue12((char)i);
1023             Asserts.assertEQ(t.field10.c, (char)i);
1024             t.field11 = null;
1025             Asserts.assertEQ(t.field11, null);
1026             t.field11 = new MyValue13((i % 2) == 0);
1027             Asserts.assertEQ(t.field11.b, (i % 2) == 0);
1028 
1029             // Write the fields again and check that we don't overwrite other fields
1030             t.checkFields(i);
1031             t.field6 = new MyValue8((byte)i);
1032             t.checkFields(i);
1033             t.field7 = new MyValue9((short)i);
1034             t.checkFields(i);
1035             t.field8 = new MyValue10(i);
1036             t.checkFields(i);
1037             t.field9 = new MyValue11((float)i);
1038             t.checkFields(i);
1039             t.field10 = new MyValue12((char)i);
1040             t.checkFields(i);
1041             t.field11 = new MyValue13((i % 2) == 0);
1042             t.checkFields(i);
1043 
1044             testNPE1();
1045             testNPE2();
1046 
1047             t.testBounds(i);
1048 
1049             // Null-free, flat, atomic
1050             MyValue8 val8 = new MyValue8((byte)i);
1051             t.field12 = val8;
1052             Asserts.assertEQ(t.field12.b, (byte)i);
1053 
1054             try {
1055                 t.field12 = null;
1056                 throw new RuntimeException("No NPE thrown");
1057             } catch (NullPointerException npe) {
1058                 // Expected
1059             }
1060 
1061             // Null-free, flat with both nullable and null-free fields
1062             t.field13 = new MyValue14(val8, val8);
1063             Asserts.assertEQ(t.field13.nullfree, val8);
1064             Asserts.assertEQ(t.field13.nullable, val8);
1065 
1066             t.field13 = new MyValue14(val8, null);
1067             Asserts.assertEQ(t.field13.nullfree, val8);
1068             Asserts.assertEQ(t.field13.nullable, null);
1069 
1070             try {
1071                 t.field13 = new MyValue14(null, null);
1072                 throw new RuntimeException("No NPE thrown");
1073             } catch (NullPointerException npe) {
1074                 // Expected
1075             }
1076             try {
1077                 t.field13 = null;
1078                 throw new RuntimeException("No NPE thrown");
1079             } catch (NullPointerException npe) {
1080                 // Expected
1081             }
1082 
1083             // Nullable, atomic, flat with both nullable and null-free fields
1084             t.field14 = null;
1085             Asserts.assertEQ(t.field14, null);
1086 
1087             t.field14 = new MyValue14(val8, val8);
1088             Asserts.assertEQ(t.field14.nullfree, val8);
1089             Asserts.assertEQ(t.field14.nullable, val8);
1090 
1091             t.field14 = new MyValue14(val8, null);
1092             Asserts.assertEQ(t.field14.nullfree, val8);
1093             Asserts.assertEQ(t.field14.nullable, null);
1094 
1095             try {
1096                 t.field14 = new MyValue14(null, null);
1097                 throw new RuntimeException("No NPE thrown");
1098             } catch (NullPointerException npe) {
1099                 // Expected
1100             }
1101 
1102             // Nullable, (atomic), flat with both nullable and null-free fields
1103             t.field15 = null;
1104             Asserts.assertEQ(t.field15, null);
1105 
1106             t.field15 = new MyValue14(val8, val8);
1107             Asserts.assertEQ(t.field15.nullfree, val8);
1108             Asserts.assertEQ(t.field15.nullable, val8);
1109 
1110             t.field15 = new MyValue14(val8, null);
1111             Asserts.assertEQ(t.field15.nullfree, val8);
1112             Asserts.assertEQ(t.field15.nullable, null);
1113 
1114             try {
1115                 t.field15 = new MyValue14(null, null);
1116                 throw new RuntimeException("No NPE thrown");
1117             } catch (NullPointerException npe) {
1118                 // Expected
1119             }
1120 
1121             // Null-free, atomic, flat with both nullable and null-free fields
1122             t.field16 = new MyValue14(val8, val8);
1123             Asserts.assertEQ(t.field16.nullfree, val8);
1124             Asserts.assertEQ(t.field16.nullable, val8);
1125 
1126             t.field16 = new MyValue14(val8, null);
1127             Asserts.assertEQ(t.field16.nullfree, val8);
1128             Asserts.assertEQ(t.field16.nullable, null);
1129 
1130             try {
1131                 t.field16 = new MyValue14(null, null);
1132                 throw new RuntimeException("No NPE thrown");
1133             } catch (NullPointerException npe) {
1134                 // Expected
1135             }
1136             try {
1137                 t.field16 = null;
1138                 throw new RuntimeException("No NPE thrown");
1139             } catch (NullPointerException npe) {
1140                 // Expected
1141             }
1142 
1143             MyValue15 val15 = new MyValue15(new MyClass(i));
1144             t.testWriteOopFields1(val15);
1145             if (i > (LIMIT - 50)) {
1146                 // After warmup, produce some garbage to trigger GC
1147                 produceGarbage();
1148             }
1149             Asserts.assertEQ(t.field17.obj.x, i);
1150             Asserts.assertEQ(t.field18.obj.x, i);
1151 
1152             MyValue16 val16 = new MyValue16(new MyClass(i), new MyClass(i));
1153             t.testWriteOopFields2(val16);
1154             if (i > (LIMIT - 50)) {
1155                 // After warmup, produce some garbage to trigger GC
1156                 produceGarbage();
1157             }
1158             Asserts.assertEQ(t.field19.obj1.x, i);
1159             Asserts.assertEQ(t.field19.obj2.x, i);
1160 
1161             MyValue17 val17 = new MyValue17(new MyClass(i), (byte)i, (byte)i);
1162             t.testWriteOopFields3(val17);
1163             if (i > (LIMIT - 50)) {
1164                 // After warmup, produce some garbage to trigger GC
1165                 produceGarbage();
1166             }
1167             Asserts.assertEQ(t.field20.obj.x, i);
1168             Asserts.assertEQ(t.field20.b1, (byte)i);
1169             Asserts.assertEQ(t.field20.b2, (byte)i);
1170             Asserts.assertEQ(t.field21.obj.x, i);
1171             Asserts.assertEQ(t.field21.b1, (byte)i);
1172             Asserts.assertEQ(t.field21.b2, (byte)i);
1173 
1174             Asserts.assertEQ(t.emptyField1, new MyValueEmpty());
1175             Asserts.assertEQ(t.emptyField2, new MyValueEmpty());
1176 
1177             // Test empty fields
1178             t.emptyField3 = new MyValueEmpty();
1179             t.emptyField4 = new MyValueEmpty();
1180             Asserts.assertEQ(t.emptyField3, new MyValueEmpty());
1181             Asserts.assertEQ(t.emptyField4, new MyValueEmpty());
1182             t.emptyField3 = null;
1183             t.emptyField4 = null;
1184             Asserts.assertEQ(t.emptyField3, null);
1185             Asserts.assertEQ(t.emptyField4, null);
1186 
1187             t.testLoadingFromConstantHolder(i);
1188 
1189             // Verify that no out of bounds accesses happen
1190             t.testOutOfBoundsAccess(i);
1191 
1192             // Test strict fields
1193             // TODO 8355277 Re-enable
1194             /*
1195             TwoBytes twoBytes = new TwoBytes((byte)i, (byte)(i + 1));
1196             t.testStrictFields(new StrictFieldHolder(val8, val8, twoBytes, twoBytes), val8, val8, twoBytes, twoBytes);
1197             t.testStrictFields(new StrictFieldHolder(null, val8, null, twoBytes), null, val8, null, twoBytes);
1198             */
1199         }
1200 
1201         // Trigger deoptimization to check that re-materialization takes the null marker into account
1202         byte x = (byte)42;
1203         t.testDeopt1(x, new MyValue1(x, new MyValue2(x), new MyValue2(x)), new MyValue1(x, null, null), true);
1204         t.testDeopt2(x, new MyValue4(new MyValue3(x), new MyValue3(x)), new MyValue4(null, null), true);
1205 
1206         MyValue5 val1 = new MyValue5(x, new MyValue5_1(x, new MyValue5_2(x, new MyValue5_3(x))));
1207         MyValue5 val2 = new MyValue5(x, new MyValue5_1(x, new MyValue5_2(x, null)));
1208         MyValue5 val3 = new MyValue5(x, new MyValue5_1(x, null));
1209         MyValue5 val4 = new MyValue5(x, null);
1210         t.testDeopt3(x, val1, val2, val3, val4, true);
1211 
1212         MyValue6 val5 = new MyValue6(new MyValueEmpty());
1213         MyValue6 val6 = new MyValue6(null);
1214         MyValue6 val7 = null;
1215         t.testDeopt4(val5, val6, val7, true);
1216 
1217         MyValue7 val8 = new MyValue7(new MyValue6(new MyValueEmpty()));
1218         MyValue7 val9 = new MyValue7(new MyValue6(null));
1219         MyValue7 val10 = new MyValue7(null);
1220         MyValue7 val11 = null;
1221         t.testDeopt5(val8, val9, val10, val11, false);
1222     }
1223 }
1224