mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
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:
@@ -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> [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"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user