1 /*
   2  * Copyright (c) 2013, 2018, 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  * @test
  26  * @modules java.base/jdk.internal.misc:+open
  27  *
  28  * @summary converted from VM Testbase metaspace/staticReferences.
  29  * VM Testbase keywords: [nonconcurrent, javac, no_cds]
  30  *
  31  * @library /vmTestbase /test/lib
  32  * @run driver jdk.test.lib.FileInstaller . .
  33  * @build sun.hotspot.WhiteBox
  34  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  35  *                                sun.hotspot.WhiteBox$WhiteBoxPermission
  36  * @run main/othervm
  37  *      -Xmx800m
  38  *      -Xbootclasspath/a:.
  39  *      -XX:+UnlockDiagnosticVMOptions
  40  *      -XX:+WhiteBoxAPI
  41  *      metaspace.staticReferences.StaticReferences
  42  */
  43 
  44 package metaspace.staticReferences;
  45 
  46 import java.lang.ref.WeakReference;
  47 import java.lang.ref.Reference;
  48 import java.lang.reflect.Field;
  49 import java.lang.reflect.Modifier;
  50 import java.util.HashMap;
  51 import java.util.LinkedList;
  52 import java.util.List;
  53 import java.util.Map;
  54 import java.util.Random;
  55 
  56 import vm.share.InMemoryJavaCompiler;
  57 import nsk.share.gc.GCTestBase;
  58 import nsk.share.test.ExecutionController;
  59 import nsk.share.test.Stresser;
  60 import nsk.share.test.TestBase;
  61 import nsk.share.test.Tests;
  62 import jdk.internal.misc.Unsafe;
  63 import vm.share.gc.TriggerUnloadingHelper;
  64 import vm.share.gc.TriggerUnloadingWithWhiteBox;
  65 
  66 /**
  67  * Test checks that static fields will be initialized in new loaded class. Test performs in loop the following routine:
  68  * 1.) Load class either by regular classloader or by Unsafe.defineAnonymousClass.
  69  * 2.) Trigger unloading. Class must be alive. Next step will check that static fields would not lost.
  70  * 3.) Change static fields.
  71  * 4.) Unload class.
  72  * 5.) Load class again as in step 1.
  73  * 6.) Check that static fields were initialized.
  74  */
  75 @SuppressWarnings("rawtypes")
  76 public class StaticReferences extends GCTestBase {
  77 
  78     private static final int UNLOADING_ATTEMPTS_LIMIT = 50;
  79 
  80     private static final Object[] NO_CP_PATCHES = new Object[0];
  81 
  82     private static String[] args;
  83 
  84     private static final int LIMIT = 100;
  85 
  86     private List<Object> keepAlive = new LinkedList<Object>();
  87 
  88     private Random random;
  89 
  90     private TriggerUnloadingHelper triggerUnloadingHelper = new TriggerUnloadingWithWhiteBox();
  91 
  92     private String[] typesArray = new String[] {"Object object", "boolean boolean", "byte byte", "char char", "double double", "float float", "int int", "long long", "short short"};
  93 
  94     public static void main(String[] args) {
  95         StaticReferences.args = args;
  96         Tests.runTest(new StaticReferences(), args);
  97     }
  98 
  99     private static Unsafe getUnsafe() {
 100         return Unsafe.getUnsafe();
 101     }
 102 
 103         @Override
 104     public void run() {
 105                 random = new Random(runParams.getSeed());
 106         ExecutionController stresser = new Stresser(args);
 107         stresser.start(1);
 108 
 109         // Generate and compile classes
 110         List<byte[]> bytecodeList = new LinkedList<byte[]>();
 111         int[] fieldQuantities = new int[9];
 112         long startTimeStamp = System.currentTimeMillis();
 113         for (int i = 0; i < LIMIT; i++) {
 114             if (!stresser.continueExecution()) {
 115                         return;
 116                 }
 117             for (int j = 0; j < fieldQuantities.length; j++) {
 118                 fieldQuantities[j] = 1 + random.nextInt(2000);
 119             }
 120             bytecodeList.add(generateAndCompile(fieldQuantities));
 121         }
 122         log.info("Compilation finished in " + ((System.currentTimeMillis() - startTimeStamp)/1000/60.0) + " minutes ");
 123 
 124         // Core of test
 125         for (byte[] classBytecode : bytecodeList) {
 126             boolean anonymous = random.nextBoolean();
 127 
 128             log.info("Load class first time");
 129             Class clazz = loadClass(classBytecode, anonymous);
 130 
 131             log.info("Trigger unloading");
 132             triggerUnloadingHelper.triggerUnloading(stresser);
 133             if (!stresser.continueExecution()) {
 134                         return;
 135                 }
 136 
 137             log.info("Set up static fields. This will check that static fields are reachable.");
 138             setupFields(clazz);
 139 
 140             log.info("Cleanup references");
 141             Reference<Class> weakReference = new WeakReference<Class>(clazz);
 142             clazz = null;
 143 
 144             log.info("Trigger unloading again");
 145             int numberOfAttemps = 0;
 146             while (weakReference.get() != null && numberOfAttemps < UNLOADING_ATTEMPTS_LIMIT) {
 147                 if (!stresser.continueExecution()) {
 148                         return;
 149                 }
 150                 triggerUnloadingHelper.triggerUnloading(stresser);
 151             }
 152             if (numberOfAttemps >= UNLOADING_ATTEMPTS_LIMIT) {
 153                 setFailed(true);
 154                 throw new RuntimeException("Test failed: was unable to unload class with " + UNLOADING_ATTEMPTS_LIMIT + " attempts.");
 155             }
 156 
 157             log.info("Load class second time");
 158             clazz = loadClass(classBytecode, anonymous);
 159 
 160             log.info("check fields reinitialized");
 161             checkStaticFields(clazz);
 162 
 163             keepAlive.add(clazz);
 164         }
 165     }
 166 
 167         private Class loadClass(byte[] classBytecode,
 168                         boolean anonymous) {
 169                 Class clazz;
 170                 if (anonymous) {
 171                         clazz = getUnsafe().defineAnonymousClass(StaticReferences.class, classBytecode, NO_CP_PATCHES);
 172                 } else {
 173                         OneUsageClassloader classloader = new OneUsageClassloader();
 174                         clazz = classloader.define(classBytecode);
 175                 }
 176                 return clazz;
 177         }
 178 
 179     private void checkStaticFields(Class clazz) {
 180         for (Field field : clazz.getFields()) {
 181             try {
 182                 if (Modifier.isStatic(field.getModifiers())) {
 183                     Class fieldType = field.getType();
 184                     if ((fieldType.equals(Object.class) && field.get(null) != null )
 185                             || (fieldType.equals(int.class) && field.getInt(null) != 0)
 186                             || (fieldType.equals(boolean.class) && field.getBoolean(null) != false)
 187                             || (fieldType.equals(char.class) && field.getChar(null) != 0)
 188                             || (fieldType.equals(long.class) && field.getLong(null) != 0)
 189                             || (fieldType.equals(short.class) && field.getShort(null) != 0)
 190                             || (fieldType.equals(float.class) && field.getFloat(null) != 0.0f)
 191                             || (fieldType.equals(double.class) && field.getDouble(null) != 0.0)
 192                             || (fieldType.equals(byte.class) && field.getByte(null) != 0)) {
 193                         setFailed(true);
 194                         throw new RuntimeException("Failing test: field "
 195                                 + field.getName() + " of type "
 196                                 + field.getType() + " in class "
 197                                 + field.getDeclaringClass().getName()
 198                                 + " was not cleared");
 199                     }
 200                 }
 201             } catch (IllegalArgumentException | IllegalAccessException e) {
 202                 e.printStackTrace();
 203                 throw new RuntimeException("Was unable to set static field "
 204                         + field.getName() + " of type "
 205                         + field.getType().getName() + " in class "
 206                         + field.getDeclaringClass().getName(), e);
 207             }
 208         }
 209     }
 210 
 211     private byte[] generateAndCompile(int[] filedQuantities) {
 212         Map<String, CharSequence> sources = new HashMap<String, CharSequence>();
 213         sources.put("A", generateSource(filedQuantities));
 214         return InMemoryJavaCompiler.compile(sources).values().iterator().next();
 215     }
 216 
 217     private StringBuffer generateSource(int[] fieldQuantities) {
 218         StringBuffer result = new StringBuffer("public class A { \n");
 219         int fieldsCounter = 0;
 220         for (int i = 0; i < typesArray.length; i++) {
 221             for (int j = 0; j < fieldQuantities[i]; j++) {
 222                 result.append(" public static " + typesArray[i] + fieldsCounter++ + ";\n");
 223             }
 224         }
 225         result.append(" } ");
 226         return result;
 227     }
 228 
 229     private void setupFields(Class clazz) {
 230         for (Field field : clazz.getFields()) {
 231             try {
 232                 if (Modifier.isStatic(field.getModifiers())) {
 233                     Class fieldType = field.getType();
 234                     if (fieldType.equals(Object.class)) {
 235                         field.set(null, this);
 236                     } else if (fieldType.equals(int.class)) {
 237                         field.setInt(null, 42);
 238                     } else if (fieldType.equals(boolean.class)) {
 239                         field.setBoolean(null, true);
 240                     } else if (fieldType.equals(char.class)) {
 241                         field.setChar(null, 'c');
 242                     } else if (fieldType.equals(long.class)) {
 243                         field.setLong(null, (long) 42);
 244                     } else if (fieldType.equals(short.class)) {
 245                         field.setShort(null, (short) 42);
 246                     } else if (fieldType.equals(float.class)) {
 247                         field.setFloat(null, 42.42f);
 248                     } else if (fieldType.equals(double.class)) {
 249                         field.setDouble(null, 42.42);
 250                     } else if (fieldType.equals(byte.class)) {
 251                         field.setByte(null, (byte) 42);
 252                     }
 253                 }
 254             } catch (IllegalArgumentException | IllegalAccessException e) {
 255                 e.printStackTrace();
 256                 throw new RuntimeException(
 257                         "Was unable to set static field " + field.getName()
 258                                 + " of type " + field.getType().getName()
 259                                 + " in class "
 260                                 + field.getDeclaringClass().getName(), e);
 261             }
 262         }
 263     }
 264 
 265 }