1 /*
   2  * Copyright (c) 2014, 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 package org.graalvm.compiler.replacements.test;
  24 
  25 import java.util.function.Function;
  26 
  27 import org.junit.Test;
  28 
  29 import org.graalvm.compiler.api.replacements.ClassSubstitution;
  30 import org.graalvm.compiler.api.replacements.MethodSubstitution;
  31 import org.graalvm.compiler.bytecode.BytecodeProvider;
  32 import org.graalvm.compiler.core.test.GraalCompilerTest;
  33 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
  34 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
  35 import org.graalvm.compiler.nodes.PiNode;
  36 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
  37 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
  38 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
  39 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
  40 
  41 import jdk.vm.ci.meta.ResolvedJavaMethod;
  42 
  43 /**
  44  * Tests for expected behavior when parsing snippets and intrinsics.
  45  */
  46 public class ReplacementsParseTest extends GraalCompilerTest {
  47 
  48     @Override
  49     protected Plugins getDefaultGraphBuilderPlugins() {
  50         Plugins ret = super.getDefaultGraphBuilderPlugins();
  51         // manually register generated factory, jvmci service providers don't work from unit tests
  52         new PluginFactory_ReplacementsParseTest().registerPlugins(ret.getInvocationPlugins(), null);
  53         return ret;
  54     }
  55 
  56     private static final Object THROW_EXCEPTION_MARKER = new Object() {
  57         @Override
  58         public String toString() {
  59             return "THROW_EXCEPTION_MARKER";
  60         }
  61     };
  62 
  63     static class TestMethods {
  64         static double next(double v) {
  65             return Math.nextAfter(v, 1.0);
  66         }
  67 
  68         static double next2(double v) {
  69             return Math.nextAfter(v, 1.0);
  70         }
  71 
  72         static double nextAfter(double x, double d) {
  73             return Math.nextAfter(x, d);
  74         }
  75 
  76         static String stringize(Object obj) {
  77             String res = String.valueOf(obj);
  78             if (res.equals(THROW_EXCEPTION_MARKER.toString())) {
  79                 // Tests exception throwing from partial intrinsification
  80                 throw new RuntimeException("ex: " + obj);
  81             }
  82             return res;
  83         }
  84 
  85         static String identity(String s) {
  86             return s;
  87         }
  88     }
  89 
  90     @ClassSubstitution(TestMethods.class)
  91     static class TestMethodsSubstitutions {
  92 
  93         @MethodSubstitution(isStatic = true)
  94         static double nextAfter(double x, double d) {
  95             double xx = (x == -0.0 ? 0.0 : x);
  96             return Math.nextAfter(xx, d);
  97         }
  98 
  99         /**
 100          * Tests partial intrinsification.
 101          */
 102         @MethodSubstitution
 103         static String stringize(Object obj) {
 104             if (obj != null && obj.getClass() == String.class) {
 105                 return asNonNullString(obj);
 106             } else {
 107                 // A recursive call denotes exiting/deoptimizing
 108                 // out of the partial intrinsification to the
 109                 // slow/uncommon case.
 110                 return stringize(obj);
 111             }
 112         }
 113 
 114         public static String asNonNullString(Object object) {
 115             return asNonNullStringIntrinsic(object, String.class, true, true);
 116         }
 117 
 118         @NodeIntrinsic(PiNode.class)
 119         private static native String asNonNullStringIntrinsic(Object object, @ConstantNodeParameter Class<?> toType, @ConstantNodeParameter boolean exactType, @ConstantNodeParameter boolean nonNull);
 120 
 121         /**
 122          * Tests that non-capturing lambdas are folded away.
 123          */
 124         @MethodSubstitution
 125         static String identity(String value) {
 126             return apply(s -> s, value);
 127         }
 128 
 129         private static String apply(Function<String, String> f, String value) {
 130             return f.apply(value);
 131         }
 132     }
 133 
 134     @Override
 135     protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) {
 136         InvocationPlugins invocationPlugins = conf.getPlugins().getInvocationPlugins();
 137         BytecodeProvider replacementBytecodeProvider = getReplacements().getReplacementBytecodeProvider();
 138         Registration r = new Registration(invocationPlugins, TestMethods.class, replacementBytecodeProvider);
 139         r.registerMethodSubstitution(TestMethodsSubstitutions.class, "nextAfter", double.class, double.class);
 140         r.registerMethodSubstitution(TestMethodsSubstitutions.class, "stringize", Object.class);
 141         if (replacementBytecodeProvider.supportsInvokedynamic()) {
 142             r.registerMethodSubstitution(TestMethodsSubstitutions.class, "identity", String.class);
 143         }
 144         return super.editGraphBuilderConfiguration(conf);
 145     }
 146 
 147     /**
 148      * Ensure that calling the original method from the substitution binds correctly.
 149      */
 150     @Test
 151     public void test1() {
 152         test("test1Snippet", 1.0);
 153     }
 154 
 155     public double test1Snippet(double d) {
 156         return TestMethods.next(d);
 157     }
 158 
 159     /**
 160      * Ensure that calling the substitution method binds to the original method properly.
 161      */
 162     @Test
 163     public void test2() {
 164         test("test2Snippet", 1.0);
 165     }
 166 
 167     public double test2Snippet(double d) {
 168         return TestMethods.next2(d);
 169     }
 170 
 171     /**
 172      * Ensure that substitution methods with assertions in them don't complain when the exception
 173      * constructor is deleted.
 174      */
 175 
 176     @Test
 177     public void testNextAfter() {
 178         double[] inArray = new double[1024];
 179         double[] outArray = new double[1024];
 180         for (int i = 0; i < inArray.length; i++) {
 181             inArray[i] = -0.0;
 182         }
 183         test("doNextAfter", inArray, outArray);
 184     }
 185 
 186     public void doNextAfter(double[] outArray, double[] inArray) {
 187         for (int i = 0; i < inArray.length; i++) {
 188             double direction = (i & 1) == 0 ? Double.POSITIVE_INFINITY : -Double.NEGATIVE_INFINITY;
 189             outArray[i] = TestMethods.nextAfter(inArray[i], direction);
 190         }
 191     }
 192 
 193     @Test
 194     public void testCallStringize() {
 195         test("callStringize", "a string");
 196         test("callStringize", THROW_EXCEPTION_MARKER);
 197         test("callStringize", Boolean.TRUE);
 198     }
 199 
 200     public static Object callStringize(Object obj) {
 201         return TestMethods.stringize(obj);
 202     }
 203 
 204     @Test
 205     public void testRootCompileStringize() {
 206         ResolvedJavaMethod method = getResolvedJavaMethod(TestMethods.class, "stringize");
 207         test(method, null, "a string");
 208         test(method, null, Boolean.TRUE);
 209         test(method, null, THROW_EXCEPTION_MARKER);
 210     }
 211 
 212     @Test
 213     public void testLambda() {
 214         test("callLambda", (String) null);
 215         test("callLambda", "a string");
 216     }
 217 
 218     public static String callLambda(String value) {
 219         return TestMethods.identity(value);
 220     }
 221 }