1 /*
   2  * Copyright (c) 2013, 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.nodes.extended;
  24 
  25 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_3;
  26 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_1;
  27 
  28 import org.graalvm.compiler.core.common.type.Stamp;
  29 import org.graalvm.compiler.core.common.type.TypeReference;
  30 import org.graalvm.compiler.debug.GraalError;
  31 import org.graalvm.compiler.graph.Node;
  32 import org.graalvm.compiler.graph.NodeClass;
  33 import org.graalvm.compiler.graph.spi.Canonicalizable;
  34 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  35 import org.graalvm.compiler.nodeinfo.NodeInfo;
  36 import org.graalvm.compiler.nodes.ConstantNode;
  37 import org.graalvm.compiler.nodes.FixedWithNextNode;
  38 import org.graalvm.compiler.nodes.ValueNode;
  39 import org.graalvm.compiler.nodes.spi.Lowerable;
  40 import org.graalvm.compiler.nodes.spi.LoweringTool;
  41 import org.graalvm.compiler.nodes.type.StampTool;
  42 
  43 import jdk.vm.ci.meta.Assumptions;
  44 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  45 import jdk.vm.ci.meta.JavaConstant;
  46 import jdk.vm.ci.meta.ResolvedJavaMethod;
  47 import jdk.vm.ci.meta.ResolvedJavaType;
  48 
  49 /**
  50  * Loads a method from the virtual method table of a given hub.
  51  */
  52 @NodeInfo(cycles = CYCLES_3, size = SIZE_1)
  53 public final class LoadMethodNode extends FixedWithNextNode implements Lowerable, Canonicalizable {
  54 
  55     public static final NodeClass<LoadMethodNode> TYPE = NodeClass.create(LoadMethodNode.class);
  56     @Input ValueNode hub;
  57     protected final ResolvedJavaMethod method;
  58     protected final ResolvedJavaType receiverType;
  59 
  60     /**
  61      * The caller or context type used to perform access checks when resolving {@link #method}.
  62      */
  63     protected final ResolvedJavaType callerType;
  64 
  65     public ValueNode getHub() {
  66         return hub;
  67     }
  68 
  69     public LoadMethodNode(@InjectedNodeParameter Stamp stamp, ResolvedJavaMethod method, ResolvedJavaType receiverType, ResolvedJavaType callerType, ValueNode hub) {
  70         super(TYPE, stamp);
  71         this.receiverType = receiverType;
  72         this.callerType = callerType;
  73         this.hub = hub;
  74         this.method = method;
  75         assert method.isConcrete() : "Cannot load abstract method from a hub";
  76         assert method.hasReceiver() : "Cannot load a static method from a hub";
  77         if (!method.isInVirtualMethodTable(receiverType)) {
  78             throw new GraalError("%s does not have a vtable entry in type %s", method, receiverType);
  79         }
  80     }
  81 
  82     @Override
  83     public void lower(LoweringTool tool) {
  84         tool.getLowerer().lower(this, tool);
  85     }
  86 
  87     @Override
  88     public Node canonical(CanonicalizerTool tool) {
  89         if (hub instanceof LoadHubNode) {
  90             ValueNode object = ((LoadHubNode) hub).getValue();
  91             TypeReference type = StampTool.typeReferenceOrNull(object);
  92             if (type != null) {
  93                 if (type.isExact()) {
  94                     return resolveExactMethod(tool, type.getType());
  95                 }
  96                 Assumptions assumptions = graph().getAssumptions();
  97                 AssumptionResult<ResolvedJavaMethod> resolvedMethod = type.getType().findUniqueConcreteMethod(method);
  98                 if (resolvedMethod != null && resolvedMethod.canRecordTo(assumptions) && !type.getType().isInterface() && method.getDeclaringClass().isAssignableFrom(type.getType())) {
  99                     resolvedMethod.recordTo(assumptions);
 100                     return ConstantNode.forConstant(stamp(), resolvedMethod.getResult().getEncoding(), tool.getMetaAccess());
 101                 }
 102             }
 103         }
 104         if (hub.isConstant()) {
 105             return resolveExactMethod(tool, tool.getConstantReflection().asJavaType(hub.asConstant()));
 106         }
 107 
 108         return this;
 109     }
 110 
 111     /**
 112      * Find the method which would be loaded.
 113      *
 114      * @param tool
 115      * @param type the exact type of object being loaded from
 116      * @return the method which would be invoked for {@code type} or null if it doesn't implement
 117      *         the method
 118      */
 119     private Node resolveExactMethod(CanonicalizerTool tool, ResolvedJavaType type) {
 120         ResolvedJavaMethod newMethod = type.resolveConcreteMethod(method, callerType);
 121         if (newMethod == null) {
 122             /*
 123              * This really represent a misuse of LoadMethod since we're loading from a class which
 124              * isn't known to implement the original method but for now at least fold it away.
 125              */
 126             return ConstantNode.forConstant(stamp(), JavaConstant.NULL_POINTER, null);
 127         } else {
 128             return ConstantNode.forConstant(stamp(), newMethod.getEncoding(), tool.getMetaAccess());
 129         }
 130     }
 131 
 132     public ResolvedJavaMethod getMethod() {
 133         return method;
 134     }
 135 
 136     public ResolvedJavaType getReceiverType() {
 137         return receiverType;
 138     }
 139 
 140     public ResolvedJavaType getCallerType() {
 141         return callerType;
 142     }
 143 }