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
  52             = new LinkedList<>();
  53 
  54     static {
  55         NON_STABLE_ARRAY_NAMES.add(
  56                 new ArrayFieldParams("booleanArrayWithValues", Unsafe.ARRAY_BOOLEAN_BASE_OFFSET,
  57                              Unsafe.ARRAY_BOOLEAN_INDEX_SCALE));
  58         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("byteArrayWithValues",
  59                                                 Unsafe.ARRAY_BYTE_BASE_OFFSET,
  60                                                 Unsafe.ARRAY_BYTE_INDEX_SCALE));
  61         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("shortArrayWithValues",
  62                                                 Unsafe.ARRAY_SHORT_BASE_OFFSET,
  63                                                 Unsafe.ARRAY_SHORT_INDEX_SCALE));
  64         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("charArrayWithValues",
  65                                                 Unsafe.ARRAY_CHAR_BASE_OFFSET,
  66                                                 Unsafe.ARRAY_CHAR_INDEX_SCALE));
  67         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("intArrayWithValues",
  68                                                 Unsafe.ARRAY_INT_BASE_OFFSET,
  69                                                 Unsafe.ARRAY_INT_INDEX_SCALE));
  70         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("longArrayWithValues",
  71                                                 Unsafe.ARRAY_LONG_BASE_OFFSET,
  72                                                 Unsafe.ARRAY_LONG_INDEX_SCALE));
  73         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("floatArrayWithValues",
  74                                                 Unsafe.ARRAY_FLOAT_BASE_OFFSET,
  75                                                 Unsafe.ARRAY_FLOAT_INDEX_SCALE));
  76         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("doubleArrayWithValues",
  77                                                 Unsafe.ARRAY_DOUBLE_BASE_OFFSET,
  78                                                 Unsafe.ARRAY_DOUBLE_INDEX_SCALE));
  79         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("objectArrayWithValues",
  80                                                 Unsafe.ARRAY_BOOLEAN_BASE_OFFSET,
  81                                                 Unsafe.ARRAY_BOOLEAN_INDEX_SCALE));
  82         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("booleanArrayArrayWithValues",
  83                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  84                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  85         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("byteArrayArrayWithValues",
  86                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  87                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  88         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("shortArrayArrayWithValues",
  89                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  90                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  91         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("charArrayArrayWithValues",
  92                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  93                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  94         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("intArrayArrayWithValues",
  95                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  96                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
  97         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("longArrayArrayWithValues",
  98                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
  99                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
 100         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("floatArrayArrayWithValues",
 101                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
 102                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
 103         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("doubleArrayArrayWithValues",
 104                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
 105                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
 106         NON_STABLE_ARRAY_NAMES.add(new ArrayFieldParams("objectArrayArrayWithValues",
 107                                                 Unsafe.ARRAY_OBJECT_BASE_OFFSET,
 108                                                 Unsafe.ARRAY_OBJECT_INDEX_SCALE));
 109     }
 110 
 111     // Stable array fields names mapped to their base offsets and index scale
 112     private static final List<ArrayFieldParams> STABLE_ARRAY_NAMES
 113             = new LinkedList<>();
 114 
 115     static {
 116         NON_STABLE_ARRAY_NAMES.stream().forEach((entry) -> {
 117             String nsFieldName = entry.name;
 118             char firstChar = nsFieldName.charAt(0);
 119             char newFirstChar = Character.toUpperCase(firstChar);
 120             String sFieldName = nsFieldName.replaceFirst("" + firstChar,
 121                                                          "" + newFirstChar);
 122             sFieldName = "stable" + sFieldName;
 123             STABLE_ARRAY_NAMES.add(new ArrayFieldParams(sFieldName, entry.offsetBase, entry.scale));
 124         });
 125     }
 126 
 127     @DataProvider(name = "readConstantArrayElementDataProvider")
 128     public static Object[][] readConstantArrayElementDataProvider() {
 129         LinkedList<Object[]> cfgSet = new LinkedList<>();
 130         for (int i : new int[]{0, 1}) {
 131             NON_STABLE_ARRAY_NAMES.stream().forEach((entry) -> {
 132                 String fieldName = entry.name;
 133                 cfgSet.add(new Object[]{
 134                         readFieldValue(fieldName),
 135                         i,
 136                         null,
 137                         "array field \"" + fieldName + "\" for index " + i});
 138             });
 139             STABLE_ARRAY_NAMES.stream().forEach((entry) -> {
 140                 String fieldName = entry.name;
 141                 cfgSet.add(new Object[]{
 142                         readFieldValue(fieldName),
 143                         i,
 144                         i == 0 ? getJavaConstant(fieldName) : null,
 145                         "array field \"" + fieldName + "\" for index " + i});
 146             });
 147         }
 148         Stream<Map.Entry<ResolvedJavaField, JavaConstant>> arraysStream1
 149                 = Stream.concat(ARRAYS_MAP.entrySet().stream(),
 150                                 ARRAY_ARRAYS_MAP.entrySet().stream());
 151         Stream<Map.Entry<ResolvedJavaField, JavaConstant>> arraysStream2
 152                 = Stream.concat(STABLE_ARRAYS_MAP.entrySet().stream(),
 153                                 STABLE_ARRAY_ARRAYS_MAP.entrySet().stream());
 154         Stream.concat(arraysStream1, arraysStream2).forEach((array) -> {
 155             for (int i : new int[]{-1, 2}) {
 156                 cfgSet.add(new Object[]{
 157                         array.getValue(),
 158                         i,
 159                         null,
 160                         "array field \"" + array.getKey() + "\" for index " + i});
 161             }
 162         });
 163         cfgSet.add(new Object[]{null, 0, null, "null"});
 164         cfgSet.add(new Object[]{JavaConstant.NULL_POINTER, 0, null, "JavaConstant.NULL_POINTER"});
 165         INSTANCE_FIELDS_MAP.values().forEach((constant) -> {
 166             cfgSet.add(new Object[]{constant, 0, null, "non-stable non-array field"});
 167         });
 168         INSTANCE_STABLE_FIELDS_MAP.values().forEach((constant) -> {
 169             cfgSet.add(new Object[]{constant, 0, null, "stable non-array field"});
 170         });
 171         return cfgSet.toArray(new Object[0][0]);
 172     }
 173 
 174     @DataProvider(name = "readConstantArrayElementForOffsetDataProvider")
 175     public static Object[][] readConstantArrayElementForOffsetDataProvider() {
 176         LinkedList<Object[]> cfgSet = new LinkedList<>();
 177         // Testing non-stable arrays. Result should be null in all cases
 178         for (double i : new double[]{-1, 0, 0.5, 1, 1.5, 2}) {
 179             NON_STABLE_ARRAY_NAMES.stream().forEach(entry -> {
 180                 String fieldName = entry.name;
 181                 long offset = (long) (entry.offsetBase + i * entry.scale);
 182                 cfgSet.add(new Object[]{
 183                         readFieldValue(fieldName),
 184                         offset,
 185                         null,
 186                         "array field \"" + fieldName + "\" for offset " + offset});
 187             });
 188         }
 189         // Testing stable arrays. Result should be null in all cases except "offset = base + 0"
 190         for (double i : new double[]{-1, 0.5, 1, 1.5, 2}) {
 191             STABLE_ARRAY_NAMES.stream().forEach(entry -> {
 192                 String fieldName = entry.name;
 193                 long offset = (long) Math.ceil(entry.offsetBase + i * entry.scale);
 194                 cfgSet.add(new Object[]{
 195                         readFieldValue(fieldName),
 196                         offset,
 197                         null,
 198                         "array field \"" + fieldName + "\" for offset " + offset});
 199             });
 200         }
 201         // Testing stable arrays "offset = base + 0". Result should be non-null
 202         STABLE_ARRAY_NAMES.stream().forEach(entry -> {
 203             String fieldName = entry.name;
 204             long offset = (long) entry.offsetBase;
 205             cfgSet.add(new Object[]{
 206                     readFieldValue(fieldName),
 207                     offset,
 208                     getJavaConstant(fieldName),
 209                     "array field \"" + fieldName + "\" for offset " + offset});
 210         });
 211         // Testing null as array
 212         cfgSet.add(new Object[]{null, 0, null, "null"});
 213         // Testing JavaConstant.NULL_POINTER as array
 214         cfgSet.add(new Object[]{JavaConstant.NULL_POINTER, 0, null, "JavaConstant.NULL_POINTER"});
 215         // Testing non-stable non-array fields
 216         INSTANCE_FIELDS_MAP.values().forEach((constant) -> {
 217             cfgSet.add(new Object[]{constant, 0, null, "non-stable non-array field"});
 218         });
 219         // Testing stable non-array fields
 220         INSTANCE_STABLE_FIELDS_MAP.values().forEach((constant) -> {
 221             cfgSet.add(new Object[]{constant, 0, null, "stable non-array field"});
 222         });
 223         return cfgSet.toArray(new Object[0][0]);
 224     }
 225 
 226     private static JavaConstant readFieldValue(String fieldName) {
 227         return CONSTANT_REFLECTION_PROVIDER.readFieldValue(getResolvedJavaField(DummyClass.class, fieldName),
 228                                                            DUMMY_CLASS_CONSTANT);
 229     }
 230 
 231     private static JavaConstant getJavaConstant(String fieldName) {
 232         Class<DummyClass> 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 
 279     private static class ArrayFieldParams {
 280         public final String name;
 281         public final int offsetBase;
 282         public final int scale;
 283 
 284        ArrayFieldParams(String name, int offsetBase, int scale) {
 285            this.name = name;
 286            this.offsetBase = offsetBase;
 287            this.scale = scale;
 288        }
 289     }
 290 }