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