-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Obsolete RuntimeHelpers.OffsetToStringData #35722
Conversation
char[] expectedValues = new char[] { 'a', 'b', 'c', 'd', 'e', 'f' }; | ||
string s = "abcdef"; | ||
|
||
fixed (char* values = s) // Compiler will use OffsetToStringData with fixed statements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fixed
keyword doesn't call OffsetToStringData
any more, so this test wasn't actually testing the right thing. I'm deleting this test since we already have coverage over in StringTests.cs:
runtime/src/libraries/System.Runtime/tests/System/StringTests.cs
Lines 871 to 895 in 61733e2
// Finally, ensure that GetPinnableReference matches the legacy 'fixed' keyword. | |
DynamicMethod dynamicMethod = new DynamicMethod("tester", typeof(bool), new[] { typeof(string) }); | |
ILGenerator ilGen = dynamicMethod.GetILGenerator(); | |
LocalBuilder pinnedLocal = ilGen.DeclareLocal(typeof(object), pinned: true); | |
ilGen.Emit(OpCodes.Ldarg_0); // load 'input' and pin it | |
ilGen.Emit(OpCodes.Stloc, pinnedLocal); | |
ilGen.Emit(OpCodes.Ldloc, pinnedLocal); // get the address of field 0 from pinned 'input' | |
ilGen.Emit(OpCodes.Conv_I); | |
ilGen.Emit(OpCodes.Call, typeof(RuntimeHelpers).GetProperty("OffsetToStringData").GetMethod); // get pointer to start of string data | |
ilGen.Emit(OpCodes.Add); | |
ilGen.Emit(OpCodes.Ldarg_0); // get value of input.GetPinnableReference() | |
ilGen.Emit(OpCodes.Callvirt, typeof(string).GetMethod("GetPinnableReference")); | |
// At this point, the top of the evaluation stack is traditional (fixed char* = input) and input.GetPinnableReference(). | |
// Compare for equality and return. | |
ilGen.Emit(OpCodes.Ceq); | |
ilGen.Emit(OpCodes.Ret); | |
Assert.True((bool)dynamicMethod.Invoke(null, new[] { input })); |
I looked through the mono code base and couldn't find any calls to the |
GetKnownMethod("GetPinnableReference", null))); | ||
codeStream.EmitStLoc(vPinnedCharRef); | ||
codeStream.EmitLdLoc(vPinnedCharRef); | ||
codeStream.Emit(ILOpcode.conv_u); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be a downside to moving this conv_u to below lCommonExit? Then the duplicate one in the other branch could be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess maybe it's better for the same type to be on the stack prior to the branching?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the stack should be the same at all possible entry points to the branch. In one case the stack will have an i4, and in the other case the stack will have a char&. I was concerned that this would break things.
The codegen pattern here is identical to the codegen pattern Roslyn emits for pattern fixed statements. See Sharplab. Honestly I think some minor optimizations are possible, but I was nervous about straying from what the compiler itself already implements.
@@ -9093,6 +9093,7 @@ public static partial class RuntimeFeature | |||
} | |||
public static partial class RuntimeHelpers | |||
{ | |||
[System.ObsoleteAttribute("OffsetToStringData is obsolete. Use string.GetPinnableReference() instead.")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be following @terrajobst's new plan for obsoletion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a good question. :)
/azp run runtime |
Azure Pipelines successfully started running 1 pipeline(s). |
Hello @GrabYourPitchforks! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
Did we obsolete something in the BCL??!? 👍🏿👍🏿👍🏿👍🏿🔥🔥🔥🔥🔥👍🏿 |
@davidfowl I know, right!? 😃 |
Do you already know about |
@mjsabby not sure the context - was that comment meant for a different issue? |
Sorry for the lack of context! |
Because in the 3.1 release it's still compiled against an older target framework (netstandard2.1). The latest nightly packages contain a build that's compiled against netcoreapp proper. So that DLL will contain the expected calls to |
Resolves #31406.
RuntimeHelpers.OffsetToStringData
is the last remaining public API which exposes the concept ofstring
's raw data as being inline directly after the object header. We want code to usestring.GetPinnableReference
instead. It's more future-proof and plays better with the experiments we're running on thestring
class./cc @jkoritzinsky, who assisted offline with the marshalling codegen. Thanks! :)
/cc @terrajobst to see if there are any concerns with the deprecation message. We obsoleted
string.Copy
in .NET Core 3.0 using a standard (no diagnostic id) obsolete attribute. I feel this is along the same lines so doesn't deserve its own diagnostic id.