/*
* Expression.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.collections.ImmutableList;
import com.strobel.core.ArrayUtilities;
import com.strobel.core.ReadOnlyList;
import com.strobel.core.VerifyArgument;
import com.strobel.reflection.BindingFlags;
import com.strobel.reflection.CallingConvention;
import com.strobel.reflection.ConstructorInfo;
import com.strobel.reflection.DynamicMethod;
import com.strobel.reflection.FieldInfo;
import com.strobel.reflection.MemberInfo;
import com.strobel.reflection.MemberList;
import com.strobel.reflection.MemberType;
import com.strobel.reflection.MethodBase;
import com.strobel.reflection.MethodInfo;
import com.strobel.reflection.MethodList;
import com.strobel.reflection.ParameterInfo;
import com.strobel.reflection.ParameterList;
import com.strobel.reflection.PrimitiveTypes;
import com.strobel.reflection.Type;
import com.strobel.reflection.TypeList;
import com.strobel.reflection.Types;
import com.strobel.reflection.emit.ConstructorBuilder;
import com.strobel.reflection.emit.MethodBuilder;
import com.strobel.reflection.emit.SwitchOptions;
import com.strobel.util.ContractUtils;
import com.strobel.util.TypeUtils;
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.lang.String.format;
/**
* The base type for all nodes in Expression Trees.
* @author Mike Strobel
*/
@SuppressWarnings( { "unchecked", "UnusedDeclaration" })
public abstract class Expression {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INSTANCE MEMBERS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected Expression() {}
/**
* Returns the node type of this {@link Expression}.
* @return the {@link ExpressionType} that represents this expression.
*/
public ExpressionType getNodeType() {
throw Error.extensionMustOverride("Expression.getNodeType()");
}
/**
* Gets the static type of the expression that this {@link Expression} represents.
* @return the {@link Type} that represents the static type of the expression.
*/
public Type<?> getType() {
throw Error.extensionMustOverride("Expression.getType()");
}
/**
* Indicates that the node can be reduced to a simpler node. If this returns {@code true},
* {@code reduce()} can be called to produce the reduced form.
* @return {@code true} if the node can be reduced; otherwise, {@code false}.
*/
public boolean canReduce() {
return false;
}
/**
* Reduces this node to a simpler expression. If {@code canReduce()} returns {@code true},
* this should return a valid expression. This method is allowed to return another node
* which itself must be reduced.
* @return the reduced expression.
*/
public Expression reduce() {
if (canReduce()) {
throw Error.reducibleMustOverride("Expression.reduce()");
}
return this;
}
/**
* Reduces this node to a simpler expression. If {@code canReduce()} returns {@code true},
* this should return a valid expression. This method is allowed to return another node
* which itself must be reduced. Unlike {@code reduce()}, this method checks that the
* reduced node satisfies certain invariants.
* @return the reduced expression.
*/
public final Expression reduceAndCheck() {
if (!canReduce()) {
throw Error.mustBeReducible();
}
final Expression newNode = reduce();
if (newNode == null || newNode == this) {
throw Error.mustReduceToDifferent();
}
if (!getType().isAssignableFrom(newNode.getType())) {
throw Error.reducedNotCompatible();
}
return newNode;
}
/**
* Reduces the expression to a known node type (i.e. not an Extension node or simply
* returns the expression if it is already a known type.
* @return the reduced expression.
*/
public final Expression reduceExtensions() {
Expression node = this;
while (node.getNodeType() == ExpressionType.Extension) {
node = node.reduceAndCheck();
}
return node;
}
/**
* Dispatches to the specific visit method for this node type. For example,
* {@link BinaryExpression} will call into {@code ExpressionVisitor.visitBinary()}.
* @param visitor the visitor to visit this node.
* @return the result of visiting this node.
*/
protected Expression accept(final ExpressionVisitor visitor) {
return visitor.visitExtension(this);
}
/**
* Reduces the node and then calls the visitor on the reduced expression. Throws an
* exception if the node isn't reducible.
* @param visitor an expression visitor
* @return the expression being visited, or an expression which should replace it in the tree.
*/
protected Expression visitChildren(final ExpressionVisitor visitor) {
if (!canReduce()) {
throw Error.mustBeReducible();
}
return visitor.visit(reduceAndCheck());
}
public final String getDebugView() {
final StringBuilder sb = new StringBuilder();
DebugViewWriter.writeTo(this, sb);
return sb.toString();
}
@Override
public String toString() {
return ExpressionStringBuilder.expressionToString(this);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FACTORY METHODS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static Expression empty() {
return new DefaultValueExpression(PrimitiveTypes.Void);
}
public static Expression self(final Type<?> type) {
return new SelfExpression(type);
}
public static Expression base(final Type<?> type) {
return new SuperExpression(type);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// LABELS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static LabelTarget label() {
return label(PrimitiveTypes.Void, null);
}
public static LabelTarget label(final String name) {
return label(PrimitiveTypes.Void, name);
}
public static LabelTarget label(final Type type) {
return label(type, null);
}
public static LabelTarget label(final Type type, final String name) {
VerifyArgument.notNull(type, "type");
return new LabelTarget(type, name);
}
public static LabelExpression label(final LabelTarget target) {
VerifyArgument.notNull(target, "target");
if (target.getType() != PrimitiveTypes.Void) {
return label(target, defaultValue(target.getType()));
}
return label(target, null);
}
public static LabelExpression label(final LabelTarget target, final Expression defaultValue) {
validateGoto(target, defaultValue, "label", "defaultValue");
return new LabelExpression(target, defaultValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GOTO, BREAK, CONTINUE, AND RETURN EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static GotoExpression makeBreak(final LabelTarget target) {
return makeGoto(GotoExpressionKind.Break, target, null, PrimitiveTypes.Void);
}
public static GotoExpression makeBreak(final LabelTarget target, final Expression value) {
return makeGoto(GotoExpressionKind.Break, target, value, PrimitiveTypes.Void);
}
public static GotoExpression makeBreak(final LabelTarget target, final Type type) {
return makeGoto(GotoExpressionKind.Break, target, null, type);
}
public static GotoExpression makeBreak(final LabelTarget target, final Expression value, final Type type) {
return makeGoto(GotoExpressionKind.Break, target, value, type);
}
public static GotoExpression makeContinue(final LabelTarget target) {
return makeGoto(GotoExpressionKind.Continue, target, null, PrimitiveTypes.Void);
}
public static GotoExpression makeContinue(final LabelTarget target, final Type type) {
return makeGoto(GotoExpressionKind.Continue, target, null, type);
}
public static GotoExpression makeReturn(final LabelTarget target) {
return makeGoto(GotoExpressionKind.Return, target, null, PrimitiveTypes.Void);
}
public static GotoExpression makeReturn(final LabelTarget target, final Type type) {
return makeGoto(GotoExpressionKind.Return, target, null, type);
}
public static GotoExpression makeReturn(final LabelTarget target, final Expression value) {
return makeGoto(GotoExpressionKind.Return, target, value, PrimitiveTypes.Void);
}
public static GotoExpression makeReturn(final LabelTarget target, final Expression value, final Type type) {
return makeGoto(GotoExpressionKind.Return, target, value, type);
}
public static GotoExpression makeGoto(final LabelTarget target) {
return makeGoto(GotoExpressionKind.Goto, target, null, PrimitiveTypes.Void);
}
public static GotoExpression makeGoto(final LabelTarget target, final Type type) {
return makeGoto(GotoExpressionKind.Goto, target, null, type);
}
public static GotoExpression makeGoto(final LabelTarget target, final Expression value) {
return makeGoto(GotoExpressionKind.Goto, target, value, PrimitiveTypes.Void);
}
public static GotoExpression makeGoto(final LabelTarget target, final Expression value, final Type type) {
return makeGoto(GotoExpressionKind.Goto, target, value, type);
}
public static GotoExpression makeGoto(final GotoExpressionKind kind, final LabelTarget target, final Expression value, final Type type) {
validateGoto(target, value, "target", "value");
return new GotoExpression(kind, target, value, type);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// LOOP EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static LoopExpression loop(final Expression body) {
return loop(body, null, null);
}
public static LoopExpression loop(final Expression body, final LabelTarget breakTarget) {
return loop(body, breakTarget, null);
}
public static LoopExpression loop(final Expression body, final LabelTarget breakTarget, final LabelTarget continueLabel) {
verifyCanRead(body, "body");
if (continueLabel != null && continueLabel.getType() != PrimitiveTypes.Void) {
throw Error.continueTargetMustBeVoid();
}
return new LoopExpression(body, breakTarget, continueLabel);
}
public static ForEachExpression forEach(
final ParameterExpression variable,
final Expression sequence,
final Expression body) {
return forEach(variable, sequence, body, null, null);
}
public static ForEachExpression forEach(
final ParameterExpression variable,
final Expression sequence,
final Expression body,
final LabelTarget breakTarget) {
return forEach(variable, sequence, body, breakTarget, null);
}
public static ForEachExpression forEach(
final ParameterExpression variable,
final Expression sequence,
final Expression body,
final LabelTarget breakTarget,
final LabelTarget continueTarget) {
VerifyArgument.notNull(variable, "variable");
verifyCanRead(sequence, "sequence");
VerifyArgument.notNull(body, "body");
if (continueTarget != null && continueTarget.getType() != PrimitiveTypes.Void) {
throw Error.continueTargetMustBeVoid();
}
return new ForEachExpression(variable, sequence, body, breakTarget, continueTarget);
}
public static ForExpression makeFor(
final ParameterExpression variable,
final Expression initializer,
final Expression test,
final Expression step,
final Expression body) {
return makeFor(variable, initializer, test, step, body, null, null);
}
public static ForExpression makeFor(
final ParameterExpression variable,
final Expression initializer,
final Expression test,
final Expression step,
final Expression body,
final LabelTarget breakTarget) {
return makeFor(variable, initializer, test, step, body, breakTarget, null);
}
public static ForExpression makeFor(
final ParameterExpression variable,
final Expression initializer,
final Expression test,
final Expression step,
final Expression body,
final LabelTarget breakTarget,
final LabelTarget continueTarget) {
VerifyArgument.notNull(variable, "variable");
verifyCanRead(initializer, "initializer");
verifyCanRead(test, "test");
VerifyArgument.notNull(step, "step");
verifyCanRead(body, "body");
if (!variable.getType().isAssignableFrom(initializer.getType()))
throw Error.initializerMustBeAssignableToVariable();
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(test.getType(), PrimitiveTypes.Boolean))
throw Error.testMustBeBooleanExpression();
if (continueTarget != null && continueTarget.getType() != PrimitiveTypes.Void) {
throw Error.continueTargetMustBeVoid();
}
return new ForExpression(variable, initializer, test, step, body, breakTarget, continueTarget);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NEW EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static NewExpression makeNew(final ConstructorInfo constructor) {
return makeNew(constructor, ExpressionList.empty());
}
public static NewExpression makeNew(final ConstructorInfo constructor, final Expression... parameters) {
return makeNew(constructor, arrayToList(parameters));
}
public static NewExpression makeNew(final ConstructorInfo constructor, final ExpressionList<? extends Expression> parameters) {
VerifyArgument.notNull(constructor, "constructor");
VerifyArgument.notNull(constructor.getDeclaringType(), "constructor.getDeclaringType()");
final ExpressionList<? extends Expression> arguments = validateArgumentTypes(
constructor,
ExpressionType.New,
parameters
);
return new NewExpression(constructor, arguments);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NEW ARRAY EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static NewArrayExpression newArrayInit(final Type elementType, final Expression... initializers) {
return newArrayInit(elementType, arrayToList(initializers));
}
public static NewArrayExpression newArrayInit(final Type elementType, final ExpressionList<? extends Expression> initializers) {
VerifyArgument.notNull(elementType, "elementType");
VerifyArgument.noNullElements(initializers, "initializers");
if (elementType.isEquivalentTo(PrimitiveTypes.Void)) {
throw Error.argumentCannotBeOfTypeVoid();
}
for (int i = 0, n = initializers.size(); i < n; i++) {
final Expression item = initializers.get(i);
verifyCanRead(item, "initializers");
if (!TypeUtils.areReferenceAssignable(elementType, item.getType())) {
throw Error.expressionTypeCannotInitializeArrayType(item.getType(), elementType);
}
}
return NewArrayInitExpression.make(
ExpressionType.NewArrayInit,
elementType.makeArrayType(),
initializers
);
}
public static NewArrayExpression newArrayBounds(final Type elementType, final Expression dimension) {
VerifyArgument.notNull(elementType, "elementType");
VerifyArgument.notNull(dimension, "dimension");
verifyCanRead(dimension, "dimension");
if (elementType.isEquivalentTo(PrimitiveTypes.Void)) {
throw Error.argumentCannotBeOfTypeVoid();
}
if (!TypeUtils.isIntegral(dimension.getType())) {
throw Error.argumentMustBeIntegral();
}
final Expression convertedDimension;
if (TypeUtils.getUnderlyingPrimitiveOrSelf(elementType) != PrimitiveTypes.Integer) {
convertedDimension = convert(dimension, PrimitiveTypes.Integer);
}
else {
convertedDimension = dimension;
}
return NewArrayInitExpression.make(
ExpressionType.NewArrayBounds,
elementType.makeArrayType(),
new ExpressionList<>(convertedDimension)
);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CONCAT EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static ConcatExpression concat(final Expression first, final Expression second) {
verifyCanRead(first, "first");
verifyCanRead(second, "second");
return concat(new ExpressionList<>(first, second));
}
public static ConcatExpression concat(final Expression first, final Expression second, final Expression... operands) {
VerifyArgument.notEmpty(operands, "operands");
return concat(arrayToList(ArrayUtilities.prepend(operands, first, second)));
}
public static ConcatExpression concat(final ExpressionList<? extends Expression> operands) {
VerifyArgument.noNullElements(operands, "operands");
if (operands.size() < 2) {
throw Error.concatRequiresAtLeastTwoOperands();
}
for (int i = 0, n = operands.size(); i < n; i++) {
final Expression item = operands.get(i);
verifyCanRead(item, "operands");
}
return new ConcatExpression(operands);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TRY/CATCH EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static CatchBlock makeCatch(final Type type, final Expression body) {
return makeCatch(type, null, body, null);
}
public static CatchBlock makeCatch(final ParameterExpression variable, final Expression body) {
VerifyArgument.notNull(variable, "variable");
return makeCatch(variable.getType(), variable, body, null);
}
public static CatchBlock makeCatch(final Type type, final Expression body, final Expression filter) {
return makeCatch(type, null, body, filter);
}
public static CatchBlock makeCatch(final ParameterExpression variable, final Expression body, final Expression filter) {
VerifyArgument.notNull(variable, "variable");
return makeCatch(variable.getType(), variable, body, filter);
}
public static CatchBlock makeCatch(
final Type type,
final ParameterExpression variable,
final Expression body,
final Expression filter) {
VerifyArgument.notNull(type, "type");
if (variable != null && !TypeUtils.areEquivalent(variable.getType(), type)) {
throw Error.catchVariableMustBeCompatibleWithCatchType(type, variable.getType());
}
verifyCanRead(body, "body");
if (filter != null) {
verifyCanRead(filter, "filter");
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(filter.getType(), PrimitiveTypes.Boolean)) {
throw Error.argumentMustBeBoolean();
}
}
return new CatchBlock(type, variable, body, filter);
}
public static CatchBlock makeCatch(
final Type type,
final ParameterExpression variable,
final Expression body) {
return makeCatch(type, variable, body, null);
}
public static TryExpression tryFinally(final Expression body, final Expression finallyBlock) {
return makeTry(null, body, finallyBlock);
}
public static TryExpression tryCatch(final Expression body, final CatchBlock... handlers) {
return makeTry(null, body, null, handlers);
}
public static TryExpression makeTry(final Type type, final Expression body, final CatchBlock... handlers) {
return makeTry(type, body, null, handlers);
}
public static TryExpression tryCatchFinally(final Expression body, final Expression finallyBlock, final CatchBlock... handlers) {
return makeTry(null, body, finallyBlock, handlers);
}
public static TryExpression makeTry(final Type type, final Expression body, final Expression finallyBlock, final CatchBlock... handlers) {
final ReadOnlyList<CatchBlock> catchBlocks;
if (handlers != null) {
catchBlocks = new ReadOnlyList<>(handlers);
}
else {
VerifyArgument.noNullElements(handlers, "handlers");
catchBlocks = ReadOnlyList.emptyList();
}
return makeTry(type, body, catchBlocks, finallyBlock);
}
public static TryExpression makeTry(
final Type type,
final Expression body,
final ReadOnlyList<CatchBlock> catchBlocks,
final Expression finallyBlock) {
verifyCanRead(body, "body");
VerifyArgument.noNullElements(catchBlocks, "catchBlocks");
validateTryAndCatchHaveSameType(type, body, catchBlocks);
if (finallyBlock != null) {
verifyCanRead(finallyBlock, "finallyBlock");
}
else if (catchBlocks.isEmpty()) {
throw Error.tryMustHaveCatchOrFinally();
}
return new TryExpression(
type != null ? type : body.getType(),
body,
catchBlocks,
finallyBlock
);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// RUNTIME VARIABLES EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static RuntimeVariablesExpression runtimeVariables(final ParameterExpression... variables) {
return runtimeVariables(arrayToList(variables));
}
public static RuntimeVariablesExpression runtimeVariables(final ParameterExpressionList variables) {
VerifyArgument.noNullElements(variables, "variables");
return new RuntimeVariablesExpression(variables);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CONDITIONAL EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static ConditionalExpression condition(
final Expression test,
final Expression ifTrue,
final Expression ifFalse) {
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(test.getType(), PrimitiveTypes.Boolean)) {
throw Error.argumentMustBeBoolean();
}
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(ifTrue.getType(), ifFalse.getType())) {
throw Error.argumentTypesMustMatch();
}
return condition(test, ifTrue, ifFalse, ifTrue.getType());
}
public static ConditionalExpression condition(
final Expression test,
final Expression ifTrue,
final Expression ifFalse,
final Type type) {
verifyCanRead(test, "test");
verifyCanRead(ifTrue, "ifTrue");
verifyCanRead(ifFalse, "ifFalse");
VerifyArgument.notNull(type, "type");
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(test.getType(), PrimitiveTypes.Boolean)) {
throw Error.argumentMustBeBoolean();
}
if (type != PrimitiveTypes.Void &&
(!TypeUtils.areReferenceAssignable(type, ifTrue.getType()) ||
!TypeUtils.areReferenceAssignable(type, ifFalse.getType()))) {
throw Error.argumentTypesMustMatch();
}
return ConditionalExpression.make(test, ifTrue, ifFalse, type);
}
public static ConditionalExpression ifThen(
final Expression test,
final Expression ifTrue) {
return condition(test, ifTrue, empty(), PrimitiveTypes.Void);
}
public static ConditionalExpression ifThenElse(
final Expression test,
final Expression ifTrue,
final Expression ifFalse) {
return condition(test, ifTrue, ifFalse, PrimitiveTypes.Void);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MEMBER ACCESS EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static MemberExpression makeMemberAccess(final Expression target, final MemberInfo member) {
VerifyArgument.notNull(member, "member");
if (member instanceof FieldInfo) {
return Expression.field(target, (FieldInfo) member);
}
throw Error.memberNotField(member);
}
public static MemberExpression field(final Expression target, final FieldInfo field) {
VerifyArgument.notNull(field, "field");
if (!field.isStatic() && target == null) {
throw Error.targetRequiredForNonStaticFieldAccess(field);
}
return new FieldExpression(target, field);
}
public static MemberExpression field(final Type<?> declaringType, final String fieldName) {
VerifyArgument.notNull(declaringType, "declaringType");
VerifyArgument.notNull(fieldName, "fieldName");
final FieldInfo field = findField(
declaringType,
fieldName,
StaticMemberBindingFlags
);
return new FieldExpression(null, field);
}
public static MemberExpression field(final Expression target, final String fieldName) {
verifyCanRead(target, "target");
VerifyArgument.notNull(fieldName, "fieldName");
final FieldInfo field = findField(
target.getType(),
fieldName,
InstanceMemberBindingFlags
);
return new FieldExpression(target, field);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CONSTANT EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static ConstantExpression constant(final Object value) {
return ConstantExpression.make(value, value == null ? Types.Object : Type.of(value.getClass()));
}
public static ConstantExpression constant(final Object value, final Type type) {
VerifyArgument.notNull(type, "type");
if (value == null && type.isPrimitive()) {
throw Error.argumentTypesMustMatch();
}
if (value != null && !type.getErasedClass().isInstance(value)) {
throw Error.argumentTypesMustMatch();
}
return ConstantExpression.make(value, type);
}
//
// PARAMETERS AND VARIABLES
//
public static ParameterExpressionList parameters(final ParameterExpression... parameters) {
return new ParameterExpressionList(parameters);
}
public static ParameterExpression parameter(final Type type) {
return parameter(type, null);
}
public static ParameterExpression variable(final Type type) {
return variable(type, null);
}
public static ParameterExpression parameter(final Type type, final String name) {
VerifyArgument.notNull(type, "type");
if (type == PrimitiveTypes.Void) {
throw Error.argumentCannotBeOfTypeVoid();
}
return ParameterExpression.make(type, name);
}
public static ParameterExpression variable(final Type type, final String name) {
VerifyArgument.notNull(type, "type");
if (type == PrimitiveTypes.Void) {
throw Error.argumentCannotBeOfTypeVoid();
}
return ParameterExpression.make(type, name);
}
//
// UNARY EXPRESSIONS
//
public static UnaryExpression makeUnary(
final ExpressionType unaryType,
final Expression operand,
final Type type) {
return makeUnary(unaryType, operand, type, null);
}
public static UnaryExpression makeUnary(
final ExpressionType unaryType,
final Expression operand,
final Type type,
final MethodInfo method) {
switch (unaryType) {
case Negate:
return negate(operand, method);
case Not:
return not(operand, method);
case IsFalse:
return isFalse(operand, method);
case IsTrue:
return isTrue(operand, method);
case OnesComplement:
return onesComplement(operand, method);
case ArrayLength:
return arrayLength(operand);
case Convert:
return convert(operand, type, method);
case Throw:
return makeThrow(operand, type);
case UnaryPlus:
return unaryPlus(operand, method);
case Unbox:
return unbox(operand, type);
case Increment:
return increment(operand, method);
case Decrement:
return decrement(operand, method);
case PreIncrementAssign:
return preIncrementAssign(operand, method);
case PostIncrementAssign:
return postIncrementAssign(operand, method);
case PreDecrementAssign:
return preDecrementAssign(operand, method);
case PostDecrementAssign:
return postDecrementAssign(operand, method);
default:
throw Error.unhandledUnary(unaryType);
}
}
public static UnaryExpression negate(final Expression expression) {
return negate(expression, null);
}
public static UnaryExpression negate(final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.isArithmetic(expression.getType())) {
return new UnaryExpression(ExpressionType.Negate, expression, expression.getType(), null);
}
return getMethodBasedUnaryOperatorOrThrow(ExpressionType.Negate, "negate", expression);
}
return getMethodBasedUnaryOperator(ExpressionType.Negate, expression, method);
}
public static UnaryExpression not(final Expression expression) {
return not(expression, null);
}
public static UnaryExpression not(final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.isIntegralOrBoolean(expression.getType())) {
return new UnaryExpression(ExpressionType.Not, expression, expression.getType(), null);
}
throw Error.unaryOperatorNotDefined(ExpressionType.Not, expression.getType());
}
return getMethodBasedUnaryOperator(ExpressionType.Not, expression, method);
}
public static UnaryExpression isFalse(final Expression expression) {
return isFalse(expression, null);
}
public static UnaryExpression isFalse(final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.isBoolean(expression.getType())) {
return new UnaryExpression(ExpressionType.IsFalse, expression, expression.getType(), null);
}
throw Error.unaryOperatorNotDefined(ExpressionType.IsFalse, expression.getType());
}
return getMethodBasedUnaryOperator(ExpressionType.IsFalse, expression, method);
}
public static UnaryExpression isTrue(final Expression expression) {
return isTrue(expression, null);
}
public static UnaryExpression isTrue(final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.isBoolean(expression.getType())) {
return new UnaryExpression(ExpressionType.IsTrue, expression, expression.getType(), null);
}
throw Error.unaryOperatorNotDefined(ExpressionType.IsTrue, expression.getType());
}
return getMethodBasedUnaryOperator(ExpressionType.IsTrue, expression, method);
}
public static UnaryExpression onesComplement(final Expression expression) {
return not(expression, null);
}
public static UnaryExpression onesComplement(final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.isIntegral(expression.getType())) {
return new UnaryExpression(ExpressionType.OnesComplement, expression, expression.getType(), null);
}
return getMethodBasedUnaryOperatorOrThrow(ExpressionType.OnesComplement, "not", expression);
}
return getMethodBasedUnaryOperator(ExpressionType.OnesComplement, expression, method);
}
public static UnaryExpression arrayLength(final Expression array) {
VerifyArgument.notNull(array, "array");
if (!array.getType().isArray()) {
throw Error.argumentMustBeArray();
}
return new UnaryExpression(ExpressionType.ArrayLength, array, PrimitiveTypes.Integer, null);
}
public static UnaryExpression convert(final Expression expression, final Type type) {
return convert(expression, type, null);
}
public static UnaryExpression convert(final Expression expression, final Type type, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(expression.getType(), type) ||
TypeUtils.hasReferenceConversion(expression.getType(), type) ||
TypeUtils.isArithmetic(expression.getType()) && TypeUtils.isArithmetic(type)) {
return new UnaryExpression(ExpressionType.Convert, expression, type, null);
}
return getMethodBasedCoercionOrThrow(ExpressionType.Convert, expression, type);
}
return getMethodBasedCoercionOperator(ExpressionType.Convert, expression, type, method);
}
public static UnaryExpression isNull(final Expression expression) {
verifyCanRead(expression, "expression");
if (expression.getType().isPrimitive()) {
throw Error.argumentMustBeReferenceType();
}
return new UnaryExpression(ExpressionType.IsNull, expression, PrimitiveTypes.Boolean, null);
}
public static UnaryExpression isNotNull(final Expression expression) {
verifyCanRead(expression, "expression");
if (expression.getType().isPrimitive()) {
throw Error.argumentMustBeReferenceType();
}
return new UnaryExpression(ExpressionType.IsNotNull, expression, PrimitiveTypes.Boolean, null);
}
public static UnaryExpression makeThrow(final Expression expression) {
return makeThrow(expression, PrimitiveTypes.Void);
}
public static UnaryExpression makeThrow(final Expression value, final Type type) {
VerifyArgument.notNull(type, "type");
if (value != null) {
verifyCanRead(value, "value");
if (!Types.Throwable.isAssignableFrom(value.getType())) {
throw Error.argumentMustBeThrowable();
}
}
return new UnaryExpression(ExpressionType.Throw, value, type, null);
}
public static UnaryExpression unaryPlus(final Expression expression) {
return negate(expression, null);
}
public static UnaryExpression unaryPlus(final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.isArithmetic(expression.getType())) {
return new UnaryExpression(ExpressionType.UnaryPlus, expression, expression.getType(), null);
}
return getMethodBasedUnaryOperatorOrThrow(ExpressionType.UnaryPlus, "abs", expression);
}
return getMethodBasedUnaryOperator(ExpressionType.UnaryPlus, expression, method);
}
public static Expression box(final Expression expression) {
verifyCanRead(expression, "expression");
final Type sourceType = expression.getType();
if (!sourceType.isPrimitive()) {
return expression;
}
return call(TypeUtils.getBoxMethod(sourceType), expression);
}
public static UnaryExpression unbox(final Expression expression) {
return unbox(
VerifyArgument.notNull(expression, "expression"),
TypeUtils.getUnderlyingPrimitiveOrSelf(expression.getType())
);
}
public static UnaryExpression unbox(final Expression expression, final Type type) {
verifyCanRead(expression, "expression");
VerifyArgument.notNull(type, "type");
final Type sourceType = expression.getType();
if (!TypeUtils.isAutoUnboxed(sourceType) && sourceType != Types.Object || !type.isPrimitive()) {
throw Error.invalidUnboxType();
}
return new UnaryExpression(ExpressionType.Unbox, expression, type, null);
}
public static UnaryExpression increment(final Expression expression) {
return increment(
expression, null);
}
public static UnaryExpression increment(final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.isArithmetic(expression.getType())) {
return new UnaryExpression(ExpressionType.Increment, expression, expression.getType(), null);
}
return getMethodBasedUnaryOperatorOrThrow(ExpressionType.Increment, "increment", expression);
}
return getMethodBasedUnaryOperator(ExpressionType.Increment, expression, method);
}
public static UnaryExpression decrement(final Expression expression) {
return negate(expression, null);
}
public static UnaryExpression decrement(final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
if (method == null) {
if (TypeUtils.isArithmetic(expression.getType())) {
return new UnaryExpression(ExpressionType.Decrement, expression, expression.getType(), null);
}
return getMethodBasedUnaryOperatorOrThrow(ExpressionType.Decrement, "decrement", expression);
}
return getMethodBasedUnaryOperator(ExpressionType.Decrement, expression, method);
}
public static UnaryExpression preIncrementAssign(final Expression expression) {
return makeOpAssignUnary(ExpressionType.PreIncrementAssign, expression, null);
}
public static UnaryExpression preIncrementAssign(final Expression expression, final MethodInfo method) {
return makeOpAssignUnary(ExpressionType.PreIncrementAssign, expression, method);
}
public static UnaryExpression postIncrementAssign(final Expression expression) {
return makeOpAssignUnary(ExpressionType.PostIncrementAssign, expression, null);
}
public static UnaryExpression postIncrementAssign(final Expression expression, final MethodInfo method) {
return makeOpAssignUnary(ExpressionType.PostIncrementAssign, expression, method);
}
public static UnaryExpression preDecrementAssign(final Expression expression) {
return makeOpAssignUnary(ExpressionType.PreDecrementAssign, expression, null);
}
public static UnaryExpression preDecrementAssign(final Expression expression, final MethodInfo method) {
return makeOpAssignUnary(ExpressionType.PreDecrementAssign, expression, method);
}
public static UnaryExpression postDecrementAssign(final Expression expression) {
return makeOpAssignUnary(ExpressionType.PostDecrementAssign, expression, null);
}
public static UnaryExpression postDecrementAssign(final Expression expression, final MethodInfo method) {
return makeOpAssignUnary(ExpressionType.PostDecrementAssign, expression, method);
}
//
// BLOCK EXPRESSIONS
//
public static BlockExpression block(final Expression arg0, final Expression arg1) {
verifyCanRead(arg0, "arg0");
verifyCanRead(arg1, "arg1");
return new Block2(arg0, arg1);
}
public static BlockExpression block(final Expression arg0, final Expression arg1, final Expression arg2) {
verifyCanRead(arg0, "arg0");
verifyCanRead(arg1, "arg1");
verifyCanRead(arg2, "arg2");
return new Block3(arg0, arg1, arg2);
}
public static BlockExpression block(
final Expression arg0,
final Expression arg1,
final Expression arg2,
final Expression arg3) {
verifyCanRead(arg0, "arg0");
verifyCanRead(arg1, "arg1");
verifyCanRead(arg2, "arg2");
verifyCanRead(arg3, "arg3");
return new Block4(arg0, arg1, arg2, arg3);
}
public static BlockExpression block(
final Expression arg0,
final Expression arg1,
final Expression arg2,
final Expression arg3,
final Expression arg4) {
verifyCanRead(arg0, "arg0");
verifyCanRead(arg1, "arg1");
verifyCanRead(arg2, "arg2");
verifyCanRead(arg3, "arg3");
verifyCanRead(arg4, "arg4");
return new Block5(arg0, arg1, arg2, arg3, arg4);
}
public static BlockExpression block(final Expression... expressions) {
VerifyArgument.notEmpty(expressions, "expressions");
VerifyArgument.noNullElements(expressions, "expressions");
switch (expressions.length) {
case 2:
return block(expressions[0], expressions[1]);
case 3:
return block(expressions[0], expressions[1], expressions[2]);
case 4:
return block(expressions[0], expressions[1], expressions[2], expressions[3]);
case 5:
return block(expressions[0], expressions[1], expressions[2], expressions[3], expressions[4]);
default:
VerifyArgument.notEmpty(expressions, "expressions");
verifyCanRead(expressions, "expressions");
return new BlockN(arrayToList(expressions));
}
}
public static BlockExpression block(final ExpressionList<? extends Expression> expressions) {
return block(ParameterExpressionList.empty(), expressions);
}
public static BlockExpression block(final ParameterExpression[] variables, final Expression... expressions) {
VerifyArgument.notEmpty(expressions, "expressions");
return block(arrayToList(variables), arrayToList(expressions));
}
public static BlockExpression block(final ParameterExpressionList variables, final Expression... expressions) {
VerifyArgument.notEmpty(expressions, "expressions");
return block(variables, arrayToList(expressions));
}
public static BlockExpression block(final ParameterExpressionList variables, final ExpressionList<? extends Expression> expressions) {
VerifyArgument.notEmpty(expressions, "expressions");
VerifyArgument.noNullElements(expressions, "expressions");
verifyCanRead(expressions, "expressions");
return block(
expressions.get(expressions.size() - 1).getType(),
variables,
expressions
);
}
public static BlockExpression block(final Type type, final Expression... expressions) {
VerifyArgument.notEmpty(expressions, "expressions");
return block(type, ParameterExpressionList.empty(), arrayToList(expressions));
}
public static BlockExpression block(final Type type, final ExpressionList<? extends Expression> expressions) {
VerifyArgument.notEmpty(expressions, "expressions");
VerifyArgument.noNullElements(expressions, "expressions");
return block(type, ParameterExpressionList.empty(), expressions);
}
public static BlockExpression block(final Type type, final ParameterExpression[] variables, final Expression... expressions) {
VerifyArgument.notEmpty(expressions, "expressions");
return block(type, arrayToList(variables), arrayToList(expressions));
}
public static BlockExpression block(final Type type, final ParameterExpressionList variables, final Expression... expressions) {
VerifyArgument.notEmpty(expressions, "expressions");
return block(type, variables, arrayToList(expressions));
}
public static BlockExpression block(final Type type, final ParameterExpressionList variables, final ExpressionList<? extends Expression> expressions) {
VerifyArgument.notNull(type, "type");
VerifyArgument.notEmpty(expressions, "expressions");
VerifyArgument.noNullElements(expressions, "expressions");
verifyCanRead(expressions, "expressions");
validateVariables(variables, "variables");
final Expression last = expressions.get(expressions.size() - 1);
if (type != PrimitiveTypes.Void) {
if (!type.isAssignableFrom(last.getType())) {
throw Error.argumentTypesMustMatch();
}
}
if (type != last.getType()) {
return new ScopeWithType(variables, expressions, type);
}
else {
if (expressions.size() == 1) {
return new Scope1(variables, expressions.get(0));
}
else {
return new ScopeN(variables, expressions);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BINARY EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static BinaryExpression makeBinary(final ExpressionType binaryType, final Expression... rest) {
VerifyArgument.notNull(binaryType, "binaryType");
VerifyArgument.notEmpty(rest, "rest");
verifyCanRead(rest, "rest");
if (rest.length < 2) {
throw Error.twoOrMoreOperandsRequired();
}
return aggregateBinary(binaryType, ImmutableList.from(rest));
}
public static BinaryExpression makeBinary(
final ExpressionType binaryType,
final Expression first,
final Expression... rest) {
VerifyArgument.notNull(binaryType, "binaryType");
verifyCanRead(first, "first");
VerifyArgument.notEmpty(rest, "rest");
verifyCanRead(rest, "rest");
return aggregateBinary(binaryType, ImmutableList.of(first, rest));
}
public static BinaryExpression makeBinary(
final ExpressionType binaryType,
final Expression left,
final Expression right) {
return makeBinary(binaryType, left, right, null, null);
}
public static BinaryExpression makeBinary(
final ExpressionType binaryType,
final Expression left,
final Expression right,
final MethodInfo method) {
return makeBinary(binaryType, left, right, method, null);
}
public static BinaryExpression makeBinary(
final ExpressionType binaryType,
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
switch (binaryType) {
case Add:
return add(left, right, method);
case Coalesce:
return coalesce(left, right, conversion);
case Subtract:
return subtract(left, right, method);
case Multiply:
return multiply(left, right, method);
case Divide:
return divide(left, right, method);
case Modulo:
return modulo(left, right, method);
case And:
return and(left, right, method);
case AndAlso:
return andAlso(left, right, method);
case Or:
return or(left, right, method);
case OrElse:
return orElse(left, right, method);
case LessThan:
return lessThan(left, right, method);
case LessThanOrEqual:
return lessThanOrEqual(left, right, method);
case GreaterThan:
return greaterThan(left, right, method);
case GreaterThanOrEqual:
return greaterThanOrEqual(left, right, method);
case Equal:
return equal(left, right, method);
case NotEqual:
return notEqual(left, right, method);
case ExclusiveOr:
return exclusiveOr(left, right, method);
case ArrayIndex:
return arrayIndex(left, right);
case RightShift:
return rightShift(left, right, method);
case UnsignedRightShift:
return unsignedRightShift(left, right, method);
case LeftShift:
return leftShift(left, right, method);
case Assign:
return assign(left, right);
case AddAssign:
return addAssign(left, right, method, conversion);
case AndAssign:
return andAssign(left, right, method, conversion);
case DivideAssign:
return divideAssign(left, right, method, conversion);
case ExclusiveOrAssign:
return exclusiveOrAssign(left, right, method, conversion);
case LeftShiftAssign:
return leftShiftAssign(left, right, method, conversion);
case ModuloAssign:
return moduloAssign(left, right, method, conversion);
case MultiplyAssign:
return multiplyAssign(left, right, method, conversion);
case OrAssign:
return orAssign(left, right, method, conversion);
case RightShiftAssign:
return rightShiftAssign(left, right, method, conversion);
case UnsignedRightShiftAssign:
return unsignedRightShiftAssign(left, right, method, conversion);
case SubtractAssign:
return subtractAssign(left, right, method, conversion);
case ReferenceEqual:
return referenceEqual(left, right);
case ReferenceNotEqual:
return referenceNotEqual(left, right);
default:
throw Error.unhandledBinary(binaryType);
}
}
public static BinaryExpression add(final Expression left, final Expression right) {
return add(left, right, null);
}
public static BinaryExpression add(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) && TypeUtils.isArithmetic(leftType)) {
return new SimpleBinaryExpression(ExpressionType.Add, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.Add, "add", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.Add, left, right, method);
}
public static BinaryExpression coalesce(final Expression left, final Expression right) {
return coalesce(left, right, null);
}
public static BinaryExpression coalesce(final Expression left, final Expression right, final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (conversion == null) {
final Type resultType = validateCoalesceArgumentTypes(left.getType(), right.getType());
return new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, resultType);
}
if (left.getType().isPrimitive()) {
throw Error.coalesceUsedOnNonNullableType();
}
final Type<?> delegateType = conversion.getType();
final MethodInfo method = getInvokeMethod(conversion);
if (method.getReturnType() == PrimitiveTypes.Void) {
throw Error.operatorMethodMustNotReturnVoid(method);
}
final ParameterList parameters = method.getParameters();
assert parameters.size() == conversion.getParameters().size();
if (parameters.size() != 1) {
throw Error.incorrectNumberOfMethodCallArguments(method);
}
//
// The return type must match exactly.
//
if (!TypeUtils.areEquivalent(method.getReturnType(), right.getType())) {
throw Error.operandTypesDoNotMatchParameters(ExpressionType.Coalesce, method);
}
//
// The parameter of the conversion lambda must either be assignable from the erased
// or un-erased type of the left hand side.
//
if (!parameterIsAssignable(parameters.get(0).getParameterType(), TypeUtils.getUnderlyingPrimitiveOrSelf(left.getType())) &&
!parameterIsAssignable(parameters.get(0).getParameterType(), left.getType())) {
throw Error.operandTypesDoNotMatchParameters(ExpressionType.Coalesce, method);
}
return new CoalesceConversionBinaryExpression(left, right, conversion);
}
public static BinaryExpression subtract(final Expression left, final Expression right) {
return subtract(left, right, null);
}
public static BinaryExpression subtract(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) && TypeUtils.isArithmetic(leftType)) {
return new SimpleBinaryExpression(ExpressionType.Subtract, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.Subtract, "subtract", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.Subtract, left, right, method);
}
public static BinaryExpression multiply(final Expression left, final Expression right) {
return multiply(left, right, null);
}
public static BinaryExpression multiply(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.isArithmetic(leftType) && TypeUtils.isArithmetic(rightType)) {
return new SimpleBinaryExpression(
ExpressionType.Multiply,
left,
right,
performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.Multiply, "multiply", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.Multiply, left, right, method);
}
public static BinaryExpression divide(final Expression left, final Expression right) {
return divide(left, right, null);
}
public static BinaryExpression divide(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) && TypeUtils.isArithmetic(leftType)) {
return new SimpleBinaryExpression(ExpressionType.Divide, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.Divide, "divide", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.Divide, left, right, method);
}
public static BinaryExpression modulo(final Expression left, final Expression right) {
return modulo(left, right, null);
}
public static BinaryExpression modulo(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) && TypeUtils.isArithmetic(leftType)) {
return new SimpleBinaryExpression(ExpressionType.Modulo, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.Modulo, "mod", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.Modulo, left, right, method);
}
public static BinaryExpression leftShift(final Expression left, final Expression right) {
return leftShift(left, right, null);
}
public static BinaryExpression leftShift(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.isArithmetic(leftType) && TypeUtils.isIntegral(rightType)) {
return new SimpleBinaryExpression(ExpressionType.LeftShift, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.LeftShift, "shiftLeft", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.LeftShift, left, right, method);
}
public static BinaryExpression rightShift(final Expression left, final Expression right) {
return rightShift(left, right, null);
}
public static BinaryExpression rightShift(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.isArithmetic(leftType) && TypeUtils.isIntegral(rightType)) {
return new SimpleBinaryExpression(ExpressionType.RightShift, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.RightShift, "shiftRight", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.RightShift, left, right, method);
}
public static BinaryExpression unsignedRightShift(final Expression left, final Expression right) {
return unsignedRightShift(left, right, null);
}
public static BinaryExpression unsignedRightShift(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.isArithmetic(leftType) && TypeUtils.isIntegral(rightType)) {
return new SimpleBinaryExpression(ExpressionType.RightShift, left, right, performBinaryNumericPromotion(leftType, rightType));
}
throw Error.binaryOperatorNotDefined(ExpressionType.UnsignedRightShift, left.getType(), right.getType());
}
return getMethodBasedBinaryOperator(ExpressionType.RightShift, left, right, method);
}
public static BinaryExpression and(final Expression left, final Expression right) {
return and(left, right, null);
}
public static BinaryExpression and(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) && TypeUtils.isIntegralOrBoolean(leftType)) {
return new SimpleBinaryExpression(ExpressionType.And, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.And, "and", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.And, left, right, method);
}
public static BinaryExpression or(final Expression left, final Expression right) {
return or(left, right, null);
}
public static BinaryExpression or(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) && TypeUtils.isIntegralOrBoolean(leftType)) {
return new SimpleBinaryExpression(ExpressionType.Or, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.Or, "or", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.Or, left, right, method);
}
public static BinaryExpression exclusiveOr(final Expression left, final Expression right) {
return exclusiveOr(left, right, null);
}
public static BinaryExpression exclusiveOr(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
final Type leftType = left.getType();
final Type rightType = right.getType();
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) && TypeUtils.isIntegralOrBoolean(leftType)) {
return new SimpleBinaryExpression(ExpressionType.ExclusiveOr, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedBinaryOperatorOrThrow(ExpressionType.ExclusiveOr, "xor", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.ExclusiveOr, left, right, method);
}
public static BinaryExpression andAlso(final Expression left, final Expression right) {
return andAlso(left, right, null);
}
public static BinaryExpression andAlso(final Expression first, final Expression... rest) {
verifyCanRead(first, "first");
VerifyArgument.notEmpty(rest, "rest");
verifyCanRead(rest, "rest");
return aggregateBinary(ExpressionType.AndAlso, ImmutableList.of(first, rest));
}
public static BinaryExpression andAlso(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
Type returnType;
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.hasIdentityPrimitiveOrBoxingConversion(left.getType(), PrimitiveTypes.Boolean)) {
return new SimpleBinaryExpression(ExpressionType.AndAlso, left, right, performBinaryNumericPromotion(leftType, rightType));
}
throw Error.binaryOperatorNotDefined(ExpressionType.AndAlso, leftType, rightType);
}
throw Error.binaryOperatorNotDefined(ExpressionType.AndAlso, leftType, rightType);
}
public static BinaryExpression orElse(final Expression left, final Expression right) {
return orElse(left, right, null);
}
public static BinaryExpression orElse(final Expression first, final Expression... rest) {
verifyCanRead(first, "first");
VerifyArgument.notEmpty(rest, "rest");
verifyCanRead(rest, "rest");
return aggregateBinary(ExpressionType.OrElse, ImmutableList.of(first, rest));
}
public static BinaryExpression orElse(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
Type returnType;
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.hasIdentityPrimitiveOrBoxingConversion(left.getType(), PrimitiveTypes.Boolean)) {
return new SimpleBinaryExpression(ExpressionType.OrElse, left, right, performBinaryNumericPromotion(leftType, rightType));
}
throw Error.binaryOperatorNotDefined(ExpressionType.OrElse, leftType, rightType);
}
throw Error.binaryOperatorNotDefined(ExpressionType.OrElse, leftType, rightType);
}
public static BinaryExpression lessThan(final Expression left, final Expression right) {
return lessThan(left, right, null);
}
public static BinaryExpression lessThan(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
return getComparisonOperator(ExpressionType.LessThan, left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.LessThan, left, right, method);
}
public static BinaryExpression lessThanOrEqual(final Expression left, final Expression right) {
return lessThanOrEqual(left, right, null);
}
public static BinaryExpression lessThanOrEqual(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
return getComparisonOperator(ExpressionType.LessThanOrEqual, left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.LessThanOrEqual, left, right, method);
}
public static BinaryExpression greaterThan(final Expression left, final Expression right) {
return greaterThan(left, right, null);
}
public static BinaryExpression greaterThan(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
return getComparisonOperator(ExpressionType.GreaterThan, left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.GreaterThan, left, right, method);
}
public static BinaryExpression greaterThanOrEqual(final Expression left, final Expression right) {
return greaterThanOrEqual(left, right, null);
}
public static BinaryExpression greaterThanOrEqual(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
return getComparisonOperator(ExpressionType.GreaterThanOrEqual, left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.GreaterThanOrEqual, left, right, method);
}
public static BinaryExpression equal(final Expression left, final Expression right) {
return equal(left, right, null);
}
public static BinaryExpression equal(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
return getEqualityComparisonOperator(ExpressionType.Equal, "equals", left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.Equal, left, right, method);
}
public static BinaryExpression notEqual(final Expression left, final Expression right) {
return notEqual(left, right, null);
}
public static BinaryExpression notEqual(final Expression left, final Expression right, final MethodInfo method) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (method == null) {
return getEqualityComparisonOperator(ExpressionType.NotEqual, null, left, right);
}
return getMethodBasedBinaryOperator(ExpressionType.NotEqual, left, right, method);
}
public static BinaryExpression referenceEqual(final Expression left, final Expression right) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (TypeUtils.hasReferenceEquality(left.getType(), right.getType())) {
return new LogicalBinaryExpression(ExpressionType.ReferenceEqual, left, right);
}
throw Error.referenceEqualityNotDefined(left.getType(), right.getType());
}
public static BinaryExpression referenceNotEqual(final Expression left, final Expression right) {
verifyCanRead(left, "left");
verifyCanRead(right, "right");
if (TypeUtils.hasReferenceEquality(left.getType(), right.getType())) {
return new LogicalBinaryExpression(ExpressionType.ReferenceNotEqual, left, right);
}
throw Error.referenceEqualityNotDefined(left.getType(), right.getType());
}
public static BinaryExpression arrayIndex(final Expression array, final Expression index) {
verifyCanRead(array, "array");
verifyCanRead(index, "index");
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(index.getType(), PrimitiveTypes.Integer)) {
throw Error.argumentMustBeArrayIndexType();
}
final Type arrayType = array.getType();
if (!arrayType.isArray()) {
throw Error.argumentMustBeArray();
}
return new SimpleBinaryExpression(ExpressionType.ArrayIndex, array, index, arrayType.getElementType());
}
public static BinaryExpression assign(final Expression left, final Expression right) {
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
if (!left.getType().isAssignableFrom(right.getType())) {
throw Error.expressionTypeDoesNotMatchAssignment(left.getType(), right.getType());
}
return new AssignBinaryExpression(left, right);
}
public static BinaryExpression addAssign(final Expression left, final Expression right) {
return addAssign(left, right, null, null);
}
public static BinaryExpression addAssign(final Expression left, final Expression right, final MethodInfo method) {
return addAssign(left, right, method, null);
}
public static BinaryExpression addAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isArithmetic(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.AddAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.AddAssign, "add", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.AddAssign, left, right, method, conversion);
}
public static BinaryExpression subtractAssign(final Expression left, final Expression right) {
return subtractAssign(left, right, null, null);
}
public static BinaryExpression subtractAssign(final Expression left, final Expression right, final MethodInfo method) {
return subtractAssign(left, right, method, null);
}
public static BinaryExpression subtractAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isArithmetic(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.SubtractAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.SubtractAssign, "subtract", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.SubtractAssign, left, right, method, conversion);
}
public static BinaryExpression multiplyAssign(final Expression left, final Expression right) {
return multiplyAssign(left, right, null, null);
}
public static BinaryExpression multiplyAssign(final Expression left, final Expression right, final MethodInfo method) {
return multiplyAssign(left, right, method, null);
}
public static BinaryExpression multiplyAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isArithmetic(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.MultiplyAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.MultiplyAssign, "multiply", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.MultiplyAssign, left, right, method, conversion);
}
public static BinaryExpression divideAssign(final Expression left, final Expression right) {
return divideAssign(left, right, null, null);
}
public static BinaryExpression divideAssign(final Expression left, final Expression right, final MethodInfo method) {
return divideAssign(left, right, method, null);
}
public static BinaryExpression divideAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isArithmetic(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.DivideAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.DivideAssign, "divide", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.DivideAssign, left, right, method, conversion);
}
public static BinaryExpression moduloAssign(final Expression left, final Expression right) {
return moduloAssign(left, right, null, null);
}
public static BinaryExpression moduloAssign(final Expression left, final Expression right, final MethodInfo method) {
return moduloAssign(left, right, method, null);
}
public static BinaryExpression moduloAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isArithmetic(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.ModuloAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.ModuloAssign, "modulo", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.ModuloAssign, left, right, method, conversion);
}
public static BinaryExpression leftShiftAssign(final Expression left, final Expression right) {
return leftShiftAssign(left, right, null, null);
}
public static BinaryExpression leftShiftAssign(final Expression left, final Expression right, final MethodInfo method) {
return leftShiftAssign(left, right, method, null);
}
public static BinaryExpression leftShiftAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isArithmetic(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.LeftShiftAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.LeftShiftAssign, "shiftLeft", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.LeftShiftAssign, left, right, method, conversion);
}
public static BinaryExpression rightShiftAssign(final Expression left, final Expression right) {
return rightShiftAssign(left, right, null, null);
}
public static BinaryExpression rightShiftAssign(final Expression left, final Expression right, final MethodInfo method) {
return rightShiftAssign(left, right, method, null);
}
public static BinaryExpression rightShiftAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isArithmetic(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.RightShiftAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.RightShiftAssign, "rightShift", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.RightShiftAssign, left, right, method, conversion);
}
public static BinaryExpression unsignedRightShiftAssign(final Expression left, final Expression right) {
return unsignedRightShiftAssign(left, right, null, null);
}
public static BinaryExpression unsignedRightShiftAssign(final Expression left, final Expression right, final MethodInfo method) {
return unsignedRightShiftAssign(left, right, method, null);
}
public static BinaryExpression unsignedRightShiftAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isArithmetic(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.UnsignedRightShiftAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
throw Error.binaryOperatorNotDefined(ExpressionType.UnsignedRightShiftAssign, left.getType(), right.getType());
}
return getMethodBasedAssignOperator(ExpressionType.UnsignedRightShiftAssign, left, right, method, conversion);
}
public static BinaryExpression orAssign(final Expression left, final Expression right) {
return orAssign(left, right, null, null);
}
public static BinaryExpression orAssign(final Expression left, final Expression right, final MethodInfo method) {
return orAssign(left, right, method, null);
}
public static BinaryExpression orAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isIntegralOrBoolean(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.OrAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.OrAssign, "or", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.OrAssign, left, right, method, conversion);
}
public static BinaryExpression andAssign(final Expression left, final Expression right) {
return andAssign(left, right, null, null);
}
public static BinaryExpression andAssign(final Expression left, final Expression right, final MethodInfo method) {
return andAssign(left, right, method, null);
}
public static BinaryExpression andAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isIntegralOrBoolean(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.AndAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.AndAssign, "and", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.AndAssign, left, right, method, conversion);
}
public static BinaryExpression exclusiveOrAssign(final Expression left, final Expression right) {
return exclusiveOrAssign(left, right, null, null);
}
public static BinaryExpression exclusiveOrAssign(final Expression left, final Expression right, final MethodInfo method) {
return exclusiveOrAssign(left, right, method, null);
}
public static BinaryExpression exclusiveOrAssign(
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
verifyCanRead(left, "left");
verifyCanWrite(left, "left");
verifyCanRead(right, "right");
final Type leftType = left.getType();
final Type rightType = right.getType();
if (method == null) {
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType) &&
TypeUtils.isIntegralOrBoolean(leftType)) {
// conversion is not supported for binary ops on arithmetic types without operator overloading
if (conversion != null) {
throw Error.conversionIsNotSupportedForArithmeticTypes();
}
return new SimpleBinaryExpression(ExpressionType.ExclusiveOrAssign, left, right, performBinaryNumericPromotion(leftType, rightType));
}
return getMethodBasedAssignOperatorOrThrow(ExpressionType.ExclusiveOrAssign, "xor", left, right, conversion);
}
return getMethodBasedAssignOperator(ExpressionType.ExclusiveOrAssign, left, right, method, conversion);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TYPE BINARY EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static TypeBinaryExpression instanceOf(final Expression expression, final Type type) {
verifyCanRead(expression, "expression");
VerifyArgument.notNull(type, "type");
verifyTypeBinaryExpressionOperand(expression, type);
return new TypeBinaryExpression(expression, type, ExpressionType.InstanceOf);
}
public static TypeBinaryExpression typeEqual(final Expression expression, final Type type) {
verifyCanRead(expression, "expression");
VerifyArgument.notNull(type, "type");
verifyTypeBinaryExpressionOperand(expression, type);
return new TypeBinaryExpression(expression, type, ExpressionType.TypeEqual);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// LAMBDA EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static LambdaExpression<?> lambda(
final String name,
final Expression body,
final ParameterExpression... parameters) {
return lambda(null, name, body, false, arrayToList(parameters));
}
public static LambdaExpression<?> lambda(
final Expression body,
final ParameterExpression... parameters) {
return lambda(null, null, body, false, arrayToList(parameters));
}
public static LambdaExpression<?> lambda(
final Expression body,
final boolean tailCall,
final ParameterExpression... parameters) {
return lambda(null, null, body, tailCall, arrayToList(parameters));
}
public static LambdaExpression<?> lambda(
final String name,
final Expression body,
final boolean tailCall,
final ParameterExpression... parameters) {
return lambda(null, name, body, false, arrayToList(parameters));
}
public static LambdaExpression<?> lambda(
final Expression body,
final ParameterExpressionList parameters) {
return lambda(null, null, body, false, parameters);
}
public static LambdaExpression<?> lambda(
final Expression body,
final boolean tailCall,
final ParameterExpressionList parameters) {
return lambda(null, null, body, tailCall, parameters);
}
public static <T> LambdaExpression<T> lambda(
final Type<?> interfaceType,
final String name,
final Expression body,
final ParameterExpression... parameters) {
return lambda(interfaceType, name, body, false, arrayToList(parameters));
}
public static <T> LambdaExpression<T> lambda(
final Type<?> interfaceType,
final Expression body,
final ParameterExpression... parameters) {
return lambda(interfaceType, null, body, false, arrayToList(parameters));
}
public static <T> LambdaExpression<T> lambda(
final Type<?> interfaceType,
final Expression body,
final boolean tailCall,
final ParameterExpression... parameters) {
return lambda(interfaceType, null, body, tailCall, arrayToList(parameters));
}
public static <T> LambdaExpression<T> lambda(
final Type<?> interfaceType,
final String name,
final Expression body,
final boolean tailCall,
final ParameterExpression... parameters) {
return lambda(interfaceType, name, body, false, arrayToList(parameters));
}
public static <T> LambdaExpression<T> lambda(
final Type<?> interfaceType,
final Expression body,
final ParameterExpressionList parameters) {
return lambda(interfaceType, null, body, false, parameters);
}
public static <T> LambdaExpression<T> lambda(
final Type<?> interfaceType,
final Expression body,
final boolean tailCall,
final ParameterExpressionList parameters) {
return lambda(interfaceType, null, body, tailCall, parameters);
}
public static <T> LambdaExpression<T> lambda(
final Type<?> interfaceType,
final String name,
final Expression body,
final boolean tailCall,
final ParameterExpressionList parameters) {
VerifyArgument.notNull(body, "body");
VerifyArgument.noNullElements(parameters, "parameters");
if (interfaceType != null) {
validateLambdaArgs(interfaceType, body, parameters);
}
return (LambdaExpression<T>)new LambdaExpression<>(interfaceType, name, body, tailCall, parameters);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INVOKE EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static InvocationExpression invoke(final Expression expression, final Expression... arguments) {
VerifyArgument.noNullElements(arguments, "arguments");
return invoke(expression, new ExpressionList<>(arguments));
}
public static InvocationExpression invoke(final Expression expression, final ExpressionList<? extends Expression> arguments) {
verifyCanRead(expression, "expression");
final MethodInfo method = getInvokeMethod(expression);
return new InvocationExpression(expression, arguments, method.getReturnType());
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// METHOD CALL EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static MethodCallExpression call(final MethodInfo method, final Expression... arguments) {
return call(null, method, arrayToList(arguments));
}
public static MethodCallExpression call(final MethodInfo method, final ExpressionList<? extends Expression> arguments) {
return call(null, method, arguments);
}
public static MethodCallExpression call(final Expression target, final MethodInfo method, final Expression... arguments) {
return call(target, method, arrayToList(arguments));
}
public static MethodCallExpression call(
final Expression target,
final MethodInfo method,
final ExpressionList<? extends Expression> arguments) {
VerifyArgument.notNull(method, "method");
final Expression actualTarget;
if (target == null && method instanceof DynamicMethod) {
final MethodHandle handle = ((DynamicMethod) method).getHandle();
if (handle == null) {
throw Error.dynamicMethodCallRequiresTargetOrMethodHandle();
}
actualTarget = constant(handle, Types.MethodHandle);
}
else {
actualTarget = target;
}
validateStaticOrInstanceMethod(actualTarget, method);
final ExpressionList<?> argumentList = validateArgumentTypes(method, ExpressionType.Call, arguments);
if (actualTarget == null) {
return new MethodCallExpressionN(method, argumentList);
}
return new InstanceMethodCallExpressionN(method, actualTarget, arguments);
}
public static MethodCallExpression call(
final Expression target,
final String methodName,
final Expression... arguments) {
return call(target, methodName, TypeList.empty(), arrayToList(arguments));
}
public static MethodCallExpression call(
final Expression target,
final String methodName,
final TypeList typeArguments,
final Expression... arguments) {
return call(target, methodName, typeArguments, arrayToList(arguments));
}
public static MethodCallExpression call(
final Expression target,
final String methodName,
final TypeList typeArguments,
final ExpressionList<? extends Expression> arguments) {
VerifyArgument.notNull(target, "target");
VerifyArgument.notNull(methodName, "methodName");
final MethodInfo resolvedMethod = findMethod(
target.getType(),
methodName,
typeArguments,
arguments,
InstanceMemberBindingFlags
);
return call(
target,
resolvedMethod,
adaptArguments(resolvedMethod, arguments)
);
}
public static MethodCallExpression call(
final Type declaringType,
final String methodName,
final Expression... arguments) {
return call(declaringType, methodName, TypeList.empty(), arrayToList(arguments));
}
public static MethodCallExpression call(
final Type declaringType,
final String methodName,
final TypeList typeArguments,
final Expression... arguments) {
return call(declaringType, methodName, typeArguments, arrayToList(arguments));
}
public static MethodCallExpression call(
final Type declaringType,
final String methodName,
final TypeList typeArguments,
final ExpressionList<? extends Expression> arguments) {
VerifyArgument.notNull(declaringType, "declaringType");
VerifyArgument.notNull(methodName, "methodName");
final MethodInfo resolvedMethod = findMethod(
declaringType,
methodName,
typeArguments,
arguments,
StaticMemberBindingFlags
);
return call(
resolvedMethod,
adaptArguments(resolvedMethod, arguments)
);
}
private static ExpressionList<? extends Expression> adaptArguments(
final MethodInfo method,
final ExpressionList<? extends Expression> arguments) {
if (method.getCallingConvention() == CallingConvention.VarArgs) {
final TypeList pt = method.getParameters().getParameterTypes();
final int varArgArrayPosition = pt.size() - 1;
final Type varArgArrayType = pt.get(varArgArrayPosition);
final boolean needArray = arguments.size() != pt.size() ||
!TypeUtils.areEquivalent(
varArgArrayType,
arguments.get(arguments.size() - 1).getType());
if (needArray) {
final Expression[] newArguments = new Expression[pt.size()];
for (int i = 0; i < varArgArrayPosition; i++) {
newArguments[i] = arguments.get(i);
}
ExpressionList<Expression> arrayInitList = ExpressionList.empty();
for (int i = varArgArrayPosition, n = arguments.size(); i < n; i++) {
arrayInitList = arrayInitList.add(arguments.get(i));
}
newArguments[varArgArrayPosition] = newArrayInit(
varArgArrayType.getElementType(),
arrayInitList
);
return new ExpressionList<>(newArguments);
}
}
return arguments;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DEFAULT VALUE EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static DefaultValueExpression defaultValue(final Type type) {
VerifyArgument.notNull(type, "type");
return new DefaultValueExpression(type);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SWITCH EXPRESSIONS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static SwitchCase switchCase(final Expression body, final Expression... testValues) {
return switchCase(body, arrayToList(testValues));
}
public static SwitchCase switchCase(
final Expression body,
final ExpressionList<? extends Expression> testValues) {
verifyCanRead(body, "body");
verifyCanRead(testValues, "testValues");
VerifyArgument.notEmpty(testValues, "testValues");
return new SwitchCase(body, testValues);
}
public static SwitchExpression makeSwitch(final Expression switchValue, final SwitchCase... cases) {
return makeSwitch(switchValue, SwitchOptions.Default, null, null, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(final Expression switchValue, final SwitchOptions options, final SwitchCase... cases) {
return makeSwitch(switchValue, options, null, null, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Expression switchValue,
final Expression defaultBody,
final SwitchCase... cases) {
return makeSwitch(switchValue, SwitchOptions.Default, defaultBody, null, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Expression switchValue,
final SwitchOptions options,
final Expression defaultBody,
final SwitchCase... cases) {
return makeSwitch(switchValue, options, defaultBody, null, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Expression switchValue,
final Expression defaultBody,
final MethodInfo comparison,
final SwitchCase... cases) {
return makeSwitch(switchValue, SwitchOptions.Default, defaultBody, comparison, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Type type,
final Expression switchValue,
final Expression defaultBody,
final SwitchCase... cases) {
return makeSwitch(type, switchValue, SwitchOptions.Default, defaultBody, null, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Expression switchValue,
final SwitchOptions options,
final Expression defaultBody,
final MethodInfo comparison,
final SwitchCase... cases) {
return makeSwitch(switchValue, options, defaultBody, comparison, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Type type,
final Expression switchValue,
final SwitchOptions options,
final Expression defaultBody,
final SwitchCase... cases) {
return makeSwitch(type, switchValue, options, defaultBody, null, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Type type,
final Expression switchValue,
final Expression defaultBody,
final MethodInfo comparison,
final SwitchCase... cases) {
return makeSwitch(type, switchValue, SwitchOptions.Default, defaultBody, comparison, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Type type,
final Expression switchValue,
final SwitchOptions options,
final Expression defaultBody,
final MethodInfo comparison,
final SwitchCase... cases) {
return makeSwitch(type, switchValue, options, defaultBody, comparison, arrayToReadOnlyList(cases));
}
public static SwitchExpression makeSwitch(
final Expression switchValue,
final Expression defaultBody,
final MethodInfo comparison,
final ReadOnlyList<SwitchCase> cases) {
return makeSwitch(null, switchValue, SwitchOptions.Default, defaultBody, comparison, cases);
}
public static SwitchExpression makeSwitch(
final Expression switchValue,
final SwitchOptions options,
final Expression defaultBody,
final MethodInfo comparison,
final ReadOnlyList<SwitchCase> cases) {
return makeSwitch(null, switchValue, options, defaultBody, comparison, cases);
}
public static SwitchExpression makeSwitch(
final Type type,
final Expression switchValue,
final SwitchOptions options,
final Expression defaultBody,
final MethodInfo comparison,
final ReadOnlyList<SwitchCase> cases) {
verifyCanRead(switchValue, "switchValue");
if (switchValue.getType() == PrimitiveTypes.Void) {
throw Error.argumentCannotBeOfTypeVoid();
}
VerifyArgument.notEmpty(cases, "cases");
VerifyArgument.noNullElements(cases, "cases");
final boolean customType = type != null;
final Type resultType = type != null ? type : cases.get(0).getBody().getType();
final MethodInfo actualComparison;
if (comparison != null) {
final ParameterList parameters = comparison.getParameters();
if (parameters.size() != 2) {
throw Error.incorrectNumberOfMethodCallArguments(comparison);
}
// Validate that the switch value's type matches the comparison method's
// left hand side parameter type.
final ParameterInfo leftArg = parameters.get(0);
final ParameterInfo rightArg = parameters.get(1);
for (int i = 0, n = cases.size(); i < n; i++) {
final SwitchCase c = cases.get(i);
validateSwitchCaseType(c.getBody(), customType, resultType, "cases");
final ExpressionList<? extends Expression> testValues = c.getTestValues();
for (int j = 0, m = testValues.size(); j < m; j++) {
// When a comparison method is provided, test values can have different type but have to
// be reference assignable to the right hand side parameter of the method.
final Type rightOperandType = testValues.get(j).getType();
if (!parameterIsAssignable(rightArg.getParameterType(), rightOperandType)) {
throw Error.testValueTypeDoesNotMatchComparisonMethodParameter(
rightOperandType,
rightArg.getParameterType()
);
}
}
}
actualComparison = comparison;
}
else {
// When comparison method is not present, all the test values must have
// the same type. Use the first test value's type as the baseline.
final Expression firstTestValue = cases.get(0).getTestValues().get(0);
for (int i = 0, n = cases.size(); i < n; i++) {
final SwitchCase c = cases.get(i);
validateSwitchCaseType(c.getBody(), customType, resultType, "cases");
final ExpressionList<? extends Expression> testValues = c.getTestValues();
// When no comparison method is provided, require all test values to have the same type.
for (int j = 0, m = testValues.size(); j < m; j++) {
if (!TypeUtils.areReferenceAssignable(firstTestValue.getType(), testValues.get(j).getType())) {
throw Error.allTestValuesMustHaveTheSameType();
}
}
}
// Now we need to validate that switchValue.Type and testValueType make sense in an
// Equal node. Fortunately, Equal throws a reasonable error, so just call it.
final BinaryExpression equal = equal(switchValue, firstTestValue, comparison);
// Get the comparison function from equals node.
actualComparison = equal.getMethod();
}
if (defaultBody == null) {
if (resultType != PrimitiveTypes.Void) {
throw Error.defaultBodyMustBeSupplied();
}
}
else {
validateSwitchCaseType(defaultBody, customType, resultType, "defaultBody");
}
// if we have a non-boolean user-defined equals, we don't want it.
if (comparison != null &&
TypeUtils.hasIdentityPrimitiveOrBoxingConversion(comparison.getReturnType(), PrimitiveTypes.Boolean)) {
throw Error.equalityMustReturnBoolean(comparison);
}
return new SwitchExpression(resultType, switchValue, defaultBody, actualComparison, cases, options);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HELPER METHODS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private static final Class UNMODIFIABLE_LIST_CLASS;
static {
Class unmodifiableListClass = null;
final Class<?>[] declaredClasses = Collections.class.getDeclaredClasses();
for (final Class clazz : declaredClasses) {
if (clazz.getName().equals("UnmodifiableCollection")) {
unmodifiableListClass = clazz;
break;
}
}
UNMODIFIABLE_LIST_CLASS = unmodifiableListClass;
}
static <T extends Expression> ExpressionList<T> arrayToList(final T[] expressions) {
if (expressions == null || expressions.length == 0) {
return ExpressionList.empty();
}
VerifyArgument.noNullElements(expressions, "expressions");
return new ExpressionList<>(expressions);
}
static <T> ReadOnlyList<T> arrayToReadOnlyList(final T[] items) {
if (items == null || items.length == 0) {
return ReadOnlyList.emptyList();
}
VerifyArgument.noNullElements(items, "items");
return new ReadOnlyList<>(items);
}
static ParameterExpressionList arrayToList(final ParameterExpression[] parameters) {
if (parameters == null || parameters.length == 0) {
return ParameterExpressionList.empty();
}
VerifyArgument.noNullElements(parameters, "parameters");
return new ParameterExpressionList(parameters);
}
private static void verifyCanRead(final Expression expression, final String parameterName) {
VerifyArgument.notNull(expression, parameterName);
// All expression types are currently readable.
}
private static void verifyCanRead(final Iterable<? extends Expression> items, final String parameterName) {
if (items == null) {
return;
}
if (items instanceof List) {
final List<? extends Expression> list = (List<? extends Expression>)items;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = list.size(); i < count; i++) {
verifyCanRead(list.get(i), parameterName);
}
}
for (final Expression item : items) {
verifyCanRead(item, parameterName);
}
}
private static void verifyCanRead(final Expression[] items, final String parameterName) {
if (items == null) {
return;
}
for (final Expression item : items) {
verifyCanRead(item, parameterName);
}
}
private static void verifyCanWrite(final Expression expression, final String parameterName) {
VerifyArgument.notNull(expression, "expression");
boolean canWrite = false;
switch (expression.getNodeType()) {
case MemberAccess:
final MemberExpression memberExpression = (MemberExpression)expression;
if (memberExpression.getMember() instanceof FieldInfo) {
final FieldInfo field = (FieldInfo)memberExpression.getMember();
canWrite = !field.isEnumConstant() /*&& !field.isFinal()*/;
}
break;
case Parameter:
canWrite = true;
break;
}
if (!canWrite) {
throw Error.expressionMustBeWriteable(parameterName);
}
}
private static void verifyCanWrite(final Iterable<? extends Expression> items, final String parameterName) {
if (items == null) {
return;
}
if (items instanceof List) {
final List<? extends Expression> list = (List<? extends Expression>)items;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, count = list.size(); i < count; i++) {
verifyCanWrite(list.get(i), parameterName);
}
}
for (final Expression item : items) {
verifyCanWrite(item, parameterName);
}
}
static void validateVariables(final ParameterExpressionList varList, final String collectionName) {
if (varList.isEmpty()) {
return;
}
final int count = varList.size();
final HashSet<ParameterExpression> set = new HashSet<>(count);
for (int i = 0; i < count; i++) {
final ParameterExpression v = varList.get(i);
if (v == null) {
throw new IllegalArgumentException(format("%s[%s] is null.", collectionName, i));
}
if (set.contains(v)) {
throw Error.duplicateVariable(v);
}
set.add(v);
}
}
private static UnaryExpression getMethodBasedUnaryOperator(
final ExpressionType unaryType,
final Expression operand,
final MethodInfo method) {
validateOperator(method);
final ParameterList parameters = method.getParameters();
if (parameters.size() != 0) {
throw Error.incorrectNumberOfMethodCallArguments(method);
}
final Type returnType = method.getReturnType();
if (TypeUtils.areReferenceAssignable(operand.getType(), returnType)) {
return new UnaryExpression(unaryType, operand, returnType, method);
}
if (TypeUtils.isAutoUnboxed(operand.getType()) &&
TypeUtils.areReferenceAssignable(TypeUtils.getUnderlyingPrimitive(operand.getType()), returnType)) {
return new UnaryExpression(
unaryType,
operand,
returnType,
method
);
}
throw Error.methodBasedOperatorMustHaveValidReturnType(unaryType, method);
}
private static UnaryExpression getMethodBasedUnaryOperatorOrThrow(
final ExpressionType unaryType,
final String methodName,
final Expression operand) {
final UnaryExpression u = getMethodBasedUnaryOperator(unaryType, methodName, operand);
if (u != null) {
validateOperator(u.getMethod());
return u;
}
throw Error.unaryOperatorNotDefined(unaryType, operand.getType());
}
private static UnaryExpression getMethodBasedUnaryOperator(
final ExpressionType unaryType,
final String methodName,
final Expression operand) {
final Type operandType = operand.getType();
assert !operandType.isPrimitive();
final MethodInfo method = operandType.getMethod(methodName);
return new UnaryExpression(unaryType, operand, method.getReturnType(), method);
}
private static void validateOperator(final MethodInfo method) {
assert method != null;
if (method.isStatic()) {
throw Error.operatorMethodMustNotBeStatic(method);
}
final Type returnType = method.getReturnType();
if (returnType == PrimitiveTypes.Void) {
throw Error.operatorMethodMustNotReturnVoid(method);
}
final ParameterList parameters = method.getParameters();
if (parameters.size() != 0) {
throw Error.operatorMethodParametersMustMatchReturnValue(method);
}
if (TypeUtils.areReferenceAssignable(method.getDeclaringType(), returnType)) {
throw Error.methodBasedOperatorMustHaveValidReturnType(method);
}
}
private static UnaryExpression getMethodBasedCoercionOrThrow(
final ExpressionType coercionType,
final Expression expression,
final Type convertToType) {
final UnaryExpression u = getMethodBasedCoercion(coercionType, expression, convertToType);
if (u != null) {
return u;
}
throw Error.coercionOperatorNotDefined(expression.getType(), convertToType);
}
private static UnaryExpression getMethodBasedCoercion(
final ExpressionType coercionType,
final Expression expression,
final Type convertToType) {
final MethodInfo method = TypeUtils.getCoercionMethod(expression.getType(), convertToType);
if (method != null) {
return new UnaryExpression(coercionType, expression, convertToType, method);
}
else {
return null;
}
}
private static UnaryExpression getMethodBasedCoercionOperator(
final ExpressionType unaryType,
final Expression operand,
final Type convertToType,
final MethodInfo method) {
assert method != null;
validateOperator(method);
final ParameterList parameters = method.getParameters();
if (parameters.size() != 0) {
throw Error.incorrectNumberOfMethodCallArguments(method);
}
final Type returnType = method.getReturnType();
if (TypeUtils.areReferenceAssignable(convertToType, returnType)) {
return new UnaryExpression(unaryType, operand, returnType, method);
}
// check for auto(un)boxing call
if (TypeUtils.isAutoUnboxed(convertToType) && returnType.isPrimitive() &&
TypeUtils.areEquivalent(returnType, TypeUtils.getUnderlyingPrimitive(convertToType))) {
return new UnaryExpression(unaryType, operand, convertToType, method);
}
throw Error.methodBasedOperatorMustHaveValidReturnType(unaryType, method);
}
private static UnaryExpression makeOpAssignUnary(final ExpressionType kind, final Expression expression, final MethodInfo method) {
verifyCanRead(expression, "expression");
verifyCanWrite(expression, "expression");
final UnaryExpression result;
if (method == null) {
if (TypeUtils.isArithmetic(expression.getType())) {
return new UnaryExpression(kind, expression, expression.getType(), null);
}
final String methodName;
if (kind == ExpressionType.PreIncrementAssign || kind == ExpressionType.PostIncrementAssign) {
methodName = "increment";
}
else {
methodName = "decrement";
}
result = getMethodBasedUnaryOperatorOrThrow(kind, methodName, expression);
}
else {
result = getMethodBasedUnaryOperator(kind, expression, method);
}
// Return type must be assignable back to the operand type
if (!TypeUtils.areReferenceAssignable(expression.getType(), result.getType())) {
throw Error.methodBasedOperatorMustHaveValidReturnType(kind, method);
}
return result;
}
static boolean parameterIsAssignable(final Type parameterType, final Type argumentType) {
return argumentType.isPrimitive() && parameterType == Types.Object ||
parameterType.isAssignableFrom(argumentType);
}
static MethodInfo getMethodValidated(
final Type type,
final String name,
final Set<BindingFlags> bindingFlags,
final CallingConvention callingConvention,
final Type... parameterTypes) {
final MethodInfo method = type.getMethod(name, bindingFlags, callingConvention, parameterTypes);
return methodArgumentsMatch(method, parameterTypes) ? method : null;
}
static boolean methodArgumentsMatch(
final MethodInfo method,
final Type... argumentTypes) {
if (method == null || argumentTypes == null) {
return false;
}
final ParameterList parameters = method.getParameters();
final int parameterCount = parameters.size();
if (parameterCount != argumentTypes.length) {
return false;
}
for (int i = 0; i < parameterCount; i++) {
if (!parameterIsAssignable(parameters.get(i).getParameterType(), argumentTypes[i])) {
return false;
}
}
return true;
}
static <T> List<T> ensureUnmodifiable(final List<T> list) {
if (UNMODIFIABLE_LIST_CLASS.isInstance(list)) {
return list;
}
return Collections.unmodifiableList(list);
}
@SuppressWarnings("unchecked")
static <T extends Expression> T returnObject(final Class<T> clazz, final Object objectOrCollection) {
if (clazz.isInstance(objectOrCollection)) {
return (T)objectOrCollection;
}
return ((ExpressionList<T>)objectOrCollection).get(0);
}
private static BinaryExpression aggregateBinary(
final ExpressionType binaryType,
final ImmutableList<Expression> operands) {
if (operands.size() == 2)
return makeBinary(binaryType, operands.head, operands.tail.head);
return makeBinary(
binaryType,
operands.head,
aggregateBinary(binaryType, operands.tail)
);
}
private static void verifyTypeBinaryExpressionOperand(final Expression expression, final Type type) {
/*
if (expression.getType().isPrimitive()) {
throw Error.primitiveCannotBeTypeBinaryOperand();
}
*/
if (type.isPrimitive()) {
throw Error.primitiveCannotBeTypeBinaryType();
}
}
static MethodInfo getInvokeMethod(final Expression expression) {
return getInvokeMethod(expression.getType(), true);
}
static MethodInfo getInvokeMethod(final Type interfaceType, final boolean throwOnError) {
if (!interfaceType.isInterface()) {
if (throwOnError) {
throw Error.expressionTypeNotInvokable(interfaceType);
}
return null;
}
final MethodList methods = interfaceType.getMethods();
MethodInfo invokeMethod = null;
for (final MethodInfo method : methods) {
if (method.isDefault()) {
continue;
}
if (invokeMethod != null) {
invokeMethod = null;
break;
}
invokeMethod = method;
}
if (invokeMethod == null && throwOnError) {
throw Error.expressionTypeNotInvokable(interfaceType);
}
return invokeMethod;
}
private static BinaryExpression getEqualityComparisonOperator(
final ExpressionType binaryType,
final String opName,
final Expression left,
final Expression right) {
final Type<?> leftType = left.getType();
final Type<?> rightType = right.getType();
// Built-in primitive and enum operators
if (TypeUtils.hasBuiltInEqualityOperator(leftType, rightType)) {
if (leftType.isEnum() || rightType.isEnum()) {
return new LogicalBinaryExpression(binaryType, left, right);
}
return new LogicalBinaryExpression(
binaryType,
leftType.isPrimitive() ? left : unbox(left),
rightType.isPrimitive() ? right : unbox(right)
);
}
// Non-primitive null comparisons of the form x==null, x!=null, null==x or null!=x
if (isNullComparison(left, right)) {
return new LogicalBinaryExpression(binaryType, left, right);
}
// look for user defined operator
final BinaryExpression b = getMethodBasedBinaryOperator(binaryType, opName, left, right);
if (b != null) {
return b;
}
throw Error.binaryOperatorNotDefined(binaryType, leftType, rightType);
}
private static MethodInfo getBinaryOperatorMethod(
final ExpressionType binaryType,
final Type leftType,
final Type rightType,
final String name) {
final MethodInfo method = getMethodValidated(
leftType,
name,
BindingFlags.PublicInstance,
CallingConvention.Standard,
rightType
);
if (method != null || TypeUtils.areEquivalent(leftType, rightType)) {
return method;
}
return getMethodValidated(
rightType,
name,
BindingFlags.PublicInstance,
CallingConvention.Standard,
leftType
);
}
private static MethodInfo getBinaryOperatorStaticMethod(
final ExpressionType binaryType,
final Type leftType,
final Type rightType,
final String name) {
final MethodInfo method = getMethodValidated(
leftType,
name,
BindingFlags.PublicStatic,
CallingConvention.Standard,
leftType,
rightType
);
if (method != null || TypeUtils.areEquivalent(leftType, rightType)) {
return method;
}
return getMethodValidated(
rightType,
name,
BindingFlags.PublicStatic,
CallingConvention.Standard,
leftType,
rightType
);
}
private static BinaryExpression getMethodBasedBinaryOperator(
final ExpressionType binaryType,
final String name,
final Expression left,
final Expression right) {
switch (binaryType) {
case Equal:
case NotEqual:
return getEqualsMethodBasedBinaryOperator(binaryType, left, right);
}
final Type leftType = left.getType();
final Type rightType = right.getType();
if (name != null) {
// try exact match first
MethodInfo method = getBinaryOperatorStaticMethod(binaryType, leftType, rightType, name);
if (method != null) {
return new MethodBinaryExpression(binaryType, left, right, method.getReturnType(), method);
}
method = getBinaryOperatorMethod(binaryType, leftType, rightType, name);
if (method != null) {
return new MethodBinaryExpression(binaryType, left, right, method.getReturnType(), method);
}
// try auto(un)boxing call
if (TypeUtils.isAutoUnboxed(rightType)) {
final Type unboxedRightType = TypeUtils.getUnderlyingPrimitive(rightType);
method = getBinaryOperatorMethod(binaryType, leftType, unboxedRightType, name);
if (method != null) {
return new MethodBinaryExpression(binaryType, left, right, method.getReturnType(), method);
}
}
else if (rightType.isPrimitive()) {
if (TypeUtils.isAutoUnboxed(rightType)) {
final Type boxedRightType = TypeUtils.getBoxedType(rightType);
method = getBinaryOperatorMethod(binaryType, leftType, boxedRightType, name);
if (method != null) {
return new MethodBinaryExpression(binaryType, left, right, method.getReturnType(), method);
}
}
}
}
switch (binaryType) {
case GreaterThan:
case GreaterThanOrEqual:
case LessThan:
case LessThanOrEqual:
return getCompareMethodBasedBinaryOperator(binaryType, left, right);
}
return null;
}
private static BinaryExpression getCompareMethodBasedBinaryOperator(
final ExpressionType binaryType,
final Expression left,
final Expression right) {
final Type leftType = left.getType();
final Type rightType = right.getType();
MethodInfo method;
if (TypeUtils.areEquivalent(leftType, rightType)) {
final Type comparable = Types.Comparable.makeGenericType(leftType);
if (leftType.implementsInterface(comparable)) {
method = getMethodValidated(
Types.Comparer,
"compare",
BindingFlags.PublicStatic,
CallingConvention.Standard,
Types.Comparable,
Types.Comparable
);
if (method != null) {
method = method.makeGenericMethod(leftType);
return new CompareMethodBasedLogicalBinaryExpression(binaryType, left, right, method);
}
}
}
if (TypeUtils.hasIdentityPrimitiveOrBoxingConversion(leftType, rightType)) {
method = getMethodValidated(
Types.Comparer,
"compare",
BindingFlags.PublicStatic,
CallingConvention.Standard,
Types.Object,
Types.Object
);
if (method != null) {
return new CompareMethodBasedLogicalBinaryExpression(binaryType, left, right, method);
}
}
return null;
}
private static BinaryExpression getEqualsMethodBasedBinaryOperator(
final ExpressionType binaryType,
final Expression left,
final Expression right) {
return new EqualsMethodBasedLogicalBinaryExpression(binaryType, left, right, null);
}
private static BinaryExpression getMethodBasedBinaryOperator(
final ExpressionType binaryType,
final Expression left,
final Expression right,
final MethodInfo method) {
assert method != null;
if (method.isStatic()) {
return getStaticMethodBasedBinaryOperator(binaryType, left, right, method);
}
final ParameterList parameters = method.getParameters();
if (parameters.size() != 1) {
throw Error.incorrectNumberOfMethodCallArguments(method);
}
final Type returnType = method.getReturnType();
final Type parameterType = parameters.get(0).getParameterType();
final Type rightType = right.getType();
if (parameterIsAssignable(parameters.get(0).getParameterType(), rightType)) {
return new MethodBinaryExpression(binaryType, left, right, returnType, method);
}
throw Error.methodBasedOperatorMustHaveValidReturnType(binaryType, method);
}
private static BinaryExpression getStaticMethodBasedBinaryOperator(
final ExpressionType binaryType,
final Expression left,
final Expression right,
final MethodInfo method) {
assert method != null;
final ParameterList parameters = method.getParameters();
if (parameters.size() != 2) {
throw Error.incorrectNumberOfMethodCallArguments(method);
}
final Type returnType = method.getReturnType();
final Type leftParameterType = parameters.get(0).getParameterType();
final Type rightParameterType = parameters.get(1).getParameterType();
final Type leftType = left.getType();
final Type rightType = right.getType();
if (parameterIsAssignable(parameters.get(0).getParameterType(), leftType) &&
parameterIsAssignable(parameters.get(1).getParameterType(), rightType)) {
return new MethodBinaryExpression(binaryType, left, right, returnType, method);
}
throw Error.methodBasedOperatorMustHaveValidReturnType(binaryType, method);
}
private static BinaryExpression getMethodBasedBinaryOperatorOrThrow(
final ExpressionType binaryType,
final String name,
final Expression left,
final Expression right) {
final BinaryExpression b = Expression.getMethodBasedBinaryOperator(binaryType, name, left, right);
if (b != null) {
return b;
}
throw Error.binaryOperatorNotDefined(binaryType, left.getType(), right.getType());
}
private static BinaryExpression getMethodBasedAssignOperator(
final ExpressionType binaryType,
final String name,
final Expression left,
final Expression right,
final LambdaExpression<?> conversion) {
final MethodInfo method = getBinaryOperatorMethod(binaryType, left.getType(), right.getType(), name);
if (method != null) {
return new MethodBinaryExpression(binaryType, left, right, method.getReturnType(), method);
}
return null;
}
private static BinaryExpression getMethodBasedAssignOperatorOrThrow(
final ExpressionType binaryType,
final String name,
final Expression left,
final Expression right,
final LambdaExpression<?> conversion) {
BinaryExpression b = getMethodBasedBinaryOperatorOrThrow(binaryType, name, left, right);
if (conversion == null) {
if (!TypeUtils.areReferenceAssignable(left.getType(), right.getType())) {
throw Error.methodBasedOperatorMustHaveValidReturnType(binaryType, b.getMethod());
}
}
else {
validateOpAssignConversionLambda(conversion, b.getLeft(), b.getMethod(), b.getNodeType());
b = new OpAssignMethodConversionBinaryExpression(
b.getNodeType(),
b.getLeft(),
b.getRight(),
b.getLeft().getType(),
b.getMethod(),
conversion
);
}
return b;
}
private static BinaryExpression getMethodBasedAssignOperator(
final ExpressionType binaryType,
final Expression left,
final Expression right,
final MethodInfo method,
final LambdaExpression<?> conversion) {
BinaryExpression b = getMethodBasedBinaryOperator(binaryType, left, right, method);
if (conversion == null) {
if (!TypeUtils.areReferenceAssignable(left.getType(), right.getType())) {
throw Error.methodBasedOperatorMustHaveValidReturnType(binaryType, b.getMethod());
}
}
else {
validateOpAssignConversionLambda(conversion, b.getLeft(), b.getMethod(), b.getNodeType());
b = new OpAssignMethodConversionBinaryExpression(
b.getNodeType(),
b.getLeft(),
b.getRight(),
b.getLeft().getType(),
b.getMethod(),
conversion
);
}
return b;
}
private static void validateOpAssignConversionLambda(
final LambdaExpression<?> conversion,
final Expression left,
final MethodInfo method,
final ExpressionType nodeType) {
final Type interfaceType = conversion.getType();
final MethodInfo invokeMethod = getInvokeMethod(conversion);
final ParameterList parameters = invokeMethod.getParameters();
if (parameters.size() != 1) {
throw Error.incorrectNumberOfMethodCallArguments(invokeMethod);
}
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(invokeMethod.getReturnType(), left.getType())) {
throw Error.operandTypesDoNotMatchParameters(nodeType, invokeMethod);
}
if (method != null) {
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(
parameters.get(0).getParameterType(),
method.getReturnType()
)) {
throw Error.overloadOperatorTypeDoesNotMatchConversionType(nodeType, method);
}
}
}
private static BinaryExpression getComparisonOperator(
final ExpressionType binaryType,
final Expression left,
final Expression right) {
if (TypeUtils.isArithmetic(left.getType()) &&
(TypeUtils.isArithmetic(right.getType()) ||
TypeUtils.hasIdentityPrimitiveOrBoxingConversion(left.getType(), right.getType()))) {
return new LogicalBinaryExpression(binaryType, left, right);
}
final BinaryExpression b = getCompareMethodBasedBinaryOperator(binaryType, left, right);
if (b != null) {
return b;
}
throw Error.binaryOperatorNotDefined(binaryType, left.getType(), right.getType());
}
private static boolean isNullConstant(final Expression e) {
return e instanceof ConstantExpression && ((ConstantExpression)e).getValue() == null ||
e instanceof DefaultValueExpression && !e.getType().isPrimitive();
}
private static boolean isNullComparison(final Expression left, final Expression right) {
// Do we have x==null, x!=null, null==x or null!=x where x is a reference type but not null?
return isNullConstant(left) && !isNullConstant(right) && !right.getType().isPrimitive() ||
isNullConstant(right) && !isNullConstant(left) && !left.getType().isPrimitive();
}
private static void validateStaticOrInstanceMethod(final Expression instance, final MethodInfo method) {
if (method.isStatic()) {
if (instance != null) {
throw Error.targetInvalidForStaticMethodCall(method);
}
}
else {
if (instance == null) {
throw Error.targetRequiredForNonStaticMethodCall(method);
}
verifyCanRead(instance, "instance");
validateCallTargetType(instance.getType(), method);
}
}
private static void validateCallTargetType(final Type targetType, final MethodInfo method) {
if (!TypeUtils.isValidInvocationTargetType(method, targetType)) {
throw Error.targetAndMethodTypeMismatch(method, targetType);
}
}
private static <T extends Expression> ExpressionList<T> validateArgumentTypes(
final MethodBase method,
final ExpressionType nodeKind,
final ExpressionList<T> arguments) {
assert nodeKind == ExpressionType.Invoke ||
nodeKind == ExpressionType.Call ||
nodeKind == ExpressionType.New;
final TypeList parameterTypes;
if (method instanceof MethodBuilder) {
parameterTypes = ((MethodBuilder) method).getParameterTypes();
}
else if (method instanceof ConstructorBuilder) {
parameterTypes = ((ConstructorBuilder) method).getMethodBuilder().getParameterTypes();
}
else {
parameterTypes = method.getParameters().getParameterTypes();
}
validateArgumentCount(method, nodeKind, arguments.size(), parameterTypes);
T[] newArgs = null;
for (int i = 0, n = parameterTypes.size(); i < n; i++) {
final Type parameterType = parameterTypes.get(i);
final T arg = validateOneArgument(method, nodeKind, arguments.get(i), parameterType);
if (arg != arguments.get(i)) {
if (newArgs == null) {
newArgs = arguments.toArray();
}
newArgs[i] = arg;
}
}
if (newArgs != null) {
return arguments.newInstance(newArgs);
}
return arguments;
}
private static <T extends Expression> T validateOneArgument(
final MethodBase method,
final ExpressionType nodeKind,
final T arg,
final Type parameterType) {
verifyCanRead(arg, "arguments");
final Type argType = arg.getType();
if (!parameterType.isAssignableFrom(argType)) {
switch (nodeKind) {
case New:
throw Error.expressionTypeDoesNotMatchConstructorParameter(argType, parameterType);
case Invoke:
throw Error.expressionTypeDoesNotMatchParameter(argType, parameterType);
case Call:
throw Error.expressionTypeDoesNotMatchMethodParameter(argType, parameterType, method);
default:
throw ContractUtils.unreachable();
}
}
return arg;
}
private static void validateArgumentCount(
final MethodBase method,
final ExpressionType nodeKind,
final int count,
final TypeList parameterTypes) {
if (parameterTypes.size() == count) {
return;
}
// Throw the right error for the node we were given
switch (nodeKind) {
case New:
throw Error.incorrectNumberOfConstructorArguments();
case Invoke:
throw Error.incorrectNumberOfLambdaArguments();
case Call:
throw Error.incorrectNumberOfMethodCallArguments(method);
default:
throw ContractUtils.unreachable();
}
}
private static <T> void validateLambdaArgs(
final Type<T> interfaceType,
final Expression body,
final ParameterExpressionList parameters) {
VerifyArgument.notNull(interfaceType, "interfaceType");
verifyCanRead(body, "body");
final MethodInfo invokeMethod = getInvokeMethod(interfaceType, false);
if (invokeMethod == null) {
throw Error.lambdaTypeMustBeSingleMethodInterface();
}
final ParameterList methodParameters = invokeMethod.getParameters();
if (methodParameters.size() > 0) {
if (parameters.size() != methodParameters.size()) {
throw Error.incorrectNumberOfLambdaArguments();
}
final Set<ParameterExpression> set = new HashSet<>(parameters.size());
for (int i = 0, n = methodParameters.size(); i < n; i++) {
final ParameterExpression pex = parameters.get(i);
final ParameterInfo pi = methodParameters.get(i);
verifyCanRead(pex, "parameters");
final Type pType = pi.getParameterType();
if (!TypeUtils.areEquivalent(pex.getType(), pType)) {
if (!pType.isGenericParameter() || !pType.isAssignableFrom(pex.getType())) {
throw Error.parameterExpressionNotValidForDelegate(pex.getType(), pType);
}
}
if (set.contains(pex)) {
throw Error.duplicateVariable(pex);
}
set.add(pex);
}
}
else if (parameters.size() > 0) {
throw Error.incorrectNumberOfLambdaDeclarationParameters();
}
final Type returnType = invokeMethod.getReturnType();
if (returnType != PrimitiveTypes.Void &&
!TypeUtils.areEquivalent(returnType, body.getType())) {
if (/*!returnType.isGenericParameter() ||*/ !returnType.isAssignableFrom(body.getType())) {
throw Error.expressionTypeDoesNotMatchReturn(body.getType(), returnType);
}
}
}
private static void validateGoto(
final LabelTarget target,
final Expression value,
final String targetParameter,
final String valueParameter) {
VerifyArgument.notNull(target, targetParameter);
if (value == null) {
if (target.getType() != PrimitiveTypes.Void) {
throw Error.labelMustBeVoidOrHaveExpression();
}
}
}
private static void validateGotoType(final Type expectedType, final Expression value, final String valueParameter) {
verifyCanRead(value, valueParameter);
if (expectedType != PrimitiveTypes.Void &&
!TypeUtils.areReferenceAssignable(expectedType, value.getType())) {
throw Error.expressionTypeDoesNotMatchLabel(value.getType(), expectedType);
}
}
private static void validateTryAndCatchHaveSameType(
final Type type,
final Expression tryBody,
final ReadOnlyList<CatchBlock> handlers) {
// Type unification ... all parts must be reference assignable to "type"
if (type != null) {
if (type != PrimitiveTypes.Void) {
if (!TypeUtils.areReferenceAssignable(type, tryBody.getType())) {
throw Error.argumentTypesMustMatch();
}
for (int i = 0, n = handlers.size(); i < n; i++) {
final CatchBlock cb = handlers.get(i);
if (!TypeUtils.areReferenceAssignable(type, cb.getBody().getType())) {
throw Error.argumentTypesMustMatch();
}
}
}
}
else if (tryBody == null || tryBody.getType() == PrimitiveTypes.Void) {
//The body of every try block must be null or have void type.
for (int i = 0, n = handlers.size(); i < n; i++) {
final Expression catchBody = handlers.get(i).getBody();
if (catchBody != null && catchBody.getType() != PrimitiveTypes.Void) {
throw Error.bodyOfCatchMustHaveSameTypeAsBodyOfTry();
}
}
}
else {
//Body of every catch must have the same type of body of try.
final Type tryType = tryBody.getType();
for (int i = 0, n = handlers.size(); i < n; i++) {
final Expression catchBody = handlers.get(i).getBody();
if (catchBody == null || !TypeUtils.areEquivalent(catchBody.getType(), tryType)) {
throw Error.bodyOfCatchMustHaveSameTypeAsBodyOfTry();
}
}
}
}
private static void validateSwitchCaseType(
final Expression caseBody,
final boolean customType,
final Type resultType,
final String parameterName) {
if (customType) {
if (resultType != PrimitiveTypes.Void) {
if (!TypeUtils.areReferenceAssignable(resultType, caseBody.getType())) {
throw Error.argumentTypesMustMatch();
}
}
}
else {
if (!TypeUtils.hasIdentityPrimitiveOrBoxingConversion(resultType, caseBody.getType())) {
throw Error.allCaseBodiesMustHaveSameType();
}
}
}
private final static Set<BindingFlags> StaticMemberBindingFlags = BindingFlags.set(
BindingFlags.Static,
BindingFlags.Public,
BindingFlags.NonPublic,
BindingFlags.FlattenHierarchy
);
private final static Set<BindingFlags> InstanceMemberBindingFlags = BindingFlags.set(
BindingFlags.Instance,
BindingFlags.Public,
BindingFlags.NonPublic,
BindingFlags.FlattenHierarchy
);
private static FieldInfo findField(
final Type declaringType,
final String fieldName,
final Set<BindingFlags> flags) {
final MemberList members = declaringType.findMembers(
MemberType.fieldsOnly(),
flags,
Type.FilterNameIgnoreCase,
fieldName
);
if (members == null || members.size() == 0) {
throw Error.fieldDoesNotExistOnType(fieldName, declaringType);
}
return (FieldInfo) members.get(0);
}
private static MethodInfo findMethod(
final Type type,
final String methodName,
final TypeList typeArguments,
final ExpressionList<? extends Expression> arguments,
final Set<BindingFlags> flags) {
final MemberList members = type.findMembers(
MemberType.methodsOnly(),
flags,
Type.FilterNameIgnoreCase,
methodName
);
if (members == null || members.size() == 0) {
throw Error.methodDoesNotExistOnType(methodName, type);
}
final ArrayList<MethodInfo> candidates = new ArrayList<>(members.size());
for (int i = 0, n = members.size(); i < n; i++) {
final MethodInfo appliedMethod = applyTypeArgs(
(MethodInfo) members.get(i),
typeArguments
);
if (appliedMethod != null)
candidates.add(appliedMethod);
}
final Type[] parameterTypes = new Type[arguments.size()];
for (int i = 0, n = arguments.size(); i < n; i++) {
parameterTypes[i] = arguments.get(i).getType();
}
final MethodInfo result = (MethodInfo) Type.DefaultBinder.selectMethod(
flags,
candidates.toArray(new MethodBase[candidates.size()]),
parameterTypes
);
if (result == null) {
throw Error.methodWithArgsDoesNotExistOnType(methodName, type);
}
return result;
}
private static int findBestMethod(
final MemberList<?> methods,
final TypeList typeArgs,
final ExpressionList<? extends Expression> arguments) {
int count = 0;
int bestMethodIndex = -1;
MethodInfo bestMethod = null;
for (int i = 0, n = methods.size(); i < n; i++) {
final MethodInfo method = applyTypeArgs((MethodInfo)methods.get(i), typeArgs);
if (method != null && isCompatible(method, arguments)) {
// Favor public over non-public methods.
if (bestMethod == null || (!bestMethod.isPublic() && method.isPublic())) {
bestMethodIndex = i;
bestMethod = method;
count = 1;
}
else {
// Only count it as additional method if they both public or both non-public.
if (bestMethod.isPublic() == method.isPublic()) {
count++;
}
}
}
}
if (count > 1)
return -2;
return bestMethodIndex;
}
private static boolean isCompatible(final MethodBase m, final ExpressionList<? extends Expression> arguments) {
VerifyArgument.noNullElements(arguments, "arguments");
final ParameterList parameters = m.getParameters();
if (parameters.size() != arguments.size()) {
return false;
}
for (int i = 0, n = arguments.size(); i < n; i++) {
final Expression argument = arguments.get(i);
final Type argumentType = argument.getType();
final Type parameterType = parameters.get(i).getParameterType();
if (!TypeUtils.areReferenceAssignable(parameterType, argumentType) &&
!(TypeUtils.isSameOrSubType(Type.of(LambdaExpression.class), parameterType) &&
parameterType.isAssignableFrom(argument.getType()))) {
return false;
}
}
return true;
}
private static MethodInfo applyTypeArgs(final MethodInfo m, final TypeList typeArgs) {
if (typeArgs == null || typeArgs.size() == 0) {
if (!m.isGenericMethodDefinition()) {
return m;
}
}
else if (m.isGenericMethodDefinition()) {
final TypeList genericParameters = m.getGenericMethodParameters();
if (genericParameters.size() == typeArgs.size()) {
for (int i = 0, n = genericParameters.size(); i < n; i++) {
if (!genericParameters.get(i).isAssignableFrom(typeArgs.get(i))) {
return null;
}
}
return m.makeGenericMethod(typeArgs);
}
}
return null;
}
static Type<?> performBinaryNumericPromotion(final Type leftType, final Type rightType) {
if (leftType == PrimitiveTypes.Double || rightType == PrimitiveTypes.Double) {
return PrimitiveTypes.Double;
}
if (leftType == PrimitiveTypes.Float || rightType == PrimitiveTypes.Float) {
return PrimitiveTypes.Float;
}
if (leftType == PrimitiveTypes.Long || rightType == PrimitiveTypes.Long) {
return PrimitiveTypes.Long;
}
if (TypeUtils.isArithmetic(leftType) || TypeUtils.isArithmetic(rightType)) {
return PrimitiveTypes.Integer;
}
return leftType;
}
private static Type validateCoalesceArgumentTypes(final Type left, final Type right) {
final Type leftStripped = TypeUtils.getUnderlyingPrimitive(left);
if (left.isPrimitive()) {
throw Error.coalesceUsedOnNonNullableType();
}
else if (TypeUtils.isAutoUnboxed(left) && TypeUtils.hasReferenceConversion(right, leftStripped)) {
return leftStripped;
}
else if (TypeUtils.hasReferenceConversion(right, left)) {
return left;
}
else if (TypeUtils.isAutoUnboxed(left) && TypeUtils.hasReferenceConversion(leftStripped, right)) {
return right;
}
else {
throw Error.argumentTypesMustMatch();
}
}
}