// // anonymous.cs: Support for anonymous methods and types // // Author: // Miguel de Icaza (miguel@ximain.com) // Marek Safar (marek.safar@gmail.com) // // Dual licensed under the terms of the MIT X11 or GNU GPL // Copyright 2003-2008 Novell, Inc. // using System; using System.Collections.Generic; #if STATIC using IKVM.Reflection; using IKVM.Reflection.Emit; #else using System.Reflection; using System.Reflection.Emit; #endif namespace Mono.CSharp { public abstract class CompilerGeneratedClass : Class { protected CompilerGeneratedClass (DeclSpace parent, MemberName name, Modifiers mod) : base (parent.NamespaceEntry, parent, name, mod | Modifiers.COMPILER_GENERATED, null) { } protected void CheckMembersDefined () { if (HasMembersDefined) throw new InternalErrorException ("Helper class already defined!"); } protected static MemberName MakeMemberName (MemberBase host, string name, int unique_id, TypeParameter[] tparams, Location loc) { string host_name = host == null ? null : host.Name; string tname = MakeName (host_name, "c", name, unique_id); TypeArguments args = null; if (tparams != null) { args = new TypeArguments (); foreach (TypeParameter tparam in tparams) args.Add (new TypeParameterName (tparam.Name, null, loc)); } return new MemberName (tname, args, loc); } public static string MakeName (string host, string typePrefix, string name, int id) { return "<" + host + ">" + typePrefix + "__" + name + id.ToString ("X"); } } public class HoistedStoreyClass : CompilerGeneratedClass { public sealed class HoistedField : Field { public HoistedField (HoistedStoreyClass parent, FullNamedExpression type, Modifiers mod, string name, Attributes attrs, Location loc) : base (parent, type, mod, new MemberName (name, loc), attrs) { } protected override bool ResolveMemberType () { if (!base.ResolveMemberType ()) return false; HoistedStoreyClass parent = ((HoistedStoreyClass) Parent).GetGenericStorey (); if (parent != null && parent.Mutator != null) member_type = parent.Mutator.Mutate (MemberType); return true; } } protected TypeParameterMutator mutator; public HoistedStoreyClass (DeclSpace parent, MemberName name, TypeParameter[] tparams, Modifiers mod) : base (parent, name, mod | Modifiers.PRIVATE) { if (tparams != null) { type_params = new TypeParameter[tparams.Length]; var src = new TypeParameterSpec[tparams.Length]; var dst = new TypeParameterSpec[tparams.Length]; for (int i = 0; i < type_params.Length; ++i) { type_params[i] = tparams[i].CreateHoistedCopy (this, spec); src[i] = tparams[i].Type; dst[i] = type_params[i].Type; } // A copy is not enough, inflate any type parameter constraints // using a new type parameters var inflator = new TypeParameterInflator (this, null, src, dst); for (int i = 0; i < type_params.Length; ++i) { src[i].InflateConstraints (inflator, dst[i]); } } } #region Properties public TypeParameterMutator Mutator { get { return mutator; } set { mutator = value; } } #endregion public HoistedStoreyClass GetGenericStorey () { DeclSpace storey = this; while (storey != null && storey.CurrentTypeParameters == null) storey = storey.Parent; return storey as HoistedStoreyClass; } } // // Anonymous method storey is created when an anonymous method uses // variable or parameter from outer scope. They are then hoisted to // anonymous method storey (captured) // public class AnonymousMethodStorey : HoistedStoreyClass { struct StoreyFieldPair { public readonly AnonymousMethodStorey Storey; public readonly Field Field; public StoreyFieldPair (AnonymousMethodStorey storey, Field field) { this.Storey = storey; this.Field = field; } } // // Needed to delay hoisted _this_ initialization. When an anonymous // method is used inside ctor and _this_ is hoisted, base ctor has to // be called first, otherwise _this_ will be initialized with // uninitialized value. // sealed class ThisInitializer : Statement { readonly HoistedThis hoisted_this; public ThisInitializer (HoistedThis hoisted_this) { this.hoisted_this = hoisted_this; } protected override void DoEmit (EmitContext ec) { hoisted_this.EmitHoistingAssignment (ec); } protected override void CloneTo (CloneContext clonectx, Statement target) { // Nothing to clone } } // Unique storey ID public readonly int ID; static int unique_id; public readonly Block OriginalSourceBlock; // A list of StoreyFieldPair with local field keeping parent storey instance List used_parent_storeys; List children_references; // A list of hoisted parameters protected List hoisted_params; protected List hoisted_locals; // Hoisted this protected HoistedThis hoisted_this; // Local variable which holds this storey instance public LocalTemporary Instance; public AnonymousMethodStorey (Block block, TypeContainer parent, MemberBase host, TypeParameter[] tparams, string name) : base (parent, MakeMemberName (host, name, unique_id, tparams, block.StartLocation), tparams, Modifiers.SEALED) { Parent = parent; OriginalSourceBlock = block; ID = unique_id++; } public void AddCapturedThisField (EmitContext ec) { TypeExpr type_expr = new TypeExpression (ec.CurrentType, Location); Field f = AddCompilerGeneratedField ("<>f__this", type_expr); f.Define (); hoisted_this = new HoistedThis (this, f); // Inflated type instance has to be updated manually if (Instance.Type is InflatedTypeSpec) { var inflator = new TypeParameterInflator (this, Instance.Type, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes); Instance.Type.MemberCache.AddMember (f.Spec.InflateMember (inflator)); inflator = new TypeParameterInflator (this, f.Parent.CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes); f.Parent.CurrentType.MemberCache.AddMember (f.Spec.InflateMember (inflator)); } } public Field AddCapturedVariable (string name, TypeSpec type) { CheckMembersDefined (); FullNamedExpression field_type = new TypeExpression (type, Location); if (!IsGeneric) return AddCompilerGeneratedField (name, field_type); const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED; Field f = new HoistedField (this, field_type, mod, name, null, Location); AddField (f); return f; } protected Field AddCompilerGeneratedField (string name, FullNamedExpression type) { const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED; Field f = new Field (this, type, mod, new MemberName (name, Location), null); AddField (f); return f; } // // Creates a link between hoisted variable block and the anonymous method storey // // An anonymous method can reference variables from any outer block, but they are // hoisted in their own ExplicitBlock. When more than one block is referenced we // need to create another link between those variable storeys // public void AddReferenceFromChildrenBlock (ExplicitBlock block) { if (children_references == null) children_references = new List (); if (!children_references.Contains (block)) children_references.Add (block); } public void AddParentStoreyReference (EmitContext ec, AnonymousMethodStorey storey) { CheckMembersDefined (); if (used_parent_storeys == null) used_parent_storeys = new List (); else if (used_parent_storeys.Exists (i => i.Storey == storey)) return; TypeExpr type_expr = storey.CreateStoreyTypeExpression (ec); Field f = AddCompilerGeneratedField ("<>f__ref$" + storey.ID, type_expr); used_parent_storeys.Add (new StoreyFieldPair (storey, f)); } public void CaptureLocalVariable (ResolveContext ec, LocalVariable local_info) { ec.CurrentBlock.Explicit.HasCapturedVariable = true; if (ec.CurrentBlock.Explicit != local_info.Block.Explicit) AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit); if (local_info.HoistedVariant != null) return; HoistedVariable var = new HoistedLocalVariable (this, local_info, GetVariableMangledName (local_info)); local_info.HoistedVariant = var; if (hoisted_locals == null) hoisted_locals = new List (); hoisted_locals.Add (var); } public void CaptureParameter (ResolveContext ec, ParameterReference param_ref) { ec.CurrentBlock.Explicit.HasCapturedVariable = true; AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit); if (param_ref.GetHoistedVariable (ec) != null) return; if (hoisted_params == null) hoisted_params = new List (2); var expr = new HoistedParameter (this, param_ref); param_ref.Parameter.HoistedVariant = expr; hoisted_params.Add (expr); } TypeExpr CreateStoreyTypeExpression (EmitContext ec) { // // Create an instance of storey type // TypeExpr storey_type_expr; if (CurrentTypeParameters != null) { // // Use current method type parameter (MVAR) for top level storey only. All // nested storeys use class type parameter (VAR) // TypeParameter[] tparams = ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null ? ec.CurrentAnonymousMethod.Storey.TypeParameters : ec.CurrentTypeParameters; TypeArguments targs = new TypeArguments (); // // Use type parameter name instead of resolved type parameter // specification to resolve to correctly nested type parameters // for (int i = 0; i < tparams.Length; ++i) targs.Add (new SimpleName (tparams [i].Name, Location)); // new TypeParameterExpr (tparams[i], Location)); storey_type_expr = new GenericTypeExpr (Definition, targs, Location); } else { storey_type_expr = new TypeExpression (CurrentType, Location); } return storey_type_expr; } public void SetNestedStoryParent (AnonymousMethodStorey parentStorey) { Parent = parentStorey; type_params = null; spec.IsGeneric = false; spec.DeclaringType = parentStorey.CurrentType; MemberName.TypeArguments = null; } protected override bool DoResolveTypeParameters () { // Although any storey can have type parameters they are all clones of method type // parameters therefore have to mutate MVAR references in any of cloned constraints if (type_params != null) { for (int i = 0; i < type_params.Length; ++i) { var spec = type_params[i].Type; spec.BaseType = mutator.Mutate (spec.BaseType); if (spec.InterfacesDefined != null) { var mutated = new TypeSpec[spec.InterfacesDefined.Length]; for (int ii = 0; ii < mutated.Length; ++ii) { mutated[ii] = mutator.Mutate (spec.InterfacesDefined[ii]); } spec.InterfacesDefined = mutated; } if (spec.TypeArguments != null) { spec.TypeArguments = mutator.Mutate (spec.TypeArguments); } } } // // Update parent cache as we most likely passed the point // where the cache was constructed // Parent.CurrentType.MemberCache.AddMember (this.spec); return true; } // // Initializes all hoisted variables // public void EmitStoreyInstantiation (EmitContext ec, ExplicitBlock block) { // There can be only one instance variable for each storey type if (Instance != null) throw new InternalErrorException (); SymbolWriter.OpenCompilerGeneratedBlock (ec); // // Create an instance of a storey // var storey_type_expr = CreateStoreyTypeExpression (ec); ResolveContext rc = new ResolveContext (ec.MemberContext); rc.CurrentBlock = block; Expression e = new New (storey_type_expr, null, Location).Resolve (rc); e.Emit (ec); Instance = new LocalTemporary (storey_type_expr.Type); Instance.Store (ec); EmitHoistedFieldsInitialization (rc, ec); SymbolWriter.DefineScopeVariable (ID, Instance.Builder); SymbolWriter.CloseCompilerGeneratedBlock (ec); } void EmitHoistedFieldsInitialization (ResolveContext rc, EmitContext ec) { // // Initialize all storey reference fields by using local or hoisted variables // if (used_parent_storeys != null) { foreach (StoreyFieldPair sf in used_parent_storeys) { // // Get instance expression of storey field // Expression instace_expr = GetStoreyInstanceExpression (ec); var fs = sf.Field.Spec; if (TypeManager.IsGenericType (instace_expr.Type)) fs = MemberCache.GetMember (instace_expr.Type, fs); FieldExpr f_set_expr = new FieldExpr (fs, Location); f_set_expr.InstanceExpression = instace_expr; SimpleAssign a = new SimpleAssign (f_set_expr, sf.Storey.GetStoreyInstanceExpression (ec)); if (a.Resolve (rc) != null) a.EmitStatement (ec); } } // // Define hoisted `this' in top-level storey only // if (OriginalSourceBlock.Explicit.HasCapturedThis && !(Parent is AnonymousMethodStorey)) { AddCapturedThisField (ec); rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this)); } // // Setting currect anonymous method to null blocks any further variable hoisting // AnonymousExpression ae = ec.CurrentAnonymousMethod; ec.CurrentAnonymousMethod = null; if (hoisted_params != null) { EmitHoistedParameters (ec, hoisted_params); } ec.CurrentAnonymousMethod = ae; } protected virtual void EmitHoistedParameters (EmitContext ec, IList hoisted) { foreach (HoistedParameter hp in hoisted) { hp.EmitHoistingAssignment (ec); } } public override void EmitType () { SymbolWriter.DefineAnonymousScope (ID); if (hoisted_this != null) hoisted_this.EmitSymbolInfo (); if (hoisted_locals != null) { foreach (HoistedVariable local in hoisted_locals) local.EmitSymbolInfo (); } if (hoisted_params != null) { foreach (HoistedParameter param in hoisted_params) param.EmitSymbolInfo (); } if (used_parent_storeys != null) { foreach (StoreyFieldPair sf in used_parent_storeys) { SymbolWriter.DefineCapturedScope (ID, sf.Storey.ID, sf.Field.Name); } } base.EmitType (); } // // Returns a field which holds referenced storey instance // Field GetReferencedStoreyField (AnonymousMethodStorey storey) { if (used_parent_storeys == null) return null; foreach (StoreyFieldPair sf in used_parent_storeys) { if (sf.Storey == storey) return sf.Field; } return null; } // // Creates storey instance expression regardless of currect IP // public Expression GetStoreyInstanceExpression (EmitContext ec) { AnonymousExpression am = ec.CurrentAnonymousMethod; // // Access from original block -> storey // if (am == null) return Instance; // // Access from anonymous method implemented as a static -> storey // if (am.Storey == null) return Instance; Field f = am.Storey.GetReferencedStoreyField (this); if (f == null) { if (am.Storey == this) { // // Access inside of same storey (S -> S) // return new CompilerGeneratedThis (CurrentType, Location); } // // External field access // return Instance; } // // Storey was cached to local field // FieldExpr f_ind = new FieldExpr (f, Location); f_ind.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location); return f_ind; } protected virtual string GetVariableMangledName (LocalVariable local_info) { // // No need to mangle anonymous method hoisted variables cause they // are hoisted in their own scopes // return local_info.Name; } public HoistedThis HoistedThis { get { return hoisted_this; } } public IList ReferencesFromChildrenBlock { get { return children_references; } } public static void Reset () { unique_id = 0; } } public abstract class HoistedVariable { // // Hoisted version of variable references used in expression // tree has to be delayed until we know its location. The variable // doesn't know its location until all stories are calculated // class ExpressionTreeVariableReference : Expression { readonly HoistedVariable hv; public ExpressionTreeVariableReference (HoistedVariable hv) { this.hv = hv; } public override Expression CreateExpressionTree (ResolveContext ec) { return hv.CreateExpressionTree (); } protected override Expression DoResolve (ResolveContext ec) { eclass = ExprClass.Value; type = ec.Module.PredefinedTypes.Expression.Resolve (Location); return this; } public override void Emit (EmitContext ec) { ResolveContext rc = new ResolveContext (ec.MemberContext); Expression e = hv.GetFieldExpression (ec).CreateExpressionTree (rc); // This should never fail e = e.Resolve (rc); if (e != null) e.Emit (ec); } } protected readonly AnonymousMethodStorey storey; protected Field field; Dictionary cached_inner_access; // TODO: Hashtable is too heavyweight FieldExpr cached_outer_access; protected HoistedVariable (AnonymousMethodStorey storey, string name, TypeSpec type) : this (storey, storey.AddCapturedVariable (name, type)) { } protected HoistedVariable (AnonymousMethodStorey storey, Field field) { this.storey = storey; this.field = field; } public void AddressOf (EmitContext ec, AddressOp mode) { GetFieldExpression (ec).AddressOf (ec, mode); } public Expression CreateExpressionTree () { return new ExpressionTreeVariableReference (this); } public void Emit (EmitContext ec) { GetFieldExpression (ec).Emit (ec); } // // Creates field access expression for hoisted variable // protected virtual FieldExpr GetFieldExpression (EmitContext ec) { if (ec.CurrentAnonymousMethod == null || ec.CurrentAnonymousMethod.Storey == null) { if (cached_outer_access != null) return cached_outer_access; // // When setting top-level hoisted variable in generic storey // change storey generic types to method generic types (VAR -> MVAR) // if (storey.Instance.Type.IsGenericOrParentIsGeneric) { var fs = MemberCache.GetMember (storey.Instance.Type, field.Spec); cached_outer_access = new FieldExpr (fs, field.Location); } else { cached_outer_access = new FieldExpr (field, field.Location); } cached_outer_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec); return cached_outer_access; } FieldExpr inner_access; if (cached_inner_access != null) { if (!cached_inner_access.TryGetValue (ec.CurrentAnonymousMethod, out inner_access)) inner_access = null; } else { inner_access = null; cached_inner_access = new Dictionary (4); } if (inner_access == null) { if (field.Parent.IsGeneric) { var fs = MemberCache.GetMember (field.Parent.CurrentType, field.Spec); inner_access = new FieldExpr (fs, field.Location); } else { inner_access = new FieldExpr (field, field.Location); } inner_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec); cached_inner_access.Add (ec.CurrentAnonymousMethod, inner_access); } return inner_access; } public abstract void EmitSymbolInfo (); public void Emit (EmitContext ec, bool leave_copy) { GetFieldExpression (ec).Emit (ec, leave_copy); } public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load) { GetFieldExpression (ec).EmitAssign (ec, source, leave_copy, false); } } public class HoistedParameter : HoistedVariable { sealed class HoistedFieldAssign : Assign { public HoistedFieldAssign (Expression target, Expression source) : base (target, source, source.Location) { } protected override Expression ResolveConversions (ResolveContext ec) { // // Implicit conversion check fails for hoisted type arguments // as they are of different types (!!0 x !0) // return this; } } readonly ParameterReference parameter; public HoistedParameter (AnonymousMethodStorey scope, ParameterReference par) : base (scope, par.Name, par.Type) { this.parameter = par; } public HoistedParameter (HoistedParameter hp, string name) : base (hp.storey, name, hp.parameter.Type) { this.parameter = hp.parameter; } public void EmitHoistingAssignment (EmitContext ec) { // // Remove hoisted redirection to emit assignment from original parameter // HoistedVariable temp = parameter.Parameter.HoistedVariant; parameter.Parameter.HoistedVariant = null; Assign a = new HoistedFieldAssign (GetFieldExpression (ec), parameter); if (a.Resolve (new ResolveContext (ec.MemberContext)) != null) a.EmitStatement (ec); parameter.Parameter.HoistedVariant = temp; } public override void EmitSymbolInfo () { SymbolWriter.DefineCapturedParameter (storey.ID, field.Name, field.Name); } public Field Field { get { return field; } } } class HoistedLocalVariable : HoistedVariable { readonly string name; public HoistedLocalVariable (AnonymousMethodStorey scope, LocalVariable local, string name) : base (scope, name, local.Type) { this.name = local.Name; } public override void EmitSymbolInfo () { SymbolWriter.DefineCapturedLocal (storey.ID, name, field.Name); } } public class HoistedThis : HoistedVariable { public HoistedThis (AnonymousMethodStorey storey, Field field) : base (storey, field) { } public void EmitHoistingAssignment (EmitContext ec) { SimpleAssign a = new SimpleAssign (GetFieldExpression (ec), new CompilerGeneratedThis (ec.CurrentType, field.Location)); if (a.Resolve (new ResolveContext (ec.MemberContext)) != null) a.EmitStatement (ec); } public override void EmitSymbolInfo () { SymbolWriter.DefineCapturedThis (storey.ID, field.Name); } public Field Field { get { return field; } } } // // Anonymous method expression as created by parser // public class AnonymousMethodExpression : Expression { // // Special conversion for nested expression tree lambdas // class Quote : ShimExpression { public Quote (Expression expr) : base (expr) { } public override Expression CreateExpressionTree (ResolveContext ec) { var args = new Arguments (1); args.Add (new Argument (expr.CreateExpressionTree (ec))); return CreateExpressionFactoryCall (ec, "Quote", args); } protected override Expression DoResolve (ResolveContext rc) { expr = expr.Resolve (rc); if (expr == null) return null; eclass = expr.eclass; type = expr.Type; return this; } } Dictionary compatibles; public ParametersBlock Block; public AnonymousMethodExpression (Location loc) { this.loc = loc; this.compatibles = new Dictionary (); } public override string ExprClassName { get { return "anonymous method"; } } public virtual bool HasExplicitParameters { get { return Parameters != ParametersCompiled.Undefined; } } public ParametersCompiled Parameters { get { return Block.Parameters; } } // // Returns true if the body of lambda expression can be implicitly // converted to the delegate of type `delegate_type' // public bool ImplicitStandardConversionExists (ResolveContext ec, TypeSpec delegate_type) { using (ec.With (ResolveContext.Options.InferReturnType, false)) { using (ec.Set (ResolveContext.Options.ProbingMode)) { return Compatible (ec, delegate_type) != null; } } } TypeSpec CompatibleChecks (ResolveContext ec, TypeSpec delegate_type) { if (delegate_type.IsDelegate) return delegate_type; if (delegate_type.IsGeneric && delegate_type.GetDefinition () == TypeManager.expression_type) { delegate_type = delegate_type.TypeArguments [0]; if (delegate_type.IsDelegate) return delegate_type; ec.Report.Error (835, loc, "Cannot convert `{0}' to an expression tree of non-delegate type `{1}'", GetSignatureForError (), TypeManager.CSharpName (delegate_type)); return null; } ec.Report.Error (1660, loc, "Cannot convert `{0}' to non-delegate type `{1}'", GetSignatureForError (), TypeManager.CSharpName (delegate_type)); return null; } protected bool VerifyExplicitParameters (ResolveContext ec, TypeSpec delegate_type, AParametersCollection parameters) { if (VerifyParameterCompatibility (ec, delegate_type, parameters, ec.IsInProbingMode)) return true; if (!ec.IsInProbingMode) ec.Report.Error (1661, loc, "Cannot convert `{0}' to delegate type `{1}' since there is a parameter mismatch", GetSignatureForError (), TypeManager.CSharpName (delegate_type)); return false; } protected bool VerifyParameterCompatibility (ResolveContext ec, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors) { if (Parameters.Count != invoke_pd.Count) { if (ignore_errors) return false; ec.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments", TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ()); return false; } bool has_implicit_parameters = !HasExplicitParameters; bool error = false; for (int i = 0; i < Parameters.Count; ++i) { Parameter.Modifier p_mod = invoke_pd.FixedParameters [i].ModFlags; if (Parameters.FixedParameters [i].ModFlags != p_mod && p_mod != Parameter.Modifier.PARAMS) { if (ignore_errors) return false; if (p_mod == Parameter.Modifier.NONE) ec.Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword", (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.FixedParameters [i].ModFlags)); else ec.Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword", (i+1).ToString (), Parameter.GetModifierSignature (p_mod)); error = true; } if (has_implicit_parameters) continue; TypeSpec type = invoke_pd.Types [i]; // We assume that generic parameters are always inflated if (TypeManager.IsGenericParameter (type)) continue; if (TypeManager.HasElementType (type) && TypeManager.IsGenericParameter (TypeManager.GetElementType (type))) continue; if (!TypeSpecComparer.IsEqual (invoke_pd.Types [i], Parameters.Types [i])) { if (ignore_errors) return false; ec.Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'", (i+1).ToString (), TypeManager.CSharpName (Parameters.Types [i]), TypeManager.CSharpName (invoke_pd.Types [i])); error = true; } } return !error; } // // Infers type arguments based on explicit arguments // public bool ExplicitTypeInference (ResolveContext ec, TypeInferenceContext type_inference, TypeSpec delegate_type) { if (!HasExplicitParameters) return false; if (!delegate_type.IsDelegate) { if (delegate_type.GetDefinition () != TypeManager.expression_type) return false; delegate_type = TypeManager.GetTypeArguments (delegate_type) [0]; if (!delegate_type.IsDelegate) return false; } AParametersCollection d_params = Delegate.GetParameters (delegate_type); if (d_params.Count != Parameters.Count) return false; for (int i = 0; i < Parameters.Count; ++i) { TypeSpec itype = d_params.Types [i]; if (!TypeManager.IsGenericParameter (itype)) { if (!TypeManager.HasElementType (itype)) continue; if (!TypeManager.IsGenericParameter (TypeManager.GetElementType (itype))) continue; } type_inference.ExactInference (Parameters.Types [i], itype); } return true; } public TypeSpec InferReturnType (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type) { Expression expr; AnonymousExpression am; if (compatibles.TryGetValue (delegate_type, out expr)) { am = expr as AnonymousExpression; return am == null ? null : am.ReturnType; } using (ec.Set (ResolveContext.Options.ProbingMode | ResolveContext.Options.InferReturnType)) { am = CompatibleMethodBody (ec, tic, InternalType.Arglist, delegate_type); if (am != null) am = am.Compatible (ec); } if (am == null) return null; // compatibles.Add (delegate_type, am); return am.ReturnType; } // // Returns AnonymousMethod container if this anonymous method // expression can be implicitly converted to the delegate type `delegate_type' // public Expression Compatible (ResolveContext ec, TypeSpec type) { Expression am; if (compatibles.TryGetValue (type, out am)) return am; TypeSpec delegate_type = CompatibleChecks (ec, type); if (delegate_type == null) return null; // // At this point its the first time we know the return type that is // needed for the anonymous method. We create the method here. // var invoke_mb = Delegate.GetInvokeMethod (delegate_type); TypeSpec return_type = invoke_mb.ReturnType; // // Second: the return type of the delegate must be compatible with // the anonymous type. Instead of doing a pass to examine the block // we satisfy the rule by setting the return type on the EmitContext // to be the delegate type return type. // var body = CompatibleMethodBody (ec, null, return_type, delegate_type); if (body == null) return null; bool etree_conversion = delegate_type != type; try { if (etree_conversion) { if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion)) { // // Nested expression tree lambda use same scope as parent // lambda, this also means no variable capturing between this // and parent scope // am = body.Compatible (ec, ec.CurrentAnonymousMethod); // // Quote nested expression tree // if (am != null) am = new Quote (am); } else { int errors = ec.Report.Errors; using (ec.Set (ResolveContext.Options.ExpressionTreeConversion)) { am = body.Compatible (ec); } // // Rewrite expressions into expression tree when targeting Expression // if (am != null && errors == ec.Report.Errors) am = CreateExpressionTree (ec, delegate_type); } } else { am = body.Compatible (ec); } } catch (CompletionResult) { throw; } catch (Exception e) { throw new InternalErrorException (e, loc); } if (!ec.IsInProbingMode) { compatibles.Add (type, am ?? EmptyExpression.Null); } return am; } protected virtual Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type) { return CreateExpressionTree (ec); } public override Expression CreateExpressionTree (ResolveContext ec) { ec.Report.Error (1946, loc, "An anonymous method cannot be converted to an expression tree"); return null; } protected virtual ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type) { var delegate_parameters = Delegate.GetParameters (delegate_type); if (Parameters == ParametersCompiled.Undefined) { // // We provide a set of inaccessible parameters // Parameter[] fixedpars = new Parameter[delegate_parameters.Count]; for (int i = 0; i < delegate_parameters.Count; i++) { Parameter.Modifier i_mod = delegate_parameters.FixedParameters [i].ModFlags; if (i_mod == Parameter.Modifier.OUT) { if (!ec.IsInProbingMode) { ec.Report.Error (1688, loc, "Cannot convert anonymous method block without a parameter list to delegate type `{0}' because it has one or more `out' parameters", delegate_type.GetSignatureForError ()); } return null; } fixedpars[i] = new Parameter ( new TypeExpression (delegate_parameters.Types [i], loc), null, delegate_parameters.FixedParameters [i].ModFlags, null, loc); } return ParametersCompiled.CreateFullyResolved (fixedpars, delegate_parameters.Types); } if (!VerifyExplicitParameters (ec, delegate_type, delegate_parameters)) { return null; } return Parameters; } protected override Expression DoResolve (ResolveContext ec) { if (ec.HasSet (ResolveContext.Options.ConstantScope)) { ec.Report.Error (1706, loc, "Anonymous methods and lambda expressions cannot be used in the current context"); return null; } // // Set class type, set type // eclass = ExprClass.Value; // // This hack means `The type is not accessible // anywhere', we depend on special conversion // rules. // type = InternalType.AnonymousMethod; if (!DoResolveParameters (ec)) return null; // FIXME: The emitted code isn't very careful about reachability // so, ensure we have a 'ret' at the end BlockContext bc = ec as BlockContext; if (bc != null && bc.CurrentBranching != null && bc.CurrentBranching.CurrentUsageVector.IsUnreachable) bc.NeedReturnLabel (); return this; } protected virtual bool DoResolveParameters (ResolveContext rc) { return Parameters.Resolve (rc); } public override void Emit (EmitContext ec) { // nothing, as we only exist to not do anything. } public static void Error_AddressOfCapturedVar (ResolveContext ec, IVariableReference var, Location loc) { ec.Report.Error (1686, loc, "Local variable or parameter `{0}' cannot have their address taken and be used inside an anonymous method or lambda expression", var.Name); } public override string GetSignatureForError () { return ExprClassName; } AnonymousMethodBody CompatibleMethodBody (ResolveContext ec, TypeInferenceContext tic, TypeSpec return_type, TypeSpec delegate_type) { ParametersCompiled p = ResolveParameters (ec, tic, delegate_type); if (p == null) return null; ParametersBlock b = ec.IsInProbingMode ? (ParametersBlock) Block.PerformClone () : Block; return CompatibleMethodFactory (return_type, delegate_type, p, b); } protected virtual AnonymousMethodBody CompatibleMethodFactory (TypeSpec return_type, TypeSpec delegate_type, ParametersCompiled p, ParametersBlock b) { return new AnonymousMethodBody (p, b, return_type, delegate_type, loc); } protected override void CloneTo (CloneContext clonectx, Expression t) { AnonymousMethodExpression target = (AnonymousMethodExpression) t; target.Block = (ParametersBlock) clonectx.LookupBlock (Block); } } // // Abstract expression for any block which requires variables hoisting // public abstract class AnonymousExpression : Expression { protected class AnonymousMethodMethod : Method { public readonly AnonymousExpression AnonymousMethod; public readonly AnonymousMethodStorey Storey; readonly string RealName; public AnonymousMethodMethod (DeclSpace parent, AnonymousExpression am, AnonymousMethodStorey storey, GenericMethod generic, TypeExpr return_type, Modifiers mod, string real_name, MemberName name, ParametersCompiled parameters) : base (parent, generic, return_type, mod | Modifiers.COMPILER_GENERATED, name, parameters, null) { this.AnonymousMethod = am; this.Storey = storey; this.RealName = real_name; Parent.PartialContainer.AddMethod (this); Block = new ToplevelBlock (am.block, parameters); } public override EmitContext CreateEmitContext (ILGenerator ig) { EmitContext ec = new EmitContext (this, ig, ReturnType); ec.CurrentAnonymousMethod = AnonymousMethod; if (AnonymousMethod.return_label != null) { ec.HasReturnLabel = true; ec.ReturnLabel = (Label) AnonymousMethod.return_label; } return ec; } protected override void DefineTypeParameters () { // Type parameters were cloned } protected override bool ResolveMemberType () { if (!base.ResolveMemberType ()) return false; if (Storey != null && Storey.Mutator != null) { if (!parameters.IsEmpty) { var mutated = Storey.Mutator.Mutate (parameters.Types); if (mutated != parameters.Types) parameters = ParametersCompiled.CreateFullyResolved ((Parameter[]) parameters.FixedParameters, mutated); } member_type = Storey.Mutator.Mutate (member_type); } return true; } public override void Emit () { if (MethodBuilder == null) { Define (); } base.Emit (); } public override void EmitExtraSymbolInfo (SourceMethod source) { source.SetRealMethodName (RealName); } } protected ParametersBlock block; public TypeSpec ReturnType; object return_label; protected AnonymousExpression (ParametersBlock block, TypeSpec return_type, Location loc) { this.ReturnType = return_type; this.block = block; this.loc = loc; } public abstract string ContainerType { get; } public abstract bool IsIterator { get; } public abstract AnonymousMethodStorey Storey { get; } public AnonymousExpression Compatible (ResolveContext ec) { return Compatible (ec, this); } public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae) { if (block.Resolved) return this; // TODO: Implement clone BlockContext aec = new BlockContext (ec, block, ReturnType); aec.CurrentAnonymousMethod = ae; ResolveContext.Options flags = 0; var am = this as AnonymousMethodBody; if (ec.HasSet (ResolveContext.Options.InferReturnType) && am != null) { am.ReturnTypeInference = new TypeInferenceContext (); } if (ec.IsInProbingMode) flags |= ResolveContext.Options.ProbingMode; if (ec.HasSet (ResolveContext.Options.FieldInitializerScope)) flags |= ResolveContext.Options.FieldInitializerScope; if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion)) flags |= ResolveContext.Options.ExpressionTreeConversion; aec.Set (flags); var errors = ec.Report.Errors; bool res = Block.Resolve (ec.CurrentBranching, aec, null); if (aec.HasReturnLabel) return_label = aec.ReturnLabel; if (am != null && am.ReturnTypeInference != null) { am.ReturnTypeInference.FixAllTypes (ec); ReturnType = am.ReturnTypeInference.InferredTypeArguments [0]; am.ReturnTypeInference = null; } if (res && errors != ec.Report.Errors) return null; return res ? this : null; } public void SetHasThisAccess () { ExplicitBlock b = block; do { if (b.HasCapturedThis) return; b.HasCapturedThis = true; b = b.Parent == null ? null : b.Parent.Explicit; } while (b != null); } // // The block that makes up the body for the anonymous method // public ParametersBlock Block { get { return block; } } } public class AnonymousMethodBody : AnonymousExpression { protected readonly ParametersCompiled parameters; AnonymousMethodStorey storey; AnonymousMethodMethod method; Field am_cache; string block_name; TypeInferenceContext return_inference; static int unique_id; public AnonymousMethodBody (ParametersCompiled parameters, ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type, Location loc) : base (block, return_type, loc) { this.type = delegate_type; this.parameters = parameters; } #region Properties public override string ContainerType { get { return "anonymous method"; } } public override bool IsIterator { get { return false; } } public TypeInferenceContext ReturnTypeInference { get { return return_inference; } set { return_inference = value; } } public override AnonymousMethodStorey Storey { get { return storey; } } #endregion public override Expression CreateExpressionTree (ResolveContext ec) { ec.Report.Error (1945, loc, "An expression tree cannot contain an anonymous method expression"); return null; } bool Define (ResolveContext ec) { if (!Block.Resolved && Compatible (ec) == null) return false; if (block_name == null) { MemberCore mc = (MemberCore) ec.MemberContext; block_name = mc.MemberName.Basename; } return true; } // // Creates a host for the anonymous method // AnonymousMethodMethod DoCreateMethodHost (EmitContext ec) { // // Anonymous method body can be converted to // // 1, an instance method in current scope when only `this' is hoisted // 2, a static method in current scope when neither `this' nor any variable is hoisted // 3, an instance method in compiler generated storey when any hoisted variable exists // Modifiers modifiers; if (Block.HasCapturedVariable || Block.HasCapturedThis) { storey = FindBestMethodStorey (); modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE; } else { if (ec.CurrentAnonymousMethod != null) storey = ec.CurrentAnonymousMethod.Storey; modifiers = Modifiers.STATIC | Modifiers.PRIVATE; } TypeContainer parent = storey != null ? storey : ec.CurrentTypeDefinition.Parent.PartialContainer; MemberCore mc = ec.MemberContext as MemberCore; string name = CompilerGeneratedClass.MakeName (parent != storey ? block_name : null, "m", null, unique_id++); MemberName member_name; GenericMethod generic_method; if (storey == null && mc.MemberName.TypeArguments != null) { member_name = new MemberName (name, mc.MemberName.TypeArguments.Clone (), Location); var hoisted_tparams = ec.CurrentTypeParameters; var type_params = new TypeParameter[hoisted_tparams.Length]; for (int i = 0; i < type_params.Length; ++i) { type_params[i] = hoisted_tparams[i].CreateHoistedCopy (parent, null); } generic_method = new GenericMethod (parent.NamespaceEntry, parent, member_name, type_params, new TypeExpression (ReturnType, Location), parameters); } else { member_name = new MemberName (name, Location); generic_method = null; } string real_name = String.Format ( "{0}~{1}{2}", mc.GetSignatureForError (), GetSignatureForError (), parameters.GetSignatureForError ()); return new AnonymousMethodMethod (parent, this, storey, generic_method, new TypeExpression (ReturnType, Location), modifiers, real_name, member_name, parameters); } protected override Expression DoResolve (ResolveContext ec) { if (!Define (ec)) return null; eclass = ExprClass.Value; return this; } public override void Emit (EmitContext ec) { // // Use same anonymous method implementation for scenarios where same // code is used from multiple blocks, e.g. field initializers // if (method == null) { // // Delay an anonymous method definition to avoid emitting unused code // for unreachable blocks or expression trees // method = DoCreateMethodHost (ec); method.Define (); } bool is_static = (method.ModFlags & Modifiers.STATIC) != 0; if (is_static && am_cache == null) { // // Creates a field cache to store delegate instance if it's not generic // if (!method.MemberName.IsGeneric) { TypeContainer parent = method.Parent.PartialContainer; int id = parent.Fields == null ? 0 : parent.Fields.Count; var cache_type = storey != null && storey.Mutator != null ? storey.Mutator.Mutate (type) : type; am_cache = new Field (parent, new TypeExpression (cache_type, loc), Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED, new MemberName (CompilerGeneratedClass.MakeName (null, "f", "am$cache", id), loc), null); am_cache.Define (); parent.AddField (am_cache); } else { // TODO: Implement caching of generated generic static methods // // Idea: // // Some extra class is needed to capture variable generic type // arguments. Maybe we could re-use anonymous types, with a unique // anonymous method id, but they are quite heavy. // // Consider : "() => typeof(T);" // // We need something like // static class Wrap { // public static DelegateType cache; // } // // We then specialize local variable to capture all generic parameters // and delegate type, e.g. "Wrap cache;" // } } Label l_initialized = ec.DefineLabel (); if (am_cache != null) { ec.Emit (OpCodes.Ldsfld, am_cache.Spec); ec.Emit (OpCodes.Brtrue_S, l_initialized); } // // Load method delegate implementation // if (is_static) { ec.Emit (OpCodes.Ldnull); } else if (storey != null) { Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext)); if (e != null) e.Emit (ec); } else { ec.Emit (OpCodes.Ldarg_0); } var delegate_method = method.Spec; if (storey != null && storey.MemberName.IsGeneric) { TypeSpec t = storey.Instance.Type; // // Mutate anonymous method instance type if we are in nested // hoisted generic anonymous method storey // if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null && ec.CurrentAnonymousMethod.Storey.Mutator != null) { t = storey.Mutator.Mutate (t); } ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ())); } else { if (delegate_method.IsGeneric) delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, method.TypeParameters); ec.Emit (OpCodes.Ldftn, delegate_method); } var constructor_method = Delegate.GetConstructor (type); ec.Emit (OpCodes.Newobj, constructor_method); if (am_cache != null) { ec.Emit (OpCodes.Stsfld, am_cache.Spec); ec.MarkLabel (l_initialized); ec.Emit (OpCodes.Ldsfld, am_cache.Spec); } } // // Look for the best storey for this anonymous method // AnonymousMethodStorey FindBestMethodStorey () { // // Use the nearest parent block which has a storey // for (Block b = Block.Parent; b != null; b = b.Parent) { AnonymousMethodStorey s = b.Explicit.AnonymousMethodStorey; if (s != null) return s; } return null; } public override string GetSignatureForError () { return TypeManager.CSharpName (type); } public static void Reset () { unique_id = 0; } } // // Anonymous type container // public class AnonymousTypeClass : CompilerGeneratedClass { // TODO: Merge with AnonymousTypeParameter public class GeneratedParameter : Parameter { public GeneratedParameter (FullNamedExpression type, AnonymousTypeParameter p) : base (type, p.Name, Modifier.NONE, null, p.Location) { } } static int types_counter; public const string ClassNamePrefix = "<>__AnonType"; public const string SignatureForError = "anonymous type"; readonly IList parameters; private AnonymousTypeClass (DeclSpace parent, MemberName name, IList parameters, Location loc) : base (parent, name, (parent.Module.Evaluator != null ? Modifiers.PUBLIC : 0) | Modifiers.SEALED) { this.parameters = parameters; } public static AnonymousTypeClass Create (TypeContainer parent, IList parameters, Location loc) { string name = ClassNamePrefix + types_counter++; ParametersCompiled all_parameters; TypeParameterName[] t_params; SimpleName[] t_args; if (parameters.Count == 0) { all_parameters = ParametersCompiled.EmptyReadOnlyParameters; t_params = new TypeParameterName[0]; t_args = null; } else { t_args = new SimpleName[parameters.Count]; t_params = new TypeParameterName[parameters.Count]; Parameter[] ctor_params = new Parameter[parameters.Count]; for (int i = 0; i < parameters.Count; ++i) { AnonymousTypeParameter p = parameters[i]; t_args[i] = new SimpleName ("<" + p.Name + ">__T", p.Location); t_params[i] = new TypeParameterName (t_args[i].Name, null, p.Location); ctor_params[i] = new GeneratedParameter (t_args[i], p); } all_parameters = new ParametersCompiled (ctor_params); } // // Create generic anonymous type host with generic arguments // named upon properties names // AnonymousTypeClass a_type = new AnonymousTypeClass (parent.NamespaceEntry.SlaveDeclSpace, new MemberName (name, new TypeArguments (t_params), loc), parameters, loc); if (parameters.Count > 0) a_type.SetParameterInfo (null); Constructor c = new Constructor (a_type, name, Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN, null, all_parameters, null, loc); c.Block = new ToplevelBlock (parent.Module.Compiler, c.ParameterInfo, loc); // // Create fields and contructor body with field initialization // bool error = false; for (int i = 0; i < parameters.Count; ++i) { AnonymousTypeParameter p = parameters [i]; Field f = new Field (a_type, t_args [i], Modifiers.PRIVATE | Modifiers.READONLY, new MemberName ("<" + p.Name + ">", p.Location), null); if (!a_type.AddField (f)) { error = true; continue; } c.Block.AddStatement (new StatementExpression ( new SimpleAssign (new MemberAccess (new This (p.Location), f.Name), c.Block.GetParameterReference (i, p.Location)))); ToplevelBlock get_block = new ToplevelBlock (parent.Module.Compiler, p.Location); get_block.AddStatement (new Return ( new MemberAccess (new This (p.Location), f.Name), p.Location)); Property prop = new Property (a_type, t_args [i], Modifiers.PUBLIC, new MemberName (p.Name, p.Location), null); prop.Get = new Property.GetMethod (prop, 0, null, p.Location); prop.Get.Block = get_block; a_type.AddProperty (prop); } if (error) return null; a_type.AddConstructor (c); return a_type; } public static void Reset () { types_counter = 0; } protected override bool AddToContainer (MemberCore symbol, string name) { MemberCore mc = GetDefinition (name); if (mc == null) { defined_names.Add (name, symbol); return true; } // A conflict between anonymous type members will be reported if (symbol is TypeParameter) { Report.SymbolRelatedToPreviousError (symbol); return false; } // Ignore other conflicts return true; } protected override bool DoDefineMembers () { if (!base.DoDefineMembers ()) return false; Location loc = Location; var equals_parameters = ParametersCompiled.CreateFullyResolved ( new Parameter (new TypeExpression (TypeManager.object_type, loc), "obj", 0, null, loc), TypeManager.object_type); Method equals = new Method (this, null, new TypeExpression (TypeManager.bool_type, loc), Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("Equals", loc), equals_parameters, null); equals_parameters[0].Resolve (equals, 0); Method tostring = new Method (this, null, new TypeExpression (TypeManager.string_type, loc), Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("ToString", loc), Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null); ToplevelBlock equals_block = new ToplevelBlock (Compiler, equals.ParameterInfo, loc); TypeExpr current_type; if (type_params != null) { var targs = new TypeArguments (); foreach (var type_param in type_params) targs.Add (new TypeParameterExpr (type_param, type_param.Location)); current_type = new GenericTypeExpr (Definition, targs, loc); } else { current_type = new TypeExpression (Definition, loc); } var li_other = LocalVariable.CreateCompilerGenerated (CurrentType, equals_block, loc); equals_block.AddStatement (new BlockVariableDeclaration (new TypeExpression (li_other.Type, loc), li_other)); var other_variable = new LocalVariableReference (li_other, loc); MemberAccess system_collections_generic = new MemberAccess (new MemberAccess ( new QualifiedAliasMember ("global", "System", loc), "Collections", loc), "Generic", loc); Expression rs_equals = null; Expression string_concat = new StringConstant ("{", loc); Expression rs_hashcode = new IntConstant (-2128831035, loc); for (int i = 0; i < parameters.Count; ++i) { var p = parameters [i]; var f = Fields [i]; MemberAccess equality_comparer = new MemberAccess (new MemberAccess ( system_collections_generic, "EqualityComparer", new TypeArguments (new SimpleName (CurrentTypeParameters [i].Name, loc)), loc), "Default", loc); Arguments arguments_equal = new Arguments (2); arguments_equal.Add (new Argument (new MemberAccess (new This (f.Location), f.Name))); arguments_equal.Add (new Argument (new MemberAccess (other_variable, f.Name))); Expression field_equal = new Invocation (new MemberAccess (equality_comparer, "Equals", loc), arguments_equal); Arguments arguments_hashcode = new Arguments (1); arguments_hashcode.Add (new Argument (new MemberAccess (new This (f.Location), f.Name))); Expression field_hashcode = new Invocation (new MemberAccess (equality_comparer, "GetHashCode", loc), arguments_hashcode); IntConstant FNV_prime = new IntConstant (16777619, loc); rs_hashcode = new Binary (Binary.Operator.Multiply, new Binary (Binary.Operator.ExclusiveOr, rs_hashcode, field_hashcode, loc), FNV_prime, loc); Expression field_to_string = new Conditional (new BooleanExpression (new Binary (Binary.Operator.Inequality, new MemberAccess (new This (f.Location), f.Name), new NullLiteral (loc), loc)), new Invocation (new MemberAccess ( new MemberAccess (new This (f.Location), f.Name), "ToString"), null), new StringConstant (string.Empty, loc), loc); if (rs_equals == null) { rs_equals = field_equal; string_concat = new Binary (Binary.Operator.Addition, string_concat, new Binary (Binary.Operator.Addition, new StringConstant (" " + p.Name + " = ", loc), field_to_string, loc), loc); continue; } // // Implementation of ToString () body using string concatenation // string_concat = new Binary (Binary.Operator.Addition, new Binary (Binary.Operator.Addition, string_concat, new StringConstant (", " + p.Name + " = ", loc), loc), field_to_string, loc); rs_equals = new Binary (Binary.Operator.LogicalAnd, rs_equals, field_equal, loc); } string_concat = new Binary (Binary.Operator.Addition, string_concat, new StringConstant (" }", loc), loc); // // Equals (object obj) override // var other_variable_assign = new TemporaryVariableReference (li_other, loc); equals_block.AddStatement (new StatementExpression ( new SimpleAssign (other_variable_assign, new As (equals_block.GetParameterReference (0, loc), current_type, loc), loc))); Expression equals_test = new Binary (Binary.Operator.Inequality, other_variable, new NullLiteral (loc), loc); if (rs_equals != null) equals_test = new Binary (Binary.Operator.LogicalAnd, equals_test, rs_equals, loc); equals_block.AddStatement (new Return (equals_test, loc)); equals.Block = equals_block; equals.Define (); AddMethod (equals); // // GetHashCode () override // Method hashcode = new Method (this, null, new TypeExpression (TypeManager.int32_type, loc), Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("GetHashCode", loc), Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null); // // Modified FNV with good avalanche behavior and uniform // distribution with larger hash sizes. // // const int FNV_prime = 16777619; // int hash = (int) 2166136261; // foreach (int d in data) // hash = (hash ^ d) * FNV_prime; // hash += hash << 13; // hash ^= hash >> 7; // hash += hash << 3; // hash ^= hash >> 17; // hash += hash << 5; ToplevelBlock hashcode_top = new ToplevelBlock (Compiler, loc); Block hashcode_block = new Block (hashcode_top, loc, loc); hashcode_top.AddStatement (new Unchecked (hashcode_block, loc)); var li_hash = LocalVariable.CreateCompilerGenerated (TypeManager.int32_type, hashcode_top, loc); hashcode_block.AddStatement (new BlockVariableDeclaration (new TypeExpression (li_hash.Type, loc), li_hash)); LocalVariableReference hash_variable_assign = new LocalVariableReference (li_hash, loc); hashcode_block.AddStatement (new StatementExpression ( new SimpleAssign (hash_variable_assign, rs_hashcode))); var hash_variable = new LocalVariableReference (li_hash, loc); hashcode_block.AddStatement (new StatementExpression ( new CompoundAssign (Binary.Operator.Addition, hash_variable, new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (13, loc), loc), loc))); hashcode_block.AddStatement (new StatementExpression ( new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable, new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (7, loc), loc), loc))); hashcode_block.AddStatement (new StatementExpression ( new CompoundAssign (Binary.Operator.Addition, hash_variable, new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (3, loc), loc), loc))); hashcode_block.AddStatement (new StatementExpression ( new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable, new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (17, loc), loc), loc))); hashcode_block.AddStatement (new StatementExpression ( new CompoundAssign (Binary.Operator.Addition, hash_variable, new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (5, loc), loc), loc))); hashcode_block.AddStatement (new Return (hash_variable, loc)); hashcode.Block = hashcode_top; hashcode.Define (); AddMethod (hashcode); // // ToString () override // ToplevelBlock tostring_block = new ToplevelBlock (Compiler, loc); tostring_block.AddStatement (new Return (string_concat, loc)); tostring.Block = tostring_block; tostring.Define (); AddMethod (tostring); return true; } public override string GetSignatureForError () { return SignatureForError; } public IList Parameters { get { return parameters; } } } }