/* * Copyright (c) 2003-2008 The University of Wroclaw. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the University may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Diagnostics; using Nemerle.Assertions; using Nemerle.Collections; using Nemerle.Compiler.Typedtree; using Nemerle.Utility; using SCG = System.Collections.Generic; using SR = System.Reflection; using SRE = System.Reflection.Emit; using PT = Nemerle.Compiler.Parsetree; namespace Nemerle.Compiler { public variant RefTo { | None | Method { value : MethodBuilder; } | Methods { values : list[MethodBuilder]; } | Type { value : TypeBuilder; } | Types { values : list[TypeBuilder]; } } [ManagerAccess (declaring_type.Manager)] public abstract class MemberBuilder : MemberInfo, IMember { /// Позволяет указать средствам интеллисенса, что данный член ссылается на /// некий (другой) метод. Этот метод может содержать код сгенерированный /// на основе кода члена содержащего данную ссылку. [Accessor (flags = WantSetter)] private mutable _related : RefTo = RefTo.None(); protected mutable loc : Location; [Accessor] protected mutable name : string; protected accessibility : Accessibility; [Accessor] protected modifiers : Modifiers; [Accessor] protected declaring_type : TypeBuilder; id : int; internal mutable ty : MType; protected mutable handle : SR.MemberInfo; public new Attributes : NemerleAttributes { get { attributes } set { attributes = value } } [Accessor] mutable _ast : PT.ClassMember; public IsGenerated : bool { get { loc.IsGenerated } } protected mutable m_has_been_used : bool; //internal mutable disable_type_attr : bool; [Accessor (flags = WantSetter)] protected mutable required_modifiers : list[System.Type] = []; [Accessor (flags = WantSetter)] protected mutable optional_modifiers : list[System.Type] = []; public Env : GlobalEnv; protected this (par : TypeBuilder, ast : PT.ClassMember) { declaring_type = par; _ast = ast; _ast._builder = this; Env = ast.Env; loc = ast.Location; modifiers = ast.modifiers; name = ast.Name; id = Util.next_id (Manager); attributes = modifiers.mods; unless (attributes %&& NemerleAttributes.AccessModifiers) attributes |= NemerleAttributes.Private; accessibility = match (attributes %& NemerleAttributes.AccessModifiers) { | NemerleAttributes.Private => Accessibility.Private | NemerleAttributes.Protected => Accessibility.Protected | NemerleAttributes.Internal => Accessibility.Internal | _ => if (attributes %&& (NemerleAttributes.Protected %| NemerleAttributes.Internal)) Accessibility.ProtectedOrInternal else Accessibility.Public } match ((par.Accessibility, accessibility)) { | (Accessibility.Private, _) => accessibility = Accessibility.Private | (Accessibility.Public, _) => () | (Accessibility.Internal, Accessibility.Public) => accessibility = Accessibility.Internal | (Accessibility.Protected, Accessibility.Internal) | (Accessibility.Internal, Accessibility.Protected) => accessibility = Accessibility.ProtectedAndInternal | _ => () } // mark public and protected members explicitly used m_has_been_used = (IsPublic || IsProtected) && declaring_type.IsExternallyAccessible //TODO: Add compiler option for naming convention and check it here || name.StartsWith ("_") || declaring_type.Name.StartsWith ("_") || name == "value__" || ast.Location.IsGenerated; is_obsolete = modifiers.FindAttribute (InternalType.Obsolete_tc, par.GlobalEnv).IsSome; } public virtual ResetCodeCache () : void { } public virtual BodyLocation : Location { get { loc } } // does current member can be accessed from inside given type public CanAccess (source : TypeInfo) : bool { IsPublic || IsInternal && source is TypeBuilder || ({ mutable result = false; for (mutable tc = source; tc != null && !result; tc = tc.DeclaringType) result = tc.Equals (declaring_type) || IsProtected && Option.IsSome (tc.SuperType (declaring_type)); result }) } public OverloadName : string { get { Util.ice ("OverloadName not implemented for non-external members") } } public abstract MemberKind : MemberKinds { get; } public GetMemType () : MType { ty } public override GlobalEnv : GlobalEnv { get { Env } } [Nemerle.OverrideObjectEquals] public Equals (o : IMember) : bool { id == o.GetHashCode () } public Location : Location { get { loc } set { loc = value } } public override GetModifiers () : Modifiers { modifiers } public override HasBeenUsed : bool { get { m_has_been_used } set { base.HasBeenUsed = value; m_has_been_used = true; declaring_type.HasBeenUsed = false; // do not trigger obsoletion warning } } internal SetInstanceUsed () : void { m_has_been_used = true; } public override GetHashCode () : int { id } public abstract GetHandle () : System.Reflection.MemberInfo; public MarkWithSpecialName () : void { attributes |= NemerleAttributes.SpecialName } static count_access (attrs : NemerleAttributes) : int { if (attrs %&& NemerleAttributes.Public) 4 else if (attrs %&& NemerleAttributes.Internal) 3 else if (attrs %&& NemerleAttributes.Protected) 2 else if (attrs %&& NemerleAttributes.Private) 1 else 1 // default to private } protected static UpdateEmbeddedModifiers (parent : NemerleAttributes, child : ref NemerleAttributes) : void { when (child %&& NemerleAttributes.Static && !(parent %&& NemerleAttributes.Static)) Message.Error ("accessor cannot be static if containing entity isn't"); // copy access modifiers only if child has none child |= (if (child %&& NemerleAttributes.AccessModifiers) parent %& ~NemerleAttributes.AccessModifiers; else parent); when (child %&& NemerleAttributes.Private) child &= ~NemerleAttributes.OverrideModifiers; when (count_access (parent) < count_access (child)) Message.Error ("accessor is more accessible than containing entity") } public override ToString () : string { DescribeMember (this) } internal static DescribeMember (m : IMember) : string { def declaring = m.DeclaringType; if (declaring != null && declaring.GetConstantObject () : object == m) "constructor of constant variant " + declaring.FullName else { def full_name = if (declaring == null) m.Name else declaring.FullName + "." + m.Name; def kind = match (m.MemberKind) { | MemberKinds.Field => "field" | MemberKinds.Method => "method" | MemberKinds.Constructor => "constructor" | MemberKinds.Property => "property" | MemberKinds.NestedType | MemberKinds.TypeInfo => "type" | MemberKinds.Event => "event" | _ => "" }; def makeTypeName (ty) { ty.TypeFullName ().Replace ('+', '.') } def add (str, iaAdd) { if (iaAdd) str + " " else "" } def mekeGetSet (canRead, canWrite) { | (false, false) => "" | (true, false) => "get;" | (false, true) => "set;" | (true, true) => "get; set;" } match (m) { | m is IMethod => def describeParm (p) { p.name + " : " + p.ty.ToString () } def parms = if (m.IsVarArgs) { def (till_last, last) = List.DivideLast (m.GetParameters ()); List.Rev ("params " + describeParm (last) :: List.RevMap (till_last, describeParm)); } else List.Map (m.GetParameters (), describeParm); def ret_ty = if (m.MemberKind == MemberKinds.Constructor) m.DeclaringType.ToString () else m.ReturnType.ToString (); kind + " " + full_name + "(" + NString.Concat (", ", parms) + ") : " + ret_ty | prop is PropertyBuilder => def getSet = mekeGetSet (prop.CanRead, prop.CanWrite); match (prop.parms) { | _ :: _ => match (prop.ty) { | MType.Fun(_, to) => $"property: `$full_name$(prop.parms) : $to { $getSet }" | _ => "" } | _ => $"property: $full_name : $(prop.ty) { $getSet }" } | p is IProperty => def pi = p.GetPropertyInfo (); _=pi; def getSet = mekeGetSet (pi.CanRead, pi.CanWrite); def indexer = if (p.IsIndexer) "[...]" else ""; def typeName = makeTypeName(pi.PropertyType); $"property: $(p.Name)$indexer : $typeName { $getSet }" | fld is FieldBuilder => def modifs = add("static", fld.IsStatic) + add("mutable", fld.IsMutable); $"field: $modifs$(fld.Name) : $(fld.ty);" | fld is IField => def fi = fld.GetFieldInfo (); def typeName = makeTypeName(fi.FieldType); def modifs = add("static", fi.IsStatic) + add("mutable", !fi.IsInitOnly && !fi.IsLiteral); $"field: $modifs$(fld.Name) : $typeName;" | _ => $ "$kind `$full_name'" } } } public abstract CheckAttributes () : void; // make sure only a valid subset of NemerleAttributes has been used protected check_for_invalid_attr (attr : NemerleAttributes, attr_name : string) : void { when (attributes %&& attr) Message.FatalError2 (loc, "invalid attribute `" + attr_name + "' specified for " + ToString ()) } /** * Performs the attributes checks that are common to methods and properties */ protected check_method_like_attributes (title : string, title_plural : string) : void { check_for_invalid_attr (NemerleAttributes.Mutable, "mutable"); check_for_invalid_attr (NemerleAttributes.Struct, "struct"); check_for_invalid_attr (NemerleAttributes.Macro, "macro"); check_for_invalid_attr (NemerleAttributes.Volatile, "volatile"); // check the access attributes for consistency match (TypeBuilder.CheckAccessAttributes (attributes)) { | Some (msg) => Message.FatalError2 (loc, $"$msg for $title: $(this)") | _ => () } def mem_is_sealed = attributes %&& NemerleAttributes.Sealed; def mem_is_override = attributes %&& NemerleAttributes.Override; def mem_is_virtual = attributes %&& NemerleAttributes.Virtual; def mem_is_new = attributes %&& NemerleAttributes.New; def mem_is_abstract = attributes %&& NemerleAttributes.Abstract; // make sure 'virtual', 'new' and 'override' never appear at once when (mem_is_override && mem_is_new) Message.Error (loc, $"both `override' and `new' attributes specified" " for $title: $(this)"); // only allow 'override' methods/properties/events to be 'sealed' when (mem_is_sealed && !mem_is_override) Message.Error (loc, $"only `override' $title are allowed to be `sealed': $(this)"); // do not allow new virtual methods/properties/events in sealed clases when (declaring_type.IsSealed && mem_is_virtual && !mem_is_override && !declaring_type.IsDelegate) Message.Error (loc, Name + " is a new virtual member in a sealed class " + declaring_type.FullName); // do not allow private abstract methods when (mem_is_abstract && IsPrivate) Message.Error (loc, "abstract " + title_plural + " are not allowed to be private: " + ToString ()); // do not allow private 'virtual' or 'override' methods/properties/events when (mem_is_virtual && IsPrivate) Message.Error (loc, "virtual " + title_plural + " are not allowed to be private: " + ToString ()); when (mem_is_override && IsPrivate) Message.Error (loc, $"override $title_plural are not allowed to be private: $(this)"); // static methods/properties cannot be abstract, virtual or override when ((mem_is_abstract || mem_is_virtual || mem_is_override) && IsStatic) Message.Error (loc, $"static $title_plural are not allowed to have the " "`virtual' or `override' modifier: $(this)"); // do not allow protected and protected internal methods in structures when (declaring_type.IsStruct && IsProtected) { def msg = if (IsInternal) "protected internal" else "protected"; Message.Error (loc, $"$title_plural defined in a struct are not" " allowed to be $msg: $(this)") } } protected abstract MacroTarget : MacroTargets { get; } protected abstract MacroSelfParams : list [PT.SyntaxElement] { get; } public virtual AddMacroAttribute (expr : PT.PExpr) : void { modifiers.macro_attrs ::= (TypesManager.AttributeMacroExpansion.Suffix (MacroTarget, MacroPhase.WithTypedMembers), expr); def expansion = TypesManager.AttributeMacroExpansion (MacroTarget, MacroPhase.WithTypedMembers, expr, MacroSelfParams, declaring_type, null); declaring_type.TyManager.AddMacroExpansion (expansion); } internal virtual ProcessMacroAttributes () : void { declaring_type.process_attributes (MacroSelfParams, MacroTarget, MacroPhase.WithTypedMembers, modifiers, null) } internal abstract CreateEmitBuilder (emit_tb : SRE.TypeBuilder) : void; internal virtual Compile () : void { _ast = null; // GC kill } public IsBodyless : bool { // only allow abstract and extern methods to have no bodies get { attributes %&& (NemerleAttributes.Abstract | NemerleAttributes.Extern) } } } public partial class FieldBuilder : MemberBuilder, IField { mutable field_builder : SRE.FieldBuilder; [Accessor (flags = WantSetter)] mutable const_value : Literal; mutable has_been_assigned = false; /// Parsed representation (AST) of field. public new Ast : PT.ClassMember.Field { get { base.Ast :> PT.ClassMember.Field } } public this (par : TypeBuilder, fieldAst : PT.ClassMember.Field) { base (par, fieldAst); _initializerLocation = fieldAst.BodyLocation; // check if field's type is valid match (fieldAst.ty) { | <[ _ ]> => Message.Error ("type inference for fields is available only when assigning literal value to them") | _ => () } ty = par.MonoBindType (fieldAst.ty); when (ty.Equals (InternalType.Void)) Message.Error ($"$this has void type, which is not allowed"); ty.CheckAccessibility (this, accessibility); } public override MemberKind : MemberKinds { get { MemberKinds.Field } } /////////////////////////////////////////////////////////////////////////// // Initializer info [Accessor] mutable _initializerLocation : Location; public IsInitializerPresent : bool { get { InitializerLocation.FileIndex != 0 } } public EnsureCompiled() : void { LookupInitializerMethod().EnsureCompiled(); } public LookupInitializerMethod() : MethodBuilder { if (IsInitializerPresent) { Trace.Assert(DeclaringType.LookupMemberAvailable); def members = DeclaringType.LookupMemberImpl("_N_field_initialiser__" + Name); match (members) { | [method is MethodBuilder] => method | _ => throw ApplicationException($"Initialiser for $Name not found."); } } else { Trace.Assert(IsInitializerPresent); throw ApplicationException("IsInitializerPresent == false"); } } public InitializerMessages : SCG.List[CompilerMessage] { get { LookupInitializerMethod().BodyMessages } } public override ResetCodeCache () : void { LookupInitializerMethod().ResetCodeCache(); } public InitializerTokens : Token.BracesGroup { get { LookupInitializerMethod().BodyTokens; } } public InitializerParsed : PT.PExpr { get { LookupInitializerMethod().BodyParsed; } } public InitializerTyped : TExpr { get { LookupInitializerMethod().BodyTyped; } } // ////////////////////////////////////////////////////////////////////////// public IsMutable : bool { get { attributes %&& NemerleAttributes.Mutable } } public IsVolatile : bool { get { attributes %&& NemerleAttributes.Volatile } } public IsLiteral : bool { get { !IsMutable && IsStatic && const_value != null } } public HasBeenAssigned : bool { get { has_been_assigned } set { unless (has_been_assigned) { HasBeenUsed = value; // true for triggering obsolete warning has_been_assigned = true; } } } public GetValue () : Literal { assert (IsLiteral, Name); const_value } public GetFieldInfo () : SRE.FieldBuilder { assert (field_builder != null, Name); field_builder } override public GetHandle () : System.Reflection.MemberInfo { assert (field_builder != null, Name); field_builder } /** * Performs attributes checks for field definition. */ public override CheckAttributes () : void { when (declaring_type.IsInterface) Message.FatalError2 (loc, "fields cannot be defined inside interface"); check_for_invalid_attr (NemerleAttributes.Extern, "extern"); check_for_invalid_attr (NemerleAttributes.Abstract, "abstract"); check_for_invalid_attr (NemerleAttributes.Virtual, "virtual"); check_for_invalid_attr (NemerleAttributes.Sealed, "sealed"); check_for_invalid_attr (NemerleAttributes.Override, "override"); check_for_invalid_attr (NemerleAttributes.Struct, "struct"); check_for_invalid_attr (NemerleAttributes.Macro, "macro"); // check the access attributes for consistency match (TypeBuilder.CheckAccessAttributes (attributes)) { | Some (msg) => Message.FatalError2 (loc, $"$msg for $(this)") | _ => () } // check for non-mutable / volatile consistency when (!IsMutable && IsVolatile) Message.Error (loc, $"only mutable fields are allowed to be volatile in $(this)"); // do not allow protected and protected internal methods in structures when (declaring_type.IsStruct && IsProtected) { def msg = if (IsInternal) "protected internal" else "protected"; Message.Error (loc, "fields defined in a struct are not allowed to be " + msg + "in " + ToString ()) } } protected override MacroTarget : MacroTargets { get { MacroTargets.Field } } protected override MacroSelfParams : list [PT.SyntaxElement] { get { [PT.SyntaxElement.TypeBuilder (declaring_type), PT.SyntaxElement.FieldBuilder (this)] } } } public partial class PropertyBuilder : MemberBuilder, IProperty { is_mutable : bool; getter : MethodBuilder; setter : MethodBuilder; internal parms : list [MType]; mutable parent_property : IProperty = null; [Accessor (flags = Override)] protected mutable _bodyLocation : Location; internal mutable property_builder : SRE.PropertyBuilder; /// Parsed representation (AST) of property. /// Note: available only in CompletionMode /// (if Manager.IsIntelliSenseMode = true) public new Ast : PT.ClassMember.Property { get { base.Ast :> PT.ClassMember.Property } } public this (par : TypeBuilder, propertyAst : PT.ClassMember.Property) { base (par, propertyAst); //TODO: Add BodyLocation support to fields and event _bodyLocation = propertyAst.BodyLocation; is_mutable = Option.IsSome (propertyAst.set); ty = par.MonoBindType (propertyAst.prop_ty); parms = List.Map (propertyAst.dims, fun (parm : PT.Fun_parm) { par.MonoBindType (parm.ty); }); // Support autoproperty (see C# 3.0 specification). when (!(propertyAst.Attributes %&& (NemerleAttributes.Abstract | NemerleAttributes.Extern)) && !IsIndexer && !par.IsInterface) match (propertyAst.get, propertyAst.set) { | (Some(PT.ClassMember.Function(_, _, FunBody.Abstract) as getr), Some(PT.ClassMember.Function(_, _, FunBody.Abstract) as setr)) => def field = Macros.NewSymbol ("field"); par.Define(<[ decl: mutable $(field : name) : $(propertyAst.ty); ]>); getr.Body = <[ $(field : name) ]>; setr.Body = <[ $(field : name) = value ]>; | _ => () } def process_accessor (_ : option [PT.ClassMember]) { | Some (fn) => def fn = fn :> PT.ClassMember.Function; Util.locate (fn.Location, UpdateEmbeddedModifiers (attributes, ref fn.modifiers.mods)); def method = Manager.ComponentsFactory.CreateMethodBuilder (par, fn, is_property = true); method.MarkWithSpecialName (); declaring_type.AddMember (method); method | None => null } getter = process_accessor (propertyAst.get); setter = process_accessor (propertyAst.set); ty.CheckAccessibility (this, accessibility); // we'll check for the get/set usage methods instead m_has_been_used = true } public IsMutable : bool { get { is_mutable } } public IsIndexer : bool { get { !(parms is []) } } public override MemberKind : MemberKinds { get { MemberKinds.Property } } public override HasBeenUsed : bool { set { base.HasBeenUsed = value; // FIXME: only one of them should be used, but typing knows it a // little bit too late when (setter != null) setter.HasBeenUsed = value; when (getter != null) getter.HasBeenUsed = value; } } public GetPropertyInfo () : SRE.PropertyBuilder { assert (property_builder != null); property_builder } public Getter : IMethod { get { GetGetter () } } public CanRead : bool { get { Getter != null } } public GetGetter () : IMethod { if (getter == null && parent_property != null) parent_property.GetGetter () else getter } public Setter : IMethod { get { GetSetter () } } public CanWrite : bool { get { Setter != null } } public GetSetter () : IMethod { if (setter == null && parent_property != null) parent_property.GetSetter () else setter } public GetParameters () : list [MType] { parms } public UpdateParentProperty (parent_property : IProperty) : void { this.parent_property = parent_property; } override public GetHandle () : System.Reflection.MemberInfo { assert (property_builder != null); property_builder } /** * Performs attributes checks for properties definitions. */ public override CheckAttributes () : void { // make sure no static indexers get defined when (IsStatic && IsIndexer) Message.FatalError2 (loc, "indexer properties are not allowed to be static in " + ToString ()); // most of the checks are common with the methods and events: check_method_like_attributes ("property", "properties") } protected override MacroTarget : MacroTargets { get { MacroTargets.Property } } protected override MacroSelfParams : list [PT.SyntaxElement] { get { [PT.SyntaxElement.TypeBuilder (declaring_type), PT.SyntaxElement.PropertyBuilder (this)] } } } public partial class MethodBuilder : MemberBuilder, IMethod { [Accessor (Header)] protected mutable fun_header : Fun_header; /// system reflection emit method/constructor builder is stored here mutable method_base : SR.MethodBase; internal mutable overridden_method : IMethod; mutable implemented_methods : list [IMethod] = []; public ImplementedMethods : list [IMethod] { get { implemented_methods } internal set { implemented_methods = value } } public IsExtension : bool { mutable is_extension : bool; public get { is_extension } internal set { is_extension = value } } public IsConstructor : bool { get { method_base != null && method_base.IsConstructor } } /// Exclusive methode body location. [Accessor (flags = Override)] protected mutable _bodyLocation : Location; [Accessor] is_var_args : bool; /// Parsed representation (AST) of function. public new Ast : PT.ClassMember.Function { get { base.Ast :> PT.ClassMember.Function } } public GetMethodBase () : SR.MethodBase { assert (method_base != null); method_base } public HasMethodBase : bool { get { method_base != null } } public override GetHandle () : SR.MemberInfo { GetMethodBase () } public new GetMemType () : MType.Fun { ty :> MType.Fun } public GetConstructorInfo () : SRE.ConstructorBuilder { assert (method_base != null && method_base.IsConstructor); method_base :> SRE.ConstructorBuilder } public GetMethodInfo () : SRE.MethodBuilder { assert (method_base != null && !method_base.IsConstructor, declaring_type.FullName + "." + Name); method_base :> SRE.MethodBuilder } public override HasBeenUsed : bool { get { m_has_been_used } set { base.HasBeenUsed = value; m_has_been_used = true; declaring_type.HasBeenUsed = value && MemberKind == MemberKinds.Constructor; // trigger obsoletion warning only if we are ctor } } public HasAbstractBody : bool { get { (fun_header.body is FunBody.Abstract) } } public RunBodyTyper () : void { match (fun_header.body) { | FunBody.Parsed => def typer = Manager.ComponentsFactory.CreateTyper (this); typer.RunFullTyping (); | _ => () } } public override MemberKind : MemberKinds { get { if (this.name == ".ctor" || this.name == ".cctor") MemberKinds.Constructor else MemberKinds.Method } } type BeforeBodyTypingHandler = MethodBuilder * PT.PExpr -> PT.PExpr; public BeforeBodyTyping : list[BeforeBodyTypingHandler] { mutable _beforeBodyTyping : list[BeforeBodyTypingHandler]; private set { _beforeBodyTyping = value; } get { _beforeBodyTyping } } public AddBeforeBodyTypingHandler([NotNull] handler : BeforeBodyTypingHandler) : void { unless (Manager.IsIntelliSenseMode) // see also property BodyParsed in http://nemerle.org/svn/vs-plugin/trunk/Nemerle.Compiler.Utils/Nemerle.Completion2/CompilerConcreteDefinitions/IntelliSenseModeMethodBuilder.n Body = handler(this, Body); if (BeforeBodyTyping == null) BeforeBodyTyping = [handler]; else BeforeBodyTyping ::= handler; } public ClearBeforeBodyTypingHandlers() : void { BeforeBodyTyping = null; } #region CompletionSpecific //*********************************************************************** // This members available only under completion engine. static NotSupported () : System.Exception { System.NotSupportedException ("available on in code-completion use-case") } public virtual EnsureCompiled() : void { throw NotSupported () } public virtual BodyMessages : SCG.List[CompilerMessage] { get { throw NotSupported () } } public virtual BodyTokens : Token.BracesGroup { get { throw NotSupported() } set { throw System.NotSupportedException ("available on in code-completion use-case " + value.ToString ()) } } public virtual BodyParsed : PT.PExpr { get { throw NotSupported () } } public virtual IsBodyCompilable : bool { get { throw NotSupported () } } public virtual BodyTyped : TExpr { get { throw NotSupported () } } // End: This members available only under completion engine. //*********************************************************************** #endregion public virtual Body : PT.PExpr { get { match (fun_header.body) { | FunBody.Parsed (e) => e | _ => Message.FatalError ("No parsetree body accessible") } } set { fun_header.body = FunBody.Parsed (value) } } public IsFinal : bool { get { attributes %&& NemerleAttributes.Sealed } } public this (par : TypeBuilder, functionAst : PT.ClassMember.Function) { this (par, functionAst, false); } public this (par : TypeBuilder, functionAst : PT.ClassMember.Function, is_property : bool) { base (par, functionAst); def f = functionAst; when (LexerBase.IsOperator (name)) unless (IsStatic && IsPublic) Message.Error ($"User defined operator '$(DeclaringType).$(name)'" " must be declared static and public"); // convert +, -, /= name to op_Addition equivalents match (OperatorLongName (this.name, List.Length (f.header.parms))) { | Some (long) => this.name = long; attributes |= NemerleAttributes.SpecialName; | None => () }; // both override and abstract imply virtual when (attributes %&& (NemerleAttributes.Override %| NemerleAttributes.Abstract)) attributes |= NemerleAttributes.Virtual; def (tenv', typarms) = declaring_type.BindTyparms (f.header.typarms); // HACK HACK HACK!!! def typarms = if (declaring_type.forced_typarms != null) { def tmp = declaring_type.forced_typarms; declaring_type.forced_typarms = null; tmp } else typarms; mutable types_to_check = []; foreach (tv in typarms) foreach (t in tv.Constraints) types_to_check = t :: types_to_check; def bind (t : PT.PExpr) { when (t is <[ _ ]>) Message.Error (t.Location, "type inference on global methods is not yet supported"); def ty = f.header.typarms.Substitute (par.MonoBindType (tenv', t)).Fix (); types_to_check ::= ty; ty } def mkparm (p : PT.Fun_parm) : Fun_parm { def name = p.ParsedName; def has_default = p.modifiers.custom_attrs.Exists (fun (x) { x is <[ System.ComponentModel.DefaultValueAttribute ($_) ]> }); def (kind, ty) = match (p.ty) { | <[ ref $t ]> => (ParmKind.Ref, MType.Ref (bind (t))) | <[ out $t ]> => (ParmKind.Out, MType.Out (bind (t))) | <[ _ ]> when has_default => (ParmKind.Normal, null) | PT.PExpr.TypedType(MType.Ref as t) with k = ParmKind.Ref | PT.PExpr.TypedType(MType.Out as t) with k = ParmKind.Out => (k, t) | <[ $t ]> => (ParmKind.Normal, bind (t)) } def fp = Fun_parm (loc = p.Location, name = name.Id, nameLocation = name.Location, color = name.color, ty = ty, typeLocation = p.ty.Location, modifiers = p.modifiers, kind = kind); fp.GetDefaultValueFromModifiers (par); fp } mutable fun_body = f.body; _bodyLocation = fun_body.CalcExclusiveBodyLocation (); /* We need to convert things like: foo (x : int * int) : int { bar } ===> foo (_1 : int, _2 : int) : int { def x = (_1, _2); bar } To be consistent. */ def parms = if (Manager.IsIntelliSenseMode) List.Map (f.header.parms, mkparm) else match (f.header.parms) { | [ PT.Fun_parm where (ty = <[ @* (..$_) ]>) as parm ] when !is_property => def types = (par.MonoBindType (tenv', parm.ty) :> MType.Tuple).args; mutable modifiers = parm.modifiers; def mkdummyparm (t : TyVar) { // make the first argument intercept custom attributes def mods = modifiers; modifiers = Modifiers (); Fun_parm (loc = parm.Location, name = Util.tmpname ("tupled"), nameLocation = Location.Default, color = Manager.MacroColors.Color, kind = ParmKind.Normal, ty = t, typeLocation = parm.ty.Location, modifiers = mods) }; def parms = List.Map (types, mkdummyparm); def refs = List.Map (parms, fun (fp : Fun_parm) { PT.PExpr.Ref (PT.Name (fp.name)) }); match (fun_body) { | FunBody.Parsed (<[ { .. $bodys } ]> as _body) | FunBody.Parsed (_body) with bodys = [_body] => fun_body = FunBody.Parsed (PT.PExpr.Sequence (_body.Location, <[ def $(parm.ParsedName : name) = ( .. $refs ); ]> :: bodys)) | _ => Util.ice () }; parms | _ => List.Map (f.header.parms, mkparm) }; when (! parms.IsEmpty) { def is_params (parm : Fun_parm) { List.Exists (parm.modifiers.custom_attrs, fun (e) { | <[ System.ParamArrayAttribute ]> | <[ System.ParamArrayAttribute () ]> | <[ System.ParamArray ]> | <[ System.ParamArray () ]> => true | _ => false }) } def (till_last, last) = List.DivideLast (parms); when (is_params (last)) { match (last.ty.Fix ()) { | MType.Array (_, 1) => is_var_args = true | _ => Message.Error ("only single-dimensional arrays are allowed in `params' parameters") } } when (List.Exists (till_last, is_params)) Message.Error ("`params' is allowed only on last parameter"); } match ((f.implemented, this.name)) { | ([], ".ctor") when IsStatic => this.m_has_been_used = true; this.name = ".cctor"; unless (parms.IsEmpty) Message.Error ("static constructor cannot have any parameters"); | ([], ".ctor") => when (par.IsValueType && parms.IsEmpty) Message.Error ("explicit parameterless constructor in value type is not allowed"); | ([], _) when IsStatic => () | (_ :: _, ".ctor") => Message.Error ("constructor cannot implement anything"); | (_ :: _, _) when IsStatic => Message.Error ("static function cannot implement anything"); | (_ :: _ as impl, name) when is_property => // update names of explicitly implemented accessor methods def prefix = if (name.StartsWith ("get_")) "get_" else "set_"; def newimpl = List.Map (impl, fun (m) { | PT.PExpr.Member (obj, mem) => def name = mem.GetName (); PT.PExpr.Member (m.Location, obj, PT.Splicable.Name (name.NewName (prefix + name.Id))) | x => Message.Error ("implemented property must be of Interface.ProprtyName form"); x }); f.implemented = newimpl | _ => () }; fun_header = Fun_header ( ret_type = bind (f.header.ret_type), ret_type_loc = f.header.ret_type.Location, typarms = typarms, name = this.name, parms = parms, tenv = tenv', loc = f.header.Location); fun_header.body = fun_body; unless (check_variance_valid (fun_header.ret_type.Fix(), 1)) Message.Error ($"return type of method `$name' must behave covariantly"); foreach (parm in parms) { match (parm.ty.Fix()) { | MType.Void => Message.Error ($ "method `$name' has void argument"); | x => unless (check_variance_valid (x, -1)) Message.Error ($"type of `$name' method's parameter `$(parm.name)' must behave contravariantly"); } } foreach (tp in typarms) { when (tp.IsCovariant || tp.IsContravariant) Message.Error ("method's generic paramameter cannot declare variance"); foreach (ctr in tp.Constraints) unless (check_variance_valid (ctr, -1)) // I'm not sure why this is forbidden, but PEVerify indeed complains :) Message.Error ($"constraints of generic parameters of method `$name' must behave contravariantly"); } /// end of variance checks ty = MType.ConstructFunctionType (fun_header); // check it at the end, since error reporting routine there // needs us to be initialized to name us foreach (t in types_to_check) t.CheckAccessibility (this, accessibility); // don't warn about the unused `Main' methods... when (IsStatic && name == "Main") Manager.MarkAsUsed (this) } // this method check the specification from CLI ECMA-335 II 9.7 internal static check_variance_valid (t : TyVar, enforced_variance_sign : int) : bool { match (t.Fix ()) { | MType.Class (_, []) => true // is always valid | MType.TyVarRef (tr) => if (enforced_variance_sign > 0) !tr.IsContravariant else if (enforced_variance_sign < 0) !tr.IsCovariant else !tr.IsCovariant && !tr.IsContravariant | MType.Array (t, _) => check_variance_valid (t, enforced_variance_sign) // array types behave covariantly | MType.Class (tc, args) => List.ForAll2 (tc.Typarms, args, fun (parm, arg) { def enforce_sign = if (parm.IsCovariant) 1 else if (parm.IsContravariant) -1 else 0; check_variance_valid (arg, enforce_sign * enforced_variance_sign) }) | MType.Fun (a,b) => check_variance_valid (a, 0) && check_variance_valid (b, 0) | MType.Tuple (args) => args.ForAll (check_variance_valid (_, 0)) | MType.Ref (a) | MType.Out (a) => check_variance_valid (a, 0) | _ => true // others should not appear here, just ignore } } public GetHeader () : Fun_header { fun_header } public GetParameters () : list [Fun_parm] { fun_header.parms } public ReturnType : TyVar { get { fun_header.ret_type } } public GetFreshType () : MType * list [TyVar] { def (subst, vars) = Subst.Fresh (fun_header.typarms); (subst.MonoApply (GetMemType ()), vars) } static OperatorLongName (op : string, params_amount : int) : option [string] { if (params_amount == 2) { match (op) { | "+" => Some ("op_Addition") | "-" => Some ("op_Subtraction") | "*" => Some ("op_Multiply") | "/" => Some ("op_Division") | "%" => Some ("op_Modulus") | "%^" => Some ("op_ExclusiveOr") | "%&" => Some ("op_BitwiseAnd") | "%|" => Some ("op_BitwiseOr") | "^" => Some ("op_ExclusiveOr") | "&" => Some ("op_BitwiseAnd") | "|" => Some ("op_BitwiseOr") | "&&" => Some ("op_LogicalAnd") | "||" => Some ("op_LogicalOr") | "=" => Some ("op_Assign") | "<<" => Some ("op_LeftShift") | ">>" => Some ("op_RightShift") // | ">>*" => Some ("op_SignedRightShift") // | ">>&" => Some ("op_UnsignedRightShift") | "==" => Some ("op_Equality") | ">" => Some ("op_GreaterThan") | "<" => Some ("op_LessThan") | "!=" => Some ("op_Inequality") | ">=" => Some ("op_GreaterThanOrEqual") | "<=" => Some ("op_LessThanOrEqual") | "*=" => Some ("op_MultiplicationAssignment") | "-=" => Some ("op_SubtractionAssignment") | "^=" => Some ("op_ExclusiveOrAssignment") | "<<=" => Some ("op_LeftShiftAssignment") | "%=" => Some ("op_ModulusAssignment") | "+=" => Some ("op_AdditionAssignment") | "&=" => Some ("op_BitwiseAndAssignment") | "|=" => Some ("op_BitwiseOrAssignment") | "," => Some ("op_Comma") | "/=" => Some ("op_DivisionAssignment") | _ => None () } } else if (params_amount == 1) { match (op) { | ":" => Some ("op_Implicit") | ":>" => Some ("op_Explicit") | "+" when params_amount == 1 => Some ("op_UnaryPlus") | "-" => Some ("op_UnaryNegation") | "--" => Some ("op_Decrement") | "++" => Some ("op_Increment") | "~" => Some ("op_OnesComplement") | "!" => Some ("op_LogicalNot") | _ => None () } } else None () } /** * Performs attributes checks for method definitions. */ public override CheckAttributes () : void { when (declaring_type.IsInterface) { unless (HasAbstractBody) Message.Error (loc, "interface method cannot have body"); unless (Ast.implemented.IsEmpty) Message.Error (loc, "interface method cannot implement anything"); } when (HasAbstractBody && !IsBodyless) Message.Error (loc, "missing body of a non-abstract and non-extern method in " + ToString ()); when (attributes %&& NemerleAttributes.Extern && !HasAbstractBody) Message.Error (loc, "method with `extern' modifier cannot have body"); // most of the checks are common with the properties and events: check_method_like_attributes ("method", "methods"); when (MemberKind == MemberKinds.Constructor) { check_for_invalid_attr (NemerleAttributes.Abstract, "abstract"); check_for_invalid_attr (NemerleAttributes.Virtual, "virtual"); check_for_invalid_attr (NemerleAttributes.Sealed, "sealed"); check_for_invalid_attr (NemerleAttributes.Override, "override"); } } public AddAsExtensionMethod () : void { Manager.LibrariesManager.AddExtensionMethod (this); } public BuiltinKind : BuiltinMethodKind { get { BuiltinMethodKind.NotBuiltin () } } protected override MacroTarget : MacroTargets { get { MacroTargets.Method } } protected override MacroSelfParams : list [PT.SyntaxElement] { get { [PT.SyntaxElement.TypeBuilder (declaring_type), PT.SyntaxElement.MethodBuilder (this)] } } internal override ProcessMacroAttributes () : void { base.ProcessMacroAttributes (); foreach (p : Fun_parm in GetParameters ()) declaring_type.process_attributes ([PT.SyntaxElement.TypeBuilder (declaring_type), PT.SyntaxElement.MethodBuilder (this), PT.SyntaxElement.ParameterBuilder (p)], MacroTargets.Parameter, MacroPhase.WithTypedMembers, p.modifiers, this); } } public partial class EventBuilder : MemberBuilder, IEvent { adder : MethodBuilder; remover : MethodBuilder; internal mutable event_builder : SRE.EventBuilder; internal storage_field : FieldBuilder; /// Parsed representation (AST) of event. /// Note: available only in CompletionMode /// (if Manager.IsIntelliSenseMode = true) public new Ast : PT.ClassMember.Event { get { base.Ast :> PT.ClassMember.Event } } public this (par : TypeBuilder, eventAst : PT.ClassMember.Event) { base (par, eventAst); ty = par.MonoBindType (eventAst.ty); // prevent closurising `this' def self = this; when (eventAst.field != null) { def update_mfunction (meth : PT.ClassMember.Function, which_accessor : string) { if (declaring_type.IsInterface) meth.body = FunBody.Abstract () else { def lock_expr = if (IsStatic) <[ typeof ($(declaring_type.ParsedName : name)) ]> else <[ this ]>; def field_name = eventAst.field.ParsedName; // according to C# spec, we have to add locks // http://www.jaggersoft.com/csharp_standard/17.7.1.htm def bd = if (which_accessor == "add") <[ lock ($lock_expr) { $(field_name : name) += $(field_name.NewName ("value") : name) } ]>; else <[ lock ($lock_expr) { $(field_name : name) -= $(field_name.NewName ("value") : name) } ]>; meth.body = FunBody.Parsed (bd) } }; unless (declaring_type.IsInterface) { when (IsStatic) eventAst.field.modifiers.mods |= NemerleAttributes.Static; storage_field = FieldBuilder (par, eventAst.field); declaring_type.AddMember (storage_field); } update_mfunction (eventAst.add, "add"); update_mfunction (eventAst.remove, "remove"); } def make_method (mfunc : PT.ClassMember.Function) { UpdateEmbeddedModifiers (self.attributes, ref mfunc.modifiers.mods); def meth' = Manager.ComponentsFactory.CreateMethodBuilder (self.declaring_type, mfunc); self.declaring_type.AddMember (meth'); meth'.MarkWithSpecialName (); meth' }; adder = make_method (eventAst.add); remover = make_method (eventAst.remove); Manager.MarkAsUsed (remover); ty.CheckAccessibility (this, accessibility); } GetEventInfo () : SR.EventInfo implements IEvent.GetEventInfo { assert (false); } public override MemberKind : MemberKinds { get { MemberKinds.Event } } public override GetHandle () : SR.MemberInfo { // for some reason !(event_builder)... /// lame spec: SRE.EventBuilder is not MemberInfo or event EventInfo /// but we return null to do not spoil API null } public GetAdder () : MethodBuilder { adder } public GetRemover () : MethodBuilder { remover } /** * Performs attributes checks for event definition. */ public override CheckAttributes () : void { // most of the checks are common with the properties and methods: check_method_like_attributes ("event", "events") } protected override MacroTarget : MacroTargets { get { MacroTargets.Event } } protected override MacroSelfParams : list [PT.SyntaxElement] { get { [PT.SyntaxElement.TypeBuilder (declaring_type), PT.SyntaxElement.EventBuilder (this)] } } } }