1 /*
   2  * Copyright (c) 2014, 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 org.graalvm.compiler.core.test;
  26 
  27 import org.graalvm.compiler.debug.DebugContext;
  28 import org.graalvm.compiler.nodes.ReturnNode;
  29 import org.graalvm.compiler.nodes.StructuredGraph;
  30 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  31 import org.junit.Ignore;
  32 import org.junit.Test;
  33 
  34 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  35 import jdk.vm.ci.meta.ResolvedJavaMethod;
  36 import jdk.vm.ci.meta.ResolvedJavaType;
  37 
  38 /**
  39  * This test illustrates problems and limitations with class hierarchy analysis when default methods
  40  * are involved.
  41  */
  42 public class FindUniqueDefaultMethodTest extends GraalCompilerTest {
  43 
  44     interface Interface1 {
  45         default int v1() {
  46             return 1;
  47         }
  48     }
  49 
  50     static class Implementor1 implements Interface1 {
  51         int callV1() {
  52             return v1();
  53         }
  54     }
  55 
  56     static class Subclass1 extends Implementor1 {
  57 
  58     }
  59 
  60     /**
  61      * HotSpot has an internal mismatch with CHA and default methods. The initial query says that
  62      * it's a unique method but the verification code that ensures that a dependence of this kind
  63      * would pass will fail an assert in debug mode.
  64      */
  65     @Test
  66     public void testFindUnique() {
  67         ResolvedJavaType cType = getMetaAccess().lookupJavaType(Implementor1.class);
  68         cType.initialize();
  69         ResolvedJavaMethod v1Method = getMetaAccess().lookupJavaMethod(this.getMethod(Interface1.class, "v1"));
  70         AssumptionResult<ResolvedJavaMethod> method = cType.findUniqueConcreteMethod(v1Method);
  71         assertDeepEquals(null, method);
  72     }
  73 
  74     interface Interface2 {
  75         default int v1() {
  76             return 1;
  77         }
  78     }
  79 
  80     static class Base2 {
  81         public int v2() {
  82             return 1;
  83         }
  84     }
  85 
  86     static class Implementor2 extends Base2 implements Interface2 {
  87         int callV1() {
  88             return v1();
  89         }
  90 
  91         int callV2() {
  92             return v2();
  93         }
  94     }
  95 
  96     static class Subclass2 extends Implementor2 {
  97 
  98     }
  99 
 100     /**
 101      * This test illustrates a common pattern where a method at the root of a hierarchy is the only
 102      * implementation and can be statically inlined.
 103      */
 104     @SuppressWarnings("unused")
 105     @Test
 106     public void testInherited() {
 107         Subclass2 s = new Subclass2();
 108         testConstantReturn("runInherited", 1);
 109     }
 110 
 111     /**
 112      * Test same pattern as above but using default methods instead. HotSpot doesn't allow this
 113      * version to be optimized.
 114      */
 115     @SuppressWarnings("unused")
 116     @Test
 117     @Ignore("HotSpot CHA doesn't treat default methods like regular methods")
 118     public void testDefault() {
 119         Subclass2 s = new Subclass2();
 120         testConstantReturn("runDefault", 1);
 121     }
 122 
 123     public int runDefault(Implementor2 i) {
 124         return i.callV1();
 125     }
 126 
 127     public int runInherited(Implementor2 i) {
 128         return i.callV2();
 129     }
 130 
 131     private void testConstantReturn(String name, Object value) {
 132         StructuredGraph result = buildGraph(name);
 133         ReturnNode ret = result.getNodes(ReturnNode.TYPE).first();
 134         assertDeepEquals(1, result.getNodes(ReturnNode.TYPE).count());
 135 
 136         assertDeepEquals(true, ret.result().isConstant());
 137         assertDeepEquals(value, ret.result().asJavaConstant().asBoxedPrimitive());
 138     }
 139 
 140     @SuppressWarnings("try")
 141     protected StructuredGraph buildGraph(final String snippet) {
 142         DebugContext debug = getDebugContext();
 143         try (DebugContext.Scope s = debug.scope("InstanceOfTest", getMetaAccess().lookupJavaMethod(getMethod(snippet)))) {
 144             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
 145             compile(graph.method(), graph);
 146             debug.dump(DebugContext.BASIC_LEVEL, graph, snippet);
 147             return graph;
 148         } catch (Throwable e) {
 149             throw debug.handle(e);
 150         }
 151     }
 152 }