Skip to content

Commit efe20ba

Browse files
jkoritzinskyjkotas
andauthored
Move the BSTR trailbyte tracking out of the sync block (#121362)
Co-authored-by: Jan Kotas <[email protected]>
1 parent 4f905a8 commit efe20ba

File tree

15 files changed

+117
-323
lines changed

15 files changed

+117
-323
lines changed

src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Runtime.InteropServices.Marshalling;
1212
using System.Runtime.Versioning;
1313
using System.Text;
14+
using System.Threading;
1415

1516
namespace System.StubHelpers
1617
{
@@ -229,6 +230,39 @@ internal static unsafe void ConvertToManaged(StringBuilder sb, IntPtr pNative)
229230

230231
internal static class BSTRMarshaler
231232
{
233+
private sealed class TrailByte(byte trailByte)
234+
{
235+
public readonly byte Value = trailByte;
236+
}
237+
238+
// In some early version of VB when there were no arrays developers used to use BSTR as arrays
239+
// The way this was done was by adding a trail byte at the end of the BSTR
240+
// To support this scenario, we need to use a ConditionalWeakTable for this special case and
241+
// save the trail character in here.
242+
// This stores the trail character when a BSTR is used as an array.
243+
private static ConditionalWeakTable<string, TrailByte>? s_trailByteTable;
244+
245+
private static bool TryGetTrailByte(string strManaged, out byte trailByte)
246+
{
247+
if (s_trailByteTable?.TryGetValue(strManaged, out TrailByte? trailByteObj) == true)
248+
{
249+
trailByte = trailByteObj.Value;
250+
return true;
251+
}
252+
253+
trailByte = 0;
254+
return false;
255+
}
256+
257+
private static void SetTrailByte(string strManaged, byte trailByte)
258+
{
259+
if (s_trailByteTable == null)
260+
{
261+
Interlocked.CompareExchange(ref s_trailByteTable, new ConditionalWeakTable<string, TrailByte>(), null);
262+
}
263+
s_trailByteTable!.Add(strManaged, new TrailByte(trailByte));
264+
}
265+
232266
internal static unsafe IntPtr ConvertToNative(string strManaged, IntPtr pNativeBuffer)
233267
{
234268
if (null == strManaged)
@@ -237,7 +271,7 @@ internal static unsafe IntPtr ConvertToNative(string strManaged, IntPtr pNativeB
237271
}
238272
else
239273
{
240-
bool hasTrailByte = StubHelpers.TryGetStringTrailByte(strManaged, out byte trailByte);
274+
bool hasTrailByte = TryGetTrailByte(strManaged, out byte trailByte);
241275

242276
uint lengthInBytes = (uint)strManaged.Length * 2;
243277

@@ -320,8 +354,7 @@ internal static unsafe IntPtr ConvertToNative(string strManaged, IntPtr pNativeB
320354

321355
if ((length & 1) == 1)
322356
{
323-
// odd-sized strings need to have the trailing byte saved in their sync block
324-
StubHelpers.SetStringTrailByte(ret, ((byte*)bstr)[length - 1]);
357+
SetTrailByte(ret, ((byte*)bstr)[length - 1]);
325358
}
326359

327360
return ret;
@@ -1489,19 +1522,6 @@ internal static void CheckStringLength(uint length)
14891522
}
14901523
}
14911524

1492-
// Try to retrieve the extra byte - returns false if not present.
1493-
[MethodImpl(MethodImplOptions.InternalCall)]
1494-
internal static extern bool TryGetStringTrailByte(string str, out byte data);
1495-
1496-
// Set extra byte for odd-sized strings that came from interop as BSTR.
1497-
internal static void SetStringTrailByte(string str, byte data)
1498-
{
1499-
SetStringTrailByte(new StringHandleOnStack(ref str!), data);
1500-
}
1501-
1502-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "StubHelpers_SetStringTrailByte")]
1503-
private static partial void SetStringTrailByte(StringHandleOnStack str, byte data);
1504-
15051525
internal static unsafe void FmtClassUpdateNativeInternal(object obj, byte* pNative, ref CleanupWorkListElement? pCleanupWorkList)
15061526
{
15071527
MethodTable* pMT = RuntimeHelpers.GetMethodTable(obj);

src/coreclr/vm/ecalllist.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,6 @@ FCFuncEnd()
352352

353353
FCFuncStart(gStubHelperFuncs)
354354
FCFuncElement("GetDelegateTarget", StubHelpers::GetDelegateTarget)
355-
FCFuncElement("TryGetStringTrailByte", StubHelpers::TryGetStringTrailByte)
356355
FCFuncElement("SetLastError", StubHelpers::SetLastError)
357356
FCFuncElement("ClearLastError", StubHelpers::ClearLastError)
358357
#ifdef FEATURE_COMINTEROP

src/coreclr/vm/object.cpp

Lines changed: 0 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -658,47 +658,6 @@ STRINGREF StringObject::NewString(INT32 length) {
658658
}
659659
}
660660

661-
662-
/*==================================NewString===================================
663-
**Action: Many years ago, VB didn't have the concept of a byte array, so enterprising
664-
** users created one by allocating a BSTR with an odd length and using it to
665-
** store bytes. A generation later, we're still stuck supporting this behavior.
666-
** The way that we do this is to take advantage of the difference between the
667-
** array length and the string length. The string length will always be the
668-
** number of characters between the start of the string and the terminating 0.
669-
** If we need an odd number of bytes, we'll take one wchar after the terminating 0.
670-
** (e.g. at position StringLength+1). The high-order byte of this wchar is
671-
** reserved for flags and the low-order byte is our odd byte. This function is
672-
** used to allocate a string of that shape, but we don't actually mark the
673-
** trailing byte as being in use yet.
674-
**Returns: A newly allocated string. Null if length is less than 0.
675-
**Arguments: length -- the length of the string to allocate
676-
** bHasTrailByte -- whether the string also has a trailing byte.
677-
**Exceptions: OutOfMemoryException if AllocateString fails.
678-
==============================================================================*/
679-
STRINGREF StringObject::NewString(INT32 length, BOOL bHasTrailByte) {
680-
CONTRACTL {
681-
GC_TRIGGERS;
682-
MODE_COOPERATIVE;
683-
PRECONDITION(length>=0 && length != INT32_MAX);
684-
} CONTRACTL_END;
685-
686-
STRINGREF pString;
687-
if (length<0 || length == INT32_MAX) {
688-
return NULL;
689-
} else if (length == 0) {
690-
return GetEmptyString();
691-
} else {
692-
pString = AllocateString(length);
693-
_ASSERTE(pString->GetBuffer()[length]==0);
694-
if (bHasTrailByte) {
695-
_ASSERTE(pString->GetBuffer()[length+1]==0);
696-
}
697-
}
698-
699-
return pString;
700-
}
701-
702661
//========================================================================
703662
// Creates a System.String object and initializes from
704663
// the supplied null-terminated C string.
@@ -887,74 +846,6 @@ STRINGREF* StringObject::InitEmptyStringRefPtr() {
887846
return EmptyStringRefPtr;
888847
}
889848

890-
/*============================InternalTrailByteCheck============================
891-
**Action: Many years ago, VB didn't have the concept of a byte array, so enterprising
892-
** users created one by allocating a BSTR with an odd length and using it to
893-
** store bytes. A generation later, we're still stuck supporting this behavior.
894-
** The way that we do this is stick the trail byte in the sync block
895-
** whenever we encounter such a situation. Since we expect this to be a very corner case
896-
** accessing the sync block seems like a good enough solution
897-
**
898-
**Returns: True if <CODE>str</CODE> contains a VB trail byte, false otherwise.
899-
**Arguments: str -- The string to be examined.
900-
**Exceptions: None
901-
==============================================================================*/
902-
BOOL StringObject::HasTrailByte() {
903-
WRAPPER_NO_CONTRACT;
904-
905-
SyncBlock * pSyncBlock = PassiveGetSyncBlock();
906-
if(pSyncBlock != NULL)
907-
{
908-
return pSyncBlock->HasCOMBstrTrailByte();
909-
}
910-
911-
return FALSE;
912-
}
913-
914-
/*=================================GetTrailByte=================================
915-
**Action: If <CODE>str</CODE> contains a vb trail byte, returns a copy of it.
916-
**Returns: True if <CODE>str</CODE> contains a trail byte. *bTrailByte is set to
917-
** the byte in question if <CODE>str</CODE> does have a trail byte, otherwise
918-
** it's set to 0.
919-
**Arguments: str -- The string being examined.
920-
** bTrailByte -- An out param to hold the value of the trail byte.
921-
**Exceptions: None.
922-
==============================================================================*/
923-
BOOL StringObject::GetTrailByte(BYTE *bTrailByte) {
924-
CONTRACTL
925-
{
926-
NOTHROW;
927-
GC_NOTRIGGER;
928-
MODE_ANY;
929-
}
930-
CONTRACTL_END;
931-
_ASSERTE(bTrailByte);
932-
*bTrailByte=0;
933-
934-
BOOL retValue = HasTrailByte();
935-
936-
if(retValue)
937-
{
938-
*bTrailByte = GET_VB_TRAIL_BYTE(GetHeader()->PassiveGetSyncBlock()->GetCOMBstrTrailByte());
939-
}
940-
941-
return retValue;
942-
}
943-
944-
/*=================================SetTrailByte=================================
945-
**Action: Sets the trail byte in the sync block
946-
**Returns: True.
947-
**Arguments: str -- The string into which to set the trail byte.
948-
** bTrailByte -- The trail byte to be added to the string.
949-
**Exceptions: None.
950-
==============================================================================*/
951-
BOOL StringObject::SetTrailByte(BYTE bTrailByte) {
952-
WRAPPER_NO_CONTRACT;
953-
954-
GetHeader()->GetSyncBlock()->SetCOMBstrTrailByte(MAKE_VB_TRAIL_BYTE(bTrailByte));
955-
return TRUE;
956-
}
957-
958849
#ifdef USE_CHECKED_OBJECTREFS
959850

960851
//-------------------------------------------------------------

src/coreclr/vm/object.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,6 @@ class StringObject : public Object
832832
// characters and the null terminator you should pass in 5 and NOT 6.
833833
//========================================================================
834834
static STRINGREF NewString(int length);
835-
static STRINGREF NewString(int length, BOOL bHasTrailByte);
836835
static STRINGREF NewString(const WCHAR *pwsz);
837836
static STRINGREF NewString(const WCHAR *pwsz, int length);
838837
static STRINGREF NewString(LPCUTF8 psz);
@@ -843,10 +842,6 @@ class StringObject : public Object
843842

844843
static STRINGREF* InitEmptyStringRefPtr();
845844

846-
BOOL HasTrailByte();
847-
BOOL GetTrailByte(BYTE *bTrailByte);
848-
BOOL SetTrailByte(BYTE bTrailByte);
849-
850845
/*=================RefInterpretGetStringValuesDangerousForGC======================
851846
**N.B.: This performs no range checking and relies on the caller to have done this.
852847
**Args: (IN)ref -- the String to be interpretted.

0 commit comments

Comments
 (0)