FunctionSignature.java revision 953:221a84ef44c0
1/*
2 * Copyright (c) 2010, 2013, 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
26package jdk.nashorn.internal.codegen;
27
28import static jdk.nashorn.internal.lookup.Lookup.MH;
29
30import java.lang.invoke.MethodType;
31import java.util.ArrayList;
32import java.util.List;
33import jdk.nashorn.internal.codegen.types.Type;
34import jdk.nashorn.internal.ir.Expression;
35import jdk.nashorn.internal.ir.FunctionNode;
36import jdk.nashorn.internal.runtime.ScriptFunction;
37import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
38
39/**
40 * Class that generates function signatures for dynamic calls
41 */
42public final class FunctionSignature {
43
44    /** parameter types that ASM can understand */
45    private final Type[] paramTypes;
46
47    /** return type that ASM can understand */
48    private final Type returnType;
49
50    /** valid Java descriptor string for function */
51    private final String descriptor;
52
53    /** {@link MethodType} for function */
54    private final MethodType methodType;
55
56    /**
57     * Constructor
58     *
59     * Create a FunctionSignature given arguments as AST Nodes
60     *
61     * @param hasSelf   does the function have a self slot?
62     * @param hasCallee does the function need a callee variable
63     * @param retType   what is the return type
64     * @param args      argument list of AST Nodes
65     */
66    public FunctionSignature(final boolean hasSelf, final boolean hasCallee, final Type retType, final List<? extends Expression> args) {
67        this(hasSelf, hasCallee, retType, FunctionSignature.typeArray(args));
68    }
69
70    /**
71     * Constructor
72     *
73     * Create a FunctionSignature given arguments as AST Nodes
74     *
75     * @param hasSelf does the function have a self slot?
76     * @param hasCallee does the function need a callee variable
77     * @param retType what is the return type
78     * @param nArgs   number of arguments
79     */
80    public FunctionSignature(final boolean hasSelf, final boolean hasCallee, final Type retType, final int nArgs) {
81        this(hasSelf, hasCallee, retType, FunctionSignature.objectArgs(nArgs));
82    }
83
84    /**
85     * Constructor
86     *
87     * Create a FunctionSignature given argument types only
88     *
89     * @param hasSelf   does the function have a self slot?
90     * @param hasCallee does the function have a callee slot?
91     * @param retType   what is the return type
92     * @param argTypes  argument list of AST Nodes
93     */
94    private FunctionSignature(final boolean hasSelf, final boolean hasCallee, final Type retType, final Type... argTypes) {
95        final boolean isVarArg;
96
97        int count = 1;
98
99        if (argTypes == null) {
100            isVarArg = true;
101        } else {
102            isVarArg = argTypes.length > LinkerCallSite.ARGLIMIT;
103            count    = isVarArg ? 1 : argTypes.length;
104        }
105
106        if (hasCallee) {
107            count++;
108        }
109        if (hasSelf) {
110            count++;
111        }
112
113        paramTypes = new Type[count];
114
115        int next = 0;
116        if (hasCallee) {
117            paramTypes[next++] = Type.typeFor(ScriptFunction.class);
118        }
119
120        if (hasSelf) {
121            paramTypes[next++] = Type.OBJECT;
122        }
123
124        if (isVarArg) {
125            paramTypes[next] = Type.OBJECT_ARRAY;
126        } else if (argTypes != null) {
127            for (int j = 0; next < count;) {
128                final Type type = argTypes[j++];
129                // TODO: for now, turn java/lang/String into java/lang/Object as we aren't as specific.
130                paramTypes[next++] = type.isObject() ? Type.OBJECT : type;
131            }
132        } else {
133            assert false : "isVarArgs cannot be false when argTypes are null";
134        }
135
136        this.returnType = retType;
137        this.descriptor = Type.getMethodDescriptor(returnType, paramTypes);
138
139        final List<Class<?>> paramTypeList = new ArrayList<>();
140        for (final Type paramType : paramTypes) {
141            paramTypeList.add(paramType.getTypeClass());
142        }
143
144        this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class<?>[paramTypes.length]));
145    }
146
147    /**
148     * Create a function signature given a function node, using as much
149     * type information for parameters and return types that is available
150     *
151     * @param functionNode the function node
152     */
153    public FunctionSignature(final FunctionNode functionNode) {
154        this(
155            true,
156            functionNode.needsCallee(),
157            functionNode.getReturnType(),
158            (functionNode.isVarArg() && !functionNode.isProgram()) ?
159                null :
160                functionNode.getParameters());
161    }
162
163    /**
164     * Internal function that converts an array of nodes to their Types
165     *
166     * @param args node arg list
167     *
168     * @return the array of types
169     */
170    private static Type[] typeArray(final List<? extends Expression> args) {
171        if (args == null) {
172            return null;
173        }
174
175        final Type[] typeArray = new Type[args.size()];
176
177        int pos = 0;
178        for (final Expression arg : args) {
179            typeArray[pos++] = arg.getType();
180        }
181
182        return typeArray;
183    }
184
185    @Override
186    public String toString() {
187        return descriptor;
188    }
189
190    /**
191     * @return the number of param types
192     */
193    public int size() {
194        return paramTypes.length;
195    }
196
197    /**
198     * Get the param types for this function signature
199     * @return cloned vector of param types
200     */
201    public Type[] getParamTypes() {
202        return paramTypes.clone();
203    }
204
205    /**
206     * Return the {@link MethodType} for this function signature
207     * @return the method type
208     */
209    public MethodType getMethodType() {
210        return methodType;
211    }
212
213    /**
214     * Return the return type for this function signature
215     * @return the return type
216     */
217    public Type getReturnType() {
218        return returnType;
219    }
220
221    private static Type[] objectArgs(final int nArgs) {
222        final Type[] array = new Type[nArgs];
223        for (int i = 0; i < nArgs; i++) {
224            array[i] = Type.OBJECT;
225        }
226        return array;
227    }
228
229}
230