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