Skip to content
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

Update string marshallers to V2 shape #71849

Merged
merged 8 commits into from
Jul 10, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 19 additions & 32 deletions src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1654,7 +1654,9 @@ class UTF8StringMarshaller : Marshaller

private ILLocalVariable? _marshallerInstance = null;

private TypeDesc Marshaller => Context.SystemModule.GetKnownType("System.Runtime.InteropServices.Marshalling", "Utf8StringMarshaller");
private MetadataType Marshaller => Context.SystemModule.GetKnownType("System.Runtime.InteropServices.Marshalling", "Utf8StringMarshaller");

private MetadataType MarshallerIn => Marshaller.GetNestedType("ManagedToUnmanagedIn");

internal override bool CleanupRequired => true;

Expand All @@ -1663,24 +1665,27 @@ internal override void EmitElementCleanup(ILCodeStream codeStream, ILEmitter emi
Debug.Assert(_marshallerInstance is null);

codeStream.Emit(ILOpcode.call, emitter.NewToken(
InteropTypes.GetMarshal(Context).GetKnownMethod("FreeCoTaskMem", null)));
Marshaller.GetKnownMethod("Free", null)));
}

protected override void TransformManagedToNative(ILCodeStream codeStream)
{
ILEmitter emitter = _ilCodeStreams.Emitter;
TypeDesc marshaller = Marshaller;

if (_marshallerInstance == null)
_marshallerInstance = emitter.NewLocal(marshaller);

if (In && !Out && !IsManagedByRef)
{
TypeDesc marshallerIn = MarshallerIn;

if (_marshallerInstance == null)
_marshallerInstance = emitter.NewLocal(marshallerIn);

var vBuffer = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.IntPtr));
codeStream.EmitLdc(LocalBufferLength);
codeStream.Emit(ILOpcode.localloc);
codeStream.EmitStLoc(vBuffer);

codeStream.EmitLdLoca(_marshallerInstance.Value);

LoadManagedValue(codeStream);

// Create ReadOnlySpan<byte> from the stack-allocated buffer
Expand All @@ -1693,62 +1698,44 @@ protected override void TransformManagedToNative(ILCodeStream codeStream)
codeStream.Emit(ILOpcode.newobj, emitter.NewToken(spanOfByte.GetKnownMethod(".ctor",
new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void),
new TypeDesc[] { Context.GetWellKnownType(WellKnownType.Void).MakePointerType(), Context.GetWellKnownType(WellKnownType.Int32) }))));
codeStream.Emit(ILOpcode.call, emitter.NewToken(marshallerIn.GetKnownMethod("FromManaged", null)));

codeStream.Emit(ILOpcode.newobj, emitter.NewToken(marshaller.GetKnownMethod(".ctor",
new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void),
new TypeDesc[] { Context.GetWellKnownType(WellKnownType.String), spanOfByte }))));
codeStream.EmitStLoc(_marshallerInstance.Value);
codeStream.EmitLdLoca(_marshallerInstance.Value);
codeStream.Emit(ILOpcode.call, emitter.NewToken(marshallerIn.GetKnownMethod("ToUnmanaged", null)));
}
else
{
LoadManagedValue(codeStream);
codeStream.Emit(ILOpcode.newobj, emitter.NewToken(marshaller.GetKnownMethod(".ctor",
new MethodSignature(0, 0, Context.GetWellKnownType(WellKnownType.Void),
new TypeDesc[] { Context.GetWellKnownType(WellKnownType.String) }))));
codeStream.EmitStLoc(_marshallerInstance.Value);
codeStream.Emit(ILOpcode.call, emitter.NewToken(Marshaller.GetKnownMethod("ConvertToUnmanaged", null)));
}

codeStream.EmitLdLoca(_marshallerInstance.Value);
codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("ToNativeValue", null)));
StoreNativeValue(codeStream);
}

protected override void TransformNativeToManaged(ILCodeStream codeStream)
{
ILEmitter emitter = _ilCodeStreams.Emitter;
TypeDesc marshaller = Marshaller;

if (_marshallerInstance == null)
_marshallerInstance = emitter.NewLocal(marshaller);

codeStream.EmitLdLoca(_marshallerInstance.Value);
LoadNativeValue(codeStream);
codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("FromNativeValue", null)));

codeStream.EmitLdLoca(_marshallerInstance.Value);
codeStream.Emit(ILOpcode.call, emitter.NewToken(marshaller.GetKnownMethod("ToManaged", null)));
codeStream.Emit(ILOpcode.call, emitter.NewToken(Marshaller.GetKnownMethod("ConvertToManaged", null)));
StoreManagedValue(codeStream);
}

protected override void EmitCleanupManaged(ILCodeStream codeStream)
{
ILEmitter emitter = _ilCodeStreams.Emitter;

if (In && !Out && !IsManagedByRef)
if (_marshallerInstance != null)
{
Debug.Assert(_marshallerInstance != null);

codeStream.EmitLdLoca(_marshallerInstance.Value);
codeStream.Emit(ILOpcode.call, emitter.NewToken(
Marshaller.GetKnownMethod("FreeNative", null)));
MarshallerIn.GetKnownMethod("Free", null)));
}
else
{
// The marshaller instance is not guaranteed to be initialized with the latest native value.
// Free the native value directly.
LoadNativeValue(codeStream);
codeStream.Emit(ILOpcode.call, emitter.NewToken(
InteropTypes.GetMarshal(Context).GetKnownMethod("FreeCoTaskMem", null)));
Marshaller.GetKnownMethod("Free", null)));
}
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/coreclr/vm/assemblynative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,6 @@ extern "C" void QCALLTYPE AssemblyNative_GetType(QCall::AssemblyHandle pAssembly

BEGIN_QCALL;

if (!wszName)
COMPlusThrowArgumentNull(W("name"), W("ArgumentNull_String"));

BOOL prohibitAsmQualifiedName = TRUE;

AssemblyBinder * pBinder = NULL;
Expand Down
29 changes: 28 additions & 1 deletion src/coreclr/vm/binder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,34 @@ PTR_MethodTable CoreLibBinder::LookupClassLocal(BinderClassID id)

const CoreLibClassDescription *d = m_classDescriptions + (int)id;

pMT = ClassLoader::LoadTypeByNameThrowing(GetModule()->GetAssembly(), d->nameSpace, d->name).AsMethodTable();
LPCUTF8 nameSpace = d->nameSpace;
LPCUTF8 name = d->name;

LPCUTF8 nestedTypeMaybe = strchr(name, '+');
if (nestedTypeMaybe == NULL)
{
NameHandle nameHandle = NameHandle(nameSpace, name);
pMT = ClassLoader::LoadTypeByNameThrowing(GetModule()->GetAssembly(), &nameHandle).AsMethodTable();
}
else
{
// Handle the nested type scenario.
// The same NameHandle must be used to retain the scope to look for the nested type.
NameHandle nameHandle(GetModule(), mdtBaseType);

SString splitName(SString::Utf8, name, (COUNT_T)(nestedTypeMaybe - name));
nameHandle.SetName(nameSpace, splitName.GetUTF8());

// The side-effect of updating the scope in the NameHandle is the point of the call.
(void)ClassLoader::LoadTypeByNameThrowing(GetModule()->GetAssembly(), &nameHandle);

// Now load the nested type.
nameHandle.SetName(NULL, nestedTypeMaybe + 1);

// We don't support nested types in nested types.
_ASSERTE(strchr(nameHandle.GetName(), '+') == NULL);
pMT = ClassLoader::LoadTypeByNameThrowing(GetModule()->GetAssembly(), &nameHandle).AsMethodTable();
}

_ASSERTE(pMT->GetModule() == GetModule());

Expand Down
27 changes: 20 additions & 7 deletions src/coreclr/vm/clsload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,14 +275,25 @@ BOOL ClassLoader::IsTypicalInstantiation(Module *pModule, mdToken token, Instant
return TRUE;
}

// External class loader entry point: load a type by name
/*static*/
TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly,
LPCUTF8 nameSpace,
LPCUTF8 name,
NotFoundAction fNotFound,
ClassLoader::LoadTypesFlag fLoadTypes,
ClassLoadLevel level)
{
WRAPPER_NO_CONTRACT;
NameHandle nameHandle(nameSpace, name);
return LoadTypeByNameThrowing(pAssembly, &nameHandle, fNotFound, fLoadTypes, level);
}

/*static*/
TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly,
NameHandle *pNameHandle,
NotFoundAction fNotFound,
ClassLoader::LoadTypesFlag fLoadTypes,
ClassLoadLevel level)
{
CONTRACT(TypeHandle)
{
Expand All @@ -294,6 +305,7 @@ TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly,
if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }

PRECONDITION(CheckPointer(pAssembly));
PRECONDITION(pNameHandle != NULL);
PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED);
POSTCONDITION(CheckPointer(RETVAL,
(fNotFound == ThrowIfNotFound && fLoadTypes == LoadTypes )? NULL_NOT_OK : NULL_OK));
Expand All @@ -305,13 +317,14 @@ TypeHandle ClassLoader::LoadTypeByNameThrowing(Assembly *pAssembly,
}
CONTRACT_END

NameHandle nameHandle(nameSpace, name);
if (fLoadTypes == DontLoadTypes)
nameHandle.SetTokenNotToLoad(tdAllTypes);
if (fNotFound == ThrowIfNotFound)
RETURN pAssembly->GetLoader()->LoadTypeHandleThrowIfFailed(&nameHandle, level);
if (fLoadTypes == ClassLoader::DontLoadTypes)
pNameHandle->SetTokenNotToLoad(tdAllTypes);

ClassLoader* classLoader = pAssembly->GetLoader();
if (fNotFound == ClassLoader::ThrowIfNotFound)
RETURN classLoader->LoadTypeHandleThrowIfFailed(pNameHandle, level);
else
RETURN pAssembly->GetLoader()->LoadTypeHandleThrowing(&nameHandle, level);
RETURN classLoader->LoadTypeHandleThrowing(pNameHandle, level);
}

#ifndef DACCESS_COMPILE
Expand Down
11 changes: 10 additions & 1 deletion src/coreclr/vm/clsload.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -699,14 +699,23 @@ class ClassLoader
LoadTypesFlag fLoadTypes = LoadTypes,
ClassLoadLevel level = CLASS_LOADED);

// Load types by name
// External class loader entry point
// Load types by name - doesn't support nested types.
// See overload using NameHandle.
static TypeHandle LoadTypeByNameThrowing(Assembly *pAssembly,
LPCUTF8 nameSpace,
LPCUTF8 name,
NotFoundAction fNotFound = ThrowIfNotFound,
LoadTypesFlag fLoadTypes = LoadTypes,
ClassLoadLevel level = CLASS_LOADED);

// Load types using a NameHandle.
static TypeHandle LoadTypeByNameThrowing(Assembly *pAssembly,
NameHandle *pNameHandle,
NotFoundAction fNotFound = ThrowIfNotFound,
LoadTypesFlag fLoadTypes = LoadTypes,
ClassLoadLevel level = CLASS_LOADED);

// Resolve a TypeRef to a TypeDef
// (Just a no-op on TypeDefs)
// Return FALSE if operation failed (e.g. type does not exist)
Expand Down
21 changes: 15 additions & 6 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
// See usage in this file itself and on the link (the assembly name for feature switch in this file will be System.Private.CoreLib),
// https://github.com/dotnet/designs/blob/main/accepted/2020/feature-switch.md#generate-the-right-input-for-the-linker-in-sdk
//
// The FOR_ILLINK define is set when this file is being processed for the IL linker.
//
#ifndef BEGIN_ILLINK_FEATURE_SWITCH
#define BEGIN_ILLINK_FEATURE_SWITCH(featureName, featureValue, featureDefault)
#endif
Expand Down Expand Up @@ -1188,12 +1190,19 @@ DEFINE_METHOD(ICASTABLEHELPERS, GETIMPLTYPE, GetImplType, SM_ICast
#endif // FEATURE_ICASTABLE

DEFINE_CLASS(UTF8STRINGMARSHALLER, Marshalling, Utf8StringMarshaller)
DEFINE_METHOD(UTF8STRINGMARSHALLER, CTOR, .ctor, IM_Str_RetVoid)
DEFINE_METHOD(UTF8STRINGMARSHALLER, CTOR_SPAN, .ctor, IM_Str_SpanOfByte_RetVoid)
DEFINE_METHOD(UTF8STRINGMARSHALLER, TO_NATIVE_VALUE, ToNativeValue, IM_RetPtrByte)
DEFINE_METHOD(UTF8STRINGMARSHALLER, FROM_NATIVE_VALUE, FromNativeValue, IM_PtrByte_RetVoid)
DEFINE_METHOD(UTF8STRINGMARSHALLER, TO_MANAGED, ToManaged, IM_RetStr)
DEFINE_METHOD(UTF8STRINGMARSHALLER, FREE_NATIVE, FreeNative, IM_RetVoid)
DEFINE_METHOD(UTF8STRINGMARSHALLER, CONVERT_TO_MANAGED, ConvertToManaged, SM_PtrByte_RetStr)
DEFINE_METHOD(UTF8STRINGMARSHALLER, CONVERT_TO_UNMANAGED, ConvertToUnmanaged, SM_Str_RetPtrByte)
DEFINE_METHOD(UTF8STRINGMARSHALLER, FREE, Free, SM_PtrByte_RetVoid)

// The generator for the linker XML doesn't understand inner classes so generation
// needs to skip the following type.
// See https://github.com/dotnet/runtime/issues/71847
#ifndef FOR_ILLINK
DEFINE_CLASS(UTF8STRINGMARSHALLER_IN, Marshalling, Utf8StringMarshaller+ManagedToUnmanagedIn)
DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, FROM_MANAGED, FromManaged, IM_Str_SpanOfByte_RetVoid)
DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, TO_UNMANAGED, ToUnmanaged, IM_RetPtrByte)
DEFINE_METHOD(UTF8STRINGMARSHALLER_IN, FREE, Free, IM_RetVoid)
#endif // FOR_ILLINK

DEFINE_CLASS(UTF8BUFFERMARSHALER, StubHelpers, UTF8BufferMarshaler)
DEFINE_METHOD(UTF8BUFFERMARSHALER, CONVERT_TO_NATIVE, ConvertToNative, NoSig)
Expand Down
42 changes: 16 additions & 26 deletions src/coreclr/vm/ilmarshalers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1958,19 +1958,24 @@ void ILCUTF8Marshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit)
{
STANDARD_VM_CONTRACT;

if (m_dwInstance == LOCAL_NUM_UNUSED)
m_dwInstance = pslILEmit->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__UTF8STRINGMARSHALLER)));

bool bPassByValueInOnly = IsIn(m_dwMarshalFlags) && !IsOut(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags);
if (bPassByValueInOnly)
{
if (m_dwInstance == LOCAL_NUM_UNUSED)
m_dwInstance = pslILEmit->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__UTF8STRINGMARSHALLER_IN)));

DWORD dwBuffer = pslILEmit->NewLocal(ELEMENT_TYPE_I);
pslILEmit->EmitLDC(LOCAL_BUFFER_LENGTH);
pslILEmit->EmitLOCALLOC();
pslILEmit->EmitSTLOC(dwBuffer);

// Load the marshaller instance.
pslILEmit->EmitLDLOCA(m_dwInstance);

// Argument 1
EmitLoadManagedValue(pslILEmit);

// Argument 2
// Create ReadOnlySpan<byte> from the stack-allocated buffer
pslILEmit->EmitLDLOC(dwBuffer);
pslILEmit->EmitLDC(LOCAL_BUFFER_LENGTH);
Expand All @@ -1979,57 +1984,42 @@ void ILCUTF8Marshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit)
TypeHandle(CoreLibBinder::GetClass(CLASS__SPAN)).Instantiate(Instantiation(&thByte, 1)).AsMethodTable(),
FALSE, Instantiation(), FALSE);
pslILEmit->EmitNEWOBJ(pslILEmit->GetToken(pSpanCtor), 2);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER_IN__FROM_MANAGED, 2, 0);

pslILEmit->EmitNEWOBJ(METHOD__UTF8STRINGMARSHALLER__CTOR_SPAN, 2);
pslILEmit->EmitSTLOC(m_dwInstance);

pslILEmit->EmitLDLOCA(m_dwInstance);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER_IN__TO_UNMANAGED, 1, 1);
}
else
{
EmitLoadManagedValue(pslILEmit);
pslILEmit->EmitNEWOBJ(METHOD__UTF8STRINGMARSHALLER__CTOR, 1);
pslILEmit->EmitSTLOC(m_dwInstance);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__CONVERT_TO_UNMANAGED, 1, 1);
}

pslILEmit->EmitLDLOCA(m_dwInstance);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__TO_NATIVE_VALUE, 1, 1);
EmitStoreNativeValue(pslILEmit);
}

void ILCUTF8Marshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit)
{
STANDARD_VM_CONTRACT;

if (m_dwInstance == LOCAL_NUM_UNUSED)
m_dwInstance = pslILEmit->NewLocal(LocalDesc(CoreLibBinder::GetClass(CLASS__UTF8STRINGMARSHALLER)));

pslILEmit->EmitLDLOCA(m_dwInstance);
EmitLoadNativeValue(pslILEmit);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__FROM_NATIVE_VALUE, 2, 0);

pslILEmit->EmitLDLOCA(m_dwInstance);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__TO_MANAGED, 1, 1);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__CONVERT_TO_MANAGED, 1, 1);
EmitStoreManagedValue(pslILEmit);
}

void ILCUTF8Marshaler::EmitClearNative(ILCodeStream* pslILEmit)
{
STANDARD_VM_CONTRACT;

bool bPassByValueInOnly = IsIn(m_dwMarshalFlags) && !IsOut(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags);
if (bPassByValueInOnly)
if (m_dwInstance != LOCAL_NUM_UNUSED)
{
_ASSERTE(m_dwInstance != LOCAL_NUM_UNUSED);

pslILEmit->EmitLDLOCA(m_dwInstance);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__FREE_NATIVE, 1, 0);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER_IN__FREE, 0, 0);
}
else
{
// The marshaller instance is not guaranteed to be initialized with the latest native value.
// Free the native value directly.
EmitLoadNativeValue(pslILEmit);
pslILEmit->EmitCALL(METHOD__MARSHAL__FREE_CO_TASK_MEM, 1, 0);
pslILEmit->EmitCALL(METHOD__UTF8STRINGMARSHALLER__FREE, 1, 0);
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/coreclr/vm/metasig.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ DEFINE_METASIG_T(SM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s))
DEFINE_METASIG(IM(Obj_Int_RetIntPtr, j i, I))

DEFINE_METASIG(IM(ArrByte_Int_Int_RetVoid, a(b) i i, v))
DEFINE_METASIG(IM(PtrByte_RetVoid, P(b), v))

DEFINE_METASIG(IM(Char_Char_RetStr, u u, s))
DEFINE_METASIG(IM(Char_Int_RetVoid, u i, v))
Expand Down Expand Up @@ -491,7 +490,6 @@ DEFINE_METASIG_T(SM(Str_CultureInfo_RetStr, s C(CULTURE_INFO), s))
DEFINE_METASIG_T(SM(Str_CultureInfo_RefBool_RetStr, s C(CULTURE_INFO) r(F), s))
DEFINE_METASIG(SM(PtrPtrChar_PtrPtrChar_Int_RetVoid, P(P(u)) P(P(u)) i, v))
DEFINE_METASIG(SM(PtrChar_Int_PtrPtrChar_RetArrStr, P(u) i P(P(u)), a(s)))
DEFINE_METASIG(IM(Str_RetVoid, s, v))
DEFINE_METASIG(SM(RefBool_RefBool_RetVoid, r(F) r(F), v))
DEFINE_METASIG_T(IM(Str_Exception_RetVoid, s C(EXCEPTION), v))
DEFINE_METASIG(IM(Str_Obj_RetVoid, s j, v))
Expand Down Expand Up @@ -615,6 +613,10 @@ DEFINE_METASIG_T(IM(Str_SpanOfByte_RetVoid, s GI(g(SPAN), 1, b), v))
DEFINE_METASIG(IM(RetPtrByte, , P(b)))
DEFINE_METASIG(IM(VoidPtr_Int_RetVoid, P(v) i, v))

DEFINE_METASIG(SM(PtrByte_RetStr, P(b), s))
DEFINE_METASIG(SM(Str_RetPtrByte, s, P(b)))
DEFINE_METASIG(SM(PtrByte_RetVoid, P(b), v))

// Undefine macros in case we include the file again in the compilation unit

#undef DEFINE_METASIG
Expand Down
Loading