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