1/*
2 * Copyright (c) 2015, 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
24package compiler.jvmci.compilerToVM;
25
26import compiler.jvmci.common.CTVMUtilities;
27import compiler.testlibrary.CompilerUtils;
28import jdk.test.lib.util.Pair;
29import jdk.test.lib.Utils;
30import jdk.vm.ci.code.InstalledCode;
31import sun.hotspot.WhiteBox;
32import sun.hotspot.code.NMethod;
33
34import java.lang.reflect.Constructor;
35import java.lang.reflect.Executable;
36import java.lang.reflect.InvocationTargetException;
37import java.lang.reflect.Method;
38import java.lang.reflect.Modifier;
39import java.util.ArrayList;
40import java.util.Arrays;
41import java.util.Collections;
42import java.util.HashMap;
43import java.util.List;
44import java.util.Map;
45
46/**
47 * A test case for tests which require compiled code.
48 */
49public class CompileCodeTestCase {
50    private static final WhiteBox WB = WhiteBox.getWhiteBox();
51    private static final int COMP_LEVEL;
52    static {
53        int[] levels = CompilerUtils.getAvailableCompilationLevels();
54        if (levels.length == 0) {
55            throw new Error("TESTBUG: no compilers available");
56        }
57        COMP_LEVEL = levels[levels.length - 1];
58    }
59    private static final Class<?>[] CLASSES = {
60            Interface.class,
61            Dummy.class,
62            DummyEx.class};
63    private static final Map<Class<?>, Object> RECEIVERS;
64
65    public final Object receiver;
66    public final Executable executable;
67    public final int bci;
68    private final boolean isOsr;
69
70    public CompileCodeTestCase(Object receiver, Executable executable,
71            int bci) {
72        this.receiver = receiver;
73        this.executable = executable;
74        this.bci = bci;
75        isOsr = (bci >= 0);
76    }
77
78    public NMethod compile() {
79        return compile(COMP_LEVEL);
80    }
81
82    public Pair<Object, ? extends Throwable> invoke(Object[] args) {
83        boolean old = executable.isAccessible();
84        executable.setAccessible(true);
85        try {
86            try {
87                if (executable instanceof Method) {
88                    Method m = (Method) executable;
89                    return new Pair<>(m.invoke(receiver, args), null);
90                }
91
92                if (executable instanceof Constructor) {
93                    Constructor c = (Constructor) executable;
94                    return new Pair<>(c.newInstance(args), null);
95                }
96            } catch (InvocationTargetException e) {
97                return new Pair<>(null, e.getCause());
98            } catch (Throwable e) {
99                return new Pair<>(null, e);
100            }
101        } finally {
102            executable.setAccessible(old);
103        }
104        throw new Error(executable + " has unsupported type "
105                + executable.getClass());
106    }
107
108    public NMethod compile(int level) {
109        String directive = "[{ match: \"" + executable.getDeclaringClass().getName().replace('.', '/')
110                + "." + (executable instanceof Constructor ? "<init>" : executable.getName())
111                + "\", " + "BackgroundCompilation: false }]";
112        if (WB.addCompilerDirective(directive) != 1) {
113            throw new Error("Failed to add compiler directive: " + directive);
114        }
115        boolean enqueued = WB.enqueueMethodForCompilation(executable,
116                level, bci);
117        if (!enqueued) {
118            throw new Error(String.format(
119                    "%s can't be enqueued for %scompilation on level %d",
120                    executable, bci >= 0 ? "osr-" : "", level));
121        }
122        Utils.waitForCondition(() -> WB.isMethodCompiled(executable, isOsr));
123        return NMethod.get(executable, isOsr);
124    }
125
126    public static List<CompileCodeTestCase> generate(int bci) {
127        ArrayList<CompileCodeTestCase> result = new ArrayList<>();
128        for (Class<?> aClass : CLASSES) {
129            Object receiver = RECEIVERS.get(aClass);
130            if (receiver == null) {
131                throw new Error("TESTBUG : no receiver for class " + aClass);
132            }
133            for (Executable m : aClass.getDeclaredConstructors()) {
134                result.add(new CompileCodeTestCase(receiver, m, bci));
135            }
136            Arrays.stream(aClass.getDeclaredMethods())
137                    .filter(m -> !Modifier.isAbstract(m.getModifiers()))
138                    .filter(m -> !Modifier.isNative(m.getModifiers()))
139                    .map(m -> new CompileCodeTestCase(receiver, m, bci))
140                    .forEach(result::add);
141        }
142        return result;
143    }
144
145    public NMethod toNMethod() {
146        return NMethod.get(executable, isOsr);
147    }
148
149    public InstalledCode toInstalledCode() {
150        NMethod nmethod = toNMethod();
151        long address = nmethod == null ? 0L : nmethod.address;
152        long entryPoint = nmethod == null ? 0L : nmethod.entry_point;
153        return CTVMUtilities.getInstalledCode(
154                executable.getName(), address, entryPoint);
155    }
156
157    @Override
158    public String toString() {
159        return "CompileCodeTestCase{" +
160                "executable=" + executable +
161                ", bci=" + bci +
162                '}';
163    }
164
165    public void deoptimize() {
166        WB.deoptimizeMethod(executable, isOsr);
167    }
168
169    public NMethod deoptimizeAndCompile() {
170        deoptimize();
171        return compile();
172    }
173
174    // classes which are used as "input" data in test cases
175    private static interface Interface {
176        Interface interfaceMethod();
177        default Long defaultOverriddenMethod(Interface[] array) {
178            return array == null ? 0L : array.length;
179        }
180        default int defaultMethod(Object o) {
181            return o != null ? o.hashCode() : 0;
182        }
183    }
184
185    private static abstract class Dummy implements Interface {
186        protected Dummy() {
187        }
188
189        private static void staticMethod() {
190        }
191
192        Dummy instanceMethod(int i) {
193            return null;
194        }
195
196        abstract Object abstractMethod(double d);
197
198        @Override
199        public Long defaultOverriddenMethod(Interface[] array) {
200            return 0L;
201        }
202    }
203
204    public static class DummyEx extends Dummy {
205        @Override
206        public boolean equals(Object o) {
207            if (this == o) {
208                return true;
209            }
210            if (o == null || getClass() != o.getClass()) {
211                return false;
212            }
213            return true;
214        }
215
216        @Override
217        public int hashCode() {
218            return 0;
219        }
220
221        public DummyEx() {
222        }
223
224        protected Dummy instanceMethod(int i) {
225            if (i == 0) {
226                return this;
227            }
228            return null;
229        }
230
231        @Override
232        Object abstractMethod(double d) {
233            return this;
234        }
235
236        @Override
237        public Interface interfaceMethod() {
238            return null;
239        }
240    }
241
242    static {
243        Map<Class<?>, Object> map = new HashMap<>();;
244        map.put(CompileCodeTestCase.DummyEx.class,
245                new CompileCodeTestCase.DummyEx());
246        map.put(CompileCodeTestCase.Dummy.class,
247                new CompileCodeTestCase.Dummy() {
248                    @Override
249                    public CompileCodeTestCase.Interface interfaceMethod() {
250                        throw new AbstractMethodError();
251                    }
252
253                    @Override
254                    Object abstractMethod(double d) {
255                        throw new AbstractMethodError();
256                    }
257                });
258        map.put(CompileCodeTestCase.Interface.class,
259                new CompileCodeTestCase.Interface() {
260                    @Override
261                    public CompileCodeTestCase.Interface interfaceMethod() {
262                        throw new AbstractMethodError();
263                    }
264                });
265        RECEIVERS = Collections.unmodifiableMap(map);
266    }
267
268}
269