Handle operations with ByRef-like types gracefully in PowerShell (#7533)

ByRef-like types are supposed to be used on stack only, so we need to fail gracefully when accessing properties, calling methods, or creating objects related to ByRef-like types.
This commit is contained in:
Dongbo Wang
2018-08-26 12:50:58 -07:00
committed by GitHub
parent bf0787f9a0
commit 86469bda33
11 changed files with 470 additions and 60 deletions
@@ -947,6 +947,7 @@ namespace Microsoft.PowerShell.Commands
}
else
{
parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest);
compilationOptions = GetDefaultCompilationOptions();
}
@@ -143,27 +143,42 @@ namespace Microsoft.PowerShell.Commands
if (e.InnerException != null && e.InnerException is TypeResolver.AmbiguousTypeException)
{
ThrowTerminatingError(
new ErrorRecord(
e,
"AmbiguousTypeReference",
ErrorCategory.InvalidType, null));
new ErrorRecord(
e,
"AmbiguousTypeReference",
ErrorCategory.InvalidType,
targetObject: null));
}
mshArgE = PSTraceSource.NewArgumentException(
"TypeName",
NewObjectStrings.TypeNotFound,
TypeName);
"TypeName",
NewObjectStrings.TypeNotFound,
TypeName);
ThrowTerminatingError(
new ErrorRecord(
mshArgE,
"TypeNotFound",
ErrorCategory.InvalidType, null));
ErrorCategory.InvalidType,
targetObject: null));
}
throw e;
}
Diagnostics.Assert(type != null, "LanguagePrimitives.TryConvertTo failed but returned true");
if (type.IsByRefLike)
{
ThrowTerminatingError(
new ErrorRecord(
PSTraceSource.NewInvalidOperationException(
NewObjectStrings.CannotInstantiateBoxedByRefLikeType,
type),
nameof(NewObjectStrings.CannotInstantiateBoxedByRefLikeType),
ErrorCategory.InvalidOperation,
targetObject: null));
}
if (Context.LanguageMode == PSLanguageMode.ConstrainedLanguage)
{
if (!CoreTypes.Contains(type))
@@ -138,6 +138,9 @@
<data name="CannotInstantiateWinRTType" xml:space="preserve">
<value>Creating instances of attribute and delegated Windows RT types is not supported.</value>
</data>
<data name="CannotInstantiateBoxedByRefLikeType" xml:space="preserve">
<value>Cannot create instances of the ByRef-like type "{0}". ByRef-like types are not supported in PowerShell.</value>
</data>
<data name="CannotCreateTypeConstrainedLanguage" xml:space="preserve">
<value>Cannot create type. Only core types are supported in this language mode.</value>
</data>
@@ -1920,6 +1920,38 @@ namespace System.Management.Automation
internal object Invoke(object target, object[] arguments)
{
// There may be parameters of ByRef-like types, but they will be taken care of
// when we resolve overloads to find the best methods -- proper exception will
// be thrown when converting arguments to the ByRef-like parameter types.
//
// So when reaching here, we only care about (1) if the method return type is
// BeRef-like; (2) if it's a constrcutor of a ByRef-like type.
if (method is ConstructorInfo ctor)
{
if (ctor.DeclaringType.IsByRefLike)
{
throw new MethodException(
nameof(ExtendedTypeSystem.CannotInstantiateBoxedByRefLikeType),
innerException: null,
ExtendedTypeSystem.CannotInstantiateBoxedByRefLikeType,
ctor.DeclaringType);
}
return ctor.Invoke(arguments);
}
var methodInfo = (MethodInfo) method;
if (methodInfo.ReturnType.IsByRefLike)
{
throw new MethodException(
nameof(ExtendedTypeSystem.CannotCallMethodWithByRefLikeReturnType),
innerException: null,
ExtendedTypeSystem.CannotCallMethodWithByRefLikeReturnType,
methodInfo.Name,
methodInfo.ReturnType);
}
if (target is PSObject)
{
if (!method.DeclaringType.IsAssignableFrom(target.GetType()))
@@ -1932,20 +1964,15 @@ namespace System.Management.Automation
{
if (_methodInvoker == null)
{
if (!(method is MethodInfo))
{
_useReflection = true;
}
else
{
_methodInvoker = GetMethodInvoker((MethodInfo)method);
}
_methodInvoker = GetMethodInvoker(methodInfo);
}
if (_methodInvoker != null)
{
return _methodInvoker(target, arguments);
}
}
return method.Invoke(target, arguments);
}
@@ -2455,13 +2482,12 @@ namespace System.Management.Automation
// require different delegates
// The same is true for generics, COM Types.
Type declaringType = property.DeclaringType;
Type propertyType = property.PropertyType;
if (declaringType.IsValueType ||
propertyType.IsGenericType ||
declaringType.IsGenericType ||
property.DeclaringType.IsCOMObject ||
property.PropertyType.IsCOMObject)
declaringType.IsCOMObject ||
propertyType.IsCOMObject)
{
this.readOnly = property.GetSetMethod() == null;
this.writeOnly = property.GetGetMethod() == null;
@@ -2511,7 +2537,9 @@ namespace System.Management.Automation
private void InitGetter()
{
if (writeOnly || useReflection)
{
return;
}
var parameter = Expression.Parameter(typeof(object));
Expression instance = null;
@@ -2568,7 +2596,9 @@ namespace System.Management.Automation
private void InitSetter()
{
if (readOnly || useReflection)
{
return;
}
var parameter = Expression.Parameter(typeof(object));
var value = Expression.Parameter(typeof(object));
@@ -3621,16 +3651,29 @@ namespace System.Management.Automation
protected override object PropertyGet(PSProperty property)
{
PropertyCacheEntry adapterData = (PropertyCacheEntry)property.adapterData;
if (adapterData.propertyType.IsByRefLike)
{
throw new GetValueException(
nameof(ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField),
innerException: null,
ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField,
adapterData.member.Name,
adapterData.propertyType);
}
PropertyInfo propertyInfo = adapterData.member as PropertyInfo;
if (propertyInfo != null)
{
if (adapterData.writeOnly)
{
throw new GetValueException("WriteOnlyProperty",
null,
throw new GetValueException(
nameof(ExtendedTypeSystem.WriteOnlyProperty),
innerException: null,
ExtendedTypeSystem.WriteOnlyProperty,
propertyInfo.Name);
}
if (adapterData.useReflection)
{
return propertyInfo.GetValue(property.baseObject, null);
@@ -3664,12 +3707,23 @@ namespace System.Management.Automation
if (adapterData.readOnly)
{
throw new SetValueException(nameof(ExtendedTypeSystem.ReadOnlyProperty),
null,
throw new SetValueException(
nameof(ExtendedTypeSystem.ReadOnlyProperty),
innerException: null,
ExtendedTypeSystem.ReadOnlyProperty,
adapterData.member.Name);
}
if (adapterData.propertyType.IsByRefLike)
{
throw new SetValueException(
nameof(ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField),
innerException: null,
ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField,
adapterData.member.Name,
adapterData.propertyType);
}
PropertyInfo propertyInfo = adapterData.member as PropertyInfo;
if (propertyInfo != null)
{
@@ -3755,9 +3809,7 @@ namespace System.Management.Automation
#pragma warning disable 56500
try
{
// We cannot call MethodBase's Invoke on a constructor
// because it requires a target we don't have.
returnValue = ((ConstructorInfo)methodInformation.method).Invoke(arguments);
returnValue = methodInformation.Invoke(target: null, arguments);
}
catch (TargetInvocationException ex)
{
@@ -4243,8 +4243,7 @@ namespace System.Management.Automation
}
}
private static Dictionary<ConversionTypePair, ConversionData> s_converterCache
= new Dictionary<ConversionTypePair, ConversionData>(256);
private static Dictionary<ConversionTypePair, ConversionData> s_converterCache = new Dictionary<ConversionTypePair, ConversionData>(256);
private static ConversionData CacheConversion<T>(Type fromType, Type toType, PSConverter<T> converter, ConversionRank rank)
{
@@ -4687,9 +4686,13 @@ namespace System.Management.Automation
bool debase;
var conversion = FigureConversion(valueToConvert, resultType, out debase);
return conversion.Invoke(debase ? PSObject.Base(valueToConvert) : valueToConvert,
resultType, recursion, debase ? (PSObject)valueToConvert : null,
formatProvider, backupTypeTable);
return conversion.Invoke(
debase ? PSObject.Base(valueToConvert) : valueToConvert,
resultType,
recursion,
debase ? (PSObject)valueToConvert : null,
formatProvider,
backupTypeTable);
}
}
@@ -4704,6 +4707,14 @@ namespace System.Management.Automation
internal static Tuple<string, string> GetInvalidCastMessages(object valueToConvert, Type resultType)
{
string errorId, errorMsg;
if (resultType.IsByRefLike)
{
typeConversion.WriteLine("Cannot convert to ByRef-Like types as they should be used on stack only.");
errorId = nameof(ExtendedTypeSystem.InvalidCastToByRefLikeType);
errorMsg = StringUtil.Format(ExtendedTypeSystem.InvalidCastToByRefLikeType, resultType);
return Tuple.Create(errorId, errorMsg);
}
if (PSObject.Base(valueToConvert) == null)
{
if (resultType.IsEnum)
@@ -5443,6 +5454,12 @@ namespace System.Management.Automation
toType == fromType ? ConversionRank.Identity : ConversionRank.Assignable);
}
if (fromType.IsByRefLike || toType.IsByRefLike)
{
// ByRef-like types are not boxable and should be used on stack only.
return CacheConversion(fromType, toType, ConvertNoConversion, ConversionRank.None);
}
if (typeof(PSObject).IsAssignableFrom(fromType) && typeof(InternalPSObject) != fromType)
{
// We don't attempt converting PSObject (or derived) to anything else,
@@ -391,6 +391,8 @@ namespace System.Management.Automation.Language
internal static readonly MethodInfo PSCreateInstanceBinder_IsTargetTypeNonPublic =
typeof(PSCreateInstanceBinder).GetMethod(nameof(PSCreateInstanceBinder.IsTargetTypeNonPublic), staticFlags);
internal static readonly MethodInfo PSCreateInstanceBinder_IsTargetTypeByRefLike =
typeof(PSCreateInstanceBinder).GetMethod(nameof(PSCreateInstanceBinder.IsTargetTypeByRefLike), staticFlags);
internal static readonly MethodInfo PSCreateInstanceBinder_GetTargetTypeName =
typeof(PSCreateInstanceBinder).GetMethod(nameof(PSCreateInstanceBinder.GetTargetTypeName), staticFlags);
@@ -596,7 +598,7 @@ namespace System.Management.Automation.Language
return Expression.Convert(expr, type);
}
if (type.ContainsGenericParameters)
if (type.ContainsGenericParameters || type.IsByRefLike)
{
return Expression.Call(
CachedReflectionInfo.LanguagePrimitives_ThrowInvalidCastException,
@@ -5504,6 +5506,22 @@ namespace System.Management.Automation.Language
var propertyInfo = memberInfo[0] as PropertyInfo;
if (propertyInfo != null)
{
if (propertyInfo.PropertyType.IsByRefLike)
{
// ByRef-like types are not boxable and should be used only on stack.
return Expression.Throw(
Expression.New(
CachedReflectionInfo.GetValueException_ctor,
Expression.Constant(nameof(ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField)),
Expression.Constant(null, typeof(Exception)),
Expression.Constant(ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField),
Expression.NewArrayInit(
typeof(object),
Expression.Constant(propertyInfo.Name),
Expression.Constant(propertyInfo.PropertyType, typeof(Type)))),
typeof(object));
}
if (propertyInfo.CanRead)
{
return Expression.Property(null, propertyInfo);
@@ -5512,6 +5530,8 @@ namespace System.Management.Automation.Language
}
else
{
// Field cannot be of a ByRef-like type unless it's an instance member of a ref struct.
// So we don't need to check 'IsByRefLike' for static field access.
return Expression.Field(null, (FieldInfo)memberInfo[0]);
}
}
@@ -4242,6 +4242,22 @@ namespace System.Management.Automation.Language
}
}
// Check return type after the argument conversion, so any no-conversion error can be thrown.
if (getter.ReturnType.IsByRefLike)
{
return errorSuggestion ?? new DynamicMetaObject(
Expression.Block(
Expression.IfThen(
Compiler.IsStrictMode(3),
Compiler.ThrowRuntimeError(
nameof(ParserStrings.CannotIndexWithByRefLikeReturnType),
ParserStrings.CannotIndexWithByRefLikeReturnType,
Expression.Constant(target.LimitType, typeof(Type)),
Expression.Constant(getter.ReturnType, typeof(Type)))),
GetNullResult()),
target.CombineRestrictions(indexes));
}
if (getterParams.Length == 1 && getterParams[0].ParameterType == typeof(int) && CanIndexFromEndWithNegativeIndex(target))
{
// PowerShell supports negative indexing for some types (specifically, types implementing IList or IList<T>).
@@ -5090,7 +5106,13 @@ namespace System.Management.Automation.Language
var adapterData = property.adapterData as DotNetAdapter.PropertyCacheEntry;
Diagnostics.Assert(adapterData != null, "We have an unknown PSProperty that we aren't correctly optimizing.");
if (!adapterData.member.DeclaringType.IsGenericTypeDefinition)
if (adapterData.member.DeclaringType.IsGenericTypeDefinition || adapterData.propertyType.IsByRefLike)
{
// This is kinda lame - we really should throw an error, but accessing property getter
// doesn't throw error in PowerShell since V2, even in strict mode.
expr = ExpressionCache.NullConstant;
}
else
{
// For static property access, the target expr must be null. For non-static, we must convert
// because target.Expression is typeof(object) because this is a dynamic site.
@@ -5113,11 +5135,6 @@ namespace System.Management.Automation.Language
expr = Expression.Field(targetExpr, (FieldInfo)adapterData.member);
}
}
else
{
// This is kinda lame - we really should throw an error, but V2 did the same thing (even in strict mode).
expr = ExpressionCache.NullConstant;
}
}
var scriptProperty = propertyInfo as PSScriptProperty;
@@ -6032,14 +6049,37 @@ namespace System.Management.Automation.Language
if (data.member.DeclaringType.IsGenericTypeDefinition)
{
Expression innerException = Expression.New(CachedReflectionInfo.SetValueException_ctor,
Expression innerException = Expression.New(
CachedReflectionInfo.SetValueException_ctor,
Expression.Constant("PropertyAssignmentException"),
Expression.Constant(null, typeof(Exception)),
Expression.Constant(ExtendedTypeSystem.CannotInvokeStaticMethodOnUninstantiatedGenericType),
Expression.NewArrayInit(typeof(object), Expression.Constant(data.member.DeclaringType.FullName)));
expr = Compiler.ThrowRuntimeErrorWithInnerException("PropertyAssignmentException",
Expression.Constant(ExtendedTypeSystem.CannotInvokeStaticMethodOnUninstantiatedGenericType), innerException,
this.ReturnType, Expression.Constant(data.member.DeclaringType.FullName));
expr = Compiler.ThrowRuntimeErrorWithInnerException(
"PropertyAssignmentException",
Expression.Constant(ExtendedTypeSystem.CannotInvokeStaticMethodOnUninstantiatedGenericType),
innerException,
this.ReturnType,
Expression.Constant(data.member.DeclaringType.FullName));
return new DynamicMetaObject(expr, restrictions).WriteToDebugLog(this);
}
if (data.propertyType.IsByRefLike)
{
expr = Expression.Throw(
Expression.New(
CachedReflectionInfo.SetValueException_ctor,
Expression.Constant(nameof(ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField)),
Expression.Constant(null, typeof(Exception)),
Expression.Constant(ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField),
Expression.NewArrayInit(
typeof(object),
Expression.Constant(data.member.Name),
Expression.Constant(data.propertyType, typeof(Type)))),
this.ReturnType);
return new DynamicMetaObject(expr, restrictions).WriteToDebugLog(this);
}
@@ -6769,6 +6809,45 @@ namespace System.Management.Automation.Language
List<Expression> initTemps = new List<Expression>();
List<Expression> copyOutTemps = new List<Expression>();
ConstructorInfo constructorInfo = null;
MethodInfo methodInfo = mi as MethodInfo;
if (methodInfo != null)
{
Type returnType = methodInfo.ReturnType;
if (returnType.IsByRefLike)
{
return Expression.Throw(
Expression.New(
CachedReflectionInfo.MethodException_ctor,
Expression.Constant(nameof(ExtendedTypeSystem.CannotCallMethodWithByRefLikeReturnType)),
Expression.Constant(null, typeof(Exception)),
Expression.Constant(ExtendedTypeSystem.CannotCallMethodWithByRefLikeReturnType),
Expression.NewArrayInit(
typeof(object),
Expression.Constant(methodInfo.Name),
Expression.Constant(returnType, typeof(Type)))),
typeof(object));
}
}
else
{
constructorInfo = (ConstructorInfo)mi;
Type declaringType = constructorInfo.DeclaringType;
if (declaringType.IsByRefLike)
{
return Expression.Throw(
Expression.New(
CachedReflectionInfo.MethodException_ctor,
Expression.Constant(nameof(ExtendedTypeSystem.CannotInstantiateBoxedByRefLikeType)),
Expression.Constant(null, typeof(Exception)),
Expression.Constant(ExtendedTypeSystem.CannotInstantiateBoxedByRefLikeType),
Expression.NewArrayInit(
typeof(object),
Expression.Constant(declaringType, typeof(Type)))),
typeof(object));
}
}
var parameters = mi.GetParameters();
var argExprs = new Expression[parameters.Length];
for (int i = 0; i < parameters.Length; ++i)
@@ -6846,13 +6925,6 @@ namespace System.Management.Automation.Language
}
}
ConstructorInfo constructorInfo = null;
var methodInfo = mi as MethodInfo;
if (methodInfo == null)
{
constructorInfo = (ConstructorInfo)mi;
}
Expression call;
if (constructorInfo != null)
{
@@ -7266,16 +7338,35 @@ namespace System.Management.Automation.Language
var instanceType = targetValue as Type ?? targetValue.GetType();
BindingRestrictions restrictions;
if (instanceType.IsByRefLike)
{
// ByRef-like types are not boxable and should be used only on stack
restrictions = BindingRestrictions.GetExpressionRestriction(
Expression.Call(CachedReflectionInfo.PSCreateInstanceBinder_IsTargetTypeByRefLike, target.Expression));
return target.ThrowRuntimeError(
restrictions,
nameof(ExtendedTypeSystem.CannotInstantiateBoxedByRefLikeType),
ExtendedTypeSystem.CannotInstantiateBoxedByRefLikeType,
Expression.Call(
CachedReflectionInfo.PSCreateInstanceBinder_GetTargetTypeName,
target.Expression)).WriteToDebugLog(this);
}
if (_publicTypeOnly && !TypeResolver.IsPublic(instanceType))
{
// If 'publicTypeOnly' specified, we only support creating instance for public types.
restrictions = BindingRestrictions.GetExpressionRestriction(
Expression.Call(CachedReflectionInfo.PSCreateInstanceBinder_IsTargetTypeNonPublic, target.Expression));
return target.ThrowRuntimeError(restrictions, "MethodNotFound", ParserStrings.MethodNotFound,
Expression.Call(
CachedReflectionInfo.PSCreateInstanceBinder_GetTargetTypeName,
target.Expression),
Expression.Constant("new")).WriteToDebugLog(this);
return target.ThrowRuntimeError(
restrictions,
nameof(ParserStrings.MethodNotFound),
ParserStrings.MethodNotFound,
Expression.Call(
CachedReflectionInfo.PSCreateInstanceBinder_GetTargetTypeName,
target.Expression),
Expression.Constant("new")).WriteToDebugLog(this);
}
var ctors = instanceType.GetConstructors();
@@ -7304,7 +7395,19 @@ namespace System.Management.Automation.Language
}
/// <summary>
/// Check if the target type is not public
/// Check if the target type is ByRef-like.
/// </summary>
internal static bool IsTargetTypeByRefLike(object target)
{
var targetValue = PSObject.Base(target);
if (targetValue == null) { return false; }
var instanceType = targetValue as Type ?? targetValue.GetType();
return instanceType.IsByRefLike;
}
/// <summary>
/// Check if the target type is not public.
/// </summary>
internal static bool IsTargetTypeNonPublic(object target)
{
@@ -7316,7 +7419,7 @@ namespace System.Management.Automation.Language
}
/// <summary>
/// Return the full name of the target type
/// Return the full name of the target type.
/// </summary>
internal static string GetTargetTypeName(object target)
{
@@ -370,4 +370,16 @@ PS&gt; [System.Collections.Generic.Comparer``1]::get_Default()
<data name="InvalidCastExceptionNonCoreType" xml:space="preserve">
<value>Cannot convert value to type "{0}". Only core types are supported in this language mode.</value>
</data>
<data name="InvalidCastToByRefLikeType" xml:space="preserve">
<value>Cannot convert to the ByRef-like type "{0}". ByRef-like types are not supported in PowerShell.</value>
</data>
<data name="CannotAccessByRefLikePropertyOrField" xml:space="preserve">
<value>Cannot get or set the property or field "{0}" of the ByRef-like type "{1}". ByRef-like types are not supported in PowerShell.</value>
</data>
<data name="CannotCallMethodWithByRefLikeReturnType" xml:space="preserve">
<value>Cannot invoke the method "{0}" of the ByRef-like return type "{1}". ByRef-like types are not supported in PowerShell.</value>
</data>
<data name="CannotInstantiateBoxedByRefLikeType" xml:space="preserve">
<value>Cannot create an instance of the ByRef-like type "{0}". ByRef-like types are not supported in PowerShell.</value>
</data>
</root>
@@ -291,7 +291,10 @@ Possible matches are</value>
<value>Cannot index into a null array.</value>
</data>
<data name="CannotIndex" xml:space="preserve">
<value>Unable to index into an object of type {0}.</value>
<value>Unable to index into an object of type "{0}".</value>
</data>
<data name="CannotIndexWithByRefLikeReturnType" xml:space="preserve">
<value>Unable to index into an object of type "{0}" with the ByRef-like return type "{1}". ByRef-like types are not supported in PowerShell.</value>
</data>
<data name="ArrayAssignmentFailed" xml:space="preserve">
<value>Array assignment to [{0}] failed: {1}.</value>
@@ -1,5 +1,6 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Describe "DotNetAPI" -Tags "CI" {
$posh_E = 2.718281828459045
$posh_pi = 3.14159265358979
@@ -24,4 +25,8 @@ Describe "DotNetAPI" -Tags "CI" {
$guidVal | Should -BeOfType Guid
}
It "Should access types in System.Console" {
[System.Console]::TreatControlCAsInput | Should -BeFalse
}
}
@@ -1,7 +1,186 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Describe ".NET class interoperability" -Tags "CI" {
It "Should access types in System.Console" {
[System.Console]::TreatControlCAsInput | Should -BeFalse
Describe "Handle ByRef-like types gracefully" -Tags "CI" {
BeforeAll {
$code = @'
using System;
namespace DotNetInterop
{
public class Test
{
public Span<int> this[int i]
{
get { return default(Span<int>); }
set { DoNothing(value); }
}
public static Span<int> Space
{
get { return default(Span<int>); }
set { DoNothing(value); }
}
public Span<int> Room
{
get { return default(Span<int>); }
set { DoNothing(value); }
}
private static void DoNothing(Span<int> param)
{
}
public string PrintMySpan(string str, Span<int> mySpan = default)
{
return str;
}
public Span<int> GetSpan(int[] array)
{
return array.AsSpan();
}
}
public ref struct MyByRefLikeType
{
public MyByRefLikeType(int i) { }
public static int Index;
}
}
'@
if (-not ("DotNetInterop.Test" -as [type]))
{
Add-Type -TypeDefinition $code -IgnoreWarnings
}
$testObj = [DotNetInterop.Test]::new()
}
It "New-Object should fail gracefully when used for a ByRef-like type" {
{ New-Object -TypeName 'System.Span[string]' } | Should -Throw -ErrorId "CannotInstantiateBoxedByRefLikeType,Microsoft.PowerShell.Commands.NewObjectCommand"
{ New-Object -TypeName 'DotNetInterop.MyByRefLikeType' } | Should -Throw -ErrorId "CannotInstantiateBoxedByRefLikeType,Microsoft.PowerShell.Commands.NewObjectCommand"
}
It "The 'new' method call should fail gracefully when used on a ByRef-like type" {
{ [System.Span[string]]::new() } | Should -Throw -ErrorId "CannotInstantiateBoxedByRefLikeType"
{ [DotNetInterop.MyByRefLikeType]::new() } | Should -Throw -ErrorId "CannotInstantiateBoxedByRefLikeType"
}
It "Calling constructor of a ByRef-like type via dotnet adapter should fail gracefully - <Number>" -TestCases @(
@{ Number = 1; Script = { [System.Span[string]]::new.Invoke("abc") } }
@{ Number = 2; Script = { [DotNetInterop.MyByRefLikeType]::new.Invoke(2) } }
) {
param($Script)
$expectedError = $null
try {
& $Script
} catch {
$expectedError = $_
}
$expectedError | Should -Not -BeNullOrEmpty
$expectedError.Exception.InnerException.ErrorRecord.FullyQualifiedErrorId | Should -BeExactly "CannotInstantiateBoxedByRefLikeType"
}
It "Cast to a ByRef-like type should fail gracefully" {
{ [System.Span[int]] ([int[]]1,2,3) } | Should -Throw -ErrorId "InvalidCastToByRefLikeType"
{ [DotNetInterop.MyByRefLikeType] "text" } | Should -Throw -ErrorId "InvalidCastToByRefLikeType"
}
It "LanguagePrimitives.ConvertTo should fail gracefully for a ByRef-like type '<Name>'" -TestCases @(
@{ Name = "Span"; Type = [System.Span[int]] }
@{ Name = "MyByRefLikeType"; Type = [DotNetInterop.MyByRefLikeType] }
) {
param($Type)
$expectedError = $null
try {
[System.Management.Automation.LanguagePrimitives]::ConvertTo(([int[]]1,2,3), $Type)
} catch {
$expectedError = $_
}
$expectedError | Should -Not -BeNullOrEmpty
$expectedError.Exception.InnerException.ErrorRecord.FullyQualifiedErrorId | Should -BeExactly "InvalidCastToByRefLikeType"
}
It "Getting value of a ByRef-like type instance property should not throw and should return null, even in strict mode - <Mechanism>" -TestCases @(
@{ Mechanism = "Compiler/Binder"; Script = { [System.Text.Encoding]::ASCII.Preamble } }
@{ Mechanism = "Dotnet-Adapter"; Script = { [System.Text.Encoding]::ASCII.PSObject.Properties["Preamble"].Value } }
) {
param($Script)
try {
Set-StrictMode -Version latest
& $Script | Should -Be $null
} finally {
Set-StrictMode -Off
}
}
It "Setting value of a ByRef-like type instance property should fail gracefully - <Mechanism>" -TestCases @(
@{ Mechanism = "Compiler/Binder"; Script = { $testObj.Room = [int[]](1,2,3) } }
@{ Mechanism = "Dotnet-Adapter"; Script = { $testObj.PSObject.Properties["Room"].Value = [int[]](1,2,3) } }
) {
param($Script)
$Script | Should -Throw -ErrorId "CannotAccessByRefLikePropertyOrField"
}
It "<Action> value of a ByRef-like type static property should fail gracefully" -TestCases @(
@{ Action = "Getting"; Script = { [DotNetInterop.Test]::Space } }
@{ Action = "Setting"; Script = { [DotNetInterop.Test]::Space = "blah" } }
) {
param($Script)
$Script | Should -Throw -ErrorId "CannotAccessByRefLikePropertyOrField"
}
It "Invoke a method with optional ByRef-like parameter could work" {
$testObj.PrintMySpan("Hello") | Should -BeExactly "Hello"
}
It "Invoke a method with ByRef-like parameter should fail gracefully - <Mechanism>" -TestCases @(
@{ Mechanism = "Compiler/Binder"; Script = { $testObj.PrintMySpan("Hello", 1) } }
@{ Mechanism = "Dotnet-Adapter"; Script = { $testObj.psobject.Methods["PrintMySpan"].Invoke("Hello", 1) } }
) {
param($Script)
$Script | Should -Throw -ErrorId "MethodArgumentConversionInvalidCastArgument"
}
It "Invoke a method with ByRef-like return type should fail gracefully - Compiler/Binder" {
{ $testObj.GetSpan([int[]]@(1,2,3)) } | Should -Throw -ErrorId "CannotCallMethodWithByRefLikeReturnType"
}
It "Invoke a method with ByRef-like return type should fail gracefully - Dotnet-Adapter" {
$expectedError = $null
try {
$testObj.psobject.Methods["GetSpan"].Invoke([int[]]@(1,2,3))
} catch {
$expectedError = $_
}
$expectedError | Should -Not -BeNullOrEmpty
$expectedError.Exception.InnerException.ErrorRecord.FullyQualifiedErrorId | Should -BeExactly "CannotCallMethodWithByRefLikeReturnType"
}
It "Access static property of a ByRef-like type" {
[DotNetInterop.MyByRefLikeType]::Index = 10
[DotNetInterop.MyByRefLikeType]::Index | Should -Be 10
}
It "Get access of an indexer that returns ByRef-like type should return null in no-strict mode" {
$testObj[1] | Should -Be $null
}
It "Get access of an indexer that returns ByRef-like type should fail gracefully in strict mode" {
try {
Set-StrictMode -Version latest
{ $testObj[1] } | Should -Throw -ErrorId "CannotIndexWithByRefLikeReturnType"
} finally {
Set-StrictMode -Off
}
}
It "Set access of an indexer that accepts ByRef-like type should fail gracefully" {
{ $testObj[1] = 1 } | Should -Throw -ErrorId "InvalidCastToByRefLikeType"
}
}