1/*
2 * Copyright (c) 2013, 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 */
23package jdk.vm.ci.runtime.test;
24
25import jdk.internal.misc.Unsafe;
26import jdk.vm.ci.meta.ConstantReflectionProvider;
27import jdk.vm.ci.meta.JavaConstant;
28import jdk.vm.ci.meta.MetaAccessProvider;
29import jdk.vm.ci.meta.ResolvedJavaField;
30import jdk.vm.ci.meta.ResolvedJavaType;
31import jdk.vm.ci.runtime.JVMCI;
32import org.junit.Test;
33
34import java.io.Serializable;
35import java.lang.reflect.Array;
36import java.lang.reflect.Field;
37import java.lang.reflect.Method;
38import java.util.AbstractCollection;
39import java.util.AbstractList;
40import java.util.ArrayDeque;
41import java.util.ArrayList;
42import java.util.Collection;
43import java.util.Collections;
44import java.util.HashMap;
45import java.util.HashSet;
46import java.util.IdentityHashMap;
47import java.util.LinkedHashMap;
48import java.util.LinkedList;
49import java.util.List;
50import java.util.Map;
51import java.util.Queue;
52import java.util.Set;
53import java.util.TreeMap;
54import java.util.stream.Collectors;
55
56import static java.lang.reflect.Modifier.isFinal;
57import static java.lang.reflect.Modifier.isStatic;
58
59/**
60 * Context for type related tests.
61 */
62public class TypeUniverse {
63
64    public static final Unsafe unsafe;
65    public static final double JAVA_VERSION = Double.valueOf(System.getProperty("java.specification.version"));
66
67    public static final MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
68    public static final ConstantReflectionProvider constantReflection = JVMCI.getRuntime().getHostJVMCIBackend().getConstantReflection();
69    public static final Collection<Class<?>> classes = new HashSet<>();
70    public static final Set<ResolvedJavaType> javaTypes;
71    public static final Map<Class<?>, Class<?>> arrayClasses = new HashMap<>();
72
73    private static List<ConstantValue> constants;
74
75    public class InnerClass {
76
77    }
78
79    public static class InnerStaticClass {
80
81    }
82
83    public static final class InnerStaticFinalClass {
84
85    }
86
87    private class PrivateInnerClass {
88
89    }
90
91    protected class ProtectedInnerClass {
92
93    }
94
95    static {
96        Unsafe theUnsafe = null;
97        try {
98            theUnsafe = Unsafe.getUnsafe();
99        } catch (Exception e) {
100            try {
101                Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
102                theUnsafeField.setAccessible(true);
103                theUnsafe = (Unsafe) theUnsafeField.get(null);
104            } catch (Exception e1) {
105                throw (InternalError) new InternalError("unable to initialize unsafe").initCause(e1);
106            }
107        }
108        unsafe = theUnsafe;
109
110        Class<?>[] initialClasses = {void.class, boolean.class, byte.class, short.class, char.class, int.class, float.class, long.class, double.class, Object.class, Class.class, boolean[].class,
111                        byte[].class, short[].class, char[].class, int[].class, float[].class, long[].class, double[].class, Object[].class, Class[].class, List[].class, boolean[][].class,
112                        byte[][].class, short[][].class, char[][].class, int[][].class, float[][].class, long[][].class, double[][].class, Object[][].class, Class[][].class, List[][].class,
113                        ClassLoader.class, String.class, Serializable.class, Cloneable.class, Test.class, TestMetaAccessProvider.class, List.class, Collection.class, Map.class, Queue.class,
114                        HashMap.class, LinkedHashMap.class, IdentityHashMap.class, AbstractCollection.class, AbstractList.class, ArrayList.class, InnerClass.class, InnerStaticClass.class,
115                        InnerStaticFinalClass.class, PrivateInnerClass.class, ProtectedInnerClass.class};
116        for (Class<?> c : initialClasses) {
117            addClass(c);
118        }
119
120        javaTypes = Collections.unmodifiableSet(classes.stream().map(c -> metaAccess.lookupJavaType(c)).collect(Collectors.toSet()));
121    }
122
123    static class ConstantsUniverse {
124        static final Object[] ARRAYS = classes.stream().map(c -> c != void.class && !c.isArray() ? Array.newInstance(c, 42) : null).filter(o -> o != null).collect(Collectors.toList()).toArray();
125        static final Object CONST1 = new ArrayList<>();
126        static final Object CONST2 = new ArrayList<>();
127        static final Object CONST3 = new IdentityHashMap<>();
128        static final Object CONST4 = new LinkedHashMap<>();
129        static final Object CONST5 = new TreeMap<>();
130        static final Object CONST6 = new ArrayDeque<>();
131        static final Object CONST7 = new LinkedList<>();
132        static final Object CONST8 = "a string";
133        static final Object CONST9 = 42;
134        static final Object CONST10 = String.class;
135        static final Object CONST11 = String[].class;
136    }
137
138    public static List<ConstantValue> constants() {
139        if (constants == null) {
140            List<ConstantValue> res = readConstants(JavaConstant.class);
141            res.addAll(readConstants(ConstantsUniverse.class));
142            constants = res;
143        }
144        return constants;
145    }
146
147    public static class ConstantValue {
148        public final String name;
149        public final JavaConstant value;
150        public final Object boxed;
151
152        public ConstantValue(String name, JavaConstant value, Object boxed) {
153            this.name = name;
154            this.value = value;
155            this.boxed = boxed;
156        }
157
158        @Override
159        public String toString() {
160            return name + "=" + value;
161        }
162
163        public String getSimpleName() {
164            return name.substring(name.lastIndexOf('.') + 1);
165        }
166    }
167
168    /**
169     * Reads the value of all {@code static final} fields from a given class into an array of
170     * {@link ConstantValue}s.
171     */
172    public static List<ConstantValue> readConstants(Class<?> fromClass) {
173        try {
174            List<ConstantValue> res = new ArrayList<>();
175            for (Field field : fromClass.getDeclaredFields()) {
176                if (isStatic(field.getModifiers()) && isFinal(field.getModifiers())) {
177                    ResolvedJavaField javaField = metaAccess.lookupJavaField(field);
178                    Object boxed = field.get(null);
179                    if (boxed instanceof JavaConstant) {
180                        res.add(new ConstantValue(javaField.format("%H.%n"), (JavaConstant) boxed, boxed));
181                    } else {
182                        JavaConstant value = constantReflection.readFieldValue(javaField, null);
183                        if (value != null) {
184                            res.add(new ConstantValue(javaField.format("%H.%n"), value, boxed));
185                            if (boxed instanceof Object[]) {
186                                Object[] arr = (Object[]) boxed;
187                                for (int i = 0; i < arr.length; i++) {
188                                    JavaConstant element = constantReflection.readArrayElement(value, i);
189                                    if (element != null) {
190                                        res.add(new ConstantValue(javaField.format("%H.%n[" + i + "]"), element, arr[i]));
191                                    }
192                                }
193                            }
194                        }
195                    }
196                }
197            }
198            return res;
199        } catch (Exception e) {
200            throw new AssertionError(e);
201        }
202    }
203
204    public synchronized Class<?> getArrayClass(Class<?> componentType) {
205        Class<?> arrayClass = arrayClasses.get(componentType);
206        if (arrayClass == null) {
207            arrayClass = Array.newInstance(componentType, 0).getClass();
208            arrayClasses.put(componentType, arrayClass);
209        }
210        return arrayClass;
211    }
212
213    public static int dimensions(Class<?> c) {
214        if (c.getComponentType() != null) {
215            return 1 + dimensions(c.getComponentType());
216        }
217        return 0;
218    }
219
220    private static void addClass(Class<?> c) {
221        if (classes.add(c)) {
222            if (c.getSuperclass() != null) {
223                addClass(c.getSuperclass());
224            }
225            for (Class<?> sc : c.getInterfaces()) {
226                addClass(sc);
227            }
228            for (Class<?> dc : c.getDeclaredClasses()) {
229                addClass(dc);
230            }
231            for (Method m : c.getDeclaredMethods()) {
232                addClass(m.getReturnType());
233                for (Class<?> p : m.getParameterTypes()) {
234                    addClass(p);
235                }
236            }
237
238            if (c != void.class && dimensions(c) < 2) {
239                Class<?> arrayClass = Array.newInstance(c, 0).getClass();
240                arrayClasses.put(c, arrayClass);
241                addClass(arrayClass);
242            }
243        }
244    }
245}
246