1 /*
   2  * Copyright (c) 2010, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.codegen;
  27 
  28 import java.lang.invoke.MethodType;
  29 import java.util.Arrays;
  30 import java.util.NoSuchElementException;
  31 import jdk.nashorn.internal.codegen.types.Type;
  32 import jdk.nashorn.internal.ir.FunctionNode;
  33 import jdk.nashorn.internal.runtime.ScriptFunction;
  34 
  35 /**
  36  * A tuple containing function id, parameter types, return type and needsCallee flag.
  37  */
  38 public final class TypeMap {
  39     private final int functionNodeId;
  40     private final Type[] paramTypes;
  41     private final Type returnType;
  42     private final boolean needsCallee;
  43 
  44     /**
  45      * Constructor
  46      * @param functionNodeId function node id
  47      * @param type           method type found at runtime corresponding to parameter guess
  48      * @param needsCallee    does the function using this type map need a callee
  49      */
  50     public TypeMap(final int functionNodeId, final MethodType type, final boolean needsCallee) {
  51         final Type[] types = new Type[type.parameterCount()];
  52         int pos = 0;
  53         for (final Class<?> p : type.parameterArray()) {
  54             types[pos++] = Type.typeFor(p);
  55         }
  56 
  57         this.functionNodeId = functionNodeId;
  58         this.paramTypes = types;
  59         this.returnType = Type.typeFor(type.returnType());
  60         this.needsCallee = needsCallee;
  61     }
  62 
  63     /**
  64      * Returns the array of parameter types for a particular function node
  65      * @param functionNodeId the ID of the function node
  66      * @return an array of parameter types
  67      * @throws NoSuchElementException if the type map has no mapping for the requested function
  68      */
  69     public Type[] getParameterTypes(final int functionNodeId) {
  70         assert this.functionNodeId == functionNodeId;
  71         return paramTypes.clone();
  72     }
  73 
  74     MethodType getCallSiteType(final FunctionNode functionNode) {
  75         assert this.functionNodeId == functionNode.getId();
  76         final Type[] types = paramTypes;
  77         MethodType mt = MethodType.methodType(returnType.getTypeClass());
  78         if (needsCallee) {
  79             mt = mt.appendParameterTypes(ScriptFunction.class);
  80         }
  81 
  82         mt = mt.appendParameterTypes(Object.class); //this
  83 
  84         for (final Type type : types) {
  85             if (type == null) {
  86                 return null; // not all parameter information is supplied
  87             }
  88             mt = mt.appendParameterTypes(type.getTypeClass());
  89         }
  90 
  91         return mt;
  92     }
  93 
  94     /**
  95      * Does the function using this TypeMap need a callee argument. This is used
  96      * to compute correct param index offsets in {@link jdk.nashorn.internal.codegen.ApplySpecialization}
  97      * @return true if a callee is needed, false otherwise
  98      */
  99     public boolean needsCallee() {
 100         return needsCallee;
 101     }
 102 
 103     /**
 104      * Get the parameter type for this parameter position, or
 105      * null if now known
 106      * @param functionNode functionNode
 107      * @param pos position
 108      * @return parameter type for this callsite if known
 109      */
 110     Type get(final FunctionNode functionNode, final int pos) {
 111         assert this.functionNodeId == functionNode.getId();
 112         final Type[] types = paramTypes;
 113         assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this;
 114         if (types != null && pos < types.length) {
 115             return types[pos];
 116         }
 117         return null;
 118     }
 119 
 120     /**
 121      * Get the return type required for the call site we're compiling for. This only determines
 122      * whether object return type is required or not.
 123      * @return Type.OBJECT for call sites with object return types, Type.UNKNOWN for everything else
 124      */
 125     Type getReturnType() {
 126         return returnType.isObject() ? Type.OBJECT : Type.UNKNOWN;
 127     }
 128 
 129     @Override
 130     public String toString() {
 131         return toString("");
 132     }
 133 
 134     String toString(final String prefix) {
 135         final StringBuilder sb = new StringBuilder();
 136 
 137         final int id = functionNodeId;
 138         sb.append(prefix).append('\t');
 139         sb.append("function ").append(id).append('\n');
 140         sb.append(prefix).append("\t\tparamTypes=");
 141         sb.append(Arrays.toString(paramTypes));
 142         sb.append('\n');
 143         sb.append(prefix).append("\t\treturnType=");
 144         final Type ret = returnType;
 145         sb.append(ret == null ? "N/A" : ret);
 146         sb.append('\n');
 147 
 148         return sb.toString();
 149     }
 150 }