From 44a38392c35d770d59ff7845a8eedd5dd8d292ad Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 20 Jul 2018 23:50:14 -0700 Subject: [PATCH 1/2] [Feature] Use 'Marshal.IsComObject(object)' since CAS is not supported in .NET Core --- .../engine/Utils.cs | 40 ------------------- .../engine/parser/Compiler.cs | 5 ++- .../engine/runtime/Binding/Binders.cs | 15 +++---- 3 files changed, 11 insertions(+), 49 deletions(-) diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 5d43d2b6af7..30b6ed7601a 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -1399,46 +1399,6 @@ internal static class Separators // String.WhitespaceChars will trim aggressively than what the underlying FS does (for ex, NTFS, FAT). internal static readonly char[] PathSearchTrimEnd = { (char)0x9, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20, (char)0x85, (char)0xA0 }; } - -#if !UNIX - // This is to reduce the runtime overhead of the feature query - private static readonly Type ComObjectType = typeof(object).Assembly.GetType("System.__ComObject"); -#endif - - internal static bool IsComObject(PSObject psObject) - { -#if UNIX - return false; -#else - if (psObject == null) { return false; } - - object obj = PSObject.Base(psObject); - return IsComObject(obj); -#endif - } - - internal static bool IsComObject(object obj) - { -#if UNIX - return false; -#else - // We can't use System.Runtime.InteropServices.Marshal.IsComObject(obj) since it doesn't work in partial trust. - // - // There could be strongly typed RWCs whose type is not 'System.__ComObject', but the more specific type should - // derive from 'System.__ComObject'. The strongly typed RWCs can be created with 'new' operation via the Primay - // Interop Assembly (PIA). - // For example, with the PIA 'Microsoft.Office.Interop.Excel', you can write the following code: - // var excelApp = new Microsoft.Office.Interop.Excel.Application(); - // Type type = excelApp.GetType(); - // Type comObjectType = typeof(object).Assembly.GetType("System.__ComObject"); - // Console.WriteLine("excelApp type: {0}", type.FullName); - // Console.WriteLine("Is __ComObject assignable from? {0}", comObjectType.IsAssignableFrom(type)); - // and the results are: - // excelApp type: Microsoft.Office.Interop.Excel.ApplicationClass - // Is __ComObject assignable from? True - return obj != null && ComObjectType.IsAssignableFrom(obj.GetType()); -#endif - } } } diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index c6159b1fe85..6ca1b7273de 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -16,6 +16,7 @@ using System.Management.Automation.Runspaces; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using Microsoft.PowerShell.Commands; @@ -498,8 +499,8 @@ internal static class CachedReflectionInfo internal static readonly MethodInfo VariableOps_SetVariableValue = typeof(VariableOps).GetMethod(nameof(VariableOps.SetVariableValue), staticFlags); - internal static readonly MethodInfo Utils_IsComObject = - typeof(Utils).GetMethod(nameof(Utils.IsComObject), staticFlags, binder: null, types: new Type[] {typeof(object)}, modifiers: null); + internal static readonly MethodInfo Marshal_IsComObject = + typeof(Marshal).GetMethod(nameof(Marshal.IsComObject), staticPublicFlags); internal static readonly MethodInfo ClassOps_ValidateSetProperty = typeof(ClassOps).GetMethod(nameof(ClassOps.ValidateSetProperty), staticPublicFlags); diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 39943665f58..00434cf8a0c 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -14,6 +14,7 @@ using System.Management.Automation.Runspaces; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -386,7 +387,7 @@ private static Expression ProcessOnePSObject(DynamicMetaObject arg, ref BindingR // The 'base' is a COM object, so bake that in the rule. restrictions = restrictions .Merge(arg.GetSimpleTypeRestriction()) - .Merge(BindingRestrictions.GetExpressionRestriction(Expression.Call(CachedReflectionInfo.Utils_IsComObject, expr))); + .Merge(BindingRestrictions.GetExpressionRestriction(Expression.Call(CachedReflectionInfo.Marshal_IsComObject, expr))); } else { @@ -619,7 +620,7 @@ public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, Dyna GetRestrictions(target))).WriteToDebugLog(this); } - if (Utils.IsComObject(targetValue)) + if (Marshal.IsComObject(targetValue)) { // Pretend that all com objects are enumerable, even if they aren't. We do this because it's technically impossible // to know if a com object is enumerable without just trying to cast it to IEnumerable. We could generate a rule like: @@ -631,7 +632,7 @@ public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, Dyna // EnumerableOps.NonEnumerableObjectEnumerator for more comments on how this works. var bindingRestrictions = BindingRestrictions.GetExpressionRestriction( - Expression.Call(CachedReflectionInfo.Utils_IsComObject, + Expression.Call(CachedReflectionInfo.Marshal_IsComObject, Expression.Call(CachedReflectionInfo.PSObject_Base, target.Expression))); return new DynamicMetaObject( Expression.Call(CachedReflectionInfo.EnumerableOps_GetCOMEnumerator, target.Expression), bindingRestrictions).WriteToDebugLog(this); @@ -715,7 +716,7 @@ private static IEnumerator AutomationNullRule(CallSite site, object obj) private static IEnumerator NotEnumerableRule(CallSite site, object obj) { - if (!(obj is PSObject) && !(obj is IEnumerable) && !(obj is IEnumerator) && !(obj is DataTable) && !Utils.IsComObject(obj)) + if (!(obj is PSObject) && !(obj is IEnumerable) && !(obj is IEnumerator) && !(obj is DataTable) && !Marshal.IsComObject(obj)) { return null; } @@ -4988,7 +4989,7 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy if (target.Value is PSObject && (PSObject.Base(target.Value) != target.Value)) { Object baseObject = PSObject.Base(target.Value); - if (baseObject != null && Utils.IsComObject(baseObject)) + if (baseObject != null && Marshal.IsComObject(baseObject)) { // We unwrap only if the 'base' is a COM object. It's unnecessary to unwrap in other cases, // especially in the case of strings, we would lose instance members on the PSObject. @@ -5859,7 +5860,7 @@ public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, Dy (value.Value is PSObject && (PSObject.Base(value.Value) != value.Value))) { Object baseObject = PSObject.Base(target.Value); - if (baseObject != null && Utils.IsComObject(baseObject)) + if (baseObject != null && Marshal.IsComObject(baseObject)) { // We unwrap only if the 'base' of 'target' is a COM object. It's unnecessary to unwrap in other cases, // especially in the case that 'target' is a string, we would lose instance members on the PSObject. @@ -6380,7 +6381,7 @@ public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target, args.Any(mo => mo.Value is PSObject && (PSObject.Base(mo.Value) != mo.Value))) { Object baseObject = PSObject.Base(target.Value); - if (baseObject != null && Utils.IsComObject(baseObject)) + if (baseObject != null && Marshal.IsComObject(baseObject)) { // We unwrap only if the 'base' of 'target' is a COM object. It's unnecessary to unwrap in other cases, // especially in the case that 'target' is a string, we would lose instance members on the PSObject. From afe7693f05fe78687226a20aea965c3284596caa Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Mon, 23 Jul 2018 16:55:39 -0700 Subject: [PATCH 2/2] [Feature] Fix a more issue --- .../engine/Utils.cs | 23 +++++++++++++++++++ .../engine/parser/Compiler.cs | 5 ++-- .../engine/runtime/Binding/Binders.cs | 9 ++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index 30b6ed7601a..fa7c24f8727 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -1399,6 +1399,29 @@ internal static class Separators // String.WhitespaceChars will trim aggressively than what the underlying FS does (for ex, NTFS, FAT). internal static readonly char[] PathSearchTrimEnd = { (char)0x9, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20, (char)0x85, (char)0xA0 }; } + + /// + /// A COM object could be directly of the type 'System.__ComObject', or it could be a strongly typed RWC, + /// whose specific type derives from 'System.__ComObject'. + /// A strongly typed RWC can be created via the 'new' operation with a Primary Interop Assembly (PIA). + /// For example, with the PIA 'Microsoft.Office.Interop.Excel', you can write the following code: + /// var excelApp = new Microsoft.Office.Interop.Excel.Application(); + /// Type type = excelApp.GetType(); + /// Type comObjectType = typeof(object).Assembly.GetType("System.__ComObject"); + /// Console.WriteLine("excelApp type: {0}", type.FullName); + /// Console.WriteLine("Is __ComObject assignable from? {0}", comObjectType.IsAssignableFrom(type)); + /// and the results are: + /// excelApp type: Microsoft.Office.Interop.Excel.ApplicationClass + /// Is __ComObject assignable from? True + /// + internal static bool IsComObject(object obj) + { +#if UNIX + return false; +#else + return obj != null && Marshal.IsComObject(obj); +#endif + } } } diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 6ca1b7273de..68ca0f4410b 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -16,7 +16,6 @@ using System.Management.Automation.Runspaces; using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text.RegularExpressions; using Microsoft.PowerShell.Commands; @@ -499,8 +498,8 @@ internal static class CachedReflectionInfo internal static readonly MethodInfo VariableOps_SetVariableValue = typeof(VariableOps).GetMethod(nameof(VariableOps.SetVariableValue), staticFlags); - internal static readonly MethodInfo Marshal_IsComObject = - typeof(Marshal).GetMethod(nameof(Marshal.IsComObject), staticPublicFlags); + internal static readonly MethodInfo Utils_IsComObject = + typeof(Utils).GetMethod(nameof(Utils.IsComObject), staticFlags); internal static readonly MethodInfo ClassOps_ValidateSetProperty = typeof(ClassOps).GetMethod(nameof(ClassOps.ValidateSetProperty), staticPublicFlags); diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 00434cf8a0c..b10d2ecac91 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -387,7 +387,7 @@ private static Expression ProcessOnePSObject(DynamicMetaObject arg, ref BindingR // The 'base' is a COM object, so bake that in the rule. restrictions = restrictions .Merge(arg.GetSimpleTypeRestriction()) - .Merge(BindingRestrictions.GetExpressionRestriction(Expression.Call(CachedReflectionInfo.Marshal_IsComObject, expr))); + .Merge(BindingRestrictions.GetExpressionRestriction(Expression.Call(CachedReflectionInfo.Utils_IsComObject, expr))); } else { @@ -632,7 +632,7 @@ public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, Dyna // EnumerableOps.NonEnumerableObjectEnumerator for more comments on how this works. var bindingRestrictions = BindingRestrictions.GetExpressionRestriction( - Expression.Call(CachedReflectionInfo.Marshal_IsComObject, + Expression.Call(CachedReflectionInfo.Utils_IsComObject, Expression.Call(CachedReflectionInfo.PSObject_Base, target.Expression))); return new DynamicMetaObject( Expression.Call(CachedReflectionInfo.EnumerableOps_GetCOMEnumerator, target.Expression), bindingRestrictions).WriteToDebugLog(this); @@ -716,6 +716,11 @@ private static IEnumerator AutomationNullRule(CallSite site, object obj) private static IEnumerator NotEnumerableRule(CallSite site, object obj) { + if (obj == null) + { + return null; + } + if (!(obj is PSObject) && !(obj is IEnumerable) && !(obj is IEnumerator) && !(obj is DataTable) && !Marshal.IsComObject(obj)) { return null;