1 /*
   2  * Copyright (c) 2014, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @test TestStableObject
  28  * @summary tests on stable fields and arrays
  29  * @library /testlibrary /testlibrary/whitebox
  30  * @build TestStableObject StableConfiguration sun.hotspot.WhiteBox
  31  * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
  32  * @run main ClassFileInstaller
  33  *           java/lang/invoke/StableConfiguration
  34  *           java/lang/invoke/TestStableObject
  35  *           java/lang/invoke/TestStableObject$ObjectStable
  36  *           java/lang/invoke/TestStableObject$StaticObjectStable
  37  *           java/lang/invoke/TestStableObject$VolatileObjectStable
  38  *           java/lang/invoke/TestStableObject$ObjectArrayDim1
  39  *           java/lang/invoke/TestStableObject$ObjectArrayDim2
  40  *           java/lang/invoke/TestStableObject$ObjectArrayDim3
  41  *           java/lang/invoke/TestStableObject$ObjectArrayDim4
  42  *           java/lang/invoke/TestStableObject$ObjectArrayLowerDim0
  43  *           java/lang/invoke/TestStableObject$ObjectArrayLowerDim1
  44  *           java/lang/invoke/TestStableObject$NestedStableField
  45  *           java/lang/invoke/TestStableObject$NestedStableField$A
  46  *           java/lang/invoke/TestStableObject$NestedStableField1
  47  *           java/lang/invoke/TestStableObject$NestedStableField1$A
  48  *           java/lang/invoke/TestStableObject$NestedStableField2
  49  *           java/lang/invoke/TestStableObject$NestedStableField2$A
  50  *           java/lang/invoke/TestStableObject$NestedStableField3
  51  *           java/lang/invoke/TestStableObject$NestedStableField3$A
  52  *           java/lang/invoke/TestStableObject$Values
  53  *           java/lang/invoke/TestStableObject$DefaultValue
  54  *           java/lang/invoke/TestStableObject$DefaultStaticValue
  55  *           java/lang/invoke/TestStableObject$ObjectArrayLowerDim2
  56  *
  57  * @run main/othervm -Xbootclasspath/a:.
  58  *                   -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp
  59  *                   -XX:+FoldStableValues
  60  *                   -XX:CompileOnly=::get,::get1,::get2,::get3,::get4
  61  *                   java.lang.invoke.TestStableObject
  62  * @run main/othervm -Xbootclasspath/a:.
  63  *                   -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp
  64  *                   -XX:-FoldStableValues
  65  *                   -XX:CompileOnly=::get,::get1,::get2,::get3,::get4
  66  *                   java.lang.invoke.TestStableObject
  67  *
  68  */
  69 package java.lang.invoke;
  70 
  71 import java.lang.reflect.InvocationTargetException;
  72 
  73 public class TestStableObject {
  74     static final boolean isStableEnabled    = StableConfiguration.isStableEnabled;
  75     static final boolean isServerWithStable = StableConfiguration.isServerWithStable;
  76 
  77     public static void main(String[] args) throws Exception {
  78         run(DefaultValue.class);
  79         run(ObjectStable.class);
  80         run(DefaultStaticValue.class);
  81         run(StaticObjectStable.class);
  82         run(VolatileObjectStable.class);
  83 
  84         // @Stable arrays: Dim 1-4
  85         run(ObjectArrayDim1.class);
  86         run(ObjectArrayDim2.class);
  87         run(ObjectArrayDim3.class);
  88         run(ObjectArrayDim4.class);
  89 
  90         // @Stable Object field: dynamic arrays
  91         run(ObjectArrayLowerDim0.class);
  92         run(ObjectArrayLowerDim1.class);
  93         run(ObjectArrayLowerDim2.class);
  94 
  95         // Nested @Stable fields
  96         run(NestedStableField.class);
  97         run(NestedStableField1.class);
  98         run(NestedStableField2.class);
  99         run(NestedStableField3.class);
 100 
 101         if (failed) {
 102             throw new Error("TEST FAILED");
 103         }
 104     }
 105 
 106     /* ==================================================== */
 107 
 108     enum Values {A, B, C, D, E, F}
 109 
 110     static class DefaultValue {
 111         public @Stable Object v;
 112 
 113         public static final DefaultValue c = new DefaultValue();
 114         public static Object get() { return c.v; }
 115         public static void test() throws Exception {
 116                             Object val1 = get();
 117             c.v = Values.A; Object val2 = get();
 118             assertEquals(val1, null);
 119             assertEquals(val2, Values.A);
 120         }
 121     }
 122 
 123     /* ==================================================== */
 124 
 125     static class ObjectStable {
 126         public @Stable Values v;
 127 
 128         public static final ObjectStable c = new ObjectStable ();
 129         public static Values get() { return c.v; }
 130         public static void test() throws Exception {
 131             c.v = Values.A; Values val1 = get();
 132             c.v = Values.B; Values val2 = get();
 133             assertEquals(val1, Values.A);
 134             assertEquals(val2, (isStableEnabled ? Values.A : Values.B));
 135         }
 136     }
 137 
 138     /* ==================================================== */
 139 
 140     static class DefaultStaticValue {
 141         public static @Stable Object v;
 142 
 143         public static final DefaultStaticValue c = new DefaultStaticValue();
 144         public static Object get() { return c.v; }
 145         public static void test() throws Exception {
 146                             Object val1 = get();
 147             c.v = Values.A; Object val2 = get();
 148             assertEquals(val1, null);
 149             assertEquals(val2, Values.A);
 150         }
 151     }
 152 
 153     /* ==================================================== */
 154 
 155     static class StaticObjectStable {
 156         public static @Stable Values v;
 157 
 158         public static final ObjectStable c = new ObjectStable ();
 159         public static Values get() { return c.v; }
 160         public static void test() throws Exception {
 161             c.v = Values.A; Values val1 = get();
 162             c.v = Values.B; Values val2 = get();
 163             assertEquals(val1, Values.A);
 164             assertEquals(val2, (isStableEnabled ? Values.A : Values.B));
 165         }
 166     }
 167 
 168     /* ==================================================== */
 169 
 170     static class VolatileObjectStable {
 171         public @Stable volatile Values v;
 172 
 173         public static final VolatileObjectStable c = new VolatileObjectStable ();
 174         public static Values get() { return c.v; }
 175         public static void test() throws Exception {
 176             c.v = Values.A; Values val1 = get();
 177             c.v = Values.B; Values val2 = get();
 178             assertEquals(val1, Values.A);
 179             assertEquals(val2, (isStableEnabled ? Values.A : Values.B));
 180         }
 181     }
 182 
 183     /* ==================================================== */
 184     // @Stable array == field && all components are stable
 185 
 186     static class ObjectArrayDim1 {
 187         public @Stable Object[] v;
 188 
 189         public static final ObjectArrayDim1 c = new ObjectArrayDim1();
 190         public static Object get() { return c.v[0]; }
 191         public static Object get1() { return c.v[10]; }
 192         public static Object[] get2() { return c.v; }
 193         public static void test() throws Exception {
 194             {
 195                 c.v = new Object[1]; c.v[0] = Values.A; Object val1 = get();
 196                                      c.v[0] = Values.B; Object val2 = get();
 197                 assertEquals(val1, Values.A);
 198                 assertEquals(val2, (isServerWithStable ? Values.A : Values.B));
 199 
 200                 c.v = new Object[1]; c.v[0] = Values.C; Object val3 = get();
 201                 assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 202                                                     : Values.C));
 203             }
 204 
 205             {
 206                 c.v = new Object[20]; c.v[10] = Values.A; Object val1 = get1();
 207                                       c.v[10] = Values.B; Object val2 = get1();
 208                 assertEquals(val1, Values.A);
 209                 assertEquals(val2, (isServerWithStable ? Values.A : Values.B));
 210 
 211                 c.v = new Object[20]; c.v[10] = Values.C; Object val3 = get1();
 212                 assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 213                                                     : Values.C));
 214             }
 215 
 216             {
 217                 c.v = new Object[1]; Object[] val1 = get2();
 218                 c.v = new Object[1]; Object[] val2 = get2();
 219                 assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2)));
 220             }
 221         }
 222     }
 223 
 224     /* ==================================================== */
 225 
 226     static class ObjectArrayDim2 {
 227         public @Stable Object[][] v;
 228 
 229         public static final ObjectArrayDim2 c = new ObjectArrayDim2();
 230         public static Object get() { return c.v[0][0]; }
 231         public static Object[] get1() { return c.v[0]; }
 232         public static Object[][] get2() { return c.v; }
 233         public static void test() throws Exception {
 234             {
 235                 c.v = new Object[1][1]; c.v[0][0] = Values.A; Object val1 = get();
 236                                         c.v[0][0] = Values.B; Object val2 = get();
 237                 assertEquals(val1, Values.A);
 238                 assertEquals(val2, (isServerWithStable ? Values.A : Values.B));
 239 
 240                 c.v = new Object[1][1]; c.v[0][0] = Values.C; Object val3 = get();
 241                 assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 242                                                     : Values.C));
 243 
 244                 c.v[0] = new Object[1]; c.v[0][0] = Values.D; Object val4 = get();
 245                 assertEquals(val4, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 246                                                     : Values.D));
 247             }
 248 
 249             {
 250                 c.v = new Object[1][1]; Object[] val1 = get1();
 251                 c.v[0] = new Object[1]; Object[] val2 = get1();
 252                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 253             }
 254 
 255             {
 256                 c.v = new Object[1][1]; Object[][] val1 = get2();
 257                 c.v = new Object[1][1]; Object[][] val2 = get2();
 258                 assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2)));
 259             }
 260         }
 261     }
 262 
 263     /* ==================================================== */
 264 
 265     static class ObjectArrayDim3 {
 266         public @Stable Object[][][] v;
 267 
 268         public static final ObjectArrayDim3 c = new ObjectArrayDim3();
 269         public static Object get() { return c.v[0][0][0]; }
 270         public static Object[] get1() { return c.v[0][0]; }
 271         public static Object[][] get2() { return c.v[0]; }
 272         public static Object[][][] get3() { return c.v; }
 273         public static void test() throws Exception {
 274             {
 275                 c.v = new Object[1][1][1]; c.v[0][0][0] = Values.A; Object val1 = get();
 276                                            c.v[0][0][0] = Values.B; Object val2 = get();
 277                 assertEquals(val1, Values.A);
 278                 assertEquals(val2, (isServerWithStable ? Values.A : Values.B));
 279 
 280                 c.v = new Object[1][1][1]; c.v[0][0][0] = Values.C; Object val3 = get();
 281                 assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 282                                                     : Values.C));
 283 
 284                 c.v[0] = new Object[1][1]; c.v[0][0][0] = Values.D; Object val4 = get();
 285                 assertEquals(val4, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 286                                                     : Values.D));
 287 
 288                 c.v[0][0] = new Object[1]; c.v[0][0][0] = Values.E; Object val5 = get();
 289                 assertEquals(val5, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 290                                                     : Values.E));
 291             }
 292 
 293             {
 294                 c.v = new Object[1][1][1]; Object[] val1 = get1();
 295                 c.v[0][0] = new Object[1]; Object[] val2 = get1();
 296                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 297             }
 298 
 299             {
 300                 c.v = new Object[1][1][1]; Object[][] val1 = get2();
 301                 c.v[0] = new Object[1][1]; Object[][] val2 = get2();
 302                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 303             }
 304 
 305             {
 306                 c.v = new Object[1][1][1]; Object[][][] val1 = get3();
 307                 c.v = new Object[1][1][1]; Object[][][] val2 = get3();
 308                 assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2)));
 309             }
 310         }
 311     }
 312 
 313     /* ==================================================== */
 314 
 315     static class ObjectArrayDim4 {
 316         public @Stable Object[][][][] v;
 317 
 318         public static final ObjectArrayDim4 c = new ObjectArrayDim4();
 319         public static Object get() { return c.v[0][0][0][0]; }
 320         public static Object[] get1() { return c.v[0][0][0]; }
 321         public static Object[][] get2() { return c.v[0][0]; }
 322         public static Object[][][] get3() { return c.v[0]; }
 323         public static Object[][][][] get4() { return c.v; }
 324         public static void test() throws Exception {
 325             {
 326                 c.v = new Object[1][1][1][1]; c.v[0][0][0][0] = Values.A; Object val1 = get();
 327                                               c.v[0][0][0][0] = Values.B; Object val2 = get();
 328                 assertEquals(val1, Values.A);
 329                 assertEquals(val2, (isServerWithStable ? Values.A : Values.B));
 330 
 331                 c.v = new Object[1][1][1][1]; c.v[0][0][0][0] = Values.C; Object val3 = get();
 332                 assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 333                                                     : Values.C));
 334 
 335                 c.v[0] = new Object[1][1][1]; c.v[0][0][0][0] = Values.D; Object val4 = get();
 336                 assertEquals(val4, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 337                                                     : Values.D));
 338 
 339                 c.v[0][0] = new Object[1][1]; c.v[0][0][0][0] = Values.E; Object val5 = get();
 340                 assertEquals(val5, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 341                                                     : Values.E));
 342 
 343                 c.v[0][0][0] = new Object[1]; c.v[0][0][0][0] = Values.F; Object val6 = get();
 344                 assertEquals(val6, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B)
 345                                                     : Values.F));
 346             }
 347 
 348             {
 349                 c.v = new Object[1][1][1][1]; Object[] val1 = get1();
 350                 c.v[0][0][0] = new Object[1]; Object[] val2 = get1();
 351                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 352             }
 353 
 354             {
 355                 c.v = new Object[1][1][1][1]; Object[][] val1 = get2();
 356                 c.v[0][0] = new Object[1][1]; Object[][] val2 = get2();
 357                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 358             }
 359 
 360             {
 361                 c.v = new Object[1][1][1][1]; Object[][][] val1 = get3();
 362                 c.v[0] = new Object[1][1][1]; Object[][][] val2 = get3();
 363                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 364             }
 365 
 366             {
 367                 c.v = new Object[1][1][1][1]; Object[][][][] val1 = get4();
 368                 c.v = new Object[1][1][1][1]; Object[][][][] val2 = get4();
 369                 assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2)));
 370             }
 371         }
 372     }
 373 
 374     /* ==================================================== */
 375     // Dynamic Dim is higher than static
 376     static class ObjectArrayLowerDim0 {
 377         public @Stable Object v;
 378 
 379         public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0();
 380         public static Object get() { return ((Object[])c.v)[0]; }
 381         public static Object[] get1() { return (Object[])c.v; }
 382 
 383         public static void test() throws Exception {
 384             {
 385                 c.v = new Object[1]; ((Object[])c.v)[0] = Values.A; Object val1 = get();
 386                                      ((Object[])c.v)[0] = Values.B; Object val2 = get();
 387 
 388                 assertEquals(val1, Values.A);
 389                 assertEquals(val2, Values.B);
 390             }
 391 
 392             {
 393                 c.v = new Object[1]; Object[] val1 = get1();
 394                 c.v = new Object[1]; Object[] val2 = get1();
 395                 assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2)));
 396             }
 397         }
 398     }
 399 
 400     /* ==================================================== */
 401 
 402     static class ObjectArrayLowerDim1 {
 403         public @Stable Object[] v;
 404 
 405         public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1();
 406         public static Object get() { return ((Object[][])c.v)[0][0]; }
 407         public static Object[] get1() { return (Object[])(c.v[0]); }
 408         public static Object[] get2() { return c.v; }
 409 
 410         public static void test() throws Exception {
 411             {
 412                 c.v = new Object[1][1]; ((Object[][])c.v)[0][0] = Values.A; Object val1 = get();
 413                                         ((Object[][])c.v)[0][0] = Values.B; Object val2 = get();
 414 
 415                 assertEquals(val1, Values.A);
 416                 assertEquals(val2, Values.B);
 417             }
 418 
 419             {
 420                 c.v = new Object[1][1]; c.v[0] = new Object[0]; Object[] val1 = get1();
 421                                      c.v[0] = new Object[0]; Object[] val2 = get1();
 422 
 423                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 424             }
 425 
 426             {
 427                 c.v = new Object[0][0]; Object[] val1 = get2();
 428                 c.v = new Object[0][0]; Object[] val2 = get2();
 429 
 430                 assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2)));
 431             }
 432         }
 433     }
 434 
 435     /* ==================================================== */
 436 
 437     static class ObjectArrayLowerDim2 {
 438         public @Stable Object[][] v;
 439 
 440         public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2();
 441         public static Object get() { return ((Object[][][])c.v)[0][0][0]; }
 442         public static Object[] get1() { return (Object[])(c.v[0][0]); }
 443         public static Object[][] get2() { return (Object[][])(c.v[0]); }
 444         public static Object[][] get3() { return c.v; }
 445 
 446         public static void test() throws Exception {
 447             {
 448                 c.v = new Object[1][1][1]; ((Object[][][])c.v)[0][0][0] = Values.A; Object val1 = get();
 449                                            ((Object[][][])c.v)[0][0][0] = Values.B; Object val2 = get();
 450 
 451                 assertEquals(val1, Values.A);
 452                 assertEquals(val2, Values.B);
 453             }
 454 
 455             {
 456                 c.v = new Object[1][1][1]; c.v[0][0] = new Object[0]; Object[] val1 = get1();
 457                                            c.v[0][0] = new Object[0]; Object[] val2 = get1();
 458 
 459                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 460             }
 461 
 462             {
 463                 c.v = new Object[1][1][1]; c.v[0] = new Object[0][0]; Object[][] val1 = get2();
 464                                            c.v[0] = new Object[0][0]; Object[][] val2 = get2();
 465 
 466                 assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2)));
 467             }
 468 
 469             {
 470                 c.v = new Object[0][0][0]; Object[][] val1 = get3();
 471                 c.v = new Object[0][0][0]; Object[][] val2 = get3();
 472 
 473                 assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2)));
 474             }
 475         }
 476     }
 477 
 478     /* ==================================================== */
 479 
 480     static class NestedStableField {
 481         static class A {
 482             public @Stable Object a;
 483 
 484         }
 485         public @Stable A v;
 486 
 487         public static final NestedStableField c = new NestedStableField();
 488         public static A get() { return c.v; }
 489         public static Object get1() { return get().a; }
 490 
 491         public static void test() throws Exception {
 492             {
 493                 c.v = new A(); c.v.a = Values.A; A val1 = get();
 494                                c.v.a = Values.B; A val2 = get();
 495 
 496                 assertEquals(val1.a, Values.B);
 497                 assertEquals(val2.a, Values.B);
 498             }
 499 
 500             {
 501                 c.v = new A(); c.v.a = Values.A; Object val1 = get1();
 502                                c.v.a = Values.B; Object val2 = get1();
 503                 c.v = new A(); c.v.a = Values.C; Object val3 = get1();
 504 
 505                 assertEquals(val1, Values.A);
 506                 assertEquals(val2, (isStableEnabled ? Values.A : Values.B));
 507                 assertEquals(val3, (isStableEnabled ? Values.A : Values.C));
 508             }
 509         }
 510     }
 511 
 512     /* ==================================================== */
 513 
 514     static class NestedStableField1 {
 515         static class A {
 516             public @Stable Object a;
 517             public @Stable A next;
 518         }
 519         public @Stable A v;
 520 
 521         public static final NestedStableField1 c = new NestedStableField1();
 522         public static A get() { return c.v.next.next.next.next.next.next.next; }
 523         public static Object get1() { return get().a; }
 524 
 525         public static void test() throws Exception {
 526             {
 527                 c.v = new A(); c.v.next = new A();   c.v.next.next  = c.v;
 528                                c.v.a = Values.A; c.v.next.a = Values.A; A val1 = get();
 529                                c.v.a = Values.B; c.v.next.a = Values.B; A val2 = get();
 530 
 531                 assertEquals(val1.a, Values.B);
 532                 assertEquals(val2.a, Values.B);
 533             }
 534 
 535             {
 536                 c.v = new A(); c.v.next = c.v;
 537                                c.v.a = Values.A; Object val1 = get1();
 538                                c.v.a = Values.B; Object val2 = get1();
 539                 c.v = new A(); c.v.next = c.v;
 540                                c.v.a = Values.C; Object val3 = get1();
 541 
 542                 assertEquals(val1, Values.A);
 543                 assertEquals(val2, (isStableEnabled ? Values.A : Values.B));
 544                 assertEquals(val3, (isStableEnabled ? Values.A : Values.C));
 545             }
 546         }
 547     }
 548    /* ==================================================== */
 549 
 550     static class NestedStableField2 {
 551         static class A {
 552             public @Stable Object a;
 553             public @Stable A left;
 554             public         A right;
 555         }
 556 
 557         public @Stable A v;
 558 
 559         public static final NestedStableField2 c = new NestedStableField2();
 560         public static Object get() { return c.v.left.left.left.a; }
 561         public static Object get1() { return c.v.left.left.right.left.a; }
 562 
 563         public static void test() throws Exception {
 564             {
 565                 c.v = new A(); c.v.left = c.v.right = c.v;
 566                                c.v.a = Values.A; Object val1 = get(); Object val2 = get1();
 567                                c.v.a = Values.B; Object val3 = get(); Object val4 = get1();
 568 
 569                 assertEquals(val1, Values.A);
 570                 assertEquals(val3, (isStableEnabled ? Values.A : Values.B));
 571 
 572                 assertEquals(val2, Values.A);
 573                 assertEquals(val4, Values.B);
 574             }
 575         }
 576     }
 577 
 578     /* ==================================================== */
 579 
 580     static class NestedStableField3 {
 581         static class A {
 582             public @Stable Object a;
 583             public @Stable A[] left;
 584             public         A[] right;
 585         }
 586 
 587         public @Stable A[] v;
 588 
 589         public static final NestedStableField3 c = new NestedStableField3();
 590         public static Object get() { return c.v[0].left[1].left[0].left[1].a; }
 591         public static Object get1() { return c.v[1].left[0].left[1].right[0].left[1].a; }
 592 
 593         public static void test() throws Exception {
 594             {
 595                 A elem = new A();
 596                 c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v;
 597                                elem.a = Values.A; Object val1 = get(); Object val2 = get1();
 598                                elem.a = Values.B; Object val3 = get(); Object val4 = get1();
 599 
 600                 assertEquals(val1, Values.A);
 601                 assertEquals(val3, (isServerWithStable ? Values.A : Values.B));
 602 
 603                 assertEquals(val2, Values.A);
 604                 assertEquals(val4, Values.B);
 605             }
 606         }
 607     }
 608 
 609     /* ==================================================== */
 610     // Auxiliary methods
 611     static void assertEquals(Object i, Object j) { if (i != j)  throw new AssertionError(i + " != " + j); }
 612     static void assertTrue(boolean b) { if (!b)  throw new AssertionError(); }
 613 
 614     static boolean failed = false;
 615 
 616     public static void run(Class<?> test) {
 617         Throwable ex = null;
 618         System.out.print(test.getName()+": ");
 619         try {
 620             test.getMethod("test").invoke(null);
 621         } catch (InvocationTargetException e) {
 622             ex = e.getCause();
 623         } catch (Throwable e) {
 624             ex = e;
 625         } finally {
 626             if (ex == null) {
 627                 System.out.println("PASSED");
 628             } else {
 629                 failed = true;
 630                 System.out.println("FAILED");
 631                 ex.printStackTrace(System.out);
 632             }
 633         }
 634     }
 635 }