blob: bb1fe3f8ec34265e921dca23f80a956136c480f1 [file] [log] [blame]
/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
package net.hydromatic.linq4j.expressions;
import net.hydromatic.linq4j.function.*;
import java.lang.reflect.*;
import java.util.*;
/**
* Represents a strongly typed lambda expression as a data structure in the form
* of an expression tree. This class cannot be inherited.
*/
public final class FunctionExpression<F extends Function<?>>
extends LambdaExpression {
public final F function;
public final BlockStatement body;
public final List<ParameterExpression> parameterList;
private F dynamicFunction;
private FunctionExpression(Class<F> type, F function, BlockStatement body,
List<ParameterExpression> parameterList) {
super(ExpressionType.Lambda, type);
assert type != null;
assert function != null || body != null;
this.function = function;
this.body = body;
this.parameterList = parameterList;
}
public FunctionExpression(F function) {
this((Class) function.getClass(), function, null,
Collections.<ParameterExpression>emptyList());
}
public FunctionExpression(Class<F> type, BlockStatement body,
List<ParameterExpression> parameters) {
this(type, null, body, parameters);
}
@Override
public Expression accept(Visitor visitor) {
BlockStatement body = this.body.accept(visitor);
return visitor.visit(this, body, parameterList);
}
public Invokable compile() {
return new Invokable() {
public Object dynamicInvoke(Object... args) {
final Evaluator evaluator = new Evaluator();
for (int i = 0; i < args.length; i++) {
evaluator.push(parameterList.get(i), args[i]);
}
return evaluator.evaluate(body);
}
};
}
public F getFunction() {
if (function != null) {
return function;
}
if (dynamicFunction == null) {
final Invokable x = compile();
//noinspection unchecked
dynamicFunction = (F) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{Types.toClass(type)},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return x.dynamicInvoke(args);
}
});
}
return dynamicFunction;
}
@Override
void accept(ExpressionWriter writer, int lprec, int rprec) {
// "new Function1() {
// public Result apply(T1 p1, ...) {
// <body>
// }
// // bridge method
// public Object apply(Object p1, ...) {
// return apply((T1) p1, ...);
// }
// }
//
// if any arguments are primitive there is an extra bridge method:
//
// new Function1() {
// public double apply(double p1, int p2) {
// <body>
// }
// // box bridge method
// public Double apply(Double p1, Integer p2) {
// return apply(p1.doubleValue(), p2.intValue());
// }
// // bridge method
// public Object apply(Object p1, Object p2) {
// return apply((Double) p1, (Integer) p2);
// }
List<String> params = new ArrayList<String>();
List<String> bridgeParams = new ArrayList<String>();
List<String> bridgeArgs = new ArrayList<String>();
List<String> boxBridgeParams = new ArrayList<String>();
List<String> boxBridgeArgs = new ArrayList<String>();
for (ParameterExpression parameterExpression : parameterList) {
final Type parameterType = parameterExpression.getType();
final String parameterTypeName = Types.className(parameterType);
final String parameterBoxTypeName = Types.boxClassName(parameterType);
params.add(parameterTypeName + " " + parameterExpression.name);
bridgeParams.add("Object " + parameterExpression.name);
bridgeArgs.add("(" + parameterBoxTypeName + ") "
+ parameterExpression.name);
boxBridgeParams.add(parameterBoxTypeName + " "
+ parameterExpression.name);
boxBridgeArgs.add(parameterExpression.name
+ (Primitive.is(parameterType)
? "." + Primitive.of(parameterType).primitiveName + "Value()"
: ""));
}
Type bridgeResultType = Functions.FUNCTION_RESULT_TYPES.get(this.type);
if (bridgeResultType == null) {
bridgeResultType = body.getType();
}
Type resultType2 = bridgeResultType;
if (bridgeResultType == Object.class
&& !params.equals(bridgeParams)
&& !(body.getType() instanceof TypeVariable)) {
resultType2 = body.getType();
}
writer.append("new ")
.append(type)
.append("()")
.begin(" {\n")
.append("public ")
.append(Types.className(resultType2))
.list(" apply(", ", ", ") ", params)
.append(Blocks.toFunctionBlock(body));
// Generate an intermediate bridge method if at least one parameter is
// primitive.
if (!boxBridgeParams.equals(params)) {
writer
.append("public ")
.append(Types.boxClassName(bridgeResultType))
.list(" apply(", ", ", ") ", boxBridgeParams)
.begin("{\n")
.list("return apply(\n", ",\n", ");\n", boxBridgeArgs)
.end("}\n");
}
// Generate a bridge method. Argument types are looser (as if every
// type parameter is set to 'Object').
//
// Skip the bridge method if there are no arguments. It would have the
// same overload as the regular method.
if (!bridgeParams.equals(params)) {
writer
.append("public ")
.append(Types.boxClassName(bridgeResultType))
.list(" apply(", ", ", ") ", bridgeParams)
.begin("{\n")
.list("return apply(\n", ",\n", ");\n", bridgeArgs)
.end("}\n");
}
writer.end("}\n");
}
public interface Invokable {
Object dynamicInvoke(Object... args);
}
}
// End FunctionExpression.java