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