Skip to content

Commit

Permalink
Merge pull request #1121 from tlakollo/RunClassConstructor
Browse files Browse the repository at this point in the history
Support RunClassConstructor in the linker
  • Loading branch information
tlakollo authored Apr 22, 2020
2 parents 55d2ccf + b11f4f0 commit 17b3c01
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 2 deletions.
46 changes: 45 additions & 1 deletion src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ enum IntrinsicId
None = 0,
IntrospectionExtensions_GetTypeInfo,
Type_GetTypeFromHandle,
Type_get_TypeHandle,

// Anything above this marker will require the method to be run through
// the reflection body scanner.
Expand Down Expand Up @@ -183,7 +184,8 @@ enum IntrinsicId
RuntimeReflectionExtensions_GetRuntimeEvent,
RuntimeReflectionExtensions_GetRuntimeField,
RuntimeReflectionExtensions_GetRuntimeMethod,
RuntimeReflectionExtensions_GetRuntimeProperty
RuntimeReflectionExtensions_GetRuntimeProperty,
RuntimeHelpers_RunClassConstructor
}

static IntrinsicId GetIntrinsicIdForMethod (MethodDefinition calledMethod)
Expand All @@ -196,6 +198,9 @@ static IntrinsicId GetIntrinsicIdForMethod (MethodDefinition calledMethod)
// System.Type.GetTypeInfo (Type type)
"GetTypeFromHandle" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_GetTypeFromHandle,

// System.Type.GetTypeHandle (Type type)
"get_TypeHandle" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_get_TypeHandle,

// static System.Type.MakeGenericType (Type [] typeArguments)
"MakeGenericType" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_MakeGenericType,

Expand Down Expand Up @@ -379,6 +384,11 @@ static IntrinsicId GetIntrinsicIdForMethod (MethodDefinition calledMethod)
&& calledMethod.HasParameterOfType (0, "System", "String")
=> IntrinsicId.Assembly_CreateInstance,

// System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type)
"RunClassConstructor" when calledMethod.IsDeclaredOnType ("System.Runtime.CompilerServices", "RuntimeHelpers")
&& calledMethod.HasParameterOfType (0, "System", "RuntimeTypeHandle")
=> IntrinsicId.RuntimeHelpers_RunClassConstructor,

_ => IntrinsicId.None,
};
}
Expand Down Expand Up @@ -418,6 +428,18 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
}
break;

case IntrinsicId.Type_get_TypeHandle: {
foreach (var value in methodParams [0].UniqueValues()) {
if (value is SystemTypeValue typeValue)
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new RuntimeTypeHandleValue (typeValue.TypeRepresented));
else if (value == NullValue.Instance)
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, value);
else
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, UnknownValue.Instance);
}
}
break;

case IntrinsicId.Type_MakeGenericType: {
// Don't care about the actual arguments, but we don't want to lose track of the type
// in case this is e.g. Activator.CreateInstance(typeof(Foo<>).MakeGenericType(...));
Expand Down Expand Up @@ -926,6 +948,28 @@ methodParams [argsParam] is ArrayValue arrayValue &&
reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' is not yet supported");
break;

//
// System.Runtime.CompilerServices.RuntimeHelpers
//
// RunClassConstructor (RuntimeTypeHandle type)
//
case IntrinsicId.RuntimeHelpers_RunClassConstructor: {
reflectionContext.AnalyzingPattern ();
foreach (var typeHandleValue in methodParams [0].UniqueValues ()) {
if (typeHandleValue is RuntimeTypeHandleValue runtimeTypeHandleValue) {
_markStep.MarkStaticConstructor (runtimeTypeHandleValue.TypeRepresented, new DependencyInfo (DependencyKind.AccessedViaReflection, reflectionContext.MethodCalling));
reflectionContext.RecordHandledPattern ();
} else if (typeHandleValue == NullValue.Instance)
reflectionContext.RecordHandledPattern ();
else {
reflectionContext.RecordUnrecognizedPattern ($"A {GetValueDescriptionForErrorMessage (typeHandleValue)} " +
$"is passed into the {GetMetadataTokenDescriptionForErrorMessage (reflectionContext.MethodCalled.Parameters[0])}. " +
$"It's not possible to guarantee availability of the target static constructor.");
}
}
}
break;

default:
if (requiresDataFlowAnalysis) {
reflectionContext.AnalyzingPattern ();
Expand Down
2 changes: 1 addition & 1 deletion src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ protected virtual bool ShouldMarkTypeStaticConstructor (TypeDefinition type)
return true;
}

protected void MarkStaticConstructor (TypeDefinition type, in DependencyInfo reason)
internal protected void MarkStaticConstructor (TypeDefinition type, in DependencyInfo reason)
{
if (MarkMethodIf (type.Methods, IsNonEmptyStaticConstructor, reason) != null)
Annotations.SetPreservedStaticCtor (type);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using System;
using System.Reflection;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
using System.Runtime.CompilerServices;

namespace Mono.Linker.Tests.Cases.Reflection
{
[SetupCSharpCompilerToUse ("csc")]
public class RunClassConstructorUsedViaReflection
{
public static void Main ()
{
TestRunClassConstructor ();
TestNonKeptStaticConstructor ();
TestNull ();
TestDataFlowType ();
TestIfElseUsingRuntimeTypeHandle (1);
TestIfElseUsingType (1);
}

[Kept]
[RecognizedReflectionAccessPattern]
static void TestRunClassConstructor ()
{
RuntimeHelpers.RunClassConstructor (typeof (OnlyUsedViaReflection).TypeHandle);
}

[Kept]
static void TestNonKeptStaticConstructor ()
{
var a = new NonKeptStaticConstructorClass();
}

[Kept]
[RecognizedReflectionAccessPattern]
static void TestNull ()
{
Type type = null;
RuntimeHelpers.RunClassConstructor (type.TypeHandle);
}

[Kept]
static Type FindType ()
{
return null;
}

[Kept]
[UnrecognizedReflectionAccessPattern (typeof (RuntimeHelpers), nameof (RuntimeHelpers.RunClassConstructor), new Type [] { typeof (RuntimeTypeHandle) },
"A value from unknown source is passed into the parameter 'type' of method "+
"'System.Void System.Runtime.CompilerServices.RuntimeHelpers::RunClassConstructor(System.RuntimeTypeHandle)'. "+
"It's not possible to guarantee availability of the target static constructor.")]

static void TestDataFlowType ()
{
Type type = FindType ();
RuntimeHelpers.RunClassConstructor (type.TypeHandle);
}

[Kept]
[RecognizedReflectionAccessPattern]
[UnrecognizedReflectionAccessPattern (typeof (RuntimeHelpers), nameof (RuntimeHelpers.RunClassConstructor), new Type [] { typeof (RuntimeTypeHandle) },
"A value from unknown source is passed into the parameter 'type' of method "+
"'System.Void System.Runtime.CompilerServices.RuntimeHelpers::RunClassConstructor(System.RuntimeTypeHandle)'. "+
"It's not possible to guarantee availability of the target static constructor.")]

static void TestIfElseUsingRuntimeTypeHandle (int i)
{
RuntimeTypeHandle myType;
if (i == 1) {
myType = typeof (IfClass).TypeHandle;
} else if (i == 2) {
myType = FindType ().TypeHandle;
} else {
myType = typeof (ElseClass).TypeHandle;
}
RuntimeHelpers.RunClassConstructor (myType);
}

[Kept]
[RecognizedReflectionAccessPattern]
static void TestIfElseUsingType (int i)
{
Type myType;
if (i == 1) {
myType = typeof (IfClass2);
}else if (i==2) {
myType = null;
}
else {
myType = typeof (ElseClass2);
}
RuntimeHelpers.RunClassConstructor (myType.TypeHandle);
}

[Kept]
[KeptMember (".cctor()")]
class OnlyUsedViaReflection
{
[Kept]
static int i = 5;
}

[Kept]
[KeptMember (".ctor()")]
class NonKeptStaticConstructorClass
{
static int i = 5;
}

[Kept]
[KeptMember (".cctor()")]
class IfClass
{
public IfClass ()
{ }
private IfClass (int foo)
{ }
}

[Kept]
[KeptMember (".cctor()")]
class ElseClass
{
[Kept]
static ElseClass ()
{ }
public ElseClass (int foo)
{ }
}
[Kept]
[KeptMember (".cctor()")]
class IfClass2
{
public IfClass2 ()
{ }
private IfClass2 (int foo)
{ }
}

[Kept]
[KeptMember (".cctor()")]
class ElseClass2
{
[Kept]
static ElseClass2 ()
{ }
public ElseClass2 (int foo)
{ }
}
}
}

0 comments on commit 17b3c01

Please sign in to comment.