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