Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4)

private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
_method.ThrowNoInvokeException();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method)
{
// This is useful for calling a constructor on an already-initialized object
// such as created from RuntimeHelpers.GetUninitializedObject(Type).
return new MethodInvoker(rci);
MethodInvoker invoker = new MethodInvoker(rci);

// Use the interpreted version to avoid having to generate a new method that doesn't allocate.
invoker._strategy = GetStrategyForUsingInterpreted();

return invoker;
}

throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
Expand DownExpand Up@@ -181,7 +186,7 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes)

private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
ThrowForBadInvocationFlags();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,13 +18,14 @@ internal static void Initialize(
{
if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
{
// Always use the native invoke; useful for testing.
strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
// Always use the native interpreted invoke.
// Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
strategy = GetStrategyForUsingInterpreted();
}
else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
{
// Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
strategy = GetStrategyForUsingEmit();
}
else
{
Expand DownExpand Up@@ -69,6 +70,18 @@ internal static void Initialize(
}
}

internal static InvokerStrategy GetStrategyForUsingInterpreted()
{
// This causes the default strategy, which is interpreted, to always be used.
return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
}

private static InvokerStrategy GetStrategyForUsingEmit()
{
// This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
}

/// <summary>
/// Confirm member invocation has an instance and is of the correct type
/// </summary>
Expand Down
167 changes: 167 additions & 0 deletions src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Xunit;

namespace System.Reflection.Tests
{
/// <summary>
/// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
/// the abstract Invoke(...) methods below.
/// </summary>
public abstract class ConstructorCommonTests
{
public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);

protected abstract bool IsExceptionWrapped { get; }

/// <summary>
/// Invoke constructor on an existing instance. Should return null.
/// </summary>
public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);

public static ConstructorInfo[] GetConstructors(Type type)
{
return type.GetTypeInfo().DeclaredConstructors.ToArray();
}

[Fact]
public void SimpleInvoke()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Equal(3, constructors.Length);
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
Assert.NotNull(obj);
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
public void Invoke_StaticConstructor_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
Assert.Equal(1, constructors.Length);
Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_OneDimensionalArray()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = { 1, 2, 99, 65535 };

// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
object[] arr = (object[])Invoke(constructors[0], new object[] { length });
Assert.Equal(arr.Length, length);
}
}

[Fact]
public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = new int[] { -1, -2, -99 };
// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
if (IsExceptionWrapped)
{
Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
Assert.IsType<OverflowException>(ex.InnerException);
}
else
{
Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
}
}
}

[Fact]
public void Invoke_OneParameter()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
Assert.Equal(100, obj.intValue);
}

[Fact]
public void Invoke_TwoParameters()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
}

[Fact]
public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
}

[Fact]
public void Invoke_ParameterWrongType_ThrowsArgumentException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
}

[Fact]
public void Invoke_ExistingInstance()
{
// Should not produce a second object.
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
Assert.Null(obj2);
Assert.Equal(999, obj1.intValue);
Assert.Equal("initialized", obj1.stringValue);
}

[Fact]
public void Invoke_NullForObj()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
public void Invoke_AbstractClass_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_SubClass()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
ConstructorInfoDerived obj = null;
obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
Assert.NotNull(obj);
}

[Fact]
public void Invoke_Struct()
{
ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
StructWith1Constructor obj;
obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
Assert.Equal(1, obj.x);
Assert.Equal(2, obj.y);
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[release/8.0] Fix calling existing ctor with MethodInvoker; share tests with invokers by github-actions[bot] · Pull Request #90968 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4)

private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
_method.ThrowNoInvokeException();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method)
{
// This is useful for calling a constructor on an already-initialized object
// such as created from RuntimeHelpers.GetUninitializedObject(Type).
return new MethodInvoker(rci);
MethodInvoker invoker = new MethodInvoker(rci);

// Use the interpreted version to avoid having to generate a new method that doesn't allocate.
invoker._strategy = GetStrategyForUsingInterpreted();

return invoker;
}

throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
Expand DownExpand Up@@ -181,7 +186,7 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes)

private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
ThrowForBadInvocationFlags();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,13 +18,14 @@ internal static void Initialize(
{
if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
{
// Always use the native invoke; useful for testing.
strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
// Always use the native interpreted invoke.
// Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
strategy = GetStrategyForUsingInterpreted();
}
else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
{
// Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
strategy = GetStrategyForUsingEmit();
}
else
{
Expand DownExpand Up@@ -69,6 +70,18 @@ internal static void Initialize(
}
}

internal static InvokerStrategy GetStrategyForUsingInterpreted()
{
// This causes the default strategy, which is interpreted, to always be used.
return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
}

private static InvokerStrategy GetStrategyForUsingEmit()
{
// This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
}

/// <summary>
/// Confirm member invocation has an instance and is of the correct type
/// </summary>
Expand Down
167 changes: 167 additions & 0 deletions src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Xunit;

namespace System.Reflection.Tests
{
/// <summary>
/// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
/// the abstract Invoke(...) methods below.
/// </summary>
public abstract class ConstructorCommonTests
{
public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);

protected abstract bool IsExceptionWrapped { get; }

/// <summary>
/// Invoke constructor on an existing instance. Should return null.
/// </summary>
public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);

public static ConstructorInfo[] GetConstructors(Type type)
{
return type.GetTypeInfo().DeclaredConstructors.ToArray();
}

[Fact]
public void SimpleInvoke()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Equal(3, constructors.Length);
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
Assert.NotNull(obj);
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
public void Invoke_StaticConstructor_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
Assert.Equal(1, constructors.Length);
Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_OneDimensionalArray()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = { 1, 2, 99, 65535 };

// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
object[] arr = (object[])Invoke(constructors[0], new object[] { length });
Assert.Equal(arr.Length, length);
}
}

[Fact]
public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = new int[] { -1, -2, -99 };
// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
if (IsExceptionWrapped)
{
Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
Assert.IsType<OverflowException>(ex.InnerException);
}
else
{
Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
}
}
}

[Fact]
public void Invoke_OneParameter()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
Assert.Equal(100, obj.intValue);
}

[Fact]
public void Invoke_TwoParameters()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
}

[Fact]
public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
}

[Fact]
public void Invoke_ParameterWrongType_ThrowsArgumentException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
}

[Fact]
public void Invoke_ExistingInstance()
{
// Should not produce a second object.
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
Assert.Null(obj2);
Assert.Equal(999, obj1.intValue);
Assert.Equal("initialized", obj1.stringValue);
}

[Fact]
public void Invoke_NullForObj()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
public void Invoke_AbstractClass_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_SubClass()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
ConstructorInfoDerived obj = null;
obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
Assert.NotNull(obj);
}

[Fact]
public void Invoke_Struct()
{
ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
StructWith1Constructor obj;
obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
Assert.Equal(1, obj.x);
Assert.Equal(2, obj.y);
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [release/8.0] Fix calling existing ctor with MethodInvoker; share tests with invokers by github-actions[bot] · Pull Request #90968 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4)

private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
_method.ThrowNoInvokeException();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method)
{
// This is useful for calling a constructor on an already-initialized object
// such as created from RuntimeHelpers.GetUninitializedObject(Type).
return new MethodInvoker(rci);
MethodInvoker invoker = new MethodInvoker(rci);

// Use the interpreted version to avoid having to generate a new method that doesn't allocate.
invoker._strategy = GetStrategyForUsingInterpreted();

return invoker;
}

throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
Expand DownExpand Up@@ -181,7 +186,7 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes)

private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
ThrowForBadInvocationFlags();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,13 +18,14 @@ internal static void Initialize(
{
if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
{
// Always use the native invoke; useful for testing.
strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
// Always use the native interpreted invoke.
// Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
strategy = GetStrategyForUsingInterpreted();
}
else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
{
// Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
strategy = GetStrategyForUsingEmit();
}
else
{
Expand DownExpand Up@@ -69,6 +70,18 @@ internal static void Initialize(
}
}

internal static InvokerStrategy GetStrategyForUsingInterpreted()
{
// This causes the default strategy, which is interpreted, to always be used.
return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
}

private static InvokerStrategy GetStrategyForUsingEmit()
{
// This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
}

/// <summary>
/// Confirm member invocation has an instance and is of the correct type
/// </summary>
Expand Down
167 changes: 167 additions & 0 deletions src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Xunit;

namespace System.Reflection.Tests
{
/// <summary>
/// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
/// the abstract Invoke(...) methods below.
/// </summary>
public abstract class ConstructorCommonTests
{
public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);

protected abstract bool IsExceptionWrapped { get; }

/// <summary>
/// Invoke constructor on an existing instance. Should return null.
/// </summary>
public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);

public static ConstructorInfo[] GetConstructors(Type type)
{
return type.GetTypeInfo().DeclaredConstructors.ToArray();
}

[Fact]
public void SimpleInvoke()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Equal(3, constructors.Length);
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
Assert.NotNull(obj);
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
public void Invoke_StaticConstructor_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
Assert.Equal(1, constructors.Length);
Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_OneDimensionalArray()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = { 1, 2, 99, 65535 };

// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
object[] arr = (object[])Invoke(constructors[0], new object[] { length });
Assert.Equal(arr.Length, length);
}
}

[Fact]
public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = new int[] { -1, -2, -99 };
// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
if (IsExceptionWrapped)
{
Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
Assert.IsType<OverflowException>(ex.InnerException);
}
else
{
Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
}
}
}

[Fact]
public void Invoke_OneParameter()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
Assert.Equal(100, obj.intValue);
}

[Fact]
public void Invoke_TwoParameters()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
}

[Fact]
public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
}

[Fact]
public void Invoke_ParameterWrongType_ThrowsArgumentException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
}

[Fact]
public void Invoke_ExistingInstance()
{
// Should not produce a second object.
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
Assert.Null(obj2);
Assert.Equal(999, obj1.intValue);
Assert.Equal("initialized", obj1.stringValue);
}

[Fact]
public void Invoke_NullForObj()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
public void Invoke_AbstractClass_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_SubClass()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
ConstructorInfoDerived obj = null;
obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
Assert.NotNull(obj);
}

[Fact]
public void Invoke_Struct()
{
ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
StructWith1Constructor obj;
obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
Assert.Equal(1, obj.x);
Assert.Equal(2, obj.y);
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [release/8.0] Fix calling existing ctor with MethodInvoker; share tests with invokers by github-actions[bot] · Pull Request #90968 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4)

private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
_method.ThrowNoInvokeException();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method)
{
// This is useful for calling a constructor on an already-initialized object
// such as created from RuntimeHelpers.GetUninitializedObject(Type).
return new MethodInvoker(rci);
MethodInvoker invoker = new MethodInvoker(rci);

// Use the interpreted version to avoid having to generate a new method that doesn't allocate.
invoker._strategy = GetStrategyForUsingInterpreted();

return invoker;
}

throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
Expand DownExpand Up@@ -181,7 +186,7 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes)

private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
ThrowForBadInvocationFlags();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,13 +18,14 @@ internal static void Initialize(
{
if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
{
// Always use the native invoke; useful for testing.
strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
// Always use the native interpreted invoke.
// Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
strategy = GetStrategyForUsingInterpreted();
}
else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
{
// Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
strategy = GetStrategyForUsingEmit();
}
else
{
Expand DownExpand Up@@ -69,6 +70,18 @@ internal static void Initialize(
}
}

internal static InvokerStrategy GetStrategyForUsingInterpreted()
{
// This causes the default strategy, which is interpreted, to always be used.
return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
}

private static InvokerStrategy GetStrategyForUsingEmit()
{
// This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
}

/// <summary>
/// Confirm member invocation has an instance and is of the correct type
/// </summary>
Expand Down
167 changes: 167 additions & 0 deletions src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Xunit;

namespace System.Reflection.Tests
{
/// <summary>
/// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
/// the abstract Invoke(...) methods below.
/// </summary>
public abstract class ConstructorCommonTests
{
public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);

protected abstract bool IsExceptionWrapped { get; }

/// <summary>
/// Invoke constructor on an existing instance. Should return null.
/// </summary>
public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);

public static ConstructorInfo[] GetConstructors(Type type)
{
return type.GetTypeInfo().DeclaredConstructors.ToArray();
}

[Fact]
public void SimpleInvoke()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Equal(3, constructors.Length);
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
Assert.NotNull(obj);
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
public void Invoke_StaticConstructor_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
Assert.Equal(1, constructors.Length);
Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_OneDimensionalArray()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = { 1, 2, 99, 65535 };

// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
object[] arr = (object[])Invoke(constructors[0], new object[] { length });
Assert.Equal(arr.Length, length);
}
}

[Fact]
public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = new int[] { -1, -2, -99 };
// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
if (IsExceptionWrapped)
{
Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
Assert.IsType<OverflowException>(ex.InnerException);
}
else
{
Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
}
}
}

[Fact]
public void Invoke_OneParameter()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
Assert.Equal(100, obj.intValue);
}

[Fact]
public void Invoke_TwoParameters()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
}

[Fact]
public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
}

[Fact]
public void Invoke_ParameterWrongType_ThrowsArgumentException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
}

[Fact]
public void Invoke_ExistingInstance()
{
// Should not produce a second object.
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
Assert.Null(obj2);
Assert.Equal(999, obj1.intValue);
Assert.Equal("initialized", obj1.stringValue);
}

[Fact]
public void Invoke_NullForObj()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
public void Invoke_AbstractClass_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_SubClass()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
ConstructorInfoDerived obj = null;
obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
Assert.NotNull(obj);
}

[Fact]
public void Invoke_Struct()
{
ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
StructWith1Constructor obj;
obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
Assert.Equal(1, obj.x);
Assert.Equal(2, obj.y);
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [release/8.0] Fix calling existing ctor with MethodInvoker; share tests with invokers by github-actions[bot] · Pull Request #90968 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4)

private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
_method.ThrowNoInvokeException();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method)
{
// This is useful for calling a constructor on an already-initialized object
// such as created from RuntimeHelpers.GetUninitializedObject(Type).
return new MethodInvoker(rci);
MethodInvoker invoker = new MethodInvoker(rci);

// Use the interpreted version to avoid having to generate a new method that doesn't allocate.
invoker._strategy = GetStrategyForUsingInterpreted();

return invoker;
}

throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
Expand DownExpand Up@@ -181,7 +186,7 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes)

private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
ThrowForBadInvocationFlags();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,13 +18,14 @@ internal static void Initialize(
{
if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
{
// Always use the native invoke; useful for testing.
strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
// Always use the native interpreted invoke.
// Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
strategy = GetStrategyForUsingInterpreted();
}
else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
{
// Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
strategy = GetStrategyForUsingEmit();
}
else
{
Expand DownExpand Up@@ -69,6 +70,18 @@ internal static void Initialize(
}
}

internal static InvokerStrategy GetStrategyForUsingInterpreted()
{
// This causes the default strategy, which is interpreted, to always be used.
return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
}

private static InvokerStrategy GetStrategyForUsingEmit()
{
// This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
}

/// <summary>
/// Confirm member invocation has an instance and is of the correct type
/// </summary>
Expand Down
167 changes: 167 additions & 0 deletions src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Xunit;

namespace System.Reflection.Tests
{
/// <summary>
/// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
/// the abstract Invoke(...) methods below.
/// </summary>
public abstract class ConstructorCommonTests
{
public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);

protected abstract bool IsExceptionWrapped { get; }

/// <summary>
/// Invoke constructor on an existing instance. Should return null.
/// </summary>
public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);

public static ConstructorInfo[] GetConstructors(Type type)
{
return type.GetTypeInfo().DeclaredConstructors.ToArray();
}

[Fact]
public void SimpleInvoke()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Equal(3, constructors.Length);
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
Assert.NotNull(obj);
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
public void Invoke_StaticConstructor_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
Assert.Equal(1, constructors.Length);
Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_OneDimensionalArray()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = { 1, 2, 99, 65535 };

// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
object[] arr = (object[])Invoke(constructors[0], new object[] { length });
Assert.Equal(arr.Length, length);
}
}

[Fact]
public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = new int[] { -1, -2, -99 };
// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
if (IsExceptionWrapped)
{
Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
Assert.IsType<OverflowException>(ex.InnerException);
}
else
{
Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
}
}
}

[Fact]
public void Invoke_OneParameter()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
Assert.Equal(100, obj.intValue);
}

[Fact]
public void Invoke_TwoParameters()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
}

[Fact]
public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
}

[Fact]
public void Invoke_ParameterWrongType_ThrowsArgumentException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
}

[Fact]
public void Invoke_ExistingInstance()
{
// Should not produce a second object.
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
Assert.Null(obj2);
Assert.Equal(999, obj1.intValue);
Assert.Equal("initialized", obj1.stringValue);
}

[Fact]
public void Invoke_NullForObj()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
public void Invoke_AbstractClass_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_SubClass()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
ConstructorInfoDerived obj = null;
obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
Assert.NotNull(obj);
}

[Fact]
public void Invoke_Struct()
{
ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
StructWith1Constructor obj;
obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
Assert.Equal(1, obj.x);
Assert.Equal(2, obj.y);
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [release/8.0] Fix calling existing ctor with MethodInvoker; share tests with invokers by github-actions[bot] · Pull Request #90968 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4)

private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
_method.ThrowNoInvokeException();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method)
{
// This is useful for calling a constructor on an already-initialized object
// such as created from RuntimeHelpers.GetUninitializedObject(Type).
return new MethodInvoker(rci);
MethodInvoker invoker = new MethodInvoker(rci);

// Use the interpreted version to avoid having to generate a new method that doesn't allocate.
invoker._strategy = GetStrategyForUsingInterpreted();

return invoker;
}

throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
Expand DownExpand Up@@ -181,7 +186,7 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes)

private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
ThrowForBadInvocationFlags();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,13 +18,14 @@ internal static void Initialize(
{
if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
{
// Always use the native invoke; useful for testing.
strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
// Always use the native interpreted invoke.
// Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
strategy = GetStrategyForUsingInterpreted();
}
else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
{
// Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
strategy = GetStrategyForUsingEmit();
}
else
{
Expand DownExpand Up@@ -69,6 +70,18 @@ internal static void Initialize(
}
}

internal static InvokerStrategy GetStrategyForUsingInterpreted()
{
// This causes the default strategy, which is interpreted, to always be used.
return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
}

private static InvokerStrategy GetStrategyForUsingEmit()
{
// This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
}

/// <summary>
/// Confirm member invocation has an instance and is of the correct type
/// </summary>
Expand Down
167 changes: 167 additions & 0 deletions src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Xunit;

namespace System.Reflection.Tests
{
/// <summary>
/// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
/// the abstract Invoke(...) methods below.
/// </summary>
public abstract class ConstructorCommonTests
{
public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);

protected abstract bool IsExceptionWrapped { get; }

/// <summary>
/// Invoke constructor on an existing instance. Should return null.
/// </summary>
public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);

public static ConstructorInfo[] GetConstructors(Type type)
{
return type.GetTypeInfo().DeclaredConstructors.ToArray();
}

[Fact]
public void SimpleInvoke()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Equal(3, constructors.Length);
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
Assert.NotNull(obj);
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
public void Invoke_StaticConstructor_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
Assert.Equal(1, constructors.Length);
Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_OneDimensionalArray()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = { 1, 2, 99, 65535 };

// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
object[] arr = (object[])Invoke(constructors[0], new object[] { length });
Assert.Equal(arr.Length, length);
}
}

[Fact]
public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = new int[] { -1, -2, -99 };
// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
if (IsExceptionWrapped)
{
Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
Assert.IsType<OverflowException>(ex.InnerException);
}
else
{
Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
}
}
}

[Fact]
public void Invoke_OneParameter()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
Assert.Equal(100, obj.intValue);
}

[Fact]
public void Invoke_TwoParameters()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
}

[Fact]
public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
}

[Fact]
public void Invoke_ParameterWrongType_ThrowsArgumentException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
}

[Fact]
public void Invoke_ExistingInstance()
{
// Should not produce a second object.
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
Assert.Null(obj2);
Assert.Equal(999, obj1.intValue);
Assert.Equal("initialized", obj1.stringValue);
}

[Fact]
public void Invoke_NullForObj()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
public void Invoke_AbstractClass_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_SubClass()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
ConstructorInfoDerived obj = null;
obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
Assert.NotNull(obj);
}

[Fact]
public void Invoke_Struct()
{
ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
StructWith1Constructor obj;
obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
Assert.Equal(1, obj.x);
Assert.Equal(2, obj.y);
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [release/8.0] Fix calling existing ctor with MethodInvoker; share tests with invokers by github-actions[bot] · Pull Request #90968 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4)

private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
_method.ThrowNoInvokeException();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method)
{
// This is useful for calling a constructor on an already-initialized object
// such as created from RuntimeHelpers.GetUninitializedObject(Type).
return new MethodInvoker(rci);
MethodInvoker invoker = new MethodInvoker(rci);

// Use the interpreted version to avoid having to generate a new method that doesn't allocate.
invoker._strategy = GetStrategyForUsingInterpreted();

return invoker;
}

throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
Expand DownExpand Up@@ -181,7 +186,7 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes)

private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
ThrowForBadInvocationFlags();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,13 +18,14 @@ internal static void Initialize(
{
if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
{
// Always use the native invoke; useful for testing.
strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
// Always use the native interpreted invoke.
// Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
strategy = GetStrategyForUsingInterpreted();
}
else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
{
// Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
strategy = GetStrategyForUsingEmit();
}
else
{
Expand DownExpand Up@@ -69,6 +70,18 @@ internal static void Initialize(
}
}

internal static InvokerStrategy GetStrategyForUsingInterpreted()
{
// This causes the default strategy, which is interpreted, to always be used.
return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
}

private static InvokerStrategy GetStrategyForUsingEmit()
{
// This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
}

/// <summary>
/// Confirm member invocation has an instance and is of the correct type
/// </summary>
Expand Down
167 changes: 167 additions & 0 deletions src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Xunit;

namespace System.Reflection.Tests
{
/// <summary>
/// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
/// the abstract Invoke(...) methods below.
/// </summary>
public abstract class ConstructorCommonTests
{
public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);

protected abstract bool IsExceptionWrapped { get; }

/// <summary>
/// Invoke constructor on an existing instance. Should return null.
/// </summary>
public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);

public static ConstructorInfo[] GetConstructors(Type type)
{
return type.GetTypeInfo().DeclaredConstructors.ToArray();
}

[Fact]
public void SimpleInvoke()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Equal(3, constructors.Length);
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
Assert.NotNull(obj);
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
public void Invoke_StaticConstructor_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
Assert.Equal(1, constructors.Length);
Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_OneDimensionalArray()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = { 1, 2, 99, 65535 };

// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
object[] arr = (object[])Invoke(constructors[0], new object[] { length });
Assert.Equal(arr.Length, length);
}
}

[Fact]
public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = new int[] { -1, -2, -99 };
// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
if (IsExceptionWrapped)
{
Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
Assert.IsType<OverflowException>(ex.InnerException);
}
else
{
Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
}
}
}

[Fact]
public void Invoke_OneParameter()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
Assert.Equal(100, obj.intValue);
}

[Fact]
public void Invoke_TwoParameters()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
}

[Fact]
public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
}

[Fact]
public void Invoke_ParameterWrongType_ThrowsArgumentException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
}

[Fact]
public void Invoke_ExistingInstance()
{
// Should not produce a second object.
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
Assert.Null(obj2);
Assert.Equal(999, obj1.intValue);
Assert.Equal("initialized", obj1.stringValue);
}

[Fact]
public void Invoke_NullForObj()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
public void Invoke_AbstractClass_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_SubClass()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
ConstructorInfoDerived obj = null;
obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
Assert.NotNull(obj);
}

[Fact]
public void Invoke_Struct()
{
ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
StructWith1Constructor obj;
obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
Assert.Equal(1, obj.x);
Assert.Equal(2, obj.y);
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [release/8.0] Fix calling existing ctor with MethodInvoker; share tests with invokers by github-actions[bot] · Pull Request #90968 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,7 +155,7 @@ public object Invoke(object? arg1, object? arg2, object? arg3, object? arg4)

private object InvokeImpl(object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
_method.ThrowNoInvokeException();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,7 +66,12 @@ public static MethodInvoker Create(MethodBase method)
{
// This is useful for calling a constructor on an already-initialized object
// such as created from RuntimeHelpers.GetUninitializedObject(Type).
return new MethodInvoker(rci);
MethodInvoker invoker = new MethodInvoker(rci);

// Use the interpreted version to avoid having to generate a new method that doesn't allocate.
invoker._strategy = GetStrategyForUsingInterpreted();

return invoker;
}

throw new ArgumentException(SR.Argument_MustBeRuntimeMethod, nameof(method));
Expand DownExpand Up@@ -181,7 +186,7 @@ private MethodInvoker(MethodBase method, RuntimeType[] argumentTypes)

private object? InvokeImpl(object? obj, object? arg1, object? arg2, object? arg3, object? arg4)
{
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
if ((_invocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0)
{
ThrowForBadInvocationFlags();
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,13 +18,14 @@ internal static void Initialize(
{
if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
{
// Always use the native invoke; useful for testing.
strategy = InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
// Always use the native interpreted invoke.
// Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
strategy = GetStrategyForUsingInterpreted();
}
else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
{
// Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
strategy = InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
strategy = GetStrategyForUsingEmit();
}
else
{
Expand DownExpand Up@@ -69,6 +70,18 @@ internal static void Initialize(
}
}

internal static InvokerStrategy GetStrategyForUsingInterpreted()
{
// This causes the default strategy, which is interpreted, to always be used.
return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
}

private static InvokerStrategy GetStrategyForUsingEmit()
{
// This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
}

/// <summary>
/// Confirm member invocation has an instance and is of the correct type
/// </summary>
Expand Down
167 changes: 167 additions & 0 deletions src/libraries/System.Reflection/tests/ConstructorCommonTests.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Linq;
using Xunit;

namespace System.Reflection.Tests
{
/// <summary>
/// These tests are shared with ConstructorInfo.Invoke and ConstructorInvoker.Invoke by using
/// the abstract Invoke(...) methods below.
/// </summary>
public abstract class ConstructorCommonTests
{
public abstract object Invoke(ConstructorInfo constructorInfo, object?[]? parameters);

protected abstract bool IsExceptionWrapped { get; }

/// <summary>
/// Invoke constructor on an existing instance. Should return null.
/// </summary>
public abstract object? Invoke(ConstructorInfo constructorInfo, object obj, object?[]? parameters);

public static ConstructorInfo[] GetConstructors(Type type)
{
return type.GetTypeInfo().DeclaredConstructors.ToArray();
}

[Fact]
public void SimpleInvoke()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Equal(3, constructors.Length);
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[0], null);
Assert.NotNull(obj);
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15024", TestRuntimes.Mono)]
public void Invoke_StaticConstructor_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWithStaticConstructor));
Assert.Equal(1, constructors.Length);
Assert.Throws<MemberAccessException>(() => Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_OneDimensionalArray()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = { 1, 2, 99, 65535 };

// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
object[] arr = (object[])Invoke(constructors[0], new object[] { length });
Assert.Equal(arr.Length, length);
}
}

[Fact]
public void Invoke_OneDimensionalArray_NegativeLengths_ThrowsOverflowException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(object[]));
int[] arraylength = new int[] { -1, -2, -99 };
// Try to invoke Array ctors with different lengths
foreach (int length in arraylength)
{
// Create big Array with elements
if (IsExceptionWrapped)
{
Exception ex = Assert.Throws<TargetInvocationException>(() => Invoke(constructors[0], new object[] { length }));
Assert.IsType<OverflowException>(ex.InnerException);
}
else
{
Assert.Throws<OverflowException>(() => Invoke(constructors[0], new object[] { length }));
}
}
}

[Fact]
public void Invoke_OneParameter()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[1], new object[] { 100 });
Assert.Equal(100, obj.intValue);
}

[Fact]
public void Invoke_TwoParameters()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj = (ClassWith3Constructors)Invoke(constructors[2], new object[] { 101, "hello" });
Assert.Equal(101, obj.intValue);
Assert.Equal("hello", obj.stringValue);
}

[Fact]
public void Invoke_NoParameters_ThowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => Invoke(constructors[2], new object[0]));
}

[Fact]
public void Invoke_ParameterMismatch_ThrowsTargetParameterCountException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetParameterCountException>(() => (ClassWith3Constructors)Invoke(constructors[2], new object[] { 121 }));
}

[Fact]
public void Invoke_ParameterWrongType_ThrowsArgumentException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
AssertExtensions.Throws<ArgumentException>(null, () => (ClassWith3Constructors)Invoke(constructors[1], new object[] { "hello" }));
}

[Fact]
public void Invoke_ExistingInstance()
{
// Should not produce a second object.
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
ClassWith3Constructors obj1 = new ClassWith3Constructors(100, "hello");
ClassWith3Constructors obj2 = (ClassWith3Constructors)Invoke(constructors[2], obj1, new object[] { 999, "initialized" });
Assert.Null(obj2);
Assert.Equal(999, obj1.intValue);
Assert.Equal("initialized", obj1.stringValue);
}

[Fact]
public void Invoke_NullForObj()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ClassWith3Constructors));
Assert.Throws<TargetException>(() => Invoke(constructors[2], obj: null, new object[] { 999, "initialized" }));
}

[Fact]
[ActiveIssue("https://github.com/mono/mono/issues/15026", TestRuntimes.Mono)]
public void Invoke_AbstractClass_ThrowsMemberAccessException()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoAbstractBase));
Assert.Throws<MemberAccessException>(() => (ConstructorInfoAbstractBase)Invoke(constructors[0], new object[0]));
}

[Fact]
public void Invoke_SubClass()
{
ConstructorInfo[] constructors = GetConstructors(typeof(ConstructorInfoDerived));
ConstructorInfoDerived obj = null;
obj = (ConstructorInfoDerived)Invoke(constructors[0], new object[] { });
Assert.NotNull(obj);
}

[Fact]
public void Invoke_Struct()
{
ConstructorInfo[] constructors = GetConstructors(typeof(StructWith1Constructor));
StructWith1Constructor obj;
obj = (StructWith1Constructor)Invoke(constructors[0], new object[] { 1, 2 });
Assert.Equal(1, obj.x);
Assert.Equal(2, obj.y);
}
}
}
Loading