aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/runtime/CompiledFunction.java
blob: 3cc9f09d2383cb5fa8763272b27255053bfb7e7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package jdk.nashorn.internal.runtime;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;

import jdk.nashorn.internal.codegen.types.Type;

/**
 * An version of a JavaScript function, native or JavaScript.
 * Supports lazily generating a constructor version of the invocation.
 */
final class CompiledFunction implements Comparable<CompiledFunction> {

    private final MethodHandle invoker;
    private MethodHandle constructor;

    CompiledFunction(final MethodHandle invoker) {
        this(invoker, null);
    }

    CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
        this.invoker = invoker;
        this.constructor = constructor; //isConstructor
    }

    @Override
    public String toString() {
        return "<invoker=" + invoker + " ctor=" + constructor + ">";
    }

    MethodHandle getInvoker() {
        return invoker;
    }

    MethodHandle getConstructor() {
        return constructor;
    }

    void setConstructor(final MethodHandle constructor) {
        this.constructor = constructor;
    }

    boolean hasConstructor() {
        return constructor != null;
    }

    MethodType type() {
        return invoker.type();
    }

    @Override
    public int compareTo(final CompiledFunction o) {
        return weight() - o.weight();
    }

    private int weight() {
        return weight(type());
    }

    private static int weight(final MethodType type) {
        if (isVarArgsType(type)) {
            return Integer.MAX_VALUE; //if there is a varargs it should be the heavist and last fallback
        }

        int weight = Type.typeFor(type.returnType()).getWeight();
        for (final Class<?> paramType : type.parameterArray()) {
            final int pweight = Type.typeFor(paramType).getWeight();
            weight += pweight;
        }
        return weight;
    }

    private static boolean isVarArgsType(final MethodType type) {
        assert type.parameterCount() >= 1 : type;
        return type.parameterType(type.parameterCount() - 1) == Object[].class;
    }

    boolean moreGenericThan(final CompiledFunction o) {
        return weight() > o.weight();
    }

    boolean moreGenericThan(final MethodType type) {
        return weight() > weight(type);
    }

    /**
     * Check whether a given method descriptor is compatible with this invocation.
     * It is compatible if the types are narrower than the invocation type so that
     * a semantically equivalent linkage can be performed.
     *
     * @param typesc
     * @return
     */
    boolean typeCompatible(final MethodType type) {
        final Class<?>[] wantedParams   = type.parameterArray();
        final Class<?>[] existingParams = type().parameterArray();

        //if we are not examining a varargs type, the number of parameters must be the same
        if (wantedParams.length != existingParams.length && !isVarArgsType(type)) {
            return false;
        }

        //we only go as far as the shortest array. the only chance to make this work if
        //parameters lengths do not match is if our type ends with a varargs argument.
        //then every trailing parameter in the given callsite can be folded into it, making
        //us compatible (albeit slower than a direct specialization)
        final int lastParamIndex = Math.min(wantedParams.length, existingParams.length);
        for (int i = 0; i < lastParamIndex; i++) {
            final Type w = Type.typeFor(wantedParams[i]);
            final Type e = Type.typeFor(existingParams[i]);

            //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
            //we also currently don't support boolean as a javascript function callsite type.
            //it will always box.
            if (w.isBoolean()) {
                return false;
            }

            //This callsite type has a vararg here. it will swallow all remaining args.
            //for consistency, check that it's the last argument
            if (e.isArray()) {
                return true;
            }

            //Our arguments must be at least as wide as the wanted one, if not wider
            if (Type.widest(w, e) != e) {
                //e.g. this invocation takes double and callsite says "object". reject. won't fit
                //but if invocation takes a double and callsite says "int" or "long" or "double", that's fine
                return false;
            }
        }

        return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
    }



}