/*
* LambdaExpression.java
*
* Copyright (c) 2012 Mike Strobel
*
* This source code is based on the Dynamic Language Runtime from Microsoft,
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.expressions;
import com.strobel.compilerservices.CallerResolver;
import com.strobel.compilerservices.DebugInfoGenerator;
import com.strobel.core.StringUtilities;
import com.strobel.core.VerifyArgument;
import com.strobel.reflection.MethodInfo;
import com.strobel.reflection.Type;
import com.strobel.reflection.emit.MethodBuilder;
import com.strobel.reflection.emit.TypeBuilder;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Modifier;
/**
* @author Mike Strobel
*/
public final class LambdaExpression<T> extends Expression {
private final String _name;
private final Expression _body;
private final ParameterExpressionList _parameters;
private final Type<T> _interfaceType;
private final boolean _tailCall;
private final Type _returnType;
private Class<?> _creationContext;
LambdaExpression(
final Type<T> interfaceType,
final String name,
final Expression body,
final boolean tailCall,
final ParameterExpressionList parameters) {
if (interfaceType != null) {
_interfaceType = interfaceType;
}
else {
_interfaceType = resolveDelegateType(body, parameters);
}
_name = name;
_body = body;
_tailCall = tailCall;
_parameters = parameters;
_returnType = _interfaceType.getMethods().get(0).getReturnType();
_creationContext = CallerResolver.getCallerClass(2);
}
@SuppressWarnings("unchecked")
private static <T> Type<T> resolveDelegateType(final Expression body, final ParameterExpressionList parameters) {
return (Type<T>) CustomDelegateTypeCache.get(
body.getType(),
parameters.getParameterTypes()
);
}
@Override
public final Type<T> getType() {
return _interfaceType;
}
@Override
public ExpressionType getNodeType() {
return ExpressionType.Lambda;
}
public final String getName() {
return _name;
}
public final Expression getBody() {
return _body;
}
public final ParameterExpressionList getParameters() {
return _parameters;
}
public final Type getReturnType() {
return _returnType;
}
public final boolean isTailCall() {
return _tailCall;
}
public final LambdaExpression<T> update(final Expression body, final ParameterExpressionList parameters) {
if (body == _body && parameters == _parameters) {
return this;
}
final LambdaExpression<T> updatedLambda = Expression.lambda(
_interfaceType,
getName(),
body,
isTailCall(),
parameters
);
updatedLambda._creationContext = _creationContext;
return updatedLambda;
}
@Override
protected Expression accept(final ExpressionVisitor visitor) {
return visitor.visitLambda(this);
}
@SuppressWarnings("unchecked")
final LambdaExpression<T> accept(final StackSpiller spiller) {
return spiller.rewrite(this);
}
final Class<?> getCreationContext() {
return _creationContext;
}
public final T compile() {
return compileDelegate().getInstance();
}
public final Delegate<T> compileDelegate() {
return LambdaCompiler.compile(this, DebugInfoGenerator.empty());
}
public final MethodHandle compileHandle() {
return LambdaCompiler.compile(this, DebugInfoGenerator.empty()).getMethodHandle();
}
public final void compileToMethod(final MethodBuilder methodBuilder) {
LambdaCompiler.compile(this, methodBuilder, DebugInfoGenerator.empty());
}
public final MethodInfo compileToMethod(final TypeBuilder<?> typeBuilder) {
final String name = getName();
return compileToMethod(
typeBuilder,
StringUtilities.isNullOrWhitespace(name)
? LambdaCompiler.getUniqueMethodName()
: name,
Modifier.PUBLIC
);
}
public final MethodInfo compileToMethod(
final TypeBuilder<?> typeBuilder,
final String name) {
return compileToMethod(typeBuilder, name, Modifier.PUBLIC);
}
public final MethodInfo compileToMethod(
final TypeBuilder<?> typeBuilder,
final String name,
final int modifiers) {
VerifyArgument.notNull(typeBuilder, "typeBuilder");
VerifyArgument.notNullOrWhitespace(name, "name");
final MethodInfo invokeMethod = Expression.getInvokeMethod(this);
final MethodBuilder methodBuilder = typeBuilder.defineMethod(
name,
modifiers,
invokeMethod.getReturnType(),
invokeMethod.getParameters().getParameterTypes()
);
LambdaCompiler.compile(this, methodBuilder, DebugInfoGenerator.empty());
return methodBuilder;
}
}