Skip to content

Commit b7eb828

Browse files
authored
Allow initonly field during ldflda and stfld import (dotnet#57385)
1 parent a53ec50 commit b7eb828

File tree

6 files changed

+86
-41
lines changed

6 files changed

+86
-41
lines changed

src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur
711711
// Default to managed since in the modopt case we need to differentiate explicitly using a calling convention that matches the default
712712
// and not specifying a calling convention at all and using the implicit default case in P/Invoke stub inlining.
713713
callConv = CorInfoCallConvExtension.Managed;
714-
if (!signature.HasEmbeddedSignatureData || signature.GetEmbeddedSignatureData() == null)
714+
if (!signature.HasEmbeddedSignatureData)
715715
return false;
716716

717717
bool found = false;

src/coreclr/tools/ILVerification/ILImporter.Verify.cs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,9 @@ private void FindEnclosingExceptionRegions()
267267
}
268268
}
269269
// Check if offset is within the range [FilterOffset, HandlerOffset[
270-
if (r.FilterOffset != -1 && r.FilterOffset <= offset && offset < r.HandlerOffset )
270+
if (r.FilterOffset != -1 && r.FilterOffset <= offset && offset < r.HandlerOffset)
271271
{
272-
if(!basicBlock.FilterIndex.HasValue)
272+
if (!basicBlock.FilterIndex.HasValue)
273273
{
274274
basicBlock.FilterIndex = j;
275275
}
@@ -301,7 +301,7 @@ private void InitialPass()
301301
ILOpcode opCode = (ILOpcode)ReadILByte();
302302

303303
previousWasPrefix = false;
304-
again:
304+
again:
305305
switch (opCode)
306306
{
307307
// Check this pointer modification
@@ -1601,7 +1601,7 @@ void ImportCall(ILOpcode opcode, int token)
16011601
}
16021602

16031603
// To support direct calls on readonly byrefs, just pretend declaredThis is readonly too
1604-
if(declaredThis.Kind == StackValueKind.ByRef && (actualThis.Kind == StackValueKind.ByRef && actualThis.IsReadOnly))
1604+
if (declaredThis.Kind == StackValueKind.ByRef && (actualThis.Kind == StackValueKind.ByRef && actualThis.IsReadOnly))
16051605
{
16061606
declaredThis.SetIsReadOnly();
16071607
}
@@ -2107,8 +2107,9 @@ void ImportAddressOfField(int token, bool isStatic)
21072107
isPermanentHome = actualThis.Kind == StackValueKind.ObjRef || actualThis.IsPermanentHome;
21082108
instance = actualThis.Type;
21092109

2110-
if (field.IsInitOnly)
2111-
Check(_method.IsConstructor && field.OwningType == _method.OwningType && actualThis.IsThisPtr, VerifierError.InitOnly);
2110+
// TODO: verification of readonly references https://github.com/dotnet/runtime/issues/57444
2111+
// if (field.IsInitOnly)
2112+
// Check(_method.IsConstructor && field.OwningType == _method.OwningType && actualThis.IsThisPtr, VerifierError.InitOnly);
21122113
}
21132114

21142115
Check(_method.OwningType.CanAccess(field, instance), VerifierError.FieldAccess);
@@ -2122,10 +2123,9 @@ void ImportStoreField(int token, bool isStatic)
21222123
ClearPendingPrefix(Prefix.Volatile);
21232124

21242125
var value = Pop();
2125-
21262126
var field = ResolveFieldToken(token);
2127-
21282127
TypeDesc instance;
2128+
21292129
if (isStatic)
21302130
{
21312131
Check(field.IsStatic, VerifierError.ExpectedStaticField);
@@ -2154,7 +2154,8 @@ void ImportStoreField(int token, bool isStatic)
21542154
instance = actualThis.Type;
21552155

21562156
if (field.IsInitOnly)
2157-
Check(_method.IsConstructor && field.OwningType == _method.OwningType && actualThis.IsThisPtr, VerifierError.InitOnly);
2157+
Check(field.OwningType == _method.OwningType && actualThis.IsThisPtr &&
2158+
(_method.IsConstructor || HasIsExternalInit(_method.Signature)), VerifierError.InitOnly);
21582159
}
21592160

21602161
// Check any constraints on the fields' class --- accessing the field might cause a class constructor to run.
@@ -2647,6 +2648,21 @@ void CheckPendingPrefix(Prefix mask)
26472648
Check((mask & Prefix.Constrained) == 0, VerifierError.Constrained);
26482649
}
26492650

2651+
static bool HasIsExternalInit(MethodSignature signature)
2652+
{
2653+
if (signature.HasEmbeddedSignatureData)
2654+
{
2655+
foreach (var data in signature.GetEmbeddedSignatureData())
2656+
{
2657+
if (data.type is MetadataType mdType && mdType.Namespace == "System.Runtime.CompilerServices" && mdType.Name == "IsExternalInit" &&
2658+
data.index == MethodSignature.IndexOfCustomModifiersOnReturnType)
2659+
return true;
2660+
}
2661+
}
2662+
2663+
return false;
2664+
}
2665+
26502666
bool HasPendingPrefix(Prefix prefix)
26512667
{
26522668
return (_pendingPrefix & prefix) != 0;

src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/SignatureTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ public SignatureTests(ITestOutputHelper output)
3636

3737
private static string GetModOptMethodSignatureInfo(MethodSignature signature)
3838
{
39-
if (!signature.HasEmbeddedSignatureData || signature.GetEmbeddedSignatureData() == null)
40-
return "";
39+
if (!signature.HasEmbeddedSignatureData)
40+
return string.Empty;
4141

4242
StringBuilder sb = new StringBuilder();
4343
foreach (EmbeddedSignatureData data in signature.GetEmbeddedSignatureData())

src/tests/ilverify/ILTests/FieldTests.il

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@
2828
.field public initonly int32 InstanceInitonlyField
2929
.field public static initonly int32 StaticInitonlyField
3030

31+
.field private initonly int32 InstanceExternalInitField
32+
33+
.method public hidebysig specialname instance void modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) 'special.StoreExternalInitField.set_MyProperty_Valid'(int32 'value') cil managed { ret }
34+
.method public hidebysig specialname instance void modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) set_MyProperty(int32 'value') cil managed
35+
{
36+
ldarg.0
37+
ldarg.1
38+
stfld int32 FieldTestsType::InstanceExternalInitField
39+
ret
40+
}
41+
3142
.method public instance void Stsfld.UnsatisfiedParentConstraints_Invalid_UnsatisfiedFieldParentInst() cil managed
3243
{
3344
ldc.i4.0
@@ -43,7 +54,8 @@
4354
ret
4455
}
4556

46-
.method public instance void Ldflda.InitonlyFieldOutsideCtor_Invalid_InitOnly() cil managed
57+
// TODO: verification of readonly references https://github.com/dotnet/runtime/issues/57444
58+
.method public instance void Ldflda.InitonlyFieldOutsideCtor_Valid() cil managed
4759
{
4860
ldarg.0
4961
ldflda int32 FieldTestsType::InstanceInitonlyField
@@ -52,7 +64,7 @@
5264
}
5365

5466
.method public hidebysig instance void 'special.StoreInitonlyField..ctor_Valid'() cil managed { ret }
55-
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
67+
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
5668
{
5769
ldarg.0
5870
call instance void [System.Runtime]System.Object::.ctor()
@@ -63,7 +75,7 @@
6375
}
6476

6577
.method public hidebysig instance void 'special.LoadAddrInitonlyField..ctor_Valid'(int32) cil managed { ret }
66-
.method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed
78+
.method public hidebysig specialname rtspecialname instance void .ctor(int32) cil managed
6779
{
6880
ldarg.0
6981
call instance void [System.Runtime]System.Object::.ctor()
@@ -74,7 +86,7 @@
7486
}
7587

7688
.method public hidebysig instance void 'special.LoadAddrInitonlyField..ctor_Valid'(int64) cil managed { ret }
77-
.method public hidebysig specialname rtspecialname instance void .ctor(int64) cil managed
89+
.method public hidebysig specialname rtspecialname instance void .ctor(int64) cil managed
7890
{
7991
ldarg.0
8092
call instance void [System.Runtime]System.Object::.ctor()
@@ -85,7 +97,7 @@
8597
}
8698

8799
.method public hidebysig instance void 'special.StoreInitonlyFieldOtherType..ctor_Invalid_InitOnly'(class OtherType c) cil managed { ret }
88-
.method public hidebysig specialname rtspecialname instance void .ctor(class OtherType c) cil managed
100+
.method public hidebysig specialname rtspecialname instance void .ctor(class OtherType c) cil managed
89101
{
90102
ldarg.0
91103
call instance void [System.Runtime]System.Object::.ctor()
@@ -96,7 +108,7 @@
96108
}
97109

98110
.method public hidebysig instance void 'special.StoreInitonlyFieldOtherInstance..ctor_Invalid_InitOnly'(class FieldTestsType c) cil managed { ret }
99-
.method public hidebysig specialname rtspecialname instance void .ctor(class FieldTestsType c) cil managed
111+
.method public hidebysig specialname rtspecialname instance void .ctor(class FieldTestsType c) cil managed
100112
{
101113
ldarg.0
102114
call instance void [System.Runtime]System.Object::.ctor()
@@ -107,7 +119,7 @@
107119
}
108120

109121
.method public hidebysig instance void 'special.StsfldInitonlyInCtor..ctor_Invalid_InitOnly'(bool) cil managed { ret }
110-
.method public hidebysig specialname rtspecialname instance void .ctor(bool) cil managed
122+
.method public hidebysig specialname rtspecialname instance void .ctor(bool) cil managed
111123
{
112124
ldarg.0
113125
call instance void [System.Runtime]System.Object::.ctor()
@@ -117,7 +129,7 @@
117129
}
118130

119131
.method public hidebysig instance void 'special.LdsfldInitonlyInCtor..ctor_Invalid_InitOnly'(char) cil managed { ret }
120-
.method public hidebysig specialname rtspecialname instance void .ctor(char) cil managed
132+
.method public hidebysig specialname rtspecialname instance void .ctor(char) cil managed
121133
{
122134
ldarg.0
123135
call instance void [System.Runtime]System.Object::.ctor()
@@ -127,7 +139,7 @@
127139
}
128140

129141
.method public hidebysig instance void 'special.LdsfldStslfdInitonlyCctor..cctor_Valid'() cil managed { ret }
130-
.method public hidebysig specialname rtspecialname instance void .cctor() cil managed
142+
.method public hidebysig specialname rtspecialname instance void .cctor() cil managed
131143
{
132144
ldsflda int32 FieldTestsType::StaticInitonlyField
133145
pop
@@ -145,8 +157,8 @@
145157
.field public initonly int32 InstanceInitonlyField
146158
.field public static initonly int32 StaticInitonlyField
147159

148-
.method public hidebysig instance void 'special.LdfldStlfdInitonlyCctor..cctor_Invalid_InitOnly.InitOnly'() cil managed { ret }
149-
.method public hidebysig specialname rtspecialname instance void .cctor() cil managed
160+
.method public hidebysig instance void 'special.LdfldStlfdInitonlyCctor..cctor_Invalid_InitOnly'() cil managed { ret }
161+
.method public hidebysig specialname rtspecialname instance void .cctor() cil managed
150162
{
151163
ldsfld class OtherType OtherType::Instance
152164
ldflda int32 OtherType::InstanceInitonlyField

src/tests/ilverify/ILTests/FunctionPointerTests.il

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
ret
5757
}
5858

59-
.method public hidebysig instance void Call.SendFunctionPointerWithInaccessibleReturnType_Invalid_MethodAccess_MethodAccess() cil managed
59+
.method public hidebysig instance void Call.SendFunctionPointerWithInaccessibleReturnType_Invalid_MethodAccess.MethodAccess() cil managed
6060
{
6161
newobj instance void C::.ctor()
6262
ldftn class D/D_Private D::StaticMethodReturningPrivateTypeInstance(int32)

src/tests/ilverify/TestDataLoader.cs

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
namespace ILVerification.Tests
1919
{
2020
/// <summary>
21-
/// Parses the methods in the test assemblies.
21+
/// Parses the methods in the test assemblies.
2222
/// It loads all assemblies from the test folder defined in <code>TestDataLoader.TestAssemblyPath</code>
2323
/// This class feeds the xunit Theories
2424
/// </summary>
@@ -106,8 +106,8 @@ private static TheoryData<TestCase> GetTestTypeFromDll(Func<string[], TypeDefini
106106
/// <summary>
107107
/// Returns all methods that contain valid IL code based on the following naming convention:
108108
/// [FriendlyName]_Valid
109-
/// The method must contain 1 '_'. The part before the '_' is a friendly name describing what the method does.
110-
/// The word after the '_' has to be 'Valid' (Case sensitive)
109+
/// The method must contain 1 '_'. The part before the '_' is a friendly name describing what the method does.
110+
/// The word after the '_' has to be 'Valid' (Case sensitive)
111111
/// E.g.: 'SimpleAdd_Valid'
112112
/// </summary>
113113
public static TheoryData<TestCase> GetMethodsWithValidIL()
@@ -129,9 +129,9 @@ public static TheoryData<TestCase> GetMethodsWithValidIL()
129129
/// The method name must contain 2 '_' characters.
130130
/// 1. part: a friendly name
131131
/// 2. part: must be the word 'Invalid' (Case sensitive)
132-
/// 3. part: the expected VerifierErrors as string separated by '.'.
132+
/// 3. part: the expected VerifierErrors as string separated by '.'.
133133
/// E.g.: SimpleAdd_Invalid_ExpectedNumericType
134-
/// </summary>
134+
/// </summary>
135135
public static TheoryData<TestCase> GetMethodsWithInvalidIL()
136136
{
137137
var methodSelector = new Func<string[], MethodDefinitionHandle, TestCase>((mparams, methodHandle) =>
@@ -176,20 +176,37 @@ private static TheoryData<TestCase> GetTestMethodsFromDll(Func<string[], MethodD
176176
var method = (EcmaMethod)testModule.GetMethod(methodHandle);
177177
var methodName = method.Name;
178178

179-
if (!String.IsNullOrEmpty(methodName) && methodName.Contains("_"))
179+
if (!methodName.Contains('_', StringComparison.Ordinal))
180+
continue;
181+
182+
var index = methodName.LastIndexOf("_Valid", StringComparison.Ordinal);
183+
if (index < 0)
184+
index = methodName.LastIndexOf("_Invalid", StringComparison.Ordinal);
185+
if (index < 0)
186+
continue;
187+
188+
var substring = methodName.Substring(index + 1);
189+
var split = substring.Split('_');
190+
string[] mparams = new string[split.Length + 1];
191+
split.CopyTo(mparams, 1);
192+
mparams[0] = methodName.Substring(0, index);
193+
// examples of methodName to mparams transformation:
194+
// * `get_Property` -> [ 'get_Property' ]
195+
// * `CheckSomething_Valid` -> [ 'CheckSomething', 'Valid' ]
196+
// * 'WrongMethod_Invalid_BranchOutOfTry' -> [ 'WrongMethod', 'Invalid', 'BranchOutOfTry' ]
197+
// * 'MoreWrongMethod_Invalid_TypeAccess.InitOnly' -> [ 'MoreWrongMethod', 'Invalid', 'TypeAccess', 'InitOnly' ]
198+
// * 'special.set_MyProperty.set_MyProperty_Invalid_InitOnly' -> [ 'special.set_MyProperty.set_MyProperty', 'Invalid', 'InitOnly' ]
199+
200+
var specialMethodHandle = HandleSpecialTests(mparams, method);
201+
var newItem = methodSelector(mparams, specialMethodHandle);
202+
203+
if (newItem != null)
180204
{
181-
var mparams = methodName.Split('_');
182-
var specialMethodHandle = HandleSpecialTests(mparams, method);
183-
var newItem = methodSelector(mparams, specialMethodHandle);
184-
185-
if (newItem != null)
186-
{
187-
newItem.TestName = mparams[0];
188-
newItem.MethodName = methodName;
189-
newItem.ModuleName = testDllName;
205+
newItem.TestName = mparams[0];
206+
newItem.MethodName = methodName;
207+
newItem.ModuleName = testDllName;
190208

191-
retVal.Add(newItem);
192-
}
209+
retVal.Add(newItem);
193210
}
194211
}
195212
}

0 commit comments

Comments
 (0)