From fabbe5d423e1d4b46feb5d666a15a1c85e5a8dd7 Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Thu, 3 May 2018 07:34:51 -0700 Subject: [PATCH 01/12] Fix Unix build breaks --- src/vm/ecalllist.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index d31ea72303db..70daeabb0545 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -116,6 +116,10 @@ FCFuncStart(gStringFuncs) #endif // FEATURE_COMINTEROP FCFuncEnd() +FCFuncStart(gUtf8StringFuncs) + FCFuncElement("FastAllocate", Utf8StringFastAllocate) +FCFuncEnd() + FCFuncStart(gValueTypeFuncs) FCFuncElement("CanCompareBits", ValueTypeHelper::CanCompareBits) FCFuncElement("FastEqualsCheck", ValueTypeHelper::FastEqualsCheck) @@ -1055,10 +1059,6 @@ FCFuncStart(gUriMarshalerFuncs) FCFuncElement("CreateNativeUriInstanceHelper", StubHelpers::UriMarshaler__CreateNativeUriInstance) FCFuncEnd() -FCFuncStart(gUtf8StringFuncs) - FCFuncElement("FastAllocate", Utf8StringFastAllocate) -FCFuncEnd() - FCFuncStart(gEventArgsMarshalerFuncs) QCFuncElement("CreateNativeNCCEventArgsInstanceHelper", StubHelpers::EventArgsMarshaler__CreateNativeNCCEventArgsInstance) QCFuncElement("CreateNativePCEventArgsInstance", StubHelpers::EventArgsMarshaler__CreateNativePCEventArgsInstance) From f8639fa5bc7edc70ec5275baac6ebff0525444a6 Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Thu, 3 May 2018 09:29:44 -0700 Subject: [PATCH 02/12] Incorporate PR feedback --- .../bcltype/utf8stringnative.cpp | 19 ------------------- src/classlibnative/bcltype/utf8stringnative.h | 8 -------- src/vm/gchelpers.cpp | 2 +- src/vm/object.h | 3 +-- 4 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/classlibnative/bcltype/utf8stringnative.cpp b/src/classlibnative/bcltype/utf8stringnative.cpp index f7dba9837490..fe76b1e4c280 100644 --- a/src/classlibnative/bcltype/utf8stringnative.cpp +++ b/src/classlibnative/bcltype/utf8stringnative.cpp @@ -1,14 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// -// File: StringNative.cpp -// - -// -// Purpose: The implementation of the Utf8String class. -// - // #include "common.h" @@ -24,12 +16,6 @@ #include "metasig.h" #include "excep.h" -// Compile the string functionality with these pragma flags (equivalent of the command line /Ox flag) -// Compiling this functionality differently gives us significant throughout gain in some cases. -#if defined(_MSC_VER) && defined(_TARGET_X86_) -#pragma optimize("tgy", on) -#endif - FCIMPL1(Utf8StringObject *, Utf8StringFastAllocate, DWORD length) { FCALL_CONTRACT; @@ -44,8 +30,3 @@ FCIMPL1(Utf8StringObject *, Utf8StringFastAllocate, DWORD length) } FCIMPLEND - -// Revert to command line compilation flags -#if defined(_MSC_VER) && defined(_TARGET_X86_) -#pragma optimize ("", on) -#endif diff --git a/src/classlibnative/bcltype/utf8stringnative.h b/src/classlibnative/bcltype/utf8stringnative.h index 4c7ff6281487..8c4e55343b87 100644 --- a/src/classlibnative/bcltype/utf8stringnative.h +++ b/src/classlibnative/bcltype/utf8stringnative.h @@ -1,14 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// -// File: StringNative.h -// - -// -// Purpose: Contains types and method signatures for the Utf8String class -// - // #include "fcall.h" diff --git a/src/vm/gchelpers.cpp b/src/vm/gchelpers.cpp index af71133cd025..42f419fbfcbf 100644 --- a/src/vm/gchelpers.cpp +++ b/src/vm/gchelpers.cpp @@ -1094,7 +1094,7 @@ UTF8STRINGREF SlowAllocateUtf8String(DWORD cchStringLength) // Initialize Object orObject->SetMethodTable(g_pUtf8StringClass); - orObject->SetUtf8StringLength(cchStringLength); + orObject->SetLength(cchStringLength); if (ObjectSize >= LARGE_OBJECT_SIZE) { diff --git a/src/vm/object.h b/src/vm/object.h index f9b9b2eb7134..6bab21f5552b 100644 --- a/src/vm/object.h +++ b/src/vm/object.h @@ -1221,10 +1221,9 @@ class Utf8StringObject : public Object // GC will see a Utf8StringObject like this: // DWORD m_StringLength // BYTE m_Characters[0] - // DWORD m_OptionalPadding (this is an optional field and will appear based on need) public: - VOID SetUtf8StringLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; } + VOID SetLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; } protected: Utf8StringObject() { LIMITED_METHOD_CONTRACT; } From bcbbbb9b886b25393ea5860aeda2691905a81b7c Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Thu, 3 May 2018 13:14:02 -0700 Subject: [PATCH 03/12] Fix allocation size & remove the manual managed code trappings --- src/classlibnative/bcltype/utf8stringnative.cpp | 6 +++--- src/strongname/api/common.h | 1 - src/vm/common.h | 1 - src/vm/gchelpers.cpp | 4 ++-- src/vm/gchelpers.h | 4 ++-- src/vm/object.h | 5 ++--- src/vm/vars.hpp | 4 ---- 7 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/classlibnative/bcltype/utf8stringnative.cpp b/src/classlibnative/bcltype/utf8stringnative.cpp index fe76b1e4c280..50904d122387 100644 --- a/src/classlibnative/bcltype/utf8stringnative.cpp +++ b/src/classlibnative/bcltype/utf8stringnative.cpp @@ -20,13 +20,13 @@ FCIMPL1(Utf8StringObject *, Utf8StringFastAllocate, DWORD length) { FCALL_CONTRACT; - UTF8STRINGREF rv = NULL; // not protected + Utf8StringObject *rv = NULL; // not protected - HELPER_METHOD_FRAME_BEGIN_RET_1(rv); + HELPER_METHOD_FRAME_BEGIN_0(); rv = SlowAllocateUtf8String(length); HELPER_METHOD_FRAME_END(); - return UTF8STRINGREFToObject(rv); + return rv; } FCIMPLEND diff --git a/src/strongname/api/common.h b/src/strongname/api/common.h index 57ecfac3b600..113885cb994b 100644 --- a/src/strongname/api/common.h +++ b/src/strongname/api/common.h @@ -146,7 +146,6 @@ typedef DPTR(class ReJitManager) PTR_ReJitManager; typedef DPTR(struct ReJitInfo) PTR_ReJitInfo; typedef DPTR(struct SharedReJitInfo) PTR_SharedReJitInfo; typedef DPTR(class StringObject) PTR_StringObject; -typedef DPTR(class Utf8StringObject) PTR_Utf8StringObject; typedef DPTR(class TypeHandle) PTR_TypeHandle; #ifdef STUB_DISPATCH typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager; diff --git a/src/vm/common.h b/src/vm/common.h index 501526626cd9..044ebb017dd9 100644 --- a/src/vm/common.h +++ b/src/vm/common.h @@ -168,7 +168,6 @@ typedef DPTR(struct ReJitInfo) PTR_ReJitInfo; typedef DPTR(struct SharedReJitInfo) PTR_SharedReJitInfo; typedef DPTR(class StringObject) PTR_StringObject; typedef DPTR(class TypeHandle) PTR_TypeHandle; -typedef DPTR(class Utf8StringObject) PTR_Utf8StringObject; typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager; typedef VPTR(class VirtualCallStubManagerManager) PTR_VirtualCallStubManagerManager; typedef VPTR(class IGCHeap) PTR_IGCHeap; diff --git a/src/vm/gchelpers.cpp b/src/vm/gchelpers.cpp index 42f419fbfcbf..586d12877767 100644 --- a/src/vm/gchelpers.cpp +++ b/src/vm/gchelpers.cpp @@ -1059,7 +1059,7 @@ STRINGREF SlowAllocateString( DWORD cchStringLength ) return( ObjectToSTRINGREF(orObject) ); } -UTF8STRINGREF SlowAllocateUtf8String(DWORD cchStringLength) +Utf8StringObject *SlowAllocateUtf8String(DWORD cchStringLength) { CONTRACTL{ THROWS; @@ -1122,7 +1122,7 @@ UTF8STRINGREF SlowAllocateUtf8String(DWORD cchStringLength) LogAlloc(ObjectSize, g_pUtf8StringClass, orObject); - return(ObjectToUTF8STRINGREF(orObject)); + return orObject; } diff --git a/src/vm/gchelpers.h b/src/vm/gchelpers.h index 67b92eaf8d1f..8357d2b2a5fc 100644 --- a/src/vm/gchelpers.h +++ b/src/vm/gchelpers.h @@ -71,7 +71,7 @@ STRINGREF AllocateString( DWORD cchStringLength ); // The slow version, implemented in gcscan.cpp STRINGREF SlowAllocateString( DWORD cchStringLength ); -UTF8STRINGREF SlowAllocateUtf8String( DWORD cchStringLength ); +Utf8StringObject *SlowAllocateUtf8String( DWORD cchStringLength ); #else @@ -85,7 +85,7 @@ OBJECTREF AllocateObjectArray(DWORD cElements, TypeHandle ElementType, BOOL bAll STRINGREF SlowAllocateString( DWORD cchStringLength ); -UTF8STRINGREF SlowAllocateUtf8String( DWORD cchStringLength ); +Utf8StringObject *SlowAllocateUtf8String( DWORD cchStringLength ); inline STRINGREF AllocateString( DWORD cchStringLength ) { diff --git a/src/vm/object.h b/src/vm/object.h index 6bab21f5552b..d6f13c502447 100644 --- a/src/vm/object.h +++ b/src/vm/object.h @@ -884,7 +884,6 @@ typedef REF U8ARRAYREF; typedef REF CHARARRAYREF; typedef REF PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. typedef REF STRINGREF; -typedef REF UTF8STRINGREF; #else // USE_CHECKED_OBJECTREFS @@ -903,7 +902,6 @@ typedef PTR_U8Array U8ARRAYREF; typedef PTR_CHARArray CHARARRAYREF; typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. typedef PTR_StringObject STRINGREF; -typedef PTR_Utf8StringObject UTF8STRINGREF; #endif // USE_CHECKED_OBJECTREFS @@ -1209,6 +1207,7 @@ class ReflectClassBaseObject : public BaseObjectWithCachedData }; +#include class Utf8StringObject : public Object { #ifdef DACCESS_COMPILE @@ -1232,7 +1231,7 @@ class Utf8StringObject : public Object public: static SIZE_T GetSize(DWORD stringLength); }; - +#include // This is the Method version of the Reflection object. // A Method has adddition information. diff --git a/src/vm/vars.hpp b/src/vm/vars.hpp index aac768c7d15e..9e98c812951f 100644 --- a/src/vm/vars.hpp +++ b/src/vm/vars.hpp @@ -315,9 +315,7 @@ class REF : public OBJECTREF #define ObjectToOBJECTREF(obj) (OBJECTREF(obj)) #define OBJECTREFToObject(objref) ((objref).operator-> ()) #define ObjectToSTRINGREF(obj) (STRINGREF(obj)) -#define ObjectToUTF8STRINGREF(obj) (UTF8STRINGREF(obj)) #define STRINGREFToObject(objref) (*( (StringObject**) &(objref) )) -#define UTF8STRINGREFToObject(objref) (*( (Utf8StringObject**) &(objref) )) #else // _DEBUG_IMPL @@ -327,9 +325,7 @@ class REF : public OBJECTREF #define ObjectToOBJECTREF(obj) ((PTR_Object) (obj)) #define OBJECTREFToObject(objref) ((PTR_Object) (objref)) #define ObjectToSTRINGREF(obj) ((PTR_StringObject) (obj)) -#define ObjectToUTF8STRINGREF(obj) ((PTR_Utf8StringObject) (obj)) #define STRINGREFToObject(objref) ((PTR_StringObject) (objref)) -#define UTF8STRINGREFToObject(objref) ((PTR_Utf8StringObject) (objref)) #endif // _DEBUG_IMPL From e4f9a43cd6483911b3dc282c0b4109bc7613f516 Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Thu, 3 May 2018 14:06:50 -0700 Subject: [PATCH 04/12] GetPinnableReference and fix x86 build --- src/classlibnative/bcltype/utf8stringnative.cpp | 2 +- src/mscorlib/src/System/Utf8String.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/classlibnative/bcltype/utf8stringnative.cpp b/src/classlibnative/bcltype/utf8stringnative.cpp index 50904d122387..28289d673ead 100644 --- a/src/classlibnative/bcltype/utf8stringnative.cpp +++ b/src/classlibnative/bcltype/utf8stringnative.cpp @@ -22,7 +22,7 @@ FCIMPL1(Utf8StringObject *, Utf8StringFastAllocate, DWORD length) Utf8StringObject *rv = NULL; // not protected - HELPER_METHOD_FRAME_BEGIN_0(); + HELPER_METHOD_FRAME_BEGIN_RET_1(rv); rv = SlowAllocateUtf8String(length); HELPER_METHOD_FRAME_END(); diff --git a/src/mscorlib/src/System/Utf8String.cs b/src/mscorlib/src/System/Utf8String.cs index d651fc50f593..75274b199db9 100644 --- a/src/mscorlib/src/System/Utf8String.cs +++ b/src/mscorlib/src/System/Utf8String.cs @@ -10,13 +10,13 @@ namespace System public sealed class Utf8String { // Do not reorder these fields. Must match layout of Utf8StringObject in object.h. - private int _length; - [CLSCompliant(false)] - public byte _firstByte; // TODO: Is public for experimentation in CoreFxLab. Will be private in its ultimate form. + private readonly int _length; + private readonly byte _firstByte; private Utf8String() { } // Suppress creation of the public constructor. No one actually calls this. public int Length => _length; + public ref readonly byte GetPinnableReference() => ref _firstByte; // Creates a new zero-initialized instance of the specified length. Actual storage allocated is "length + 1" bytes (the extra // +1 is for the NUL terminator.) From 624897fbd7fc6de6cb2ce573ae4491fa686f3939 Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Mon, 7 May 2018 08:35:27 -0700 Subject: [PATCH 05/12] Realign with System.String changes --- src/vm/appdomain.cpp | 2 -- src/vm/methodtablebuilder.cpp | 2 +- src/vm/object.h | 12 ++---------- src/vm/object.inl | 10 ++++++++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index b71115c2db3b..602ea189e5e2 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -2762,8 +2762,6 @@ void SystemDomain::LoadBaseSystemClasses() // Load Utf8String g_pUtf8StringClass = MscorlibBinder::GetClass(CLASS__UTF8_STRING); - _ASSERTE(g_pUtf8StringClass->GetBaseSize() == ObjSizeOf(Utf8StringObject)+sizeof(BYTE)); - _ASSERTE(g_pUtf8StringClass->GetComponentSize() == 1); // Used by Buffer::BlockCopy g_pByteArrayMT = ClassLoader::LoadArrayTypeThrowing( diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp index 21b5cbc184b3..752a5675fcfd 100644 --- a/src/vm/methodtablebuilder.cpp +++ b/src/vm/methodtablebuilder.cpp @@ -9748,7 +9748,7 @@ void MethodTableBuilder::CheckForSystemTypes() { // Utf8Strings are not "normal" objects, so we need to mess with their method table a bit // so that the GC can figure out how big each string is... - DWORD baseSize = ObjSizeOf(Utf8StringObject) + sizeof(BYTE); + DWORD baseSize = Utf8StringObject::GetBaseSize(); pMT->SetBaseSize(baseSize); // NULL character included GetHalfBakedClass()->SetBaseSizePadding(baseSize - bmtFP->NumInstanceFieldBytes); diff --git a/src/vm/object.h b/src/vm/object.h index 177cfcf00a5e..aa7722cc976d 100644 --- a/src/vm/object.h +++ b/src/vm/object.h @@ -1198,11 +1198,6 @@ class ReflectClassBaseObject : public BaseObjectWithCachedData }; -#ifdef _MSC_VER -#pragma warning(disable : 4200) // disable zero-sized array warning -#endif - -#include class Utf8StringObject : public Object { #ifdef DACCESS_COMPILE @@ -1211,10 +1206,7 @@ class Utf8StringObject : public Object private: DWORD m_StringLength; - BYTE m_Characters[0]; - // GC will see a Utf8StringObject like this: - // DWORD m_StringLength - // BYTE m_Characters[0] + BYTE m_FirstChar; public: VOID SetLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; } @@ -1224,9 +1216,9 @@ class Utf8StringObject : public Object ~Utf8StringObject() { LIMITED_METHOD_CONTRACT; } public: + static DWORD GetBaseSize(); static SIZE_T GetSize(DWORD stringLength); }; -#include // This is the Method version of the Reflection object. // A Method has adddition information. diff --git a/src/vm/object.inl b/src/vm/object.inl index 62c2ca85422b..aa0c0b9773f5 100644 --- a/src/vm/object.inl +++ b/src/vm/object.inl @@ -72,12 +72,18 @@ __forceinline /*static*/ SIZE_T StringObject::GetSize(DWORD strLen) return GetBaseSize() + strLen * sizeof(WCHAR); } +__forceinline /*static*/ DWORD Utf8StringObject::GetBaseSize() +{ + LIMITED_METHOD_DAC_CONTRACT; + + return ObjSizeOf(Object) + sizeof(DWORD) /* length */ + sizeof(BYTE) /* null terminator */; +} + __forceinline /*static*/ SIZE_T Utf8StringObject::GetSize(DWORD strLen) { LIMITED_METHOD_DAC_CONTRACT; - // Extra BYTE for null terminator - return ObjSizeOf(Utf8StringObject) + sizeof(BYTE) + strLen; + return GetBaseSize() + strLen; } #ifdef DACCESS_COMPILE From a00b751624e8a3269f15ec9084dc5f8bc142fc7d Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Fri, 18 May 2018 07:45:12 -0700 Subject: [PATCH 06/12] Bring up the Utf8 constructors --- .../src/System/Utf8String.cs | 56 ++++++++++++++++++- src/vm/ecall.cpp | 36 +++++++++--- src/vm/ecall.h | 2 + src/vm/ecalllist.h | 2 + src/vm/metasig.h | 5 ++ src/vm/mscorlib.h | 2 + 6 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Utf8String.cs b/src/System.Private.CoreLib/src/System/Utf8String.cs index 75274b199db9..cee7d89588c5 100644 --- a/src/System.Private.CoreLib/src/System/Utf8String.cs +++ b/src/System.Private.CoreLib/src/System/Utf8String.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Text; +using System.Runtime.InteropServices; using System.Runtime.CompilerServices; namespace System @@ -11,13 +13,65 @@ public sealed class Utf8String { // Do not reorder these fields. Must match layout of Utf8StringObject in object.h. private readonly int _length; - private readonly byte _firstByte; + private byte _firstByte; // would like to mark "readonly" but need to take ref to it in our magic "constructor" that C# doesn't recognize as a constructor. private Utf8String() { } // Suppress creation of the public constructor. No one actually calls this. public int Length => _length; public ref readonly byte GetPinnableReference() => ref _firstByte; + // Utf8String constructors + // These are special. The implementation methods for these have a different signature from the + // declared constructors. + + [MethodImpl(MethodImplOptions.InternalCall)] + public extern Utf8String(ReadOnlySpan value); + +#if PROJECTN + [DependencyReductionRoot] +#endif +#if !CORECLR + static +#endif + private Utf8String Ctor(ReadOnlySpan value) + { + Utf8String newString = FastAllocate(value.Length); + unsafe + { + fixed (byte* pDst = &newString._firstByte) + fixed (byte* pSrc = &MemoryMarshal.GetNonNullPinnableReference(value)) + { + Buffer.Memcpy(dest: pDst, src: pSrc, len: value.Length); + } + } + return newString; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + public extern Utf8String(ReadOnlySpan value); + +#if PROJECTN + [DependencyReductionRoot] +#endif +#if !CORECLR + static +#endif + private Utf8String Ctor(ReadOnlySpan value) + { + Encoding e = Encoding.UTF8; + int length = e.GetByteCount(value); + Utf8String newString = FastAllocate(length); + unsafe + { + fixed (byte* pFirstByte = &newString._firstByte) + fixed (char* pFirstChar = &MemoryMarshal.GetNonNullPinnableReference(value)) + { + e.GetBytes(pFirstChar, length, pFirstByte, length); + } + } + return newString; + } + // Creates a new zero-initialized instance of the specified length. Actual storage allocated is "length + 1" bytes (the extra // +1 is for the NUL terminator.) [MethodImpl(MethodImplOptions.InternalCall)] diff --git a/src/vm/ecall.cpp b/src/vm/ecall.cpp index 3812ff10305e..0a59f51edde3 100644 --- a/src/vm/ecall.cpp +++ b/src/vm/ecall.cpp @@ -55,6 +55,31 @@ static_assert_no_msg(ECallCtor_First + 8 == ECall::CtorSBytePtrStartLengthEncodi #define NumberOfStringConstructors 9 +#define METHOD__UTF8STRING__CTORF_FIRST METHOD__UTF8_STRING__CTORF_READONLYSPANOFBYTE +static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 0 == METHOD__UTF8_STRING__CTORF_READONLYSPANOFBYTE); +static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 1 == METHOD__UTF8_STRING__CTORF_READONLYSPANOFCHAR); + +#define ECallUtf8String_Ctor_First ECall::Utf8StringCtorReadOnlySpanOfByteManaged +static_assert_no_msg(ECallUtf8String_Ctor_First + 0 == ECall::Utf8StringCtorReadOnlySpanOfByteManaged); +static_assert_no_msg(ECallUtf8String_Ctor_First + 1 == ECall::Utf8StringCtorReadOnlySpanOfCharManaged); + +#define NumberOfUtf8StringConstructors 2 + +static void PopulateConstructors(DWORD firstECallIndex, BinderMethodID firstMethod, int numConstructors) +{ + STANDARD_VM_CONTRACT; + + for (int i = 0; i < numConstructors; i++) + { + MethodDesc* pMD = MscorlibBinder::GetMethod((BinderMethodID)(firstMethod + i)); + _ASSERTE(pMD != NULL); + + PCODE pDest = pMD->GetMultiCallableAddrOfCode(); + + ECall::DynamicallyAssignFCallImpl(pDest, firstECallIndex + i); + } +} + void ECall::PopulateManagedStringConstructors() { STANDARD_VM_CONTRACT; @@ -62,16 +87,11 @@ void ECall::PopulateManagedStringConstructors() INDEBUG(static bool fInitialized = false); _ASSERTE(!fInitialized); // assume this method is only called once _ASSERTE(g_pStringClass != NULL); + _ASSERTE(g_pUtf8StringClass != NULL); - for (int i = 0; i < NumberOfStringConstructors; i++) - { - MethodDesc* pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__STRING__CTORF_FIRST + i)); - _ASSERTE(pMD != NULL); - - PCODE pDest = pMD->GetMultiCallableAddrOfCode(); + PopulateConstructors(ECallCtor_First, METHOD__STRING__CTORF_FIRST, NumberOfStringConstructors); + PopulateConstructors(ECallUtf8String_Ctor_First, METHOD__UTF8STRING__CTORF_FIRST, NumberOfUtf8StringConstructors); - ECall::DynamicallyAssignFCallImpl(pDest, ECallCtor_First + i); - } INDEBUG(fInitialized = true); } diff --git a/src/vm/ecall.h b/src/vm/ecall.h index 27afa8052cc0..aa1fd827004a 100644 --- a/src/vm/ecall.h +++ b/src/vm/ecall.h @@ -116,6 +116,8 @@ class ECall DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorSBytePtrStartLengthEncodingManaged, NULL) \ DYNAMICALLY_ASSIGNED_FCALL_IMPL(FastAllocateUtf8String, FramedAllocateUtf8String) \ DYNAMICALLY_ASSIGNED_FCALL_IMPL(InternalGetCurrentThread, NULL) \ + DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorReadOnlySpanOfByteManaged, NULL) \ + DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorReadOnlySpanOfCharManaged, NULL) \ enum { diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index e8ab4ca90c67..0e1515fe9be2 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -118,6 +118,8 @@ FCFuncEnd() FCFuncStart(gUtf8StringFuncs) FCDynamic("FastAllocate", CORINFO_INTRINSIC_Illegal, ECall::FastAllocateUtf8String) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfByte_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorReadOnlySpanOfByteManaged) + FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorReadOnlySpanOfCharManaged) FCFuncEnd() FCFuncStart(gValueTypeFuncs) diff --git a/src/vm/metasig.h b/src/vm/metasig.h index 49e26f17e9cc..d8e4c1889958 100644 --- a/src/vm/metasig.h +++ b/src/vm/metasig.h @@ -396,6 +396,7 @@ DEFINE_METASIG(IM(Bool_Bool_RetStr, F F, s)) DEFINE_METASIG(IM(PtrChar_RetVoid, P(u), v)) DEFINE_METASIG(IM(PtrChar_Int_Int_RetVoid, P(u) i i, v)) +DEFINE_METASIG_T(IM(ReadOnlySpanOfByte_RetVoid, GI(g(READONLY_SPAN), 1, b), v)) DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetVoid, GI(g(READONLY_SPAN), 1, u), v)) DEFINE_METASIG(IM(PtrSByt_RetVoid, P(B), v)) DEFINE_METASIG(IM(PtrSByt_Int_Int_RetVoid, P(B) i i, v)) @@ -414,6 +415,10 @@ DEFINE_METASIG(IM(PtrSByt_Int_Int_RetStr, P(B) i i, s)) DEFINE_METASIG_T(IM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s)) DEFINE_METASIG(IM(Obj_Int_RetIntPtr, j i, I)) +DEFINE_METASIG_T(IM(ArrChar_RetUtf8Str, a(u), C(UTF8_STRING))) +DEFINE_METASIG_T(IM(ReadOnlySpanOfByte_RetUtf8Str, GI(g(READONLY_SPAN), 1, b), C(UTF8_STRING))) +DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetUtf8Str, GI(g(READONLY_SPAN), 1, u), C(UTF8_STRING))) + DEFINE_METASIG(IM(Char_Char_RetStr, u u, s)) DEFINE_METASIG(IM(Char_Int_RetVoid, u i, v)) DEFINE_METASIG_T(IM(CultureInfo_RetVoid, C(CULTURE_INFO), v)) diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h index 6cecd55135ad..ffb7a94b273a 100644 --- a/src/vm/mscorlib.h +++ b/src/vm/mscorlib.h @@ -885,6 +885,8 @@ DEFINE_METHOD(STRING, WCSLEN, wcslen, DEFINE_PROPERTY(STRING, LENGTH, Length, Int) DEFINE_CLASS(UTF8_STRING, System, Utf8String) +DEFINE_METHOD(UTF8_STRING, CTORF_READONLYSPANOFBYTE,Ctor, IM_ReadOnlySpanOfByte_RetUtf8Str) +DEFINE_METHOD(UTF8_STRING, CTORF_READONLYSPANOFCHAR,Ctor, IM_ReadOnlySpanOfChar_RetUtf8Str) DEFINE_CLASS(STRING_BUILDER, Text, StringBuilder) DEFINE_PROPERTY(STRING_BUILDER, LENGTH, Length, Int) From bbdcae4edea96525a052aa073e9c1f51e58abc1d Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Fri, 18 May 2018 08:36:23 -0700 Subject: [PATCH 07/12] Block RuntimeHelpers.GetUninitializedObject() --- src/System.Private.CoreLib/Resources/Strings.resx | 3 +++ src/vm/reflectioninvocation.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/System.Private.CoreLib/Resources/Strings.resx b/src/System.Private.CoreLib/Resources/Strings.resx index cb7243ae09e2..1b0ebb76b521 100644 --- a/src/System.Private.CoreLib/Resources/Strings.resx +++ b/src/System.Private.CoreLib/Resources/Strings.resx @@ -1399,6 +1399,9 @@ Uninitialized Strings cannot be created. + + Uninitialized Utf8Strings cannot be created. + The object's type must not be a Windows Runtime type. diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp index 6dea9b760c46..df0ccc9a8e98 100644 --- a/src/vm/reflectioninvocation.cpp +++ b/src/vm/reflectioninvocation.cpp @@ -2680,8 +2680,8 @@ FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBa PREFIX_ASSUME(pMT != NULL); //We don't allow unitialized strings. - if (pMT == g_pStringClass) { - COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings")); + if (pMT->HasComponentSize()) { + COMPlusThrow(kArgumentException, pMT == g_pStringClass ? W("Argument_NoUninitializedStrings") : W("Argument_NoUninitializedUtf8Strings")); } // if this is an abstract class or an interface type then we will From a382e1dccb20a54e361c98876d924a6653dd125c Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Fri, 18 May 2018 09:44:59 -0700 Subject: [PATCH 08/12] Incorporate PR feedback --- src/System.Private.CoreLib/Resources/Strings.resx | 5 +---- src/System.Private.CoreLib/src/System/Utf8String.cs | 8 ++++++++ src/vm/reflectioninvocation.cpp | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/System.Private.CoreLib/Resources/Strings.resx b/src/System.Private.CoreLib/Resources/Strings.resx index 1b0ebb76b521..fd24321c1b96 100644 --- a/src/System.Private.CoreLib/Resources/Strings.resx +++ b/src/System.Private.CoreLib/Resources/Strings.resx @@ -1397,10 +1397,7 @@ The object has no underlying COM data associated with it. - Uninitialized Strings cannot be created. - - - Uninitialized Utf8Strings cannot be created. + Uninitialized strings cannot be created. The object's type must not be a Windows Runtime type. diff --git a/src/System.Private.CoreLib/src/System/Utf8String.cs b/src/System.Private.CoreLib/src/System/Utf8String.cs index cee7d89588c5..2c7ce8d226b7 100644 --- a/src/System.Private.CoreLib/src/System/Utf8String.cs +++ b/src/System.Private.CoreLib/src/System/Utf8String.cs @@ -20,6 +20,8 @@ private Utf8String() { } // Suppress creation of the public constructor. No one public int Length => _length; public ref readonly byte GetPinnableReference() => ref _firstByte; + public static readonly Utf8String Empty = FastAllocate(0); + // Utf8String constructors // These are special. The implementation methods for these have a different signature from the // declared constructors. @@ -35,6 +37,9 @@ private Utf8String() { } // Suppress creation of the public constructor. No one #endif private Utf8String Ctor(ReadOnlySpan value) { + if (value.Length == 0) + return Empty; + Utf8String newString = FastAllocate(value.Length); unsafe { @@ -58,6 +63,9 @@ private Utf8String Ctor(ReadOnlySpan value) #endif private Utf8String Ctor(ReadOnlySpan value) { + if (value.Length == 0) + return Empty; + Encoding e = Encoding.UTF8; int length = e.GetByteCount(value); Utf8String newString = FastAllocate(length); diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp index df0ccc9a8e98..fb91093abfd7 100644 --- a/src/vm/reflectioninvocation.cpp +++ b/src/vm/reflectioninvocation.cpp @@ -2681,7 +2681,7 @@ FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBa //We don't allow unitialized strings. if (pMT->HasComponentSize()) { - COMPlusThrow(kArgumentException, pMT == g_pStringClass ? W("Argument_NoUninitializedStrings") : W("Argument_NoUninitializedUtf8Strings")); + COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings")); } // if this is an abstract class or an interface type then we will From 56ea91c675482adf49362da635c29fb5bc9ef5eb Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Fri, 18 May 2018 13:10:53 -0700 Subject: [PATCH 09/12] No longer need private constructor --- src/System.Private.CoreLib/src/System/Utf8String.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Utf8String.cs b/src/System.Private.CoreLib/src/System/Utf8String.cs index 2c7ce8d226b7..d6c5290caf1d 100644 --- a/src/System.Private.CoreLib/src/System/Utf8String.cs +++ b/src/System.Private.CoreLib/src/System/Utf8String.cs @@ -15,8 +15,6 @@ public sealed class Utf8String private readonly int _length; private byte _firstByte; // would like to mark "readonly" but need to take ref to it in our magic "constructor" that C# doesn't recognize as a constructor. - private Utf8String() { } // Suppress creation of the public constructor. No one actually calls this. - public int Length => _length; public ref readonly byte GetPinnableReference() => ref _firstByte; From 42791394999ad5cffcd3a584f3d27b8f07e7308b Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Fri, 18 May 2018 13:23:25 -0700 Subject: [PATCH 10/12] Put the readonly back. --- src/System.Private.CoreLib/src/System/Utf8String.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Private.CoreLib/src/System/Utf8String.cs b/src/System.Private.CoreLib/src/System/Utf8String.cs index d6c5290caf1d..6ff7e0a1b0e9 100644 --- a/src/System.Private.CoreLib/src/System/Utf8String.cs +++ b/src/System.Private.CoreLib/src/System/Utf8String.cs @@ -13,7 +13,7 @@ public sealed class Utf8String { // Do not reorder these fields. Must match layout of Utf8StringObject in object.h. private readonly int _length; - private byte _firstByte; // would like to mark "readonly" but need to take ref to it in our magic "constructor" that C# doesn't recognize as a constructor. + private readonly byte _firstByte; public int Length => _length; public ref readonly byte GetPinnableReference() => ref _firstByte; From a3ce65a13db0c817a3f7a13e1e2e9ff8c444a212 Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Fri, 18 May 2018 13:44:17 -0700 Subject: [PATCH 11/12] Make MethodBase.Invoke aware of Utf8String's specialness --- src/vm/reflectioninvocation.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp index fb91093abfd7..67bde28a10ac 100644 --- a/src/vm/reflectioninvocation.cpp +++ b/src/vm/reflectioninvocation.cpp @@ -1007,6 +1007,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod, // Skip the activation optimization for remoting because of remoting proxy is not always activated. // It would be nice to clean this up and get remoting to always activate methodtable behind the proxy. BOOL fForceActivationForRemoting = FALSE; + BOOL fCtorOfVariableSizedObject = FALSE; if (fConstructor) { @@ -1024,7 +1025,8 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod, MethodTable * pMT = ownerType.AsMethodTable(); { - if (pMT != g_pStringClass) + fCtorOfVariableSizedObject = pMT->HasComponentSize(); + if (!fCtorOfVariableSizedObject) gc.retVal = pMT->Allocate(); } } @@ -1331,7 +1333,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod, if (fConstructor) { // We have a special case for Strings...The object is returned... - if (ownerType == TypeHandle(g_pStringClass)) { + if (fCtorOfVariableSizedObject) { PVOID pReturnValue = &callDescrData.returnValue; gc.retVal = *(OBJECTREF *)pReturnValue; } From b9b8040fe5e2bab090b3db9ff412f7bae5ebe530 Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Tue, 5 Jun 2018 07:42:05 -0700 Subject: [PATCH 12/12] Fix build break --- src/vm/object.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm/object.inl b/src/vm/object.inl index efa94275f1d1..91247b60925e 100644 --- a/src/vm/object.inl +++ b/src/vm/object.inl @@ -76,7 +76,7 @@ __forceinline /*static*/ DWORD Utf8StringObject::GetBaseSize() { LIMITED_METHOD_DAC_CONTRACT; - return ObjSizeOf(Object) + sizeof(DWORD) /* length */ + sizeof(BYTE) /* null terminator */; + return OBJECT_BASESIZE + sizeof(DWORD) /* length */ + sizeof(BYTE) /* null terminator */; } __forceinline /*static*/ SIZE_T Utf8StringObject::GetSize(DWORD strLen)