mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
5.8 KiB
229 lines
5.8 KiB
// |
|
// lambda.cs: support for lambda expressions |
|
// |
|
// Authors: Miguel de Icaza (miguel@gnu.org) |
|
// Marek Safar (marek.safar@gmail.com) |
|
// |
|
// Dual licensed under the terms of the MIT X11 or GNU GPL |
|
// |
|
// Copyright 2007-2008 Novell, Inc |
|
// Copyright 2011 Xamarin Inc |
|
// |
|
|
|
#if STATIC |
|
using IKVM.Reflection.Emit; |
|
#else |
|
using System.Reflection.Emit; |
|
#endif |
|
|
|
namespace Mono.CSharp { |
|
public class LambdaExpression : AnonymousMethodExpression |
|
{ |
|
// |
|
// The parameters can either be: |
|
// A list of Parameters (explicitly typed parameters) |
|
// An ImplicitLambdaParameter |
|
// |
|
public LambdaExpression (Location loc) |
|
: base (loc) |
|
{ |
|
} |
|
|
|
protected override Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type) |
|
{ |
|
if (ec.IsInProbingMode) |
|
return this; |
|
|
|
BlockContext bc = new BlockContext (ec.MemberContext, ec.ConstructorBlock, ec.BuiltinTypes.Void) { |
|
CurrentAnonymousMethod = ec.CurrentAnonymousMethod |
|
}; |
|
|
|
Expression args = Parameters.CreateExpressionTree (bc, loc); |
|
Expression expr = Block.CreateExpressionTree (ec); |
|
if (expr == null) |
|
return null; |
|
|
|
Arguments arguments = new Arguments (2); |
|
arguments.Add (new Argument (expr)); |
|
arguments.Add (new Argument (args)); |
|
return CreateExpressionFactoryCall (ec, "Lambda", |
|
new TypeArguments (new TypeExpression (delegate_type, loc)), |
|
arguments); |
|
} |
|
|
|
public override bool HasExplicitParameters { |
|
get { |
|
return Parameters.Count > 0 && !(Parameters.FixedParameters [0] is ImplicitLambdaParameter); |
|
} |
|
} |
|
|
|
protected override ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegateType) |
|
{ |
|
if (!delegateType.IsDelegate) |
|
return null; |
|
|
|
AParametersCollection d_params = Delegate.GetParameters (delegateType); |
|
|
|
if (HasExplicitParameters) { |
|
if (!VerifyExplicitParameters (ec, delegateType, d_params)) |
|
return null; |
|
|
|
return Parameters; |
|
} |
|
|
|
// |
|
// If L has an implicitly typed parameter list we make implicit parameters explicit |
|
// Set each parameter of L is given the type of the corresponding parameter in D |
|
// |
|
if (!VerifyParameterCompatibility (ec, delegateType, d_params, ec.IsInProbingMode)) |
|
return null; |
|
|
|
TypeSpec [] ptypes = new TypeSpec [Parameters.Count]; |
|
for (int i = 0; i < d_params.Count; i++) { |
|
// D has no ref or out parameters |
|
if ((d_params.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != 0) |
|
return null; |
|
|
|
TypeSpec d_param = d_params.Types [i]; |
|
|
|
// |
|
// When type inference context exists try to apply inferred type arguments |
|
// |
|
if (tic != null) { |
|
d_param = tic.InflateGenericArgument (ec, d_param); |
|
} |
|
|
|
ptypes [i] = d_param; |
|
ImplicitLambdaParameter ilp = (ImplicitLambdaParameter) Parameters.FixedParameters [i]; |
|
ilp.SetParameterType (d_param); |
|
ilp.Resolve (null, i); |
|
} |
|
|
|
Parameters.Types = ptypes; |
|
return Parameters; |
|
} |
|
|
|
protected override AnonymousMethodBody CompatibleMethodFactory (TypeSpec returnType, TypeSpec delegateType, ParametersCompiled p, ParametersBlock b) |
|
{ |
|
return new LambdaMethod (p, b, returnType, delegateType, loc); |
|
} |
|
|
|
protected override bool DoResolveParameters (ResolveContext rc) |
|
{ |
|
// |
|
// Only explicit parameters can be resolved at this point |
|
// |
|
if (HasExplicitParameters) { |
|
return Parameters.Resolve (rc); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public override string GetSignatureForError () |
|
{ |
|
return "lambda expression"; |
|
} |
|
|
|
public override object Accept (StructuralVisitor visitor) |
|
{ |
|
return visitor.Visit (this); |
|
} |
|
} |
|
|
|
class LambdaMethod : AnonymousMethodBody |
|
{ |
|
public LambdaMethod (ParametersCompiled parameters, |
|
ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type, |
|
Location loc) |
|
: base (parameters, block, return_type, delegate_type, loc) |
|
{ |
|
} |
|
|
|
#region Properties |
|
|
|
public override string ContainerType { |
|
get { |
|
return "lambda expression"; |
|
} |
|
} |
|
|
|
#endregion |
|
|
|
protected override void CloneTo (CloneContext clonectx, Expression target) |
|
{ |
|
// TODO: nothing ?? |
|
} |
|
|
|
public override Expression CreateExpressionTree (ResolveContext ec) |
|
{ |
|
BlockContext bc = new BlockContext (ec.MemberContext, Block, ReturnType); |
|
Expression args = parameters.CreateExpressionTree (bc, loc); |
|
Expression expr = Block.CreateExpressionTree (ec); |
|
if (expr == null) |
|
return null; |
|
|
|
Arguments arguments = new Arguments (2); |
|
arguments.Add (new Argument (expr)); |
|
arguments.Add (new Argument (args)); |
|
return CreateExpressionFactoryCall (ec, "Lambda", |
|
new TypeArguments (new TypeExpression (type, loc)), |
|
arguments); |
|
} |
|
} |
|
|
|
// |
|
// This is a return statement that is prepended lambda expression bodies that happen |
|
// to be expressions. Depending on the return type of the delegate this will behave |
|
// as either { expr (); return (); } or { return expr (); } |
|
// |
|
public class ContextualReturn : Return |
|
{ |
|
ExpressionStatement statement; |
|
|
|
public ContextualReturn (Expression expr) |
|
: base (expr, expr.Location) |
|
{ |
|
} |
|
|
|
public override Expression CreateExpressionTree (ResolveContext ec) |
|
{ |
|
return Expr.CreateExpressionTree (ec); |
|
} |
|
|
|
protected override void DoEmit (EmitContext ec) |
|
{ |
|
if (statement != null) { |
|
statement.EmitStatement (ec); |
|
if (unwind_protect) |
|
ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ()); |
|
else { |
|
ec.Emit (OpCodes.Ret); |
|
} |
|
return; |
|
} |
|
|
|
base.DoEmit (ec); |
|
} |
|
|
|
protected override bool DoResolve (BlockContext ec) |
|
{ |
|
// |
|
// When delegate returns void, only expression statements can be used |
|
// |
|
if (ec.ReturnType.Kind == MemberKind.Void) { |
|
Expr = Expr.Resolve (ec); |
|
if (Expr == null) |
|
return false; |
|
|
|
statement = Expr as ExpressionStatement; |
|
if (statement == null) |
|
Expr.Error_InvalidExpressionStatement (ec); |
|
|
|
return true; |
|
} |
|
|
|
return base.DoResolve (ec); |
|
} |
|
} |
|
}
|
|
|