1 /*
   2  * Copyright (c) 2016, 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 package jdk.vm.ci.hotspot.test;
  26 
  27 import static jdk.vm.ci.hotspot.test.TestHelper.ARRAYS_MAP;
  28 import static jdk.vm.ci.hotspot.test.TestHelper.ARRAY_ARRAYS_MAP;
  29 import static jdk.vm.ci.hotspot.test.TestHelper.CONSTANT_REFLECTION_PROVIDER;
  30 import static jdk.vm.ci.hotspot.test.TestHelper.DUMMY_CLASS_CONSTANT;
  31 import static jdk.vm.ci.hotspot.test.TestHelper.DUMMY_CLASS_INSTANCE;
  32 import static jdk.vm.ci.hotspot.test.TestHelper.getResolvedJavaField;
  33 import static jdk.vm.ci.hotspot.test.TestHelper.INSTANCE_STABLE_FIELDS_MAP;
  34 import static jdk.vm.ci.hotspot.test.TestHelper.INSTANCE_FIELDS_MAP;
  35 import static jdk.vm.ci.hotspot.test.TestHelper.STABLE_ARRAYS_MAP;
  36 import static jdk.vm.ci.hotspot.test.TestHelper.STABLE_ARRAY_ARRAYS_MAP;
  37 
  38 import java.lang.reflect.Field;
  39 import java.util.LinkedList;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.stream.Stream;
  43 
  44 import jdk.vm.ci.meta.JavaConstant;
  45 import org.testng.annotations.DataProvider;
  46 import jdk.internal.misc.Unsafe;
  47 import jdk.test.lib.Triple;
  48 import jdk.vm.ci.meta.ResolvedJavaField;
  49 
  50 public class ReadConstantArrayElementDataProvider {
  51 
  52     // Non-stable array fields names mapped to their base offsets and index scale
  53     private static final List<Triple<String, Integer, Integer>> NON_STABLE_ARRAY_NAMES
  54             = new LinkedList<>();
  55 
  56     static {
  57         NON_STABLE_ARRAY_NAMES.add(
  58                 new Triple<>("booleanArrayWithValues", Unsafe.ARRAY_BOOLEAN_BASE_OFFSET,
  59                              Unsafe.ARRAY_BOOLEAN_INDEX_SCALE));
  60         NON_STABLE_ARRAY_NAMES.add(new Triple<>("byteArrayWithValues",
  61                                                 Unsafe.ARRAY_BYTE_BASE_OFFSET,
  62                                                 Unsafe.ARRAY_BYTE_INDEX_SCALE));
  63         NON_STABLE_ARRAY_NAMES.add(new Triple<>("shortArrayWithValues",
  64                                                 Unsafe.ARRAY_SHORT_BASE_OFFSET,
  65                                                 Unsafe.ARRAY_SHORT_INDEX_SCALE));
  66         NON_STABLE_ARRAY_NAMES.add(new Triple<>("charArrayWithValues",
  67                                                 Unsafe.ARRAY_CHAR_BASE_OFFSET,
  68                                                 Unsafe.ARRAY_CHAR_INDEX_SCALE));
  69         NON_STABLE_ARRAY_NAMES.add(new Triple<>("intArrayWithValues",
  70                                                 Unsafe.ARRAY_INT_BASE_OFFSET,
  71                                                 Unsafe.ARRAY_INT_INDEX_SCALE));
  72         NON_STABLE_ARRAY_NAMES.add(new Triple<>("longArrayWithValues",
  73                                                 Unsafe.ARRAY_LONG_BASE_OFFSET,
  74                                                 Unsafe.ARRAY_LONG_INDEX_SCALE));
  75         NON_STABLE_ARRAY_NAMES.add(new Triple<>("floatArrayWithValues",
  76                                                 Unsafe.ARRAY_FLOAT_BASE_OFFSET,
  77                                                 Unsafe.ARRAY_FLOAT_INDEX_SCALE));
  78         NON_STABLE_ARRAY_NAMES.add(new Triple<>("doubleArrayWithValues",
  79                                                 Unsafe.ARRAY_DOUBLE_BASE_OFFSET,
  80                                                 Unsafe.ARRAY_DOUBLE_INDEX_SCALE));
  81         NON_STABLE_ARRAY_NAMES.add(new Triple<>("objectArrayWithValues",
  82                                                 Unsafe.ARRAY_BOOLEAN_BASE_OFFSET,
  83                                                 Unsafe.ARRAY_BOOLEAN_INDEX_SCALE));
  84         NON_STABLE_ARRAY_NAMES.add(new Triple<>("booleanArrayArrayWithValues",
  85                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  86                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  87         NON_STABLE_ARRAY_NAMES.add(new Triple<>("byteArrayArrayWithValues",
  88                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  89                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  90         NON_STABLE_ARRAY_NAMES.add(new Triple<>("shortArrayArrayWithValues",
  91                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  92                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  93         NON_STABLE_ARRAY_NAMES.add(new Triple<>("charArrayArrayWithValues",
  94                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  95                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  96         NON_STABLE_ARRAY_NAMES.add(new Triple<>("intArrayArrayWithValues",
  97                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  98                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  99         NON_STABLE_ARRAY_NAMES.add(new Triple<>("longArrayArrayWithValues",
 100                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
 101                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
 102         NON_STABLE_ARRAY_NAMES.add(new Triple<>("floatArrayArrayWithValues",
 103                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
 104                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
 105         NON_STABLE_ARRAY_NAMES.add(new Triple<>("doubleArrayArrayWithValues",
 106                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
 107                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
 108         NON_STABLE_ARRAY_NAMES.add(new Triple<>("objectArrayArrayWithValues",
 109                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
 110                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
 111     }
 112 
 113     // Stable array fields names mapped to their base offsets and index scale
 114     private static final List<Triple<String, Integer, Integer>> STABLE_ARRAY_NAMES
 115             = new LinkedList<>();
 116 
 117     static {
 118         NON_STABLE_ARRAY_NAMES.stream().forEach((entry) -> {
 119             String nsFieldName = entry.getFirst();
 120             char firstChar = nsFieldName.charAt(0);
 121             char newFirstChar = Character.toUpperCase(firstChar);
 122             String sFieldName = nsFieldName.replaceFirst("" + firstChar,
 123                                                          "" + newFirstChar);
 124             sFieldName = "stable" + sFieldName;
 125             STABLE_ARRAY_NAMES.add(new Triple<>(sFieldName,
 126                                                 entry.getSecond(),
 127                                                 entry.getThird()));
 128         });
 129     }
 130 
 131     @DataProvider(name = "readConstantArrayElementDataProvider")
 132     public static Object[][] readConstantArrayElementDataProvider() {
 133         LinkedList<Object[]> cfgSet = new LinkedList<>();
 134         for (int i : new int[]{0, 1}) {
 135             NON_STABLE_ARRAY_NAMES.stream().forEach((entry) -> {
 136                 String fieldName = entry.getFirst();
 137                 cfgSet.add(new Object[]{
 138                         readFieldValue(fieldName),
 139                         i,
 140                         null,
 141                         "array field \"" + fieldName + "\" for index " + i});
 142             });
 143             STABLE_ARRAY_NAMES.stream().forEach((entry) -> {
 144                 String fieldName = entry.getFirst();
 145                 cfgSet.add(new Object[]{
 146                         readFieldValue(fieldName),
 147                         i,
 148                         i == 0 ? getJavaConstant(fieldName) : null,
 149                         "array field \"" + fieldName + "\" for index " + i});
 150             });
 151         }
 152         Stream<Map.Entry<ResolvedJavaField, JavaConstant>> arraysStream1
 153                 = Stream.concat(ARRAYS_MAP.entrySet().stream(),
 154                                 ARRAY_ARRAYS_MAP.entrySet().stream());
 155         Stream<Map.Entry<ResolvedJavaField, JavaConstant>> arraysStream2
 156                 = Stream.concat(STABLE_ARRAYS_MAP.entrySet().stream(),
 157                                 STABLE_ARRAY_ARRAYS_MAP.entrySet().stream());
 158         Stream.concat(arraysStream1, arraysStream2).forEach((array) -> {
 159             for (int i : new int[]{-1, 2}) {
 160                 cfgSet.add(new Object[]{
 161                         array.getValue(),
 162                         i,
 163                         null,
 164                         "array field \"" + array.getKey() + "\" for index " + i});
 165             }
 166         });
 167         cfgSet.add(new Object[]{null, 0, null, "null"});
 168         cfgSet.add(new Object[]{JavaConstant.NULL_POINTER, 0, null, "JavaConstant.NULL_POINTER"});
 169         INSTANCE_FIELDS_MAP.values().forEach((constant) -> {
 170             cfgSet.add(new Object[]{constant, 0, null, "non-stable non-array field"});
 171         });
 172         INSTANCE_STABLE_FIELDS_MAP.values().forEach((constant) -> {
 173             cfgSet.add(new Object[]{constant, 0, null, "stable non-array field"});
 174         });
 175         return cfgSet.toArray(new Object[0][0]);
 176     }
 177 
 178     @DataProvider(name = "readConstantArrayElementForOffsetDataProvider")
 179     public static Object[][] readConstantArrayElementForOffsetDataProvider() {
 180         LinkedList<Object[]> cfgSet = new LinkedList<>();
 181         // Testing non-stable arrays. Result should be null in all cases
 182         for (double i : new double[]{-1, 0, 0.5, 1, 1.5, 2}) {
 183             NON_STABLE_ARRAY_NAMES.stream().forEach(entry -> {
 184                 String fieldName = entry.getFirst();
 185                 long offset = (long) (entry.getSecond() + i * entry.getThird());
 186                 cfgSet.add(new Object[]{
 187                         readFieldValue(fieldName),
 188                         offset,
 189                         null,
 190                         "array field \"" + fieldName + "\" for offset " + offset});
 191             });
 192         }
 193         // Testing stable arrays. Result should be null in all cases except "offset = base + 0"
 194         for (double i : new double[]{-1, 0.5, 1, 1.5, 2}) {
 195             STABLE_ARRAY_NAMES.stream().forEach(entry -> {
 196                 String fieldName = entry.getFirst();
 197                 long offset = (long) Math.ceil(entry.getSecond() + i * entry.getThird());
 198                 cfgSet.add(new Object[]{
 199                         readFieldValue(fieldName),
 200                         offset,
 201                         null,
 202                         "array field \"" + fieldName + "\" for offset " + offset});
 203             });
 204         }
 205         // Testing stable arrays "offset = base + 0". Result should be non-null
 206         STABLE_ARRAY_NAMES.stream().forEach(entry -> {
 207             String fieldName = entry.getFirst();
 208             long offset = (long) entry.getSecond();
 209             cfgSet.add(new Object[]{
 210                     readFieldValue(fieldName),
 211                     offset,
 212                     getJavaConstant(fieldName),
 213                     "array field \"" + fieldName + "\" for offset " + offset});
 214         });
 215         // Testing null as array
 216         cfgSet.add(new Object[]{null, 0, null, "null"});
 217         // Testing JavaConstant.NULL_POINTER as array
 218         cfgSet.add(new Object[]{JavaConstant.NULL_POINTER, 0, null, "JavaConstant.NULL_POINTER"});
 219         // Testing non-stable non-array fields
 220         INSTANCE_FIELDS_MAP.values().forEach((constant) -> {
 221             cfgSet.add(new Object[]{constant, 0, null, "non-stable non-array field"});
 222         });
 223         // Testing stable non-array fields
 224         INSTANCE_STABLE_FIELDS_MAP.values().forEach((constant) -> {
 225             cfgSet.add(new Object[]{constant, 0, null, "stable non-array field"});
 226         });
 227         return cfgSet.toArray(new Object[0][0]);
 228     }
 229 
 230     private static JavaConstant readFieldValue(String fieldName) {
 231         return CONSTANT_REFLECTION_PROVIDER
 232                 .readFieldValue(getResolvedJavaField(DummyClass.class, fieldName),
 233                                 DUMMY_CLASS_CONSTANT);
 234     }
 235 
 236     private static JavaConstant getJavaConstant(String fieldName) {
 237         Class dummyClass = DummyClass.class;
 238         Field arrayField;
 239         try {
 240             arrayField = dummyClass.getDeclaredField(fieldName);
 241         } catch (NoSuchFieldException ex) {
 242             throw new Error("Test bug: wrong field name " + ex, ex);
 243         } catch (SecurityException ex) {
 244             throw new Error("Unexpected error: " + ex, ex);
 245         }
 246         arrayField.setAccessible(true);
 247         Class componentType = arrayField.getType().getComponentType();
 248         if (componentType == null) {
 249             throw new Error("Test error: field is not an array");
 250         }
 251         Object value;
 252         try {
 253             value = arrayField.get(DUMMY_CLASS_INSTANCE);
 254         } catch (IllegalArgumentException | IllegalAccessException ex) {
 255             throw new Error("Unexpected error: " + ex, ex);
 256         }
 257         if (componentType == boolean.class) {
 258             return JavaConstant.forBoolean(((boolean[]) value)[0]);
 259         }
 260         if (componentType == byte.class) {
 261             return JavaConstant.forByte(((byte[]) value)[0]);
 262         }
 263         if (componentType == short.class) {
 264             return JavaConstant.forShort(((short[]) value)[0]);
 265         }
 266         if (componentType == char.class) {
 267             return JavaConstant.forChar(((char[]) value)[0]);
 268         }
 269         if (componentType == int.class) {
 270             return JavaConstant.forInt(((int[]) value)[0]);
 271         }
 272         if (componentType == long.class) {
 273             return JavaConstant.forLong(((long[]) value)[0]);
 274         }
 275         if (componentType == float.class) {
 276             return JavaConstant.forFloat(((float[]) value)[0]);
 277         }
 278         if (componentType == double.class) {
 279             return JavaConstant.forDouble(((double[]) value)[0]);
 280         }
 281         return CONSTANT_REFLECTION_PROVIDER.forObject(((Object[]) value)[0]);
 282     }
 283 }