|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols.Metadata.PE
{
// CONSIDER: it might be worthwhile to promote some of these sample types to a test resource DLL
public class LoadingIndexers : CSharpTestBase
{
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadReadWriteIndexer()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('Item')}
.method public hidebysig specialname instance int32
get_Item(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname instance void
set_Item(int32 x,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 Item(int32)
{
.get instance int32 C::get_Item(int32)
.set instance void C::set_Item(int32, int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
Assert.Equal("Item", @class.DefaultMemberName);
var indexer = @class.GetIndexer<PEPropertySymbol>("Item");
CheckIndexer(indexer, true, true, "System.Int32 C.this[System.Int32 x] { get; set; }");
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadWriteOnlyIndexer()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('Item')}
.method public hidebysig specialname instance void
set_Item(int32 x,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 Item(int32)
{
.set instance void C::set_Item(int32, int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
Assert.Equal("Item", @class.DefaultMemberName);
var indexer = @class.GetIndexer<PEPropertySymbol>("Item");
CheckIndexer(indexer, false, true, "System.Int32 C.this[System.Int32 x] { set; }");
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadReadOnlyIndexer()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('Item')}
.method public hidebysig specialname instance int32
get_Item(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 Item(int32)
{
.get instance int32 C::get_Item(int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
Assert.Equal("Item", @class.DefaultMemberName);
var indexer = @class.GetIndexer<PEPropertySymbol>("Item");
CheckIndexer(indexer, true, false, "System.Int32 C.this[System.Int32 x] { get; }");
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadIndexerWithAlternateName()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('NotItem')}
.method public hidebysig specialname instance int32
get_NotItem(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname instance void
set_NotItem(int32 x,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 NotItem(int32)
{
.get instance int32 C::get_NotItem(int32)
.set instance void C::set_NotItem(int32, int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
Assert.Equal("NotItem", @class.DefaultMemberName);
var indexer = @class.GetIndexer<PEPropertySymbol>("NotItem");
CheckIndexer(indexer, true, true, "System.Int32 C.this[System.Int32 x] { get; set; }");
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadIndexerWithAccessorAsDefaultMember()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('get_NotItem')}
.method public hidebysig specialname instance int32
get_NotItem(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname instance void
set_NotItem(int32 x,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 NotItem(int32)
{
.get instance int32 C::get_NotItem(int32)
.set instance void C::set_NotItem(int32, int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
Assert.Equal("get_NotItem", @class.DefaultMemberName);
var indexer = @class.GetIndexer<PEPropertySymbol>("NotItem");
CheckIndexer(indexer, true, true, "System.Int32 C.this[System.Int32 x] { get; set; }");
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadComplexIndexers()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('Accessor1')}
.method public hidebysig specialname instance int32
Accessor1(int32 x, int64 y) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname instance void
Accessor2(int32 x, int64 y,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname instance void
Accessor3(int32 x, int64 y,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 Indexer1(int32, int64)
{
.get instance int32 C::Accessor1(int32, int64)
.set instance void C::Accessor2(int32, int64, int32)
}
.property instance int32 Indexer2(int32, int64)
{
.get instance int32 C::Accessor1(int32, int64)
.set instance void C::Accessor3(int32, int64, int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
Assert.Equal("Accessor1", @class.DefaultMemberName);
var indexer1 = @class.GetIndexer<PEPropertySymbol>("Indexer1");
CheckIndexer(indexer1, true, true, "System.Int32 C.this[System.Int32 x, System.Int64 y] { get; set; }", suppressAssociatedPropertyCheck: true);
var indexer2 = @class.GetIndexer<PEPropertySymbol>("Indexer2");
CheckIndexer(indexer2, true, true, "System.Int32 C.this[System.Int32 x, System.Int64 y] { get; set; }", suppressAssociatedPropertyCheck: true);
});
}
[ClrOnlyFact]
public void LoadNonIndexer_NoDefaultMember()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.method public hidebysig specialname instance int32
get_Item(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 Item(int32)
{
.get instance int32 C::get_Item(int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
Assert.Equal("", @class.DefaultMemberName); //placeholder value to avoid refetching
var property = @class.GetMember<PEPropertySymbol>("Item");
CheckNonIndexer(property, true, false, "System.Int32 C.Item[System.Int32 x] { get; }");
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadNonIndexer_NotDefaultMember()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('NotItem')}
.method public hidebysig specialname instance int32
get_Item(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 Item(int32)
{
.get instance int32 C::get_Item(int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
Assert.Equal("NotItem", @class.DefaultMemberName);
var property = @class.GetMember<PEPropertySymbol>("Item");
CheckNonIndexer(property, true, false, "System.Int32 C.Item[System.Int32 x] { get; }");
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadNonGenericIndexers()
{
string ilSource = @"
.class public auto ansi beforefieldinit NonGeneric
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('get_Item')}
.method public hidebysig newslot specialname virtual
instance int32 get_Item(int64 x) cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
instance void set_Item(int64 x, int32 'value') cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
static int32 get_Item(int64 x) cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
static void set_Item(int64 x, int32 'value') cil managed
{
ldnull
throw
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldnull
throw
}
.property instance int32 Instance(int64)
{
.get instance int32 NonGeneric::get_Item(int64)
.set instance void NonGeneric::set_Item(int64, int32)
}
.property int32 Static(int64)
{
.get int32 NonGeneric::get_Item(int64)
.set void NonGeneric::set_Item(int64, int32)
}
} // end of class NonGeneric
";
CompileWithCustomILSource("", ilSource, compilation =>
CheckInstanceAndStaticIndexers(compilation, "NonGeneric", "System.Int32 NonGeneric.this[System.Int64 x] { get; set; }"));
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadGenericIndexers()
{
string ilSource = @"
.class public auto ansi beforefieldinit Generic`2<T,U>
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('get_Item')}
.method public hidebysig newslot specialname virtual
instance !T get_Item(!U u) cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
instance void set_Item(!U u, !T 'value') cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
static !T get_Item(!U u) cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
static void set_Item(!U u, !T 'value') cil managed
{
ldnull
throw
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldnull
throw
}
.property instance !T Instance(!U)
{
.get instance !T Generic`2::get_Item(!U)
.set instance void Generic`2::set_Item(!U, !T)
}
.property !T Static(!U)
{
.get !T Generic`2::get_Item(!U)
.set void Generic`2::set_Item(!U, !T)
}
} // end of class Generic`2
";
CompileWithCustomILSource("", ilSource, compilation =>
CheckInstanceAndStaticIndexers(compilation, "Generic", "T Generic<T, U>.this[U u] { get; set; }"));
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadClosedGenericIndexers()
{
string ilSource = @"
.class public auto ansi beforefieldinit ClosedGeneric
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('get_Item')}
.method public hidebysig newslot specialname virtual
instance class [mscorlib]System.Collections.Generic.List`1<int32>
get_Item(class [mscorlib]System.Action`1<int16> u) cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
instance void set_Item(class [mscorlib]System.Action`1<int16> u,
class [mscorlib]System.Collections.Generic.List`1<int32> 'value') cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
static class [mscorlib]System.Collections.Generic.List`1<int32>
get_Item(class [mscorlib]System.Action`1<int16> u) cil managed
{
ldnull
throw
}
.method public hidebysig newslot specialname virtual
static void set_Item(class [mscorlib]System.Action`1<int16> u,
class [mscorlib]System.Collections.Generic.List`1<int32> 'value') cil managed
{
ldnull
throw
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldnull
throw
}
.property instance class [mscorlib]System.Collections.Generic.List`1<int32>
Instance(class [mscorlib]System.Action`1<int16>)
{
.get instance class [mscorlib]System.Collections.Generic.List`1<int32> ClosedGeneric::get_Item(class [mscorlib]System.Action`1<int16>)
.set instance void ClosedGeneric::set_Item(class [mscorlib]System.Action`1<int16>,
class [mscorlib]System.Collections.Generic.List`1<int32>)
}
.property class [mscorlib]System.Collections.Generic.List`1<int32>
Static(class [mscorlib]System.Action`1<int16>)
{
.get class [mscorlib]System.Collections.Generic.List`1<int32> ClosedGeneric::get_Item(class [mscorlib]System.Action`1<int16>)
.set void ClosedGeneric::set_Item(class [mscorlib]System.Action`1<int16>,
class [mscorlib]System.Collections.Generic.List`1<int32>)
}
} // end of class ClosedGeneric
";
CompileWithCustomILSource("", ilSource, compilation =>
CheckInstanceAndStaticIndexers(compilation, "ClosedGeneric", "System.Collections.Generic.List<System.Int32> ClosedGeneric.this[System.Action<System.Int16> u] { get; set; }"));
}
[Fact]
public void LoadIndexerWithRefParam()
{
var assembly = MetadataTestHelpers.GetSymbolForReference(TestReferences.SymbolsTests.Indexers);
var @class = assembly.GlobalNamespace.GetMember<NamedTypeSymbol>("RefIndexer");
var indexer = (PropertySymbol)@class.GetMembers().Where(m => m.Kind == SymbolKind.Property).Single();
Assert.Equal(RefKind.Ref, indexer.Parameters.Single().RefKind);
Assert.True(indexer.MustCallMethodsDirectly);
}
private static void CheckInstanceAndStaticIndexers(CSharpCompilation compilation, string className, string indexerDisplayString)
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>(className);
var instanceIndexer = @class.GetIndexer<PEPropertySymbol>("Instance");
Assert.False(instanceIndexer.IsStatic);
CheckIndexer(instanceIndexer, true, true, indexerDisplayString);
var staticIndexer = @class.GetIndexer<PEPropertySymbol>("Static"); //not allowed in C#
Assert.True(staticIndexer.IsStatic);
CheckIndexer(staticIndexer, true, true, indexerDisplayString);
}
/// <summary>
/// The accessor and the property have signatures.
/// </summary>
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadAccessorPropertySignatureMismatch()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('get_Item')}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.method public hidebysig specialname instance int32
get_Item(string s) cil managed
{
ldc.i4.0
ret
}
.property instance int32 ParameterCount(string, char)
{
.get instance int32 C::get_Item(string)
}
.method public hidebysig specialname instance int32
get_Item(string s, string c) cil managed
{
ldc.i4.0
ret
}
.property instance int32 ParameterTypes(string, char)
{
.get instance int32 C::get_Item(string, string)
}
.method public hidebysig specialname instance int32
get_Item(string s, char modopt(int32) c) cil managed
{
ldc.i4.0
ret
}
.property instance int32 ReturnType(string, char)
{
.get instance char C::get_Item(string, string)
}
.property instance int32 ParameterModopt(string, char)
{
.get instance int32 C::get_Item(string, char modopt(int32))
}
.method public hidebysig specialname instance char
get_Item(string s, string c) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname instance int32 modopt(int32)
get_Item(string s, char c) cil managed
{
ldc.i4.0
ret
}
.property instance int32 ReturnTypeModopt(string, char)
{
.get instance int32 modopt(int32) C::get_Item(string, char)
}
} // end of class C
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
var parameterCountIndexer = @class.GetIndexer<PEPropertySymbol>("ParameterCount");
Assert.True(parameterCountIndexer.IsIndexer);
Assert.True(parameterCountIndexer.MustCallMethodsDirectly);
Assert.NotEqual(parameterCountIndexer.ParameterCount, parameterCountIndexer.GetMethod.ParameterCount);
var parameterTypesIndexer = @class.GetIndexer<PEPropertySymbol>("ParameterTypes");
Assert.True(parameterTypesIndexer.IsIndexer);
Assert.True(parameterTypesIndexer.MustCallMethodsDirectly);
Assert.NotEqual(parameterTypesIndexer.Parameters.Last().Type, parameterTypesIndexer.GetMethod.Parameters.Last().Type);
var returnTypeIndexer = @class.GetIndexer<PEPropertySymbol>("ReturnType");
Assert.True(returnTypeIndexer.IsIndexer);
Assert.True(returnTypeIndexer.MustCallMethodsDirectly);
Assert.NotEqual(returnTypeIndexer.Type, returnTypeIndexer.GetMethod.ReturnType);
var parameterModoptIndexer = @class.GetIndexer<PEPropertySymbol>("ParameterModopt");
Assert.True(parameterModoptIndexer.IsIndexer);
Assert.False(parameterModoptIndexer.MustCallMethodsDirectly); //NB: we allow this amount of variation (modopt is on, rather than in parameter type)
Assert.NotEqual(parameterModoptIndexer.Parameters.Last().TypeWithAnnotations.CustomModifiers.Length, parameterModoptIndexer.GetMethod.Parameters.Last().TypeWithAnnotations.CustomModifiers.Length);
var returnTypeModoptIndexer = @class.GetIndexer<PEPropertySymbol>("ReturnTypeModopt");
Assert.True(returnTypeModoptIndexer.IsIndexer);
Assert.False(returnTypeModoptIndexer.MustCallMethodsDirectly); //NB: we allow this amount of variation (modopt is on, rather than in return type)
Assert.NotEqual(returnTypeModoptIndexer.TypeWithAnnotations.CustomModifiers.Length, returnTypeModoptIndexer.GetMethod.ReturnTypeWithAnnotations.CustomModifiers.Length);
});
}
[ClrOnlyFact]
public void LoadParameterNames()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.method public hidebysig specialname instance int32
get_Item(int32 x) cil managed
{
ldc.i4.0
ret
}
// NB: getter and setter have different parameter names
.method public hidebysig specialname instance void
set_Item(int32 y,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 ReadWrite(int32)
{
.get instance int32 C::get_Item(int32)
.set instance void C::set_Item(int32, int32)
}
.property instance int32 ReadOnly(int32)
{
.get instance int32 C::get_Item(int32)
}
.property instance int32 WriteOnly(int32)
{
.set instance void C::set_Item(int32, int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
var property1 = @class.GetMember<PEPropertySymbol>("ReadWrite");
var property1ParamName = property1.Parameters.Single().Name;
// NOTE: prefer setter
Assert.NotEqual(property1ParamName, property1.GetMethod.Parameters.Single().Name);
Assert.Equal(property1ParamName, property1.SetMethod.Parameters.First().Name);
var property2 = @class.GetMember<PEPropertySymbol>("ReadOnly");
var property2ParamName = property2.Parameters.Single().Name;
Assert.Equal(property2ParamName, property2.GetMethod.Parameters.Single().Name);
var property3 = @class.GetMember<PEPropertySymbol>("WriteOnly");
var property3ParamName = property3.Parameters.Single().Name;
Assert.Equal(property3ParamName, property3.SetMethod.Parameters.First().Name);
});
}
/// <remarks>
/// Only testing parameter count mismatch. There isn't specific handling for other
/// types of bogus properties - just setter param name if setter available and getter
/// param name if getter available (i.e. same as success case).
/// </remarks>
[ClrOnlyFact]
public void LoadBogusParameterNames()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.method public hidebysig specialname instance int32
get_Item(int32 x, int32 y) cil managed
{
ldc.i4.0
ret
}
// accessor has too many parameters
.property instance int32 TooMany(int32)
{
.get instance int32 C::get_Item(int32, int32)
}
// accessor has too few parameters
.property instance int32 TooFew(int32, int32, int32)
{
.get instance int32 C::get_Item(int32, int32)
}
}
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
var accessor = @class.GetMember<MethodSymbol>("get_Item");
var accessParam0Name = accessor.Parameters[0].Name;
var accessParam1Name = accessor.Parameters[1].Name;
var property1 = @class.GetMember<PEPropertySymbol>("TooMany");
Assert.Equal(accessParam0Name, property1.Parameters[0].Name);
var property2 = @class.GetMember<PEPropertySymbol>("TooFew");
var property2Params = property2.Parameters;
Assert.Equal(accessParam0Name, property2Params[0].Name);
Assert.Equal(accessParam1Name, property2Params[1].Name);
Assert.Equal("value", property2Params[2].Name); //filler name
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadParamArrayAttribute()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('accessor')}
.method public hidebysig specialname instance int32
accessor(int32[] a) cil managed
{
.param [1]
.custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = {}
ldc.i4.0
ret
}
.method public hidebysig specialname instance void
accessor(int32[] a,
int32 'value') cil managed
{
.param [1]
.custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = {}
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 ReadWrite(int32[])
{
.get instance int32 C::accessor(int32[])
.set instance void C::accessor(int32[], int32)
}
.property instance int32 ReadOnly(int32[])
{
.get instance int32 C::accessor(int32[])
}
.property instance int32 WriteOnly(int32[])
{
.set instance void C::accessor(int32[], int32)
}
} // end of class C
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
var readWrite = @class.GetIndexer<PEPropertySymbol>("ReadWrite");
Assert.True(readWrite.IsIndexer);
Assert.False(readWrite.MustCallMethodsDirectly);
Assert.True(readWrite.Parameters.Last().IsParams);
Assert.True(readWrite.Parameters.Last().IsParamsArray);
Assert.False(readWrite.Parameters.Last().IsParamsCollection);
var readOnly = @class.GetIndexer<PEPropertySymbol>("ReadOnly");
Assert.True(readOnly.IsIndexer);
Assert.False(readOnly.MustCallMethodsDirectly);
Assert.True(readOnly.Parameters.Last().IsParams);
Assert.True(readOnly.Parameters.Last().IsParamsArray);
Assert.False(readOnly.Parameters.Last().IsParamsCollection);
var writeOnly = @class.GetIndexer<PEPropertySymbol>("WriteOnly");
Assert.True(writeOnly.IsIndexer);
Assert.False(writeOnly.MustCallMethodsDirectly);
Assert.True(writeOnly.Parameters.Last().IsParams);
Assert.True(writeOnly.Parameters.Last().IsParamsArray);
Assert.False(writeOnly.Parameters.Last().IsParamsCollection);
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadBogusParamArrayAttribute()
{
string ilSource = @"
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('params')}
.method public hidebysig specialname instance int32
params(int32[] a) cil managed
{
.param [1]
.custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = {}
ldc.i4.0
ret
}
.method public hidebysig specialname instance int32
noParams(int32[] a) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname instance void
params(int32[] a,
int32 'value') cil managed
{
.param [1]
.custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = {}
ret
}
.method public hidebysig specialname instance void
noParams(int32[] a,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 OnlyGetter(int32[])
{
.get instance int32 C::params(int32[])
.set instance void C::noParams(int32[], int32)
}
.property instance int32 OnlySetter(int32[])
{
.get instance int32 C::noParams(int32[])
.set instance void C::params(int32[], int32)
}
} // end of class C
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
var readWrite = @class.GetIndexer<PEPropertySymbol>("OnlyGetter");
Assert.True(readWrite.IsIndexer);
Assert.True(readWrite.MustCallMethodsDirectly);
Assert.False(readWrite.Parameters.Last().IsParams); //favour setter
Assert.False(readWrite.Parameters.Last().IsParamsArray); //favour setter
Assert.False(readWrite.Parameters.Last().IsParamsCollection); //favour setter
var readOnly = @class.GetIndexer<PEPropertySymbol>("OnlySetter");
Assert.True(readWrite.IsIndexer);
Assert.True(readOnly.MustCallMethodsDirectly);
Assert.True(readOnly.Parameters.Last().IsParams); //favour setter
Assert.True(readOnly.Parameters.Last().IsParamsArray); //favour setter
Assert.False(readOnly.Parameters.Last().IsParamsCollection); //favour setter
});
}
private static void CheckIndexer(PropertySymbol indexer, bool expectGetter, bool expectSetter, string indexerDisplayString, bool suppressAssociatedPropertyCheck = false)
{
CheckParameterizedProperty(indexer, expectGetter, expectSetter, indexerDisplayString, true, suppressAssociatedPropertyCheck);
}
private static void CheckNonIndexer(PropertySymbol property, bool expectGetter, bool expectSetter, string propertyDisplayString)
{
CheckParameterizedProperty(property, expectGetter, expectSetter, propertyDisplayString, false, true);
}
private static void CheckParameterizedProperty(PropertySymbol property, bool expectGetter, bool expectSetter, string propertyDisplayString, bool expectIndexer, bool suppressAssociatedPropertyCheck)
{
Assert.Equal(SymbolKind.Property, property.Kind);
Assert.Equal(expectIndexer, property.IsIndexer);
Assert.NotEqual(expectIndexer, property.MustCallMethodsDirectly);
Assert.Equal(propertyDisplayString, property.ToTestDisplayString());
if (expectGetter)
{
CheckAccessorShape(property.GetMethod, true, property, expectIndexer, suppressAssociatedPropertyCheck);
}
else
{
Assert.Null(property.GetMethod);
}
if (expectSetter)
{
CheckAccessorShape(property.SetMethod, false, property, expectIndexer, suppressAssociatedPropertyCheck);
}
else
{
Assert.Null(property.SetMethod);
}
}
private static void CheckAccessorShape(MethodSymbol accessor, bool accessorIsGetMethod, PropertySymbol property, bool propertyIsIndexer, bool suppressAssociatedPropertyCheck)
{
Assert.NotNull(accessor);
if (propertyIsIndexer)
{
if (!suppressAssociatedPropertyCheck)
{
Assert.Same(property, accessor.AssociatedSymbol);
}
}
else
{
Assert.Null(accessor.AssociatedSymbol);
Assert.Equal(MethodKind.Ordinary, accessor.MethodKind);
}
if (accessorIsGetMethod)
{
Assert.Equal(propertyIsIndexer ? MethodKind.PropertyGet : MethodKind.Ordinary, accessor.MethodKind);
Assert.Equal(property.Type, accessor.ReturnType);
Assert.Equal(property.ParameterCount, accessor.ParameterCount);
}
else
{
Assert.Equal(propertyIsIndexer ? MethodKind.PropertySet : MethodKind.Ordinary, accessor.MethodKind);
Assert.Equal(SpecialType.System_Void, accessor.ReturnType.SpecialType);
Assert.Equal(property.Type, accessor.Parameters.Last().Type);
Assert.Equal(property.ParameterCount + 1, accessor.ParameterCount);
}
// NOTE: won't check last param of setter - that was handled above.
for (int i = 0; i < property.ParameterCount; i++)
{
Assert.Equal(property.Parameters[i].Type, accessor.Parameters[i].Type);
}
Assert.Equal(property.IsAbstract, accessor.IsAbstract);
Assert.Equal(property.IsOverride, @accessor.IsOverride);
Assert.Equal(property.IsVirtual, @accessor.IsVirtual);
Assert.Equal(property.IsSealed, @accessor.IsSealed);
Assert.Equal(property.IsExtern, @accessor.IsExtern);
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void LoadExplicitImplementation()
{
string ilSource = @"
.class interface public abstract auto ansi I
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('Item')}
.method public hidebysig newslot specialname abstract virtual
instance int32 get_Item(int32 x) cil managed
{
} // end of method I::get_Item
.method public hidebysig newslot specialname abstract virtual
instance void set_Item(int32 x,
int32 'value') cil managed
{
} // end of method I::set_Item
.property instance int32 Item(int32)
{
.get instance int32 I::get_Item(int32)
.set instance void I::set_Item(int32,
int32)
} // end of property I::Item
} // end of class I
.class public auto ansi beforefieldinit C
extends [mscorlib]System.Object
implements I
{
.method private hidebysig newslot specialname virtual final
instance int32 I.get_Item(int32 x) cil managed
{
.override I::get_Item
ldnull
throw
}
.method private hidebysig newslot specialname virtual final
instance void I.set_Item(int32 x,
int32 'value') cil managed
{
.override I::set_Item
ldnull
throw
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 I.Item(int32)
{
.get instance int32 C::I.get_Item(int32)
.set instance void C::I.set_Item(int32,
int32)
}
} // end of class C
";
CompileWithCustomILSource("", ilSource, compilation =>
{
var @interface = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("I");
var interfaceIndexer = @interface.Indexers.Single();
Assert.True(interfaceIndexer.IsIndexer);
var @class = compilation.GlobalNamespace.GetMember<PENamedTypeSymbol>("C");
var classIndexer = (PropertySymbol)@class.GetMembers().Single(s => s.Kind == SymbolKind.Property);
Assert.False(classIndexer.IsIndexer);
Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interfaceIndexer));
Assert.Equal(interfaceIndexer, classIndexer.ExplicitInterfaceImplementations.Single());
});
}
[Fact]
public void LoadImplicitImplementation()
{
}
[Fact]
public void LoadOverriding()
{
}
[Fact]
public void LoadHiding()
{
}
}
}
|