e46a49ecf1
Former-commit-id: d0813289fa2d35e1f8ed77530acb4fb1df441bc0
1869 lines
51 KiB
C#
1869 lines
51 KiB
C#
//
|
|
// property.cs: Property based handlers
|
|
//
|
|
// Authors: Miguel de Icaza (miguel@gnu.org)
|
|
// Martin Baulig (martin@ximian.com)
|
|
// Marek Safar (marek.safar@seznam.cz)
|
|
//
|
|
// Dual licensed under the terms of the MIT X11 or GNU GPL
|
|
//
|
|
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
|
|
// Copyright 2004-2008 Novell, Inc
|
|
// Copyright 2011 Xamarin Inc
|
|
//
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using Mono.CompilerServices.SymbolWriter;
|
|
|
|
#if MOBILE
|
|
using XmlElement = System.Object;
|
|
#endif
|
|
|
|
#if STATIC
|
|
using IKVM.Reflection;
|
|
using IKVM.Reflection.Emit;
|
|
#else
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
#endif
|
|
|
|
namespace Mono.CSharp
|
|
{
|
|
// It is used as a base class for all property based members
|
|
// This includes properties, indexers, and events
|
|
public abstract class PropertyBasedMember : InterfaceMemberBase
|
|
{
|
|
protected PropertyBasedMember (TypeDefinition parent, FullNamedExpression type, Modifiers mod, Modifiers allowed_mod, MemberName name, Attributes attrs)
|
|
: base (parent, type, mod, allowed_mod, name, attrs)
|
|
{
|
|
}
|
|
|
|
protected void CheckReservedNameConflict (string prefix, MethodSpec accessor)
|
|
{
|
|
string name;
|
|
AParametersCollection parameters;
|
|
if (accessor != null) {
|
|
name = accessor.Name;
|
|
parameters = accessor.Parameters;
|
|
} else {
|
|
name = prefix + ShortName;
|
|
if (IsExplicitImpl)
|
|
name = MemberName.Left + "." + name;
|
|
|
|
if (this is Indexer) {
|
|
parameters = ((Indexer) this).ParameterInfo;
|
|
if (prefix[0] == 's') {
|
|
var data = new IParameterData[parameters.Count + 1];
|
|
Array.Copy (parameters.FixedParameters, data, data.Length - 1);
|
|
data[data.Length - 1] = new ParameterData ("value", Parameter.Modifier.NONE);
|
|
var types = new TypeSpec[data.Length];
|
|
Array.Copy (parameters.Types, types, data.Length - 1);
|
|
types[data.Length - 1] = member_type;
|
|
|
|
parameters = new ParametersImported (data, types, false);
|
|
}
|
|
} else {
|
|
if (prefix[0] == 's')
|
|
parameters = ParametersCompiled.CreateFullyResolved (new[] { member_type });
|
|
else
|
|
parameters = ParametersCompiled.EmptyReadOnlyParameters;
|
|
}
|
|
}
|
|
|
|
var conflict = MemberCache.FindMember (Parent.Definition,
|
|
new MemberFilter (name, 0, MemberKind.Method, parameters, null),
|
|
BindingRestriction.DeclaredOnly | BindingRestriction.NoAccessors);
|
|
|
|
if (conflict != null) {
|
|
Report.SymbolRelatedToPreviousError (conflict);
|
|
Report.Error (82, Location, "A member `{0}' is already reserved", conflict.GetSignatureForError ());
|
|
}
|
|
}
|
|
|
|
protected override bool VerifyClsCompliance ()
|
|
{
|
|
if (!base.VerifyClsCompliance ())
|
|
return false;
|
|
|
|
if (!MemberType.IsCLSCompliant ()) {
|
|
Report.Warning (3003, 1, Location, "Type of `{0}' is not CLS-compliant",
|
|
GetSignatureForError ());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
public class PropertySpec : MemberSpec, IInterfaceMemberSpec
|
|
{
|
|
PropertyInfo info;
|
|
TypeSpec memberType;
|
|
MethodSpec set, get;
|
|
|
|
public PropertySpec (MemberKind kind, TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, PropertyInfo info, Modifiers modifiers)
|
|
: base (kind, declaringType, definition, modifiers)
|
|
{
|
|
this.info = info;
|
|
this.memberType = memberType;
|
|
}
|
|
|
|
#region Properties
|
|
|
|
public MethodSpec Get {
|
|
get {
|
|
return get;
|
|
}
|
|
set {
|
|
get = value;
|
|
get.IsAccessor = true;
|
|
}
|
|
}
|
|
|
|
public MethodSpec Set {
|
|
get {
|
|
return set;
|
|
}
|
|
set {
|
|
set = value;
|
|
set.IsAccessor = true;
|
|
}
|
|
}
|
|
|
|
public bool HasDifferentAccessibility {
|
|
get {
|
|
return HasGet && HasSet &&
|
|
(Get.Modifiers & Modifiers.AccessibilityMask) != (Set.Modifiers & Modifiers.AccessibilityMask);
|
|
}
|
|
}
|
|
|
|
public bool HasGet {
|
|
get {
|
|
return Get != null;
|
|
}
|
|
}
|
|
|
|
public bool HasSet {
|
|
get {
|
|
return Set != null;
|
|
}
|
|
}
|
|
|
|
public PropertyInfo MetaInfo {
|
|
get {
|
|
if ((state & StateFlags.PendingMetaInflate) != 0)
|
|
throw new NotSupportedException ();
|
|
|
|
return info;
|
|
}
|
|
}
|
|
|
|
public TypeSpec MemberType {
|
|
get {
|
|
return memberType;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public override MemberSpec InflateMember (TypeParameterInflator inflator)
|
|
{
|
|
var ps = (PropertySpec) base.InflateMember (inflator);
|
|
ps.memberType = inflator.Inflate (memberType);
|
|
return ps;
|
|
}
|
|
|
|
public override List<MissingTypeSpecReference> ResolveMissingDependencies (MemberSpec caller)
|
|
{
|
|
return memberType.ResolveMissingDependencies (this);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Properties and Indexers both generate PropertyBuilders, we use this to share
|
|
// their common bits.
|
|
//
|
|
abstract public class PropertyBase : PropertyBasedMember {
|
|
|
|
public class GetMethod : PropertyMethod
|
|
{
|
|
static readonly string[] attribute_targets = new string [] { "method", "return" };
|
|
|
|
internal const string Prefix = "get_";
|
|
|
|
public GetMethod (PropertyBase method, Modifiers modifiers, Attributes attrs, Location loc)
|
|
: base (method, Prefix, modifiers, attrs, loc)
|
|
{
|
|
}
|
|
|
|
public override void Define (TypeContainer parent)
|
|
{
|
|
base.Define (parent);
|
|
|
|
Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, ModFlags);
|
|
|
|
method_data = new MethodData (method, ModFlags, flags, this);
|
|
|
|
method_data.Define (parent.PartialContainer, method.GetFullName (MemberName));
|
|
}
|
|
|
|
public override TypeSpec ReturnType {
|
|
get {
|
|
return method.MemberType;
|
|
}
|
|
}
|
|
|
|
public override ParametersCompiled ParameterInfo {
|
|
get {
|
|
return ParametersCompiled.EmptyReadOnlyParameters;
|
|
}
|
|
}
|
|
|
|
public override string[] ValidAttributeTargets {
|
|
get {
|
|
return attribute_targets;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class SetMethod : PropertyMethod {
|
|
|
|
static readonly string[] attribute_targets = new string[] { "method", "param", "return" };
|
|
|
|
internal const string Prefix = "set_";
|
|
|
|
protected ParametersCompiled parameters;
|
|
|
|
public SetMethod (PropertyBase method, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc)
|
|
: base (method, Prefix, modifiers, attrs, loc)
|
|
{
|
|
this.parameters = parameters;
|
|
}
|
|
|
|
protected override void ApplyToExtraTarget (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if (a.Target == AttributeTargets.Parameter) {
|
|
parameters[parameters.Count - 1].ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
return;
|
|
}
|
|
|
|
base.ApplyToExtraTarget (a, ctor, cdata, pa);
|
|
}
|
|
|
|
public override ParametersCompiled ParameterInfo {
|
|
get {
|
|
return parameters;
|
|
}
|
|
}
|
|
|
|
public override void Define (TypeContainer parent)
|
|
{
|
|
parameters.Resolve (this);
|
|
|
|
base.Define (parent);
|
|
|
|
Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, ModFlags);
|
|
|
|
method_data = new MethodData (method, ModFlags, flags, this);
|
|
|
|
method_data.Define (parent.PartialContainer, method.GetFullName (MemberName));
|
|
}
|
|
|
|
public override TypeSpec ReturnType {
|
|
get {
|
|
return Parent.Compiler.BuiltinTypes.Void;
|
|
}
|
|
}
|
|
|
|
public override string[] ValidAttributeTargets {
|
|
get {
|
|
return attribute_targets;
|
|
}
|
|
}
|
|
}
|
|
|
|
static readonly string[] attribute_targets = new string[] { "property" };
|
|
|
|
public abstract class PropertyMethod : AbstractPropertyEventMethod
|
|
{
|
|
const Modifiers AllowedModifiers =
|
|
Modifiers.PUBLIC |
|
|
Modifiers.PROTECTED |
|
|
Modifiers.INTERNAL |
|
|
Modifiers.PRIVATE;
|
|
|
|
protected readonly PropertyBase method;
|
|
protected MethodAttributes flags;
|
|
|
|
public PropertyMethod (PropertyBase method, string prefix, Modifiers modifiers, Attributes attrs, Location loc)
|
|
: base (method, prefix, attrs, loc)
|
|
{
|
|
this.method = method;
|
|
this.ModFlags = ModifiersExtensions.Check (AllowedModifiers, modifiers, 0, loc, Report);
|
|
this.ModFlags |= (method.ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE));
|
|
}
|
|
|
|
public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if (a.Type == pa.MethodImpl) {
|
|
method.is_external_implementation = a.IsInternalCall ();
|
|
}
|
|
|
|
base.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
}
|
|
|
|
public override AttributeTargets AttributeTargets {
|
|
get {
|
|
return AttributeTargets.Method;
|
|
}
|
|
}
|
|
|
|
public override bool IsClsComplianceRequired ()
|
|
{
|
|
return method.IsClsComplianceRequired ();
|
|
}
|
|
|
|
public virtual void Define (TypeContainer parent)
|
|
{
|
|
var container = parent.PartialContainer;
|
|
|
|
//
|
|
// Check for custom access modifier
|
|
//
|
|
if ((ModFlags & Modifiers.AccessibilityMask) == 0) {
|
|
ModFlags |= method.ModFlags;
|
|
flags = method.flags;
|
|
} else {
|
|
CheckModifiers (ModFlags);
|
|
ModFlags |= (method.ModFlags & (~Modifiers.AccessibilityMask));
|
|
ModFlags |= Modifiers.PROPERTY_CUSTOM;
|
|
|
|
if (container.Kind == MemberKind.Interface) {
|
|
Report.Error (275, Location, "`{0}': accessibility modifiers may not be used on accessors in an interface",
|
|
GetSignatureForError ());
|
|
} else if ((ModFlags & Modifiers.PRIVATE) != 0) {
|
|
if ((method.ModFlags & Modifiers.ABSTRACT) != 0) {
|
|
Report.Error (442, Location, "`{0}': abstract properties cannot have private accessors", GetSignatureForError ());
|
|
}
|
|
|
|
ModFlags &= ~Modifiers.VIRTUAL;
|
|
}
|
|
|
|
flags = ModifiersExtensions.MethodAttr (ModFlags) | MethodAttributes.SpecialName;
|
|
}
|
|
|
|
CheckAbstractAndExtern (block != null);
|
|
CheckProtectedModifier ();
|
|
|
|
if (block != null) {
|
|
if (block.IsIterator)
|
|
Iterator.CreateIterator (this, Parent.PartialContainer, ModFlags);
|
|
|
|
if (Compiler.Settings.WriteMetadataOnly)
|
|
block = null;
|
|
}
|
|
}
|
|
|
|
public bool HasCustomAccessModifier {
|
|
get {
|
|
return (ModFlags & Modifiers.PROPERTY_CUSTOM) != 0;
|
|
}
|
|
}
|
|
|
|
public PropertyBase Property {
|
|
get {
|
|
return method;
|
|
}
|
|
}
|
|
|
|
public override ObsoleteAttribute GetAttributeObsolete ()
|
|
{
|
|
return method.GetAttributeObsolete ();
|
|
}
|
|
|
|
public override string GetSignatureForError()
|
|
{
|
|
return method.GetSignatureForError () + "." + prefix.Substring (0, 3);
|
|
}
|
|
|
|
void CheckModifiers (Modifiers modflags)
|
|
{
|
|
if (!ModifiersExtensions.IsRestrictedModifier (modflags & Modifiers.AccessibilityMask, method.ModFlags & Modifiers.AccessibilityMask)) {
|
|
Report.Error (273, Location,
|
|
"The accessibility modifier of the `{0}' accessor must be more restrictive than the modifier of the property or indexer `{1}'",
|
|
GetSignatureForError (), method.GetSignatureForError ());
|
|
}
|
|
}
|
|
}
|
|
|
|
PropertyMethod get, set, first;
|
|
PropertyBuilder PropertyBuilder;
|
|
|
|
protected PropertyBase (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, Modifiers allowed_mod, MemberName name, Attributes attrs)
|
|
: base (parent, type, mod_flags, allowed_mod, name, attrs)
|
|
{
|
|
}
|
|
|
|
#region Properties
|
|
|
|
public override AttributeTargets AttributeTargets {
|
|
get {
|
|
return AttributeTargets.Property;
|
|
}
|
|
}
|
|
|
|
public PropertyMethod AccessorFirst {
|
|
get {
|
|
return first;
|
|
}
|
|
}
|
|
|
|
public PropertyMethod AccessorSecond {
|
|
get {
|
|
return first == get ? set : get;
|
|
}
|
|
}
|
|
|
|
public override Variance ExpectedMemberTypeVariance {
|
|
get {
|
|
return (get != null && set != null) ?
|
|
Variance.None : set == null ?
|
|
Variance.Covariant :
|
|
Variance.Contravariant;
|
|
}
|
|
}
|
|
|
|
public PropertyMethod Get {
|
|
get {
|
|
return get;
|
|
}
|
|
set {
|
|
get = value;
|
|
if (first == null)
|
|
first = value;
|
|
|
|
Parent.AddNameToContainer (get, get.MemberName.Basename);
|
|
}
|
|
}
|
|
|
|
public PropertyMethod Set {
|
|
get {
|
|
return set;
|
|
}
|
|
set {
|
|
set = value;
|
|
if (first == null)
|
|
first = value;
|
|
|
|
Parent.AddNameToContainer (set, set.MemberName.Basename);
|
|
}
|
|
}
|
|
|
|
public override string[] ValidAttributeTargets {
|
|
get {
|
|
return attribute_targets;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if (a.HasSecurityAttribute) {
|
|
a.Error_InvalidSecurityParent ();
|
|
return;
|
|
}
|
|
|
|
if (a.Type == pa.Dynamic) {
|
|
a.Error_MisusedDynamicAttribute ();
|
|
return;
|
|
}
|
|
|
|
PropertyBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata);
|
|
}
|
|
|
|
void CheckMissingAccessor (MemberKind kind, ParametersCompiled parameters, bool get)
|
|
{
|
|
if (IsExplicitImpl) {
|
|
MemberFilter filter;
|
|
if (kind == MemberKind.Indexer)
|
|
filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, kind, parameters, null);
|
|
else
|
|
filter = new MemberFilter (MemberName.Name, 0, kind, null, null);
|
|
|
|
var implementing = MemberCache.FindMember (InterfaceType, filter, BindingRestriction.DeclaredOnly) as PropertySpec;
|
|
|
|
if (implementing == null)
|
|
return;
|
|
|
|
var accessor = get ? implementing.Get : implementing.Set;
|
|
if (accessor != null) {
|
|
Report.SymbolRelatedToPreviousError (accessor);
|
|
Report.Error (551, Location, "Explicit interface implementation `{0}' is missing accessor `{1}'",
|
|
GetSignatureForError (), accessor.GetSignatureForError ());
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override bool CheckOverrideAgainstBase (MemberSpec base_member)
|
|
{
|
|
var ok = base.CheckOverrideAgainstBase (base_member);
|
|
|
|
//
|
|
// Check base property accessors conflict
|
|
//
|
|
var base_prop = (PropertySpec) base_member;
|
|
if (Get == null) {
|
|
if ((ModFlags & Modifiers.SEALED) != 0 && base_prop.HasGet && !base_prop.Get.IsAccessible (this)) {
|
|
// TODO: Should be different error code but csc uses for some reason same
|
|
Report.SymbolRelatedToPreviousError (base_prop);
|
|
Report.Error (545, Location,
|
|
"`{0}': cannot override because `{1}' does not have accessible get accessor",
|
|
GetSignatureForError (), base_prop.GetSignatureForError ());
|
|
ok = false;
|
|
}
|
|
} else {
|
|
if (!base_prop.HasGet) {
|
|
if (ok) {
|
|
Report.SymbolRelatedToPreviousError (base_prop);
|
|
Report.Error (545, Get.Location,
|
|
"`{0}': cannot override because `{1}' does not have an overridable get accessor",
|
|
Get.GetSignatureForError (), base_prop.GetSignatureForError ());
|
|
ok = false;
|
|
}
|
|
} else if (Get.HasCustomAccessModifier || base_prop.HasDifferentAccessibility) {
|
|
if (!base_prop.Get.IsAccessible (this)) {
|
|
// Same as csc but it should be different error code
|
|
Report.Error (115, Get.Location, "`{0}' is marked as an override but no accessible `get' accessor found to override",
|
|
GetSignatureForError ());
|
|
ok = false;
|
|
} else if (!CheckAccessModifiers (Get, base_prop.Get)) {
|
|
Error_CannotChangeAccessModifiers (Get, base_prop.Get);
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Set == null) {
|
|
if (base_prop.HasSet) {
|
|
if ((ModFlags & Modifiers.SEALED) != 0 && !base_prop.Set.IsAccessible (this)) {
|
|
// TODO: Should be different error code but csc uses for some reason same
|
|
Report.SymbolRelatedToPreviousError (base_prop);
|
|
Report.Error (546, Location,
|
|
"`{0}': cannot override because `{1}' does not have accessible set accessor",
|
|
GetSignatureForError (), base_prop.GetSignatureForError ());
|
|
ok = false;
|
|
}
|
|
|
|
if ((ModFlags & Modifiers.AutoProperty) != 0) {
|
|
Report.Error (8080, Location, "`{0}': Auto-implemented properties must override all accessors of the overridden property",
|
|
GetSignatureForError ());
|
|
ok = false;
|
|
}
|
|
}
|
|
} else {
|
|
if (!base_prop.HasSet) {
|
|
if (ok) {
|
|
Report.SymbolRelatedToPreviousError (base_prop);
|
|
Report.Error (546, Set.Location,
|
|
"`{0}': cannot override because `{1}' does not have an overridable set accessor",
|
|
Set.GetSignatureForError (), base_prop.GetSignatureForError ());
|
|
ok = false;
|
|
}
|
|
} else if (Set.HasCustomAccessModifier || base_prop.HasDifferentAccessibility) {
|
|
if (!base_prop.Set.IsAccessible (this)) {
|
|
// Same as csc but it should be different error code
|
|
Report.Error (115, Set.Location, "`{0}' is marked as an override but no accessible `set' accessor found to override",
|
|
GetSignatureForError ());
|
|
ok = false;
|
|
} else if (!CheckAccessModifiers (Set, base_prop.Set)) {
|
|
Error_CannotChangeAccessModifiers (Set, base_prop.Set);
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((Set == null || !Set.HasCustomAccessModifier) && (Get == null || !Get.HasCustomAccessModifier)) {
|
|
if (!CheckAccessModifiers (this, base_prop)) {
|
|
Error_CannotChangeAccessModifiers (this, base_prop);
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
protected override void DoMemberTypeDependentChecks ()
|
|
{
|
|
base.DoMemberTypeDependentChecks ();
|
|
|
|
IsTypePermitted ();
|
|
|
|
if (MemberType.IsStatic)
|
|
Error_StaticReturnType ();
|
|
}
|
|
|
|
protected override void DoMemberTypeIndependentChecks ()
|
|
{
|
|
base.DoMemberTypeIndependentChecks ();
|
|
|
|
//
|
|
// Accessors modifiers check
|
|
//
|
|
if (AccessorSecond != null) {
|
|
if ((Get.ModFlags & Modifiers.AccessibilityMask) != 0 && (Set.ModFlags & Modifiers.AccessibilityMask) != 0) {
|
|
Report.Error (274, Location, "`{0}': Cannot specify accessibility modifiers for both accessors of the property or indexer",
|
|
GetSignatureForError ());
|
|
}
|
|
} else if ((ModFlags & Modifiers.OVERRIDE) == 0 &&
|
|
((Get == null && (Set.ModFlags & Modifiers.AccessibilityMask) != 0) || (Set == null && (Get.ModFlags & Modifiers.AccessibilityMask) != 0))) {
|
|
Report.Error (276, Location,
|
|
"`{0}': accessibility modifiers on accessors may only be used if the property or indexer has both a get and a set accessor",
|
|
GetSignatureForError ());
|
|
}
|
|
}
|
|
|
|
protected bool DefineAccessors ()
|
|
{
|
|
first.Define (Parent);
|
|
if (AccessorSecond != null)
|
|
AccessorSecond.Define (Parent);
|
|
|
|
return true;
|
|
}
|
|
|
|
protected void DefineBuilders (MemberKind kind, ParametersCompiled parameters)
|
|
{
|
|
PropertyBuilder = Parent.TypeBuilder.DefineProperty (
|
|
GetFullName (MemberName), PropertyAttributes.None,
|
|
#if !BOOTSTRAP_BASIC // Requires trunk version mscorlib
|
|
IsStatic ? 0 : CallingConventions.HasThis,
|
|
#endif
|
|
MemberType.GetMetaInfo (), null, null,
|
|
parameters.GetMetaInfo (), null, null);
|
|
|
|
PropertySpec spec;
|
|
if (kind == MemberKind.Indexer)
|
|
spec = new IndexerSpec (Parent.Definition, this, MemberType, parameters, PropertyBuilder, ModFlags);
|
|
else
|
|
spec = new PropertySpec (kind, Parent.Definition, this, MemberType, PropertyBuilder, ModFlags);
|
|
|
|
if (Get != null) {
|
|
spec.Get = Get.Spec;
|
|
Parent.MemberCache.AddMember (this, Get.Spec.Name, Get.Spec);
|
|
} else {
|
|
CheckMissingAccessor (kind, parameters, true);
|
|
}
|
|
|
|
if (Set != null) {
|
|
spec.Set = Set.Spec;
|
|
Parent.MemberCache.AddMember (this, Set.Spec.Name, Set.Spec);
|
|
} else {
|
|
CheckMissingAccessor (kind, parameters, false);
|
|
}
|
|
|
|
Parent.MemberCache.AddMember (this, PropertyBuilder.Name, spec);
|
|
}
|
|
|
|
public override void Emit ()
|
|
{
|
|
CheckReservedNameConflict (GetMethod.Prefix, get == null ? null : get.Spec);
|
|
CheckReservedNameConflict (SetMethod.Prefix, set == null ? null : set.Spec);
|
|
|
|
if (OptAttributes != null)
|
|
OptAttributes.Emit ();
|
|
|
|
if (member_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
|
|
Module.PredefinedAttributes.Dynamic.EmitAttribute (PropertyBuilder);
|
|
} else if (member_type.HasDynamicElement) {
|
|
Module.PredefinedAttributes.Dynamic.EmitAttribute (PropertyBuilder, member_type, Location);
|
|
}
|
|
|
|
if (member_type.HasNamedTupleElement) {
|
|
Module.PredefinedAttributes.TupleElementNames.EmitAttribute (PropertyBuilder, member_type, Location);
|
|
}
|
|
|
|
ConstraintChecker.Check (this, member_type, type_expr.Location);
|
|
|
|
first.Emit (Parent);
|
|
if (AccessorSecond != null)
|
|
AccessorSecond.Emit (Parent);
|
|
|
|
base.Emit ();
|
|
}
|
|
|
|
public override bool IsUsed {
|
|
get {
|
|
if (IsExplicitImpl)
|
|
return true;
|
|
|
|
return Get.IsUsed | Set.IsUsed;
|
|
}
|
|
}
|
|
|
|
public override void PrepareEmit ()
|
|
{
|
|
AccessorFirst.PrepareEmit ();
|
|
if (AccessorSecond != null)
|
|
AccessorSecond.PrepareEmit ();
|
|
|
|
if (get != null) {
|
|
var method = Get.Spec.GetMetaInfo () as MethodBuilder;
|
|
if (method != null)
|
|
PropertyBuilder.SetGetMethod (method);
|
|
}
|
|
|
|
if (set != null) {
|
|
var method = Set.Spec.GetMetaInfo () as MethodBuilder;
|
|
if (method != null)
|
|
PropertyBuilder.SetSetMethod (method);
|
|
}
|
|
}
|
|
|
|
protected override void SetMemberName (MemberName new_name)
|
|
{
|
|
base.SetMemberName (new_name);
|
|
|
|
if (Get != null)
|
|
Get.UpdateName (this);
|
|
|
|
if (Set != null)
|
|
Set.UpdateName (this);
|
|
}
|
|
|
|
public override void WriteDebugSymbol (MonoSymbolFile file)
|
|
{
|
|
if (get != null)
|
|
get.WriteDebugSymbol (file);
|
|
|
|
if (set != null)
|
|
set.WriteDebugSymbol (file);
|
|
}
|
|
|
|
//
|
|
// Represents header string for documentation comment.
|
|
//
|
|
public override string DocCommentHeader {
|
|
get { return "P:"; }
|
|
}
|
|
}
|
|
|
|
public class Property : PropertyBase
|
|
{
|
|
public sealed class BackingFieldDeclaration : Field
|
|
{
|
|
readonly Property property;
|
|
const Modifiers DefaultModifiers = Modifiers.BACKING_FIELD | Modifiers.COMPILER_GENERATED | Modifiers.PRIVATE | Modifiers.DEBUGGER_HIDDEN;
|
|
|
|
public BackingFieldDeclaration (Property p, bool readOnly)
|
|
: base (p.Parent, p.type_expr, DefaultModifiers | (p.ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE)),
|
|
new MemberName ("<" + p.GetFullName (p.MemberName) + ">k__BackingField", p.Location), null)
|
|
{
|
|
this.property = p;
|
|
if (readOnly)
|
|
ModFlags |= Modifiers.READONLY;
|
|
}
|
|
|
|
public Property OriginalProperty {
|
|
get {
|
|
return property;
|
|
}
|
|
}
|
|
|
|
public override string GetSignatureForError ()
|
|
{
|
|
return property.GetSignatureForError ();
|
|
}
|
|
}
|
|
|
|
static readonly string[] attribute_target_auto = new string[] { "property", "field" };
|
|
|
|
public Property (TypeDefinition parent, FullNamedExpression type, Modifiers mod,
|
|
MemberName name, Attributes attrs)
|
|
: base (parent, type, mod,
|
|
parent.PartialContainer.Kind == MemberKind.Interface ? AllowedModifiersInterface :
|
|
parent.PartialContainer.Kind == MemberKind.Struct ? AllowedModifiersStruct :
|
|
AllowedModifiersClass,
|
|
name, attrs)
|
|
{
|
|
}
|
|
|
|
public BackingFieldDeclaration BackingField { get; private set; }
|
|
|
|
public Expression Initializer { get; set; }
|
|
|
|
public override void Accept (StructuralVisitor visitor)
|
|
{
|
|
visitor.Visit (this);
|
|
}
|
|
|
|
public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if (a.Target == AttributeTargets.Field) {
|
|
BackingField.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
return;
|
|
}
|
|
|
|
base.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
}
|
|
|
|
void CreateAutomaticProperty ()
|
|
{
|
|
// Create backing field
|
|
BackingField = new BackingFieldDeclaration (this, Initializer == null && Set == null);
|
|
if (!BackingField.Define ())
|
|
return;
|
|
|
|
if (Initializer != null) {
|
|
BackingField.Initializer = Initializer;
|
|
Parent.RegisterFieldForInitialization (BackingField, new FieldInitializer (BackingField, Initializer, Location));
|
|
BackingField.ModFlags |= Modifiers.READONLY;
|
|
}
|
|
|
|
Parent.PartialContainer.Members.Add (BackingField);
|
|
|
|
FieldExpr fe = new FieldExpr (BackingField, Location);
|
|
if ((BackingField.ModFlags & Modifiers.STATIC) == 0) {
|
|
fe.InstanceExpression = new CompilerGeneratedThis (Parent.CurrentType, Location);
|
|
Parent.PartialContainer.HasInstanceField = true;
|
|
}
|
|
|
|
//
|
|
// Create get block but we careful with location to
|
|
// emit only single sequence point per accessor. This allow
|
|
// to set a breakpoint on it even with no user code
|
|
//
|
|
Get.Block = new ToplevelBlock (Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null);
|
|
Return r = new Return (fe, Get.Location);
|
|
Get.Block.AddStatement (r);
|
|
Get.ModFlags |= Modifiers.COMPILER_GENERATED;
|
|
|
|
// Create set block
|
|
if (Set != null) {
|
|
Set.Block = new ToplevelBlock (Compiler, Set.ParameterInfo, Location.Null);
|
|
Assign a = new SimpleAssign (fe, new SimpleName ("value", Location.Null), Location.Null);
|
|
Set.Block.AddStatement (new StatementExpression (a, Set.Location));
|
|
Set.ModFlags |= Modifiers.COMPILER_GENERATED;
|
|
}
|
|
}
|
|
|
|
public override bool Define ()
|
|
{
|
|
if (!base.Define ())
|
|
return false;
|
|
|
|
flags |= MethodAttributes.HideBySig | MethodAttributes.SpecialName;
|
|
|
|
bool auto = AccessorFirst.Block == null && (AccessorSecond == null || AccessorSecond.Block == null) &&
|
|
(ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN)) == 0;
|
|
|
|
if (Initializer != null) {
|
|
if (!auto)
|
|
Report.Error (8050, Location, "`{0}': Only auto-implemented properties can have initializers",
|
|
GetSignatureForError ());
|
|
|
|
if (IsInterface)
|
|
Report.Error (8052, Location, "`{0}': Properties inside interfaces cannot have initializers",
|
|
GetSignatureForError ());
|
|
|
|
if (Compiler.Settings.Version < LanguageVersion.V_6)
|
|
Report.FeatureIsNotAvailable (Compiler, Location, "auto-implemented property initializer");
|
|
}
|
|
|
|
if (auto) {
|
|
ModFlags |= Modifiers.AutoProperty;
|
|
if (Get == null) {
|
|
Report.Error (8051, Location, "Auto-implemented property `{0}' must have get accessor",
|
|
GetSignatureForError ());
|
|
return false;
|
|
}
|
|
|
|
if (MemberType.Kind == MemberKind.ByRef) {
|
|
Report.Error (8145, Location, "Auto-implemented property `{0}' cannot return by reference",
|
|
GetSignatureForError ());
|
|
return false;
|
|
}
|
|
|
|
if ((Parent.PartialContainer.ModFlags & Modifiers.READONLY) != 0 && Set != null && !IsStatic) {
|
|
Report.Error (8341, Location, "Auto-implemented instance property `{0}' in readonly structs must be readonly",
|
|
GetSignatureForError ());
|
|
}
|
|
|
|
if (Compiler.Settings.Version < LanguageVersion.V_3 && Initializer == null)
|
|
Report.FeatureIsNotAvailable (Compiler, Location, "auto-implemented properties");
|
|
|
|
CreateAutomaticProperty ();
|
|
}
|
|
|
|
if (!DefineAccessors ())
|
|
return false;
|
|
|
|
if (AccessorSecond == null) {
|
|
PropertyMethod pm;
|
|
if (AccessorFirst is GetMethod)
|
|
pm = new SetMethod (this, 0, ParametersCompiled.EmptyReadOnlyParameters, null, Location);
|
|
else
|
|
pm = new GetMethod (this, 0, null, Location);
|
|
|
|
Parent.AddNameToContainer (pm, pm.MemberName.Basename);
|
|
}
|
|
|
|
if (!CheckBase ())
|
|
return false;
|
|
|
|
DefineBuilders (MemberKind.Property, ParametersCompiled.EmptyReadOnlyParameters);
|
|
return true;
|
|
}
|
|
|
|
public override void Emit ()
|
|
{
|
|
if ((AccessorFirst.ModFlags & (Modifiers.STATIC | Modifiers.AutoProperty)) == Modifiers.AutoProperty && Parent.PartialContainer.HasExplicitLayout) {
|
|
Report.Error (842, Location,
|
|
"Automatically implemented property `{0}' cannot be used inside a type with an explicit StructLayout attribute",
|
|
GetSignatureForError ());
|
|
}
|
|
|
|
base.Emit ();
|
|
}
|
|
|
|
public override string[] ValidAttributeTargets {
|
|
get {
|
|
return Get != null && ((Get.ModFlags & Modifiers.COMPILER_GENERATED) != 0) ?
|
|
attribute_target_auto : base.ValidAttributeTargets;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// For case when event is declared like property (with add and remove accessors).
|
|
/// </summary>
|
|
public class EventProperty: Event {
|
|
public abstract class AEventPropertyAccessor : AEventAccessor
|
|
{
|
|
protected AEventPropertyAccessor (EventProperty method, string prefix, Attributes attrs, Location loc)
|
|
: base (method, prefix, attrs, loc)
|
|
{
|
|
}
|
|
|
|
public override void Define (TypeContainer ds)
|
|
{
|
|
CheckAbstractAndExtern (block != null);
|
|
base.Define (ds);
|
|
}
|
|
|
|
public override string GetSignatureForError ()
|
|
{
|
|
return method.GetSignatureForError () + "." + prefix.Substring (0, prefix.Length - 1);
|
|
}
|
|
}
|
|
|
|
public sealed class AddDelegateMethod: AEventPropertyAccessor
|
|
{
|
|
public AddDelegateMethod (EventProperty method, Attributes attrs, Location loc)
|
|
: base (method, AddPrefix, attrs, loc)
|
|
{
|
|
}
|
|
}
|
|
|
|
public sealed class RemoveDelegateMethod: AEventPropertyAccessor
|
|
{
|
|
public RemoveDelegateMethod (EventProperty method, Attributes attrs, Location loc)
|
|
: base (method, RemovePrefix, attrs, loc)
|
|
{
|
|
}
|
|
}
|
|
|
|
static readonly string[] attribute_targets = new string [] { "event" };
|
|
|
|
public EventProperty (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs)
|
|
: base (parent, type, mod_flags, name, attrs)
|
|
{
|
|
}
|
|
|
|
public override void Accept (StructuralVisitor visitor)
|
|
{
|
|
visitor.Visit (this);
|
|
}
|
|
|
|
public override bool Define()
|
|
{
|
|
if (!base.Define ())
|
|
return false;
|
|
|
|
SetIsUsed ();
|
|
return true;
|
|
}
|
|
|
|
public override string[] ValidAttributeTargets {
|
|
get {
|
|
return attribute_targets;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event is declared like field.
|
|
/// </summary>
|
|
public class EventField : Event
|
|
{
|
|
abstract class EventFieldAccessor : AEventAccessor
|
|
{
|
|
protected EventFieldAccessor (EventField method, string prefix)
|
|
: base (method, prefix, null, method.Location)
|
|
{
|
|
}
|
|
|
|
protected abstract MethodSpec GetOperation (Location loc);
|
|
|
|
public override void Emit (TypeDefinition parent)
|
|
{
|
|
if ((method.ModFlags & (Modifiers.ABSTRACT | Modifiers.EXTERN)) == 0 && !Compiler.Settings.WriteMetadataOnly) {
|
|
block = new ToplevelBlock (Compiler, ParameterInfo, Location) {
|
|
IsCompilerGenerated = true
|
|
};
|
|
FabricateBodyStatement ();
|
|
}
|
|
|
|
base.Emit (parent);
|
|
}
|
|
|
|
void FabricateBodyStatement ()
|
|
{
|
|
//
|
|
// Delegate obj1 = backing_field
|
|
// do {
|
|
// Delegate obj2 = obj1;
|
|
// obj1 = Interlocked.CompareExchange (ref backing_field, Delegate.Combine|Remove(obj2, value), obj1);
|
|
// } while ((object)obj1 != (object)obj2)
|
|
//
|
|
|
|
var field_info = ((EventField) method).backing_field;
|
|
FieldExpr f_expr = new FieldExpr (field_info, Location);
|
|
if (!IsStatic)
|
|
f_expr.InstanceExpression = new CompilerGeneratedThis (Parent.CurrentType, Location);
|
|
|
|
var obj1 = LocalVariable.CreateCompilerGenerated (field_info.MemberType, block, Location);
|
|
var obj2 = LocalVariable.CreateCompilerGenerated (field_info.MemberType, block, Location);
|
|
|
|
block.AddStatement (new StatementExpression (new SimpleAssign (new LocalVariableReference (obj1, Location), f_expr)));
|
|
|
|
var cond = new BooleanExpression (new Binary (Binary.Operator.Inequality,
|
|
new Cast (new TypeExpression (Module.Compiler.BuiltinTypes.Object, Location), new LocalVariableReference (obj1, Location), Location),
|
|
new Cast (new TypeExpression (Module.Compiler.BuiltinTypes.Object, Location), new LocalVariableReference (obj2, Location), Location)));
|
|
|
|
var body = new ExplicitBlock (block, Location, Location);
|
|
block.AddStatement (new Do (body, cond, Location, Location));
|
|
|
|
body.AddStatement (new StatementExpression (
|
|
new SimpleAssign (new LocalVariableReference (obj2, Location), new LocalVariableReference (obj1, Location))));
|
|
|
|
var args_oper = new Arguments (2);
|
|
args_oper.Add (new Argument (new LocalVariableReference (obj2, Location)));
|
|
args_oper.Add (new Argument (block.GetParameterReference (0, Location)));
|
|
|
|
var op_method = GetOperation (Location);
|
|
|
|
var args = new Arguments (3);
|
|
args.Add (new Argument (f_expr, Argument.AType.Ref));
|
|
args.Add (new Argument (new Cast (
|
|
new TypeExpression (field_info.MemberType, Location),
|
|
new Invocation (MethodGroupExpr.CreatePredefined (op_method, op_method.DeclaringType, Location), args_oper),
|
|
Location)));
|
|
args.Add (new Argument (new LocalVariableReference (obj1, Location)));
|
|
|
|
var cas = Module.PredefinedMembers.InterlockedCompareExchange_T.Get ();
|
|
if (cas == null) {
|
|
if (Module.PredefinedMembers.MonitorEnter_v4.Get () != null || Module.PredefinedMembers.MonitorEnter.Get () != null) {
|
|
// Workaround for cripled (e.g. microframework) mscorlib without CompareExchange
|
|
body.AddStatement (new Lock (
|
|
block.GetParameterReference (0, Location),
|
|
new StatementExpression (new SimpleAssign (
|
|
f_expr, args [1].Expr, Location), Location), Location));
|
|
} else {
|
|
Module.PredefinedMembers.InterlockedCompareExchange_T.Resolve (Location);
|
|
}
|
|
} else {
|
|
body.AddStatement (new StatementExpression (new SimpleAssign (
|
|
new LocalVariableReference (obj1, Location),
|
|
new Invocation (MethodGroupExpr.CreatePredefined (cas, cas.DeclaringType, Location), args))));
|
|
}
|
|
}
|
|
}
|
|
|
|
sealed class AddDelegateMethod: EventFieldAccessor
|
|
{
|
|
public AddDelegateMethod (EventField method):
|
|
base (method, AddPrefix)
|
|
{
|
|
}
|
|
|
|
protected override MethodSpec GetOperation (Location loc)
|
|
{
|
|
return Module.PredefinedMembers.DelegateCombine.Resolve (loc);
|
|
}
|
|
}
|
|
|
|
sealed class RemoveDelegateMethod: EventFieldAccessor
|
|
{
|
|
public RemoveDelegateMethod (EventField method):
|
|
base (method, RemovePrefix)
|
|
{
|
|
}
|
|
|
|
protected override MethodSpec GetOperation (Location loc)
|
|
{
|
|
return Module.PredefinedMembers.DelegateRemove.Resolve (loc);
|
|
}
|
|
}
|
|
|
|
|
|
static readonly string[] attribute_targets = new string [] { "event", "field", "method" };
|
|
static readonly string[] attribute_targets_interface = new string[] { "event", "method" };
|
|
|
|
Expression initializer;
|
|
Field backing_field;
|
|
List<FieldDeclarator> declarators;
|
|
|
|
public EventField (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs)
|
|
: base (parent, type, mod_flags, name, attrs)
|
|
{
|
|
Add = new AddDelegateMethod (this);
|
|
Remove = new RemoveDelegateMethod (this);
|
|
}
|
|
|
|
#region Properties
|
|
|
|
public List<FieldDeclarator> Declarators {
|
|
get {
|
|
return this.declarators;
|
|
}
|
|
}
|
|
|
|
bool HasBackingField {
|
|
get {
|
|
return !IsInterface && (ModFlags & Modifiers.ABSTRACT) == 0;
|
|
}
|
|
}
|
|
|
|
public Expression Initializer {
|
|
get {
|
|
return initializer;
|
|
}
|
|
set {
|
|
initializer = value;
|
|
}
|
|
}
|
|
|
|
public override string[] ValidAttributeTargets {
|
|
get {
|
|
return HasBackingField ? attribute_targets : attribute_targets_interface;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
public override void Accept (StructuralVisitor visitor)
|
|
{
|
|
visitor.Visit (this);
|
|
}
|
|
|
|
public void AddDeclarator (FieldDeclarator declarator)
|
|
{
|
|
if (declarators == null)
|
|
declarators = new List<FieldDeclarator> (2);
|
|
|
|
declarators.Add (declarator);
|
|
|
|
Parent.AddNameToContainer (this, declarator.Name.Value);
|
|
}
|
|
|
|
public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if (a.Target == AttributeTargets.Field) {
|
|
backing_field.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
return;
|
|
}
|
|
|
|
if (a.Target == AttributeTargets.Method) {
|
|
int errors = Report.Errors;
|
|
Add.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
if (errors == Report.Errors)
|
|
Remove.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
return;
|
|
}
|
|
|
|
base.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
}
|
|
|
|
protected override void DoMemberTypeIndependentChecks ()
|
|
{
|
|
if ((Parent.PartialContainer.ModFlags & Modifiers.READONLY) != 0 && (ModFlags & Modifiers.STATIC) == 0) {
|
|
Report.Error (8342, Location, "`{0}': Field-like instance events are not allowed in readonly structs",
|
|
GetSignatureForError ());
|
|
}
|
|
|
|
base.DoMemberTypeIndependentChecks ();
|
|
}
|
|
|
|
public override bool Define()
|
|
{
|
|
var mod_flags_src = ModFlags;
|
|
|
|
if (!base.Define ())
|
|
return false;
|
|
|
|
if (declarators != null) {
|
|
if ((mod_flags_src & Modifiers.DEFAULT_ACCESS_MODIFIER) != 0)
|
|
mod_flags_src &= ~(Modifiers.AccessibilityMask | Modifiers.DEFAULT_ACCESS_MODIFIER);
|
|
|
|
var t = new TypeExpression (MemberType, TypeExpression.Location);
|
|
foreach (var d in declarators) {
|
|
var ef = new EventField (Parent, t, mod_flags_src, new MemberName (d.Name.Value, d.Name.Location), OptAttributes);
|
|
|
|
if (d.Initializer != null)
|
|
ef.initializer = d.Initializer;
|
|
|
|
ef.Define ();
|
|
Parent.PartialContainer.Members.Add (ef);
|
|
}
|
|
}
|
|
|
|
if (!HasBackingField) {
|
|
SetIsUsed ();
|
|
return true;
|
|
}
|
|
|
|
backing_field = new Field (Parent,
|
|
new TypeExpression (MemberType, Location),
|
|
Modifiers.BACKING_FIELD | Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN | Modifiers.PRIVATE | (ModFlags & (Modifiers.STATIC | Modifiers.UNSAFE)),
|
|
MemberName, null);
|
|
|
|
Parent.PartialContainer.Members.Add (backing_field);
|
|
backing_field.Initializer = Initializer;
|
|
|
|
// Call define because we passed fields definition
|
|
backing_field.Define ();
|
|
|
|
// Set backing field for event fields
|
|
spec.BackingField = backing_field.Spec;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public abstract class Event : PropertyBasedMember
|
|
{
|
|
public abstract class AEventAccessor : AbstractPropertyEventMethod
|
|
{
|
|
protected readonly Event method;
|
|
readonly ParametersCompiled parameters;
|
|
|
|
static readonly string[] attribute_targets = new string [] { "method", "param", "return" };
|
|
|
|
public const string AddPrefix = "add_";
|
|
public const string RemovePrefix = "remove_";
|
|
|
|
protected AEventAccessor (Event method, string prefix, Attributes attrs, Location loc)
|
|
: base (method, prefix, attrs, loc)
|
|
{
|
|
this.method = method;
|
|
this.ModFlags = method.ModFlags;
|
|
this.parameters = ParametersCompiled.CreateImplicitParameter (method.TypeExpression, loc);
|
|
}
|
|
|
|
public bool IsInterfaceImplementation {
|
|
get { return method_data.implementing != null; }
|
|
}
|
|
|
|
public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if (a.Type == pa.MethodImpl) {
|
|
method.is_external_implementation = a.IsInternalCall ();
|
|
}
|
|
|
|
base.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
}
|
|
|
|
protected override void ApplyToExtraTarget (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if (a.Target == AttributeTargets.Parameter) {
|
|
parameters[0].ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
return;
|
|
}
|
|
|
|
base.ApplyToExtraTarget (a, ctor, cdata, pa);
|
|
}
|
|
|
|
public override AttributeTargets AttributeTargets {
|
|
get {
|
|
return AttributeTargets.Method;
|
|
}
|
|
}
|
|
|
|
public override bool IsClsComplianceRequired ()
|
|
{
|
|
return method.IsClsComplianceRequired ();
|
|
}
|
|
|
|
public virtual void Define (TypeContainer parent)
|
|
{
|
|
// Fill in already resolved event type to speed things up and
|
|
// avoid confusing duplicate errors
|
|
((Parameter) parameters.FixedParameters[0]).Type = method.member_type;
|
|
parameters.Types = new TypeSpec[] { method.member_type };
|
|
|
|
method_data = new MethodData (method, method.ModFlags,
|
|
method.flags | MethodAttributes.HideBySig | MethodAttributes.SpecialName, this);
|
|
|
|
if (!method_data.Define (parent.PartialContainer, method.GetFullName (MemberName)))
|
|
return;
|
|
|
|
if (Compiler.Settings.WriteMetadataOnly)
|
|
block = null;
|
|
|
|
Spec = new MethodSpec (MemberKind.Method, parent.PartialContainer.Definition, this, ReturnType, ParameterInfo, method.ModFlags);
|
|
Spec.IsAccessor = true;
|
|
}
|
|
|
|
public override TypeSpec ReturnType {
|
|
get {
|
|
return Parent.Compiler.BuiltinTypes.Void;
|
|
}
|
|
}
|
|
|
|
public override ObsoleteAttribute GetAttributeObsolete ()
|
|
{
|
|
return method.GetAttributeObsolete ();
|
|
}
|
|
|
|
public MethodData MethodData {
|
|
get {
|
|
return method_data;
|
|
}
|
|
}
|
|
|
|
public override string[] ValidAttributeTargets {
|
|
get {
|
|
return attribute_targets;
|
|
}
|
|
}
|
|
|
|
public override ParametersCompiled ParameterInfo {
|
|
get {
|
|
return parameters;
|
|
}
|
|
}
|
|
}
|
|
|
|
AEventAccessor add, remove;
|
|
EventBuilder EventBuilder;
|
|
protected EventSpec spec;
|
|
|
|
protected Event (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs)
|
|
: base (parent, type, mod_flags,
|
|
parent.PartialContainer.Kind == MemberKind.Interface ? AllowedModifiersInterface :
|
|
parent.PartialContainer.Kind == MemberKind.Struct ? AllowedModifiersStruct :
|
|
AllowedModifiersClass,
|
|
name, attrs)
|
|
{
|
|
}
|
|
|
|
#region Properties
|
|
|
|
public override AttributeTargets AttributeTargets {
|
|
get {
|
|
return AttributeTargets.Event;
|
|
}
|
|
}
|
|
|
|
public AEventAccessor Add {
|
|
get {
|
|
return this.add;
|
|
}
|
|
set {
|
|
add = value;
|
|
Parent.AddNameToContainer (value, value.MemberName.Basename);
|
|
}
|
|
}
|
|
|
|
public override Variance ExpectedMemberTypeVariance {
|
|
get {
|
|
return Variance.Contravariant;
|
|
}
|
|
}
|
|
|
|
public AEventAccessor Remove {
|
|
get {
|
|
return this.remove;
|
|
}
|
|
set {
|
|
remove = value;
|
|
Parent.AddNameToContainer (value, value.MemberName.Basename);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if ((a.HasSecurityAttribute)) {
|
|
a.Error_InvalidSecurityParent ();
|
|
return;
|
|
}
|
|
|
|
EventBuilder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata);
|
|
}
|
|
|
|
protected override bool CheckOverrideAgainstBase (MemberSpec base_member)
|
|
{
|
|
var ok = base.CheckOverrideAgainstBase (base_member);
|
|
|
|
if (!CheckAccessModifiers (this, base_member)) {
|
|
Error_CannotChangeAccessModifiers (this, base_member);
|
|
ok = false;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
public override bool Define ()
|
|
{
|
|
if (!base.Define ())
|
|
return false;
|
|
|
|
if (!MemberType.IsDelegate) {
|
|
Report.Error (66, Location, "`{0}': event must be of a delegate type", GetSignatureForError ());
|
|
}
|
|
|
|
if (!CheckBase ())
|
|
return false;
|
|
|
|
//
|
|
// Now define the accessors
|
|
//
|
|
add.Define (Parent);
|
|
remove.Define (Parent);
|
|
|
|
EventBuilder = Parent.TypeBuilder.DefineEvent (GetFullName (MemberName), EventAttributes.None, MemberType.GetMetaInfo ());
|
|
|
|
spec = new EventSpec (Parent.Definition, this, MemberType, ModFlags, Add.Spec, remove.Spec);
|
|
|
|
Parent.MemberCache.AddMember (this, GetFullName (MemberName), spec);
|
|
Parent.MemberCache.AddMember (this, Add.Spec.Name, Add.Spec);
|
|
Parent.MemberCache.AddMember (this, Remove.Spec.Name, remove.Spec);
|
|
|
|
return true;
|
|
}
|
|
|
|
public override void Emit ()
|
|
{
|
|
CheckReservedNameConflict (null, add.Spec);
|
|
CheckReservedNameConflict (null, remove.Spec);
|
|
|
|
if (OptAttributes != null) {
|
|
OptAttributes.Emit ();
|
|
}
|
|
|
|
ConstraintChecker.Check (this, member_type, type_expr.Location);
|
|
|
|
Add.Emit (Parent);
|
|
Remove.Emit (Parent);
|
|
|
|
base.Emit ();
|
|
}
|
|
|
|
public override void PrepareEmit ()
|
|
{
|
|
base.PrepareEmit ();
|
|
|
|
add.PrepareEmit ();
|
|
remove.PrepareEmit ();
|
|
|
|
EventBuilder.SetAddOnMethod (add.MethodData.MethodBuilder);
|
|
EventBuilder.SetRemoveOnMethod (remove.MethodData.MethodBuilder);
|
|
}
|
|
|
|
public override void WriteDebugSymbol (MonoSymbolFile file)
|
|
{
|
|
add.WriteDebugSymbol (file);
|
|
remove.WriteDebugSymbol (file);
|
|
}
|
|
|
|
//
|
|
// Represents header string for documentation comment.
|
|
//
|
|
public override string DocCommentHeader {
|
|
get { return "E:"; }
|
|
}
|
|
}
|
|
|
|
public class EventSpec : MemberSpec, IInterfaceMemberSpec
|
|
{
|
|
MethodSpec add, remove;
|
|
FieldSpec backing_field;
|
|
|
|
public EventSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec eventType, Modifiers modifiers, MethodSpec add, MethodSpec remove)
|
|
: base (MemberKind.Event, declaringType, definition, modifiers)
|
|
{
|
|
this.AccessorAdd = add;
|
|
this.AccessorRemove = remove;
|
|
this.MemberType = eventType;
|
|
}
|
|
|
|
#region Properties
|
|
|
|
public MethodSpec AccessorAdd {
|
|
get {
|
|
return add;
|
|
}
|
|
set {
|
|
add = value;
|
|
}
|
|
}
|
|
|
|
public MethodSpec AccessorRemove {
|
|
get {
|
|
return remove;
|
|
}
|
|
set {
|
|
remove = value;
|
|
}
|
|
}
|
|
|
|
public FieldSpec BackingField {
|
|
get {
|
|
return backing_field;
|
|
}
|
|
set {
|
|
backing_field = value;
|
|
}
|
|
}
|
|
|
|
public TypeSpec MemberType { get; private set; }
|
|
|
|
#endregion
|
|
|
|
public override MemberSpec InflateMember (TypeParameterInflator inflator)
|
|
{
|
|
var es = (EventSpec) base.InflateMember (inflator);
|
|
es.MemberType = inflator.Inflate (MemberType);
|
|
|
|
if (backing_field != null)
|
|
es.backing_field = (FieldSpec) backing_field.InflateMember (inflator);
|
|
|
|
return es;
|
|
}
|
|
|
|
public override List<MissingTypeSpecReference> ResolveMissingDependencies (MemberSpec caller)
|
|
{
|
|
return MemberType.ResolveMissingDependencies (this);
|
|
}
|
|
}
|
|
|
|
public class Indexer : PropertyBase, IParametersMember
|
|
{
|
|
public class GetIndexerMethod : GetMethod, IParametersMember
|
|
{
|
|
ParametersCompiled parameters;
|
|
|
|
public GetIndexerMethod (PropertyBase property, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc)
|
|
: base (property, modifiers, attrs, loc)
|
|
{
|
|
this.parameters = parameters;
|
|
}
|
|
|
|
public override void Define (TypeContainer parent)
|
|
{
|
|
// Disable reporting, parameters are resolved twice
|
|
Report.DisableReporting ();
|
|
try {
|
|
parameters.Resolve (this);
|
|
} finally {
|
|
Report.EnableReporting ();
|
|
}
|
|
|
|
base.Define (parent);
|
|
}
|
|
|
|
public override ParametersCompiled ParameterInfo {
|
|
get {
|
|
return parameters;
|
|
}
|
|
}
|
|
|
|
#region IParametersMember Members
|
|
|
|
AParametersCollection IParametersMember.Parameters {
|
|
get {
|
|
return parameters;
|
|
}
|
|
}
|
|
|
|
TypeSpec IInterfaceMemberSpec.MemberType {
|
|
get {
|
|
return ReturnType;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class SetIndexerMethod : SetMethod, IParametersMember
|
|
{
|
|
public SetIndexerMethod (PropertyBase property, Modifiers modifiers, ParametersCompiled parameters, Attributes attrs, Location loc)
|
|
: base (property, modifiers, parameters, attrs, loc)
|
|
{
|
|
}
|
|
|
|
#region IParametersMember Members
|
|
|
|
AParametersCollection IParametersMember.Parameters {
|
|
get {
|
|
return parameters;
|
|
}
|
|
}
|
|
|
|
TypeSpec IInterfaceMemberSpec.MemberType {
|
|
get {
|
|
return ReturnType;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
const Modifiers AllowedModifiers =
|
|
Modifiers.NEW |
|
|
Modifiers.PUBLIC |
|
|
Modifiers.PROTECTED |
|
|
Modifiers.INTERNAL |
|
|
Modifiers.PRIVATE |
|
|
Modifiers.VIRTUAL |
|
|
Modifiers.SEALED |
|
|
Modifiers.OVERRIDE |
|
|
Modifiers.UNSAFE |
|
|
Modifiers.EXTERN |
|
|
Modifiers.ABSTRACT;
|
|
|
|
const Modifiers AllowedInterfaceModifiers =
|
|
Modifiers.NEW;
|
|
|
|
readonly ParametersCompiled parameters;
|
|
|
|
public Indexer (TypeDefinition parent, FullNamedExpression type, MemberName name, Modifiers mod, ParametersCompiled parameters, Attributes attrs)
|
|
: base (parent, type, mod,
|
|
parent.PartialContainer.Kind == MemberKind.Interface ? AllowedInterfaceModifiers : AllowedModifiers,
|
|
name, attrs)
|
|
{
|
|
this.parameters = parameters;
|
|
}
|
|
|
|
#region Properties
|
|
|
|
AParametersCollection IParametersMember.Parameters {
|
|
get {
|
|
return parameters;
|
|
}
|
|
}
|
|
|
|
public ParametersCompiled ParameterInfo {
|
|
get {
|
|
return parameters;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
public override void Accept (StructuralVisitor visitor)
|
|
{
|
|
visitor.Visit (this);
|
|
}
|
|
|
|
public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
|
|
{
|
|
if (a.Type == pa.IndexerName) {
|
|
// Attribute was copied to container
|
|
return;
|
|
}
|
|
|
|
base.ApplyAttributeBuilder (a, ctor, cdata, pa);
|
|
}
|
|
|
|
protected override bool CheckForDuplications ()
|
|
{
|
|
return Parent.MemberCache.CheckExistingMembersOverloads (this, parameters);
|
|
}
|
|
|
|
public override bool Define ()
|
|
{
|
|
if (!base.Define ())
|
|
return false;
|
|
|
|
if (!DefineParameters (parameters))
|
|
return false;
|
|
|
|
if (OptAttributes != null) {
|
|
Attribute indexer_attr = OptAttributes.Search (Module.PredefinedAttributes.IndexerName);
|
|
if (indexer_attr != null) {
|
|
var compiling = indexer_attr.Type.MemberDefinition as TypeContainer;
|
|
if (compiling != null)
|
|
compiling.Define ();
|
|
|
|
if (IsExplicitImpl) {
|
|
Report.Error (415, indexer_attr.Location,
|
|
"The `{0}' attribute is valid only on an indexer that is not an explicit interface member declaration",
|
|
indexer_attr.Type.GetSignatureForError ());
|
|
} else if ((ModFlags & Modifiers.OVERRIDE) != 0) {
|
|
Report.Error (609, indexer_attr.Location,
|
|
"Cannot set the `IndexerName' attribute on an indexer marked override");
|
|
} else {
|
|
string name = indexer_attr.GetIndexerAttributeValue ();
|
|
|
|
if (!string.IsNullOrEmpty (name)) {
|
|
SetMemberName (new MemberName (MemberName.Left, name, Location));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (InterfaceType != null) {
|
|
string base_IndexerName = InterfaceType.MemberDefinition.GetAttributeDefaultMember ();
|
|
if (base_IndexerName != ShortName) {
|
|
SetMemberName (new MemberName (MemberName.Left, base_IndexerName, new TypeExpression (InterfaceType, Location), Location));
|
|
}
|
|
}
|
|
|
|
Parent.AddNameToContainer (this, MemberName.Basename);
|
|
|
|
flags |= MethodAttributes.HideBySig | MethodAttributes.SpecialName;
|
|
|
|
if (!DefineAccessors ())
|
|
return false;
|
|
|
|
if (!CheckBase ())
|
|
return false;
|
|
|
|
DefineBuilders (MemberKind.Indexer, parameters);
|
|
return true;
|
|
}
|
|
|
|
public override bool EnableOverloadChecks (MemberCore overload)
|
|
{
|
|
if (overload is Indexer) {
|
|
caching_flags |= Flags.MethodOverloadsExist;
|
|
return true;
|
|
}
|
|
|
|
return base.EnableOverloadChecks (overload);
|
|
}
|
|
|
|
public override void Emit ()
|
|
{
|
|
parameters.CheckConstraints (this);
|
|
|
|
base.Emit ();
|
|
}
|
|
|
|
public override string GetSignatureForError ()
|
|
{
|
|
StringBuilder sb = new StringBuilder (Parent.GetSignatureForError ());
|
|
if (MemberName.ExplicitInterface != null) {
|
|
sb.Append (".");
|
|
sb.Append (MemberName.ExplicitInterface.GetSignatureForError ());
|
|
}
|
|
|
|
sb.Append (".this");
|
|
sb.Append (parameters.GetSignatureForError ("[", "]", parameters.Count));
|
|
return sb.ToString ();
|
|
}
|
|
|
|
public override string GetSignatureForDocumentation ()
|
|
{
|
|
return base.GetSignatureForDocumentation () + parameters.GetSignatureForDocumentation ();
|
|
}
|
|
|
|
public override void PrepareEmit ()
|
|
{
|
|
base.PrepareEmit ();
|
|
parameters.ResolveDefaultValues (this);
|
|
}
|
|
|
|
protected override bool VerifyClsCompliance ()
|
|
{
|
|
if (!base.VerifyClsCompliance ())
|
|
return false;
|
|
|
|
parameters.VerifyClsCompliance (this);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public class IndexerSpec : PropertySpec, IParametersMember
|
|
{
|
|
AParametersCollection parameters;
|
|
|
|
public IndexerSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, AParametersCollection parameters, PropertyInfo info, Modifiers modifiers)
|
|
: base (MemberKind.Indexer, declaringType, definition, memberType, info, modifiers)
|
|
{
|
|
this.parameters = parameters;
|
|
}
|
|
|
|
#region Properties
|
|
public AParametersCollection Parameters {
|
|
get {
|
|
return parameters;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public static ParametersImported CreateParametersFromSetter (MethodSpec setter, int set_param_count)
|
|
{
|
|
//
|
|
// Creates indexer parameters based on setter method parameters (the last parameter has to be removed)
|
|
//
|
|
var data = new IParameterData [set_param_count];
|
|
var types = new TypeSpec [set_param_count];
|
|
Array.Copy (setter.Parameters.FixedParameters, data, set_param_count);
|
|
Array.Copy (setter.Parameters.Types, types, set_param_count);
|
|
return new ParametersImported (data, types, setter.Parameters.HasParams);
|
|
}
|
|
|
|
public override string GetSignatureForDocumentation ()
|
|
{
|
|
return base.GetSignatureForDocumentation () + parameters.GetSignatureForDocumentation ();
|
|
}
|
|
|
|
public override string GetSignatureForError ()
|
|
{
|
|
return DeclaringType.GetSignatureForError () + ".this" + parameters.GetSignatureForError ("[", "]", parameters.Count);
|
|
}
|
|
|
|
public override MemberSpec InflateMember (TypeParameterInflator inflator)
|
|
{
|
|
var spec = (IndexerSpec) base.InflateMember (inflator);
|
|
spec.parameters = parameters.Inflate (inflator);
|
|
return spec;
|
|
}
|
|
|
|
public override List<MissingTypeSpecReference> ResolveMissingDependencies (MemberSpec caller)
|
|
{
|
|
var missing = base.ResolveMissingDependencies (caller);
|
|
|
|
foreach (var pt in parameters.Types) {
|
|
var m = pt.GetMissingDependencies (caller);
|
|
if (m == null)
|
|
continue;
|
|
|
|
if (missing == null)
|
|
missing = new List<MissingTypeSpecReference> ();
|
|
|
|
missing.AddRange (m);
|
|
}
|
|
|
|
return missing;
|
|
}
|
|
}
|
|
}
|