blob: 3cc9f09d2383cb5fa8763272b27255053bfb7e7c [file] [log] [blame]
/*
* 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.
}
}