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
internal virtual bool SiteBinderCanOptimize { get { return false; } }
internal virtual bool CanSiteBinderOptimize(MemberTypes typeToOperateOn) { return false; }
protected static IEnumerable<string> GetDotNetTypeNameHierarchy(Type type)
{
@ -3519,7 +3519,7 @@ namespace System.Management.Automation
#region member
internal override bool SiteBinderCanOptimize { get { return true; } }
internal override bool CanSiteBinderOptimize(MemberTypes typeToOperateOn) { return true; }
private static ConcurrentDictionary<Type, ConsolidatedString> s_typeToTypeNameDictionary =
new ConcurrentDictionary<Type, ConsolidatedString>();
@ -4545,7 +4545,24 @@ namespace System.Management.Automation
/// </summary>
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)
{

View file

@ -5053,7 +5053,7 @@ namespace System.Management.Automation.Language
bool canOptimize;
Type aliasConversionType;
memberInfo = GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType);
memberInfo = GetPSMemberInfo(target, out restrictions, out canOptimize, out aliasConversionType, MemberTypes.Property);
if (!canOptimize)
{
@ -5415,8 +5415,8 @@ namespace System.Management.Automation.Language
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;
}
@ -5424,6 +5424,7 @@ namespace System.Management.Automation.Language
out BindingRestrictions restrictions,
out bool canOptimize,
out Type aliasConversionType,
MemberTypes memberTypeToOperateOn,
HashSet<string> aliases = null,
List<BindingRestrictions> aliasRestrictions = null)
{
@ -5493,7 +5494,7 @@ namespace System.Management.Automation.Language
var adapterSet = PSObject.GetMappedAdapter(value, typeTable);
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.
if (canOptimize)
{
@ -5969,7 +5970,7 @@ namespace System.Management.Automation.Language
BindingRestrictions restrictions;
bool canOptimize;
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());
@ -6464,7 +6465,7 @@ namespace System.Management.Automation.Language
BindingRestrictions restrictions;
bool canOptimize;
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()));
// 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
}
}