Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 29 additions & 8 deletions src/System.Management.Automation/engine/parser/PSType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Management.Automation.Internal;
Expand Down Expand Up @@ -276,7 +277,7 @@ private sealed class DefineTypeHelper
internal readonly TypeBuilder _staticHelpersTypeBuilder;
private readonly Dictionary<string, PropertyMemberAst> _definedProperties;
private readonly Dictionary<string, List<Tuple<FunctionMemberAst, Type[]>>> _definedMethods;
private HashSet<Tuple<string, Type>> _abstractProperties;
private Dictionary<Tuple<string, Type>, PropertyInfo> _abstractProperties;
internal readonly List<(string fieldName, IParameterMetadataProvider bodyAst, bool isStatic)> _fieldsToInitForMemberFunctions;
private bool _baseClassHasDefaultCtor;

Expand Down Expand Up @@ -444,11 +445,11 @@ private Type GetBaseTypes(Parser parser, TypeDefinitionAst typeDefinitionAst, ou
return baseClass ?? typeof(object);
}

private bool ShouldImplementProperty(string name, Type type)
private bool ShouldImplementProperty(string name, Type type, [NotNullWhen(true)] out PropertyInfo interfaceProperty)
{
if (_abstractProperties == null)
{
_abstractProperties = new HashSet<Tuple<string, Type>>();
_abstractProperties = new Dictionary<Tuple<string, Type>, PropertyInfo>();
var allInterfaces = new HashSet<Type>();

// TypeBuilder.GetInterfaces() returns only the interfaces that was explicitly passed to its constructor.
Expand All @@ -467,7 +468,7 @@ private bool ShouldImplementProperty(string name, Type type)
{
foreach (var property in interfaceType.GetProperties())
{
_abstractProperties.Add(Tuple.Create(property.Name, property.PropertyType));
_abstractProperties.Add(Tuple.Create(property.Name, property.PropertyType), property);
}
}

Expand All @@ -477,13 +478,13 @@ private bool ShouldImplementProperty(string name, Type type)
{
if (property.GetAccessors().Any(m => m.IsAbstract))
{
_abstractProperties.Add(Tuple.Create(property.Name, property.PropertyType));
_abstractProperties.Add(Tuple.Create(property.Name, property.PropertyType), property);
}
}
}
}

return _abstractProperties.Contains(Tuple.Create(name, type));
return _abstractProperties.TryGetValue(Tuple.Create(name, type), out interfaceProperty);
}

public void DefineMembers()
Expand Down Expand Up @@ -629,9 +630,19 @@ private PropertyBuilder EmitPropertyIl(PropertyMemberAst propertyMemberAst, Type
// The property set and property get methods require a special set of attributes.
var getSetAttributes = Reflection.MethodAttributes.SpecialName | Reflection.MethodAttributes.HideBySig;
getSetAttributes |= propertyMemberAst.IsPublic ? Reflection.MethodAttributes.Public : Reflection.MethodAttributes.Private;
if (ShouldImplementProperty(propertyMemberAst.Name, type))
MethodInfo implementingGetter = null;
MethodInfo implementingSetter = null;
if (ShouldImplementProperty(propertyMemberAst.Name, type, out PropertyInfo interfaceProperty))
{
getSetAttributes |= Reflection.MethodAttributes.Virtual;
if (propertyMemberAst.IsStatic)
{
implementingGetter = interfaceProperty.GetGetMethod();
implementingSetter = interfaceProperty.GetSetMethod();
}
else
{
getSetAttributes |= Reflection.MethodAttributes.Virtual;
}
}

if (propertyMemberAst.IsStatic)
Expand Down Expand Up @@ -677,6 +688,11 @@ private PropertyBuilder EmitPropertyIl(PropertyMemberAst propertyMemberAst, Type
getIlGen.Emit(OpCodes.Ret);
}

if (implementingGetter != null)
{
_typeBuilder.DefineMethodOverride(getMethod, implementingGetter);
}

// Define the "set" accessor method.
MethodBuilder setMethod = _typeBuilder.DefineMethod(string.Concat("set_", propertyMemberAst.Name), getSetAttributes, null, new Type[] { type });
ILGenerator setIlGen = setMethod.GetILGenerator();
Expand Down Expand Up @@ -710,6 +726,11 @@ private PropertyBuilder EmitPropertyIl(PropertyMemberAst propertyMemberAst, Type

setIlGen.Emit(OpCodes.Ret);

if (implementingSetter != null)
{
_typeBuilder.DefineMethodOverride(setMethod, implementingSetter);
}

// Map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
property.SetGetMethod(getMethod);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,49 @@ Describe 'Classes inheritance syntax' -Tags "CI" {
$getter.Attributes -band [System.Reflection.MethodAttributes]::Virtual | Should -Be ([System.Reflection.MethodAttributes]::Virtual)
}

It 'can implement .NET interface static properties' {
Add-Type -TypeDefinition @'
public interface IInterfaceWithStaticAbstractProperty
{
static abstract int Getter { get; }
static abstract int Setter { get; set; }
}

public static class InterfaceStaticAbstractPropertyTest
{
public static int GetGetter<T>() where T : IInterfaceWithStaticAbstractProperty
=> T.Getter;

public static int GetSetter<T>() where T : IInterfaceWithStaticAbstractProperty
=> T.Setter;

public static int SetSetter<T>(int value) where T : IInterfaceWithStaticAbstractProperty
=> T.Setter = value;
}
'@

$C1 = Invoke-Expression @'
class ClassWithStaticAbstractInterface : IInterfaceWithStaticAbstractProperty {
static [int]$Getter = 1
static [int]$Setter = 2
}

[ClassWithStaticAbstractInterface]
'@

$C1::Getter | Should -Be 1
$C1::Getter | Should -BeOfType ([int])
$C1::Setter | Should -Be 2
$C1::Setter | Should -BeOfType ([int])
$C1::Setter = 3
$C1::Setter | Should -Be 3

[InterfaceStaticAbstractPropertyTest]::GetGetter[ClassWithStaticAbstractInterface]() | Should -Be 1
[InterfaceStaticAbstractPropertyTest]::GetSetter[ClassWithStaticAbstractInterface]() | Should -Be 3
[InterfaceStaticAbstractPropertyTest]::SetSetter[ClassWithStaticAbstractInterface](4)
[InterfaceStaticAbstractPropertyTest]::GetSetter[ClassWithStaticAbstractInterface]() | Should -Be 4
}

It 'allows use of defined later type as a property type' {
class A { static [B]$b }
class B : A {}
Expand Down
Loading