Fix 'PropertyOnlyAdapter' to allow calling base methods (#6394)

For a PropertyOnlyAdapter, the property may come from various sources, but methods, including parameterized properties, still come from DotNetAdapter. So, the binder can optimize on method calls for objects that map to a custom PropertyOnlyAdapter.
This commit is contained in:
Dongbo Wang 2018-03-15 15:09:38 -07:00 committed by GitHub
parent a099786cdc
commit 294def7b11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 9 deletions

View file

@ -46,7 +46,7 @@ namespace System.Management.Automation
#region member #region member
internal virtual bool SiteBinderCanOptimize { get { return false; } } internal virtual bool CanSiteBinderOptimize(MemberTypes typeToOperateOn) { return false; }
protected static IEnumerable<string> GetDotNetTypeNameHierarchy(Type type) protected static IEnumerable<string> GetDotNetTypeNameHierarchy(Type type)
{ {
@ -3519,7 +3519,7 @@ namespace System.Management.Automation
#region member #region member
internal override bool SiteBinderCanOptimize { get { return true; } } internal override bool CanSiteBinderOptimize(MemberTypes typeToOperateOn) { return true; }
private static ConcurrentDictionary<Type, ConsolidatedString> s_typeToTypeNameDictionary = private static ConcurrentDictionary<Type, ConsolidatedString> s_typeToTypeNameDictionary =
new ConcurrentDictionary<Type, ConsolidatedString>(); new ConcurrentDictionary<Type, ConsolidatedString>();
@ -4545,7 +4545,24 @@ namespace System.Management.Automation
/// </summary> /// </summary>
internal abstract class PropertyOnlyAdapter : DotNetAdapter internal abstract class PropertyOnlyAdapter : DotNetAdapter
{ {
internal override bool SiteBinderCanOptimize { get { return false; } } /// <summary>
/// For a PropertyOnlyAdapter, the property may come from various sources,
/// but methods, including parameterized properties, still come from DotNetAdapter.
/// So, the binder can optimize on method calls for objects that map to a
/// custom PropertyOnlyAdapter.
/// </summary>
internal override bool CanSiteBinderOptimize(MemberTypes typeToOperateOn)
{
switch (typeToOperateOn)
{
case MemberTypes.Property:
return false;
case MemberTypes.Method:
return true;
default:
throw new InvalidOperationException("Should be unreachable. Update code if other member types need to be handled here.");
}
}
protected override ConsolidatedString GetInternedTypeNameHierarchy(object obj) protected override ConsolidatedString GetInternedTypeNameHierarchy(object obj)
{ {

View file

@ -5053,7 +5053,7 @@ namespace System.Management.Automation.Language
bool canOptimize; bool canOptimize;
Type aliasConversionType; Type aliasConversionType;
memberInfo = GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType); memberInfo = GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType, MemberTypes.Property);
if (!canOptimize) if (!canOptimize)
{ {
@ -5415,8 +5415,8 @@ namespace System.Management.Automation.Language
return null; return null;
} }
PSMemberInfo result = binder.GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType, aliases, aliasRestrictions); PSMemberInfo result = binder.GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType,
MemberTypes.Property, aliases, aliasRestrictions);
return result; return result;
} }
@ -5424,6 +5424,7 @@ namespace System.Management.Automation.Language
out BindingRestrictions restrictions, out BindingRestrictions restrictions,
out bool canOptimize, out bool canOptimize,
out Type aliasConversionType, out Type aliasConversionType,
MemberTypes memberTypeToOperateOn,
HashSet<string> aliases = null, HashSet<string> aliases = null,
List<BindingRestrictions> aliasRestrictions = null) List<BindingRestrictions> aliasRestrictions = null)
{ {
@ -5493,7 +5494,7 @@ namespace System.Management.Automation.Language
var adapterSet = PSObject.GetMappedAdapter(value, typeTable); var adapterSet = PSObject.GetMappedAdapter(value, typeTable);
if (memberInfo == null) if (memberInfo == null)
{ {
canOptimize = adapterSet.OriginalAdapter.SiteBinderCanOptimize; canOptimize = adapterSet.OriginalAdapter.CanSiteBinderOptimize(memberTypeToOperateOn);
// Don't bother looking for the member if we're not going to use it. // Don't bother looking for the member if we're not going to use it.
if (canOptimize) if (canOptimize)
{ {
@ -5969,7 +5970,7 @@ namespace System.Management.Automation.Language
BindingRestrictions restrictions; BindingRestrictions restrictions;
bool canOptimize; bool canOptimize;
Type aliasConversionType; Type aliasConversionType;
memberInfo = _getMemberBinder.GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType); memberInfo = _getMemberBinder.GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType, MemberTypes.Property);
restrictions = restrictions.Merge(value.PSGetTypeRestriction()); restrictions = restrictions.Merge(value.PSGetTypeRestriction());
@ -6464,7 +6465,7 @@ namespace System.Management.Automation.Language
BindingRestrictions restrictions; BindingRestrictions restrictions;
bool canOptimize; bool canOptimize;
Type aliasConversionType; Type aliasConversionType;
var methodInfo = _getMemberBinder.GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType) as PSMethodInfo; var methodInfo = _getMemberBinder.GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType, MemberTypes.Method) as PSMethodInfo;
restrictions = args.Aggregate(restrictions, (current, arg) => current.Merge(arg.PSGetMethodArgumentRestriction())); restrictions = args.Aggregate(restrictions, (current, arg) => current.Merge(arg.PSGetMethodArgumentRestriction()));
// If the process has ever used ConstrainedLanguage, then we need to add the language mode // If the process has ever used ConstrainedLanguage, then we need to add the language mode

View file

@ -315,3 +315,49 @@ Describe "DataRow and DataRowView Adapter tests" -tags "CI" {
} }
} }
} }
Describe "Base method call on object mapped to PropertyOnlyAdapter should work" -tags "CI" {
It "Base method call on object of a subclass of 'XmlDocument' -- Add-Type" {
$code =@'
namespace BaseMethodCallTest.OnXmlDocument {
public class Foo : System.Xml.XmlDocument {
public string MyName { get; set; }
public override void LoadXml(string content) {
MyName = content;
}
}
}
'@
try {
$null = [BaseMethodCallTest.OnXmlDocument.Foo]
} catch {
Add-Type -TypeDefinition $code
}
$foo = [BaseMethodCallTest.OnXmlDocument.Foo]::new()
$foo.LoadXml('<test>bar</test>')
$foo.MyName | Should -BeExactly '<test>bar</test>'
$foo.ChildNodes.Count | Should -Be 0
([System.Xml.XmlDocument]$foo).LoadXml('<test>bar</test>')
$foo.test | Should -BeExactly 'bar'
$foo.ChildNodes.Count | Should -Be 1
}
It "Base method call on object of a subclass of 'XmlDocument' -- PowerShell Class" {
class XmlDocChild : System.Xml.XmlDocument {
[string] $MyName
[void] LoadXml([string]$content) {
$this.MyName = $content
# Try to call the base type's .LoadXml() method.
([System.Xml.XmlDocument] $this).LoadXml($content)
}
}
$child = [XmlDocChild]::new()
$child.LoadXml('<test>bar</test>')
$child.MyName | Should -BeExactly '<test>bar</test>'
$child.test | Should -BeExactly 'bar'
$child.ChildNodes.Count | Should -Be 1
}
}