Permalink
Fetching contributors…
Cannot retrieve contributors at this time
14477 lines (11889 sloc) 460 KB
// 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: JITinterface.CPP
//
// ===========================================================================
#include "common.h"
#include "jitinterface.h"
#include "codeman.h"
#include "method.hpp"
#include "class.h"
#include "object.h"
#include "field.h"
#include "stublink.h"
#include "virtualcallstub.h"
#include "corjit.h"
#include "eeconfig.h"
#include "excep.h"
#include "log.h"
#include "excep.h"
#include "float.h" // for isnan
#include "dbginterface.h"
#include "dllimport.h"
#include "gcheaputilities.h"
#include "comdelegate.h"
#include "jitperf.h" // to track jit perf
#include "corprof.h"
#include "eeprofinterfaces.h"
#include "perfcounters.h"
#ifdef PROFILING_SUPPORTED
#include "proftoeeinterfaceimpl.h"
#include "eetoprofinterfaceimpl.h"
#include "eetoprofinterfaceimpl.inl"
#include "profilepriv.h"
#endif
#include "ecall.h"
#include "generics.h"
#include "typestring.h"
#include "stackprobe.h"
#include "typedesc.h"
#include "genericdict.h"
#include "array.h"
#include "debuginfostore.h"
#include "safemath.h"
#include "runtimehandles.h"
#include "sigbuilder.h"
#include "openum.h"
#ifdef HAVE_GCCOVER
#include "gccover.h"
#endif // HAVE_GCCOVER
#include "mdaassistants.h"
#ifdef FEATURE_PREJIT
#include "compile.h"
#include "corcompile.h"
#endif // FEATURE_PREJIT
#ifdef FEATURE_INTERPRETER
#include "interpreter.h"
#endif // FEATURE_INTERPRETER
#ifdef FEATURE_PERFMAP
#include "perfmap.h"
#endif
// The Stack Overflow probe takes place in the COOPERATIVE_TRANSITION_BEGIN() macro
//
#define JIT_TO_EE_TRANSITION() MAKE_CURRENT_THREAD_AVAILABLE_EX(m_pThread); \
_ASSERTE(CURRENT_THREAD == GetThread()); \
INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; \
COOPERATIVE_TRANSITION_BEGIN(); \
START_NON_JIT_PERF();
#define EE_TO_JIT_TRANSITION() STOP_NON_JIT_PERF(); \
COOPERATIVE_TRANSITION_END(); \
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE;
#define JIT_TO_EE_TRANSITION_LEAF()
#define EE_TO_JIT_TRANSITION_LEAF()
#if defined(CROSSGEN_COMPILE)
static const char *const hlpNameTable[CORINFO_HELP_COUNT] = {
#define JITHELPER(code, pfnHelper, sig) #code,
#include "jithelpers.h"
};
#endif
#ifdef DACCESS_COMPILE
// The real definitions are in jithelpers.cpp. However, those files are not included in the DAC build.
// Hence, we add them here.
GARY_IMPL(VMHELPDEF, hlpFuncTable, CORINFO_HELP_COUNT);
GARY_IMPL(VMHELPDEF, hlpDynamicFuncTable, DYNAMIC_CORINFO_HELP_COUNT);
#else // DACCESS_COMPILE
/*********************************************************************/
#if defined(ENABLE_PERF_COUNTERS)
LARGE_INTEGER g_lastTimeInJitCompilation;
#endif
/*********************************************************************/
inline CORINFO_MODULE_HANDLE GetScopeHandle(MethodDesc* method)
{
LIMITED_METHOD_CONTRACT;
if (method->IsDynamicMethod())
{
return MakeDynamicScope(method->AsDynamicMethodDesc()->GetResolver());
}
else
{
return GetScopeHandle(method->GetModule());
}
}
//This is common refactored code from within several of the access check functions.
BOOL ModifyCheckForDynamicMethod(DynamicResolver *pResolver,
TypeHandle *pOwnerTypeForSecurity,
AccessCheckOptions::AccessCheckType *pAccessCheckType,
DynamicResolver** ppAccessContext)
{
CONTRACTL {
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pResolver));
PRECONDITION(CheckPointer(pOwnerTypeForSecurity));
PRECONDITION(CheckPointer(pAccessCheckType));
PRECONDITION(CheckPointer(ppAccessContext));
PRECONDITION(*pAccessCheckType == AccessCheckOptions::kNormalAccessibilityChecks);
} CONTRACTL_END;
BOOL doAccessCheck = TRUE;
//Do not blindly initialize fields, since they've already got important values.
DynamicResolver::SecurityControlFlags dwSecurityFlags = DynamicResolver::Default;
TypeHandle dynamicOwner;
pResolver->GetJitContext(&dwSecurityFlags, &dynamicOwner);
if (!dynamicOwner.IsNull())
*pOwnerTypeForSecurity = dynamicOwner;
if (dwSecurityFlags & DynamicResolver::SkipVisibilityChecks)
{
doAccessCheck = FALSE;
}
else if (dwSecurityFlags & DynamicResolver::RestrictedSkipVisibilityChecks)
{
*pAccessCheckType = AccessCheckOptions::kRestrictedMemberAccessNoTransparency;
}
else
{
*pAccessCheckType = AccessCheckOptions::kNormalAccessNoTransparency;
}
return doAccessCheck;
}
/*****************************************************************************/
// Initialize from data we passed across to the JIT
inline static void GetTypeContext(const CORINFO_SIG_INST *info, SigTypeContext *pTypeContext)
{
LIMITED_METHOD_CONTRACT;
SigTypeContext::InitTypeContext(
Instantiation((TypeHandle *) info->classInst, info->classInstCount),
Instantiation((TypeHandle *) info->methInst, info->methInstCount),
pTypeContext);
}
static MethodDesc* GetMethodFromContext(CORINFO_CONTEXT_HANDLE context)
{
LIMITED_METHOD_CONTRACT;
if (((size_t) context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
{
return NULL;
}
else
{
return GetMethod((CORINFO_METHOD_HANDLE)((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK));
}
}
static TypeHandle GetTypeFromContext(CORINFO_CONTEXT_HANDLE context)
{
LIMITED_METHOD_CONTRACT;
if (((size_t) context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
{
return TypeHandle((CORINFO_CLASS_HANDLE) ((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK));
}
else
{
MethodTable * pMT = GetMethodFromContext(context)->GetMethodTable();
return TypeHandle(pMT);
}
}
// Initialize from a context parameter passed to the JIT and back. This is a parameter
// that indicates which method is being jitted.
inline static void GetTypeContext(CORINFO_CONTEXT_HANDLE context, SigTypeContext *pTypeContext)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
SO_TOLERANT;
MODE_ANY;
PRECONDITION(context != NULL);
}
CONTRACTL_END;
if (GetMethodFromContext(context))
{
SigTypeContext::InitTypeContext(GetMethodFromContext(context), pTypeContext);
}
else
{
SigTypeContext::InitTypeContext(GetTypeFromContext(context), pTypeContext);
}
}
static BOOL ContextIsShared(CORINFO_CONTEXT_HANDLE context)
{
LIMITED_METHOD_CONTRACT;
MethodDesc *pContextMD = GetMethodFromContext(context);
if (pContextMD != NULL)
{
return pContextMD->IsSharedByGenericInstantiations();
}
else
{
// Type handle contexts are non-shared and are used for inlining of
// non-generic methods in generic classes
return FALSE;
}
}
// Returns true if context is providing any generic variables
static BOOL ContextIsInstantiated(CORINFO_CONTEXT_HANDLE context)
{
LIMITED_METHOD_CONTRACT;
if (GetMethodFromContext(context))
{
return GetMethodFromContext(context)->HasClassOrMethodInstantiation();
}
else
{
return GetTypeFromContext(context).HasInstantiation();
}
}
/*********************************************************************/
// This normalizes EE type information into the form expected by the JIT.
//
// If typeHnd contains exact type information, then *clsRet will contain
// the normalized CORINFO_CLASS_HANDLE information on return.
// Static
CorInfoType CEEInfo::asCorInfoType(CorElementType eeType,
TypeHandle typeHnd, /* optional in */
CORINFO_CLASS_HANDLE *clsRet/* optional out */ ) {
CONTRACT(CorInfoType) {
THROWS;
GC_TRIGGERS;
PRECONDITION((CorTypeInfo::IsGenericVariable(eeType)) ==
(!typeHnd.IsNull() && typeHnd.IsGenericVariable()));
PRECONDITION(eeType != ELEMENT_TYPE_GENERICINST);
} CONTRACT_END;
TypeHandle typeHndUpdated = typeHnd;
if (!typeHnd.IsNull())
{
CorElementType normType = typeHnd.GetInternalCorElementType();
// If we have a type handle, then it has the better type
// in some cases
if (eeType == ELEMENT_TYPE_VALUETYPE && !CorTypeInfo::IsObjRef(normType))
eeType = normType;
// Zap the typeHnd when the type _really_ is a primitive
// as far as verification is concerned. Returning a null class
// handle means it is is a primitive.
//
// Enums are exactly like primitives, even from a verification standpoint,
// so we zap the type handle in this case.
//
// However RuntimeTypeHandle etc. are reported as E_T_INT (or something like that)
// but don't count as primitives as far as verification is concerned...
//
// To make things stranger, TypedReference returns true for "IsTruePrimitive".
// However the JIT likes us to report the type handle in that case.
if (!typeHnd.IsTypeDesc() && (
(typeHnd.AsMethodTable()->IsTruePrimitive() && typeHnd != TypeHandle(g_TypedReferenceMT))
|| typeHnd.AsMethodTable()->IsEnum()) )
{
typeHndUpdated = TypeHandle();
}
}
static const BYTE map[] = {
CORINFO_TYPE_UNDEF,
CORINFO_TYPE_VOID,
CORINFO_TYPE_BOOL,
CORINFO_TYPE_CHAR,
CORINFO_TYPE_BYTE,
CORINFO_TYPE_UBYTE,
CORINFO_TYPE_SHORT,
CORINFO_TYPE_USHORT,
CORINFO_TYPE_INT,
CORINFO_TYPE_UINT,
CORINFO_TYPE_LONG,
CORINFO_TYPE_ULONG,
CORINFO_TYPE_FLOAT,
CORINFO_TYPE_DOUBLE,
CORINFO_TYPE_STRING,
CORINFO_TYPE_PTR, // PTR
CORINFO_TYPE_BYREF,
CORINFO_TYPE_VALUECLASS,
CORINFO_TYPE_CLASS,
CORINFO_TYPE_VAR, // VAR (type variable)
CORINFO_TYPE_CLASS, // ARRAY
CORINFO_TYPE_CLASS, // WITH
CORINFO_TYPE_REFANY,
CORINFO_TYPE_UNDEF, // VALUEARRAY_UNSUPPORTED
CORINFO_TYPE_NATIVEINT, // I
CORINFO_TYPE_NATIVEUINT, // U
CORINFO_TYPE_UNDEF, // R_UNSUPPORTED
// put the correct type when we know our implementation
CORINFO_TYPE_PTR, // FNPTR
CORINFO_TYPE_CLASS, // OBJECT
CORINFO_TYPE_CLASS, // SZARRAY
CORINFO_TYPE_VAR, // MVAR
CORINFO_TYPE_UNDEF, // CMOD_REQD
CORINFO_TYPE_UNDEF, // CMOD_OPT
CORINFO_TYPE_UNDEF, // INTERNAL
};
_ASSERTE(sizeof(map) == ELEMENT_TYPE_MAX);
_ASSERTE(eeType < (CorElementType) sizeof(map));
// spot check of the map
_ASSERTE((CorInfoType) map[ELEMENT_TYPE_I4] == CORINFO_TYPE_INT);
_ASSERTE((CorInfoType) map[ELEMENT_TYPE_PTR] == CORINFO_TYPE_PTR);
_ASSERTE((CorInfoType) map[ELEMENT_TYPE_TYPEDBYREF] == CORINFO_TYPE_REFANY);
CorInfoType res = ((unsigned)eeType < ELEMENT_TYPE_MAX) ? ((CorInfoType) map[(unsigned)eeType]) : CORINFO_TYPE_UNDEF;
if (clsRet)
*clsRet = CORINFO_CLASS_HANDLE(typeHndUpdated.AsPtr());
RETURN res;
}
inline static CorInfoType toJitType(TypeHandle typeHnd, CORINFO_CLASS_HANDLE *clsRet = NULL)
{
WRAPPER_NO_CONTRACT;
return CEEInfo::asCorInfoType(typeHnd.GetInternalCorElementType(), typeHnd, clsRet);
}
void CheckForEquivalenceAndLoadTypeBeforeCodeIsRun(Module *pModule, mdToken token, Module *pDefModule, mdToken defToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
SO_INTOLERANT;
}
CONTRACTL_END;
if (IsTypeDefEquivalent(defToken, pDefModule))
{
SigPointer sigPtr(*ptr);
TypeHandle th = sigPtr.GetTypeHandleThrowing(pModule, pTypeContext);
((ICorDynamicInfo *)pData)->classMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE(th.AsPtr()));
}
}
inline static void TypeEquivalenceFixupSpecificationHelper(ICorDynamicInfo * pCorInfo, MethodDesc *pMD)
{
STANDARD_VM_CONTRACT;
// A fixup is necessary to ensure that the parameters to the method are loaded before the method
// is called. In these cases we will not perform the appropriate loading when we load parameter
// types because with type equivalence, the parameter types at the call site do not necessarily
// match that those in the actual function. (They must be equivalent, but not necessarily the same.)
// In non-ngen scenarios this code here will force the types to be loaded directly by the call to
// HasTypeEquivalentStructParameters.
if (!pMD->IsVirtual())
{
if (pMD->HasTypeEquivalentStructParameters())
{
if (IsCompilationProcess())
pMD->WalkValueTypeParameters(pMD->GetMethodTable(), CheckForEquivalenceAndLoadTypeBeforeCodeIsRun, pCorInfo);
}
}
else
{
if (pMD->GetMethodTable()->DependsOnEquivalentOrForwardedStructs())
{
if (pMD->HasTypeEquivalentStructParameters())
pCorInfo->classMustBeLoadedBeforeCodeIsRun((CORINFO_CLASS_HANDLE)pMD->GetMethodTable());
}
}
}
//---------------------------------------------------------------------------------------
//
//@GENERICS:
// The method handle is used to instantiate method and class type parameters
// It's also used to determine whether an extra dictionary parameter is required
//
// sig - Input metadata signature
// scopeHnd - The signature is to be interpreted in the context of this scope (module)
// token - Metadata token used to refer to the signature (may be mdTokenNil for dynamic methods)
// sigRet - Resulting output signature in a format that is understood by native compilers
// pContextMD - The method with any instantiation information (may be NULL)
// localSig - Is it a local variables declaration, or a method signature (with return type, etc).
// contextType - The type with any instantiaton information
//
//static
void
CEEInfo::ConvToJitSig(
PCCOR_SIGNATURE pSig,
DWORD cbSig,
CORINFO_MODULE_HANDLE scopeHnd,
mdToken token,
CORINFO_SIG_INFO * sigRet,
MethodDesc * pContextMD,
bool localSig,
TypeHandle contextType)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
} CONTRACTL_END;
SigTypeContext typeContext;
if (pContextMD)
{
SigTypeContext::InitTypeContext(pContextMD, contextType, &typeContext);
}
else
{
SigTypeContext::InitTypeContext(contextType, &typeContext);
}
_ASSERTE(CORINFO_CALLCONV_DEFAULT == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_DEFAULT);
_ASSERTE(CORINFO_CALLCONV_VARARG == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_VARARG);
_ASSERTE(CORINFO_CALLCONV_MASK == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_MASK);
_ASSERTE(CORINFO_CALLCONV_HASTHIS == (CorInfoCallConv) IMAGE_CEE_CS_CALLCONV_HASTHIS);
TypeHandle typeHnd = TypeHandle();
sigRet->pSig = pSig;
sigRet->cbSig = cbSig;
sigRet->retTypeClass = 0;
sigRet->retTypeSigClass = 0;
sigRet->scope = scopeHnd;
sigRet->token = token;
sigRet->sigInst.classInst = (CORINFO_CLASS_HANDLE *) typeContext.m_classInst.GetRawArgs();
sigRet->sigInst.classInstCount = (unsigned) typeContext.m_classInst.GetNumArgs();
sigRet->sigInst.methInst = (CORINFO_CLASS_HANDLE *) typeContext.m_methodInst.GetRawArgs();
sigRet->sigInst.methInstCount = (unsigned) typeContext.m_methodInst.GetNumArgs();
SigPointer sig(pSig, cbSig);
if (!localSig)
{
// This is a method signature which includes calling convention, return type,
// arguments, etc
_ASSERTE(!sig.IsNull());
Module * module = GetModule(scopeHnd);
sigRet->flags = 0;
ULONG data;
IfFailThrow(sig.GetCallingConvInfo(&data));
sigRet->callConv = (CorInfoCallConv) data;
#ifdef PLATFORM_UNIX
if ((isCallConv(sigRet->callConv, IMAGE_CEE_CS_CALLCONV_VARARG)) ||
(isCallConv(sigRet->callConv, IMAGE_CEE_CS_CALLCONV_NATIVEVARARG)))
{
// This signature corresponds to a method that uses varargs, which are not supported.
COMPlusThrow(kInvalidProgramException, IDS_EE_VARARG_NOT_SUPPORTED);
}
#endif // PLATFORM_UNIX
// Skip number of type arguments
if (sigRet->callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
IfFailThrow(sig.GetData(NULL));
ULONG numArgs;
IfFailThrow(sig.GetData(&numArgs));
if (numArgs != (unsigned short) numArgs)
COMPlusThrowHR(COR_E_INVALIDPROGRAM);
sigRet->numArgs = (unsigned short) numArgs;
CorElementType type = sig.PeekElemTypeClosed(module, &typeContext);
if (!CorTypeInfo::IsPrimitiveType(type))
{
typeHnd = sig.GetTypeHandleThrowing(module, &typeContext);
_ASSERTE(!typeHnd.IsNull());
// I believe it doesn't make any diff. if this is
// GetInternalCorElementType or GetSignatureCorElementType
type = typeHnd.GetSignatureCorElementType();
}
sigRet->retType = CEEInfo::asCorInfoType(type, typeHnd, &sigRet->retTypeClass);
sigRet->retTypeSigClass = CORINFO_CLASS_HANDLE(typeHnd.AsPtr());
IfFailThrow(sig.SkipExactlyOne()); // must to a skip so we skip any class tokens associated with the return type
_ASSERTE(sigRet->retType < CORINFO_TYPE_COUNT);
sigRet->args = (CORINFO_ARG_LIST_HANDLE)sig.GetPtr();
}
else
{
// This is local variables declaration
sigRet->callConv = CORINFO_CALLCONV_DEFAULT;
sigRet->retType = CORINFO_TYPE_VOID;
sigRet->flags = CORINFO_SIGFLAG_IS_LOCAL_SIG;
sigRet->numArgs = 0;
if (!sig.IsNull())
{
ULONG callConv;
IfFailThrow(sig.GetCallingConvInfo(&callConv));
if (callConv != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG)
{
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_CALLCONV_NOT_LOCAL_SIG);
}
ULONG numArgs;
IfFailThrow(sig.GetData(&numArgs));
if (numArgs != (unsigned short) numArgs)
COMPlusThrowHR(COR_E_INVALIDPROGRAM);
sigRet->numArgs = (unsigned short) numArgs;
}
sigRet->args = (CORINFO_ARG_LIST_HANDLE)sig.GetPtr();
}
_ASSERTE(SigInfoFlagsAreValid(sigRet));
} // CEEInfo::ConvToJitSig
//---------------------------------------------------------------------------------------
//
CORINFO_CLASS_HANDLE CEEInfo::getTokenTypeAsHandle (CORINFO_RESOLVED_TOKEN * pResolvedToken)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CORINFO_CLASS_HANDLE tokenType = NULL;
JIT_TO_EE_TRANSITION();
_ASSERTE((pResolvedToken->hMethod == NULL) || (pResolvedToken->hField == NULL));
BinderClassID classID = CLASS__TYPE_HANDLE;
if (pResolvedToken->hMethod != NULL)
{
classID = CLASS__METHOD_HANDLE;
}
else
if (pResolvedToken->hField != NULL)
{
classID = CLASS__FIELD_HANDLE;
}
tokenType = CORINFO_CLASS_HANDLE(MscorlibBinder::GetClass(classID));
EE_TO_JIT_TRANSITION();
return tokenType;
}
/*********************************************************************/
size_t CEEInfo::findNameOfToken (
CORINFO_MODULE_HANDLE scopeHnd,
mdToken metaTOK,
__out_ecount (FQNameCapacity) char * szFQName,
size_t FQNameCapacity)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
size_t NameLen = 0;
JIT_TO_EE_TRANSITION();
if (IsDynamicScope(scopeHnd))
{
strncpy_s (szFQName, FQNameCapacity, "DynamicToken", FQNameCapacity - 1);
NameLen = strlen (szFQName);
}
else
{
Module* module = (Module *)scopeHnd;
NameLen = findNameOfToken(module, metaTOK, szFQName, FQNameCapacity);
}
EE_TO_JIT_TRANSITION();
return NameLen;
}
CorInfoCanSkipVerificationResult CEEInfo::canSkipMethodVerification(CORINFO_METHOD_HANDLE ftnHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
return CORINFO_VERIFICATION_CAN_SKIP;
}
/*********************************************************************/
BOOL CEEInfo::shouldEnforceCallvirtRestriction(
CORINFO_MODULE_HANDLE scopeHnd)
{
LIMITED_METHOD_CONTRACT;
return TRUE;
}
#ifdef FEATURE_READYTORUN_COMPILER
// Returns true if assemblies are in the same version bubble
// Right now each assembly is in its own version bubble.
// If the need arises (i.e. performance issues) we will define sets of assemblies (e.g. all app assemblies)
// The main point is that all this logic is concentrated in one place.
// NOTICE: If you change this logic to allow multi-assembly version bubbles you
// need to consider the impact on diagnostic tools. Currently there is an inlining
// table which tracks inliner/inlinee relationships in R2R images but it is not
// yet capable of encoding cross-assembly inlines. The scenario where this
// may show are instrumenting profilers that want to instrument a given method A
// using the ReJit APIs. If method A happens to inlined within method B in another
// assembly then the profiler needs to know that so it can rejit B too.
// The recommended approach is to upgrade the inlining table (vm\inlinetracking.h\.cpp)
// now that presumably R2R images have some way to refer to methods in other
// assemblies in their version bubble. Chat with the diagnostics team if you need more
// details.
//
// There already is a case where cross-assembly inlining occurs in an
// unreported fashion for methods marked NonVersionable. There is a specific
// exemption called out for this on ICorProfilerInfo6::EnumNgenModuleMethodsInliningThisMethod
// and the impact of the cut was vetted with partners. It would not be appropriate
// to increase that unreported set without additional review.
bool IsInSameVersionBubble(Assembly * current, Assembly * target)
{
LIMITED_METHOD_CONTRACT;
// trivial case: current and target are identical
// DO NOT change this without reading the notice above
if (current == target)
return true;
return false;
}
// Returns true if the assemblies defining current and target are in the same version bubble
static bool IsInSameVersionBubble(MethodDesc* pCurMD, MethodDesc *pTargetMD)
{
LIMITED_METHOD_CONTRACT;
// DO NOT change this without reading the notice above
if (IsInSameVersionBubble(pCurMD->GetModule()->GetAssembly(),
pTargetMD->GetModule()->GetAssembly()))
{
return true;
}
if (IsReadyToRunCompilation())
{
if (pTargetMD->GetModule()->GetMDImport()->GetCustomAttributeByName(pTargetMD->GetMemberDef(),
NONVERSIONABLE_TYPE, NULL, NULL) == S_OK)
{
return true;
}
}
return false;
}
#endif // FEATURE_READYTORUN_COMPILER
static bool CallerAndCalleeInSystemVersionBubble(MethodDesc* pCaller, MethodDesc* pCallee)
{
LIMITED_METHOD_CONTRACT;
#ifdef FEATURE_READYTORUN_COMPILER
if (IsReadyToRunCompilation())
return pCallee->GetModule()->IsSystem() && IsInSameVersionBubble(pCaller, pCallee);
#endif
return false;
}
/*********************************************************************/
CorInfoCanSkipVerificationResult CEEInfo::canSkipVerification(
CORINFO_MODULE_HANDLE moduleHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
return CORINFO_VERIFICATION_CAN_SKIP;
}
/*********************************************************************/
// Checks if the given metadata token is valid
BOOL CEEInfo::isValidToken (
CORINFO_MODULE_HANDLE module,
mdToken metaTOK)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
} CONTRACTL_END;
BOOL result = FALSE;
JIT_TO_EE_TRANSITION_LEAF();
if (IsDynamicScope(module))
{
// No explicit token validation for dynamic code. Validation is
// side-effect of token resolution.
result = TRUE;
}
else
{
result = ((Module *)module)->GetMDImport()->IsValidToken(metaTOK);
}
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
/*********************************************************************/
// Checks if the given metadata token is valid StringRef
BOOL CEEInfo::isValidStringRef (
CORINFO_MODULE_HANDLE module,
mdToken metaTOK)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
BOOL result = FALSE;
JIT_TO_EE_TRANSITION();
if (IsDynamicScope(module))
{
result = GetDynamicResolver(module)->IsValidStringRef(metaTOK);
}
else
{
result = ((Module *)module)->CheckStringRef(metaTOK);
if (result)
{
DWORD dwCharCount;
LPCWSTR pString;
result = (!FAILED(((Module *)module)->GetMDImport()->GetUserString(metaTOK, &dwCharCount, NULL, &pString)) &&
pString != NULL);
}
}
EE_TO_JIT_TRANSITION();
return result;
}
/* static */
size_t CEEInfo::findNameOfToken (Module* module,
mdToken metaTOK,
__out_ecount (FQNameCapacity) char * szFQName,
size_t FQNameCapacity)
{
CONTRACTL {
NOTHROW;
GC_TRIGGERS;
} CONTRACTL_END;
#ifdef _DEBUG
PCCOR_SIGNATURE sig = NULL;
DWORD cSig;
LPCUTF8 pszNamespace = NULL;
LPCUTF8 pszClassName = NULL;
mdToken tokType = TypeFromToken(metaTOK);
switch(tokType)
{
case mdtTypeRef:
{
if (FAILED(module->GetMDImport()->GetNameOfTypeRef(metaTOK, &pszNamespace, &pszClassName)))
{
pszNamespace = pszClassName = "Invalid TypeRef record";
}
ns::MakePath(szFQName, (int)FQNameCapacity, pszNamespace, pszClassName);
break;
}
case mdtTypeDef:
{
if (FAILED(module->GetMDImport()->GetNameOfTypeDef(metaTOK, &pszClassName, &pszNamespace)))
{
pszClassName = pszNamespace = "Invalid TypeDef record";
}
ns::MakePath(szFQName, (int)FQNameCapacity, pszNamespace, pszClassName);
break;
}
case mdtFieldDef:
{
LPCSTR szFieldName;
if (FAILED(module->GetMDImport()->GetNameOfFieldDef(metaTOK, &szFieldName)))
{
szFieldName = "Invalid FieldDef record";
}
strncpy_s(szFQName, FQNameCapacity, (char*)szFieldName, FQNameCapacity - 1);
break;
}
case mdtMethodDef:
{
LPCSTR szMethodName;
if (FAILED(module->GetMDImport()->GetNameOfMethodDef(metaTOK, &szMethodName)))
{
szMethodName = "Invalid MethodDef record";
}
strncpy_s(szFQName, FQNameCapacity, (char*)szMethodName, FQNameCapacity - 1);
break;
}
case mdtMemberRef:
{
LPCSTR szName;
if (FAILED(module->GetMDImport()->GetNameAndSigOfMemberRef((mdMemberRef)metaTOK, &sig, &cSig, &szName)))
{
szName = "Invalid MemberRef record";
}
strncpy_s(szFQName, FQNameCapacity, (char *)szName, FQNameCapacity - 1);
break;
}
default:
sprintf_s(szFQName, FQNameCapacity, "!TK_%x", metaTOK);
break;
}
#else // !_DEBUG
strncpy_s (szFQName, FQNameCapacity, "<UNKNOWN>", FQNameCapacity - 1);
#endif // _DEBUG
return strlen (szFQName);
}
CorInfoHelpFunc CEEInfo::getLazyStringLiteralHelper(CORINFO_MODULE_HANDLE handle)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CorInfoHelpFunc result = CORINFO_HELP_UNDEF;
JIT_TO_EE_TRANSITION_LEAF();
result = IsDynamicScope(handle) ? CORINFO_HELP_UNDEF : CORINFO_HELP_STRCNS;
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
CHECK CheckContext(CORINFO_MODULE_HANDLE scopeHnd, CORINFO_CONTEXT_HANDLE context)
{
CHECK_MSG(scopeHnd != NULL, "Illegal null scope");
CHECK_MSG(((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK) != NULL, "Illegal null context");
if (((size_t) context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS)
{
TypeHandle handle((CORINFO_CLASS_HANDLE) ((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK));
CHECK_MSG(handle.GetModule() == GetModule(scopeHnd), "Inconsistent scope and context");
}
else
{
MethodDesc* handle = (MethodDesc*) ((size_t) context & ~CORINFO_CONTEXTFLAGS_MASK);
CHECK_MSG(handle->GetModule() == GetModule(scopeHnd), "Inconsistent scope and context");
}
CHECK_OK;
}
static DECLSPEC_NORETURN void ThrowBadTokenException(CORINFO_RESOLVED_TOKEN * pResolvedToken)
{
switch (pResolvedToken->tokenType & CORINFO_TOKENKIND_Mask)
{
case CORINFO_TOKENKIND_Class:
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_CLASS_TOKEN);
case CORINFO_TOKENKIND_Method:
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_METHOD_TOKEN);
case CORINFO_TOKENKIND_Field:
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_FIELD_TOKEN);
default:
COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
}
}
/*********************************************************************/
void CEEInfo::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
JIT_TO_EE_TRANSITION();
_ASSERTE(CheckContext(pResolvedToken->tokenScope, pResolvedToken->tokenContext));
pResolvedToken->pTypeSpec = NULL;
pResolvedToken->cbTypeSpec = NULL;
pResolvedToken->pMethodSpec = NULL;
pResolvedToken->cbMethodSpec = NULL;
TypeHandle th;
MethodDesc * pMD = NULL;
FieldDesc * pFD = NULL;
CorInfoTokenKind tokenType = pResolvedToken->tokenType;
if (IsDynamicScope(pResolvedToken->tokenScope))
{
GetDynamicResolver(pResolvedToken->tokenScope)->ResolveToken(pResolvedToken->token, &th, &pMD, &pFD);
//
// Check that we got the expected handles and fill in missing data if necessary
//
CorTokenType tkType = (CorTokenType)TypeFromToken(pResolvedToken->token);
if (pMD != NULL)
{
if ((tkType != mdtMethodDef) && (tkType != mdtMemberRef))
ThrowBadTokenException(pResolvedToken);
if ((tokenType & CORINFO_TOKENKIND_Method) == 0)
ThrowBadTokenException(pResolvedToken);
if (th.IsNull())
th = pMD->GetMethodTable();
// "PermitUninstDefOrRef" check
if ((tokenType != CORINFO_TOKENKIND_Ldtoken) && pMD->ContainsGenericVariables())
{
COMPlusThrow(kInvalidProgramException);
}
// if this is a BoxedEntryPointStub get the UnboxedEntryPoint one
if (pMD->IsUnboxingStub())
{
pMD = pMD->GetMethodTable()->GetUnboxedEntryPointMD(pMD);
}
// Activate target if required
if (tokenType != CORINFO_TOKENKIND_Ldtoken)
{
ScanTokenForDynamicScope(pResolvedToken, th, pMD);
}
}
else
if (pFD != NULL)
{
if ((tkType != mdtFieldDef) && (tkType != mdtMemberRef))
ThrowBadTokenException(pResolvedToken);
if ((tokenType & CORINFO_TOKENKIND_Field) == 0)
ThrowBadTokenException(pResolvedToken);
if (th.IsNull())
th = pFD->GetApproxEnclosingMethodTable();
if (pFD->IsStatic() && (tokenType != CORINFO_TOKENKIND_Ldtoken))
{
ScanTokenForDynamicScope(pResolvedToken, th);
}
}
else
{
if ((tkType != mdtTypeDef) && (tkType != mdtTypeRef))
ThrowBadTokenException(pResolvedToken);
if ((tokenType & CORINFO_TOKENKIND_Class) == 0)
ThrowBadTokenException(pResolvedToken);
if (th.IsNull())
ThrowBadTokenException(pResolvedToken);
if (tokenType == CORINFO_TOKENKIND_Box || tokenType == CORINFO_TOKENKIND_Constrained)
{
ScanTokenForDynamicScope(pResolvedToken, th);
}
}
_ASSERTE((pMD == NULL) || (pFD == NULL));
_ASSERTE(!th.IsNull());
// "PermitUninstDefOrRef" check
if ((tokenType != CORINFO_TOKENKIND_Ldtoken) && th.ContainsGenericVariables())
{
COMPlusThrow(kInvalidProgramException);
}
// The JIT always wants to see normalized typedescs for arrays
if (!th.IsTypeDesc() && th.AsMethodTable()->IsArray())
{
MethodTable * pMT = th.AsMethodTable();
// Load the TypeDesc for the array type.
DWORD rank = pMT->GetRank();
TypeHandle elemType = pMT->GetApproxArrayElementTypeHandle();
th = ClassLoader::LoadArrayTypeThrowing(elemType, pMT->GetInternalCorElementType(), rank);
}
}
else
{
unsigned metaTOK = pResolvedToken->token;
Module * pModule = (Module *)pResolvedToken->tokenScope;
switch (TypeFromToken(metaTOK))
{
case mdtModuleRef:
if ((tokenType & CORINFO_TOKENKIND_Class) == 0)
ThrowBadTokenException(pResolvedToken);
{
DomainFile *pTargetModule = pModule->LoadModule(GetAppDomain(), metaTOK, FALSE /* loadResources */);
if (pTargetModule == NULL)
COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
th = TypeHandle(pTargetModule->GetModule()->GetGlobalMethodTable());
if (th.IsNull())
COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
}
break;
case mdtTypeDef:
case mdtTypeRef:
if ((tokenType & CORINFO_TOKENKIND_Class) == 0)
ThrowBadTokenException(pResolvedToken);
th = ClassLoader::LoadTypeDefOrRefThrowing(pModule, metaTOK,
ClassLoader::ThrowIfNotFound,
(tokenType == CORINFO_TOKENKIND_Ldtoken) ?
ClassLoader::PermitUninstDefOrRef : ClassLoader::FailIfUninstDefOrRef);
break;
case mdtTypeSpec:
{
if ((tokenType & CORINFO_TOKENKIND_Class) == 0)
ThrowBadTokenException(pResolvedToken);
IfFailThrow(pModule->GetMDImport()->GetTypeSpecFromToken(metaTOK, &pResolvedToken->pTypeSpec, &pResolvedToken->cbTypeSpec));
SigTypeContext typeContext;
GetTypeContext(pResolvedToken->tokenContext, &typeContext);
SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
th = sigptr.GetTypeHandleThrowing(pModule, &typeContext);
}
break;
case mdtMethodDef:
if ((tokenType & CORINFO_TOKENKIND_Method) == 0)
ThrowBadTokenException(pResolvedToken);
pMD = MemberLoader::GetMethodDescFromMethodDef(pModule, metaTOK, (tokenType != CORINFO_TOKENKIND_Ldtoken));
th = pMD->GetMethodTable();
break;
case mdtFieldDef:
if ((tokenType & CORINFO_TOKENKIND_Field) == 0)
ThrowBadTokenException(pResolvedToken);
pFD = MemberLoader::GetFieldDescFromFieldDef(pModule, metaTOK, (tokenType != CORINFO_TOKENKIND_Ldtoken));
th = pFD->GetEnclosingMethodTable();
break;
case mdtMemberRef:
{
SigTypeContext typeContext;
GetTypeContext(pResolvedToken->tokenContext, &typeContext);
MemberLoader::GetDescFromMemberRef(pModule, metaTOK, &pMD, &pFD, &typeContext, (tokenType != CORINFO_TOKENKIND_Ldtoken),
&th, TRUE, &pResolvedToken->pTypeSpec, &pResolvedToken->cbTypeSpec);
_ASSERTE((pMD != NULL) ^ (pFD != NULL));
_ASSERTE(!th.IsNull());
if (pMD != NULL)
{
if ((tokenType & CORINFO_TOKENKIND_Method) == 0)
ThrowBadTokenException(pResolvedToken);
}
else
{
if ((tokenType & CORINFO_TOKENKIND_Field) == 0)
ThrowBadTokenException(pResolvedToken);
}
}
break;
case mdtMethodSpec:
{
if ((tokenType & CORINFO_TOKENKIND_Method) == 0)
ThrowBadTokenException(pResolvedToken);
SigTypeContext typeContext;
GetTypeContext(pResolvedToken->tokenContext, &typeContext);
// We need the method desc to carry exact instantiation, thus allowInstParam == FALSE.
pMD = MemberLoader::GetMethodDescFromMethodSpec(pModule, metaTOK, &typeContext, (tokenType != CORINFO_TOKENKIND_Ldtoken), FALSE /* allowInstParam */,
&th, TRUE, &pResolvedToken->pTypeSpec, &pResolvedToken->cbTypeSpec, &pResolvedToken->pMethodSpec, &pResolvedToken->cbMethodSpec);
}
break;
default:
ThrowBadTokenException(pResolvedToken);
}
//
// Module dependency tracking
//
if (pMD != NULL)
{
ScanToken(pModule, pResolvedToken, th, pMD);
}
else
if (pFD != NULL)
{
if (pFD->IsStatic())
ScanToken(pModule, pResolvedToken, th);
}
else
{
// It should not be required to trigger the modules cctors for ldtoken, it is done for backward compatibility only.
if (tokenType == CORINFO_TOKENKIND_Box || tokenType == CORINFO_TOKENKIND_Constrained || tokenType == CORINFO_TOKENKIND_Ldtoken)
ScanToken(pModule, pResolvedToken, th);
}
}
//
// tokenType specific verification and transformations
//
CorElementType et = th.GetInternalCorElementType();
switch (tokenType)
{
case CORINFO_TOKENKIND_Ldtoken:
// Allow everything.
break;
case CORINFO_TOKENKIND_Newarr:
// Disallow ELEMENT_TYPE_BYREF and ELEMENT_TYPE_VOID
if (et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_VOID)
COMPlusThrow(kInvalidProgramException);
th = ClassLoader::LoadArrayTypeThrowing(th);
break;
default:
// Disallow ELEMENT_TYPE_BYREF and ELEMENT_TYPE_VOID
if (et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_VOID)
COMPlusThrow(kInvalidProgramException);
break;
}
// The JIT interface should always return fully loaded types
_ASSERTE(th.IsFullyLoaded());
pResolvedToken->hClass = CORINFO_CLASS_HANDLE(th.AsPtr());
pResolvedToken->hMethod = CORINFO_METHOD_HANDLE(pMD);
pResolvedToken->hField = CORINFO_FIELD_HANDLE(pFD);
EE_TO_JIT_TRANSITION();
}
/*********************************************************************/
struct TryResolveTokenFilterParam
{
CEEInfo* m_this;
CORINFO_RESOLVED_TOKEN* m_resolvedToken;
EXCEPTION_POINTERS m_exceptionPointers;
bool m_success;
};
bool isValidTokenForTryResolveToken(CEEInfo* info, CORINFO_RESOLVED_TOKEN* resolvedToken)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
SO_TOLERANT;
MODE_ANY;
} CONTRACTL_END;
if (!info->isValidToken(resolvedToken->tokenScope, resolvedToken->token))
{
return false;
}
CorInfoTokenKind tokenType = resolvedToken->tokenType;
switch (TypeFromToken(resolvedToken->token))
{
case mdtModuleRef:
case mdtTypeDef:
case mdtTypeRef:
case mdtTypeSpec:
if ((tokenType & CORINFO_TOKENKIND_Class) == 0)
return false;
break;
case mdtMethodDef:
case mdtMethodSpec:
if ((tokenType & CORINFO_TOKENKIND_Method) == 0)
return false;
break;
case mdtFieldDef:
if ((tokenType & CORINFO_TOKENKIND_Field) == 0)
return false;
break;
case mdtMemberRef:
if ((tokenType & (CORINFO_TOKENKIND_Method | CORINFO_TOKENKIND_Field)) == 0)
return false;
break;
default:
return false;
}
return true;
}
LONG EEFilterException(struct _EXCEPTION_POINTERS* exceptionPointers, void* unused);
LONG TryResolveTokenFilter(struct _EXCEPTION_POINTERS* exceptionPointers, void* theParam)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
SO_TOLERANT;
MODE_ANY;
} CONTRACTL_END;
// Backward compatibility: Convert bad image format exceptions thrown while resolving tokens
// to simple true/false successes. This is done for backward compatibility only. Ideally,
// we would always treat bad tokens in the IL stream as fatal errors.
if (exceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_COMPLUS)
{
auto* param = reinterpret_cast<TryResolveTokenFilterParam*>(theParam);
if (!isValidTokenForTryResolveToken(param->m_this, param->m_resolvedToken))
{
param->m_exceptionPointers = *exceptionPointers;
return EEFilterException(exceptionPointers, nullptr);
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
bool CEEInfo::tryResolveToken(CORINFO_RESOLVED_TOKEN* resolvedToken)
{
// No dynamic contract here because SEH is used
STATIC_CONTRACT_SO_TOLERANT;
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_MODE_PREEMPTIVE;
TryResolveTokenFilterParam param;
param.m_this = this;
param.m_resolvedToken = resolvedToken;
param.m_success = true;
PAL_TRY(TryResolveTokenFilterParam*, pParam, &param)
{
pParam->m_this->resolveToken(pParam->m_resolvedToken);
}
PAL_EXCEPT_FILTER(TryResolveTokenFilter)
{
if (param.m_exceptionPointers.ExceptionRecord->ExceptionCode == EXCEPTION_COMPLUS)
{
HandleException(&param.m_exceptionPointers);
}
param.m_success = false;
}
PAL_ENDTRY
return param.m_success;
}
/*********************************************************************/
// We have a few frequently used constants in mscorlib that are defined as
// readonly static fields for historic reasons. Check for them here and
// allow them to be treated as actual constants by the JIT.
static CORINFO_FIELD_ACCESSOR getFieldIntrinsic(FieldDesc * field)
{
STANDARD_VM_CONTRACT;
if (MscorlibBinder::GetField(FIELD__STRING__EMPTY) == field)
{
return CORINFO_FIELD_INTRINSIC_EMPTY_STRING;
}
else
if ((MscorlibBinder::GetField(FIELD__INTPTR__ZERO) == field) ||
(MscorlibBinder::GetField(FIELD__UINTPTR__ZERO) == field))
{
return CORINFO_FIELD_INTRINSIC_ZERO;
}
else
if (MscorlibBinder::GetField(FIELD__BITCONVERTER__ISLITTLEENDIAN) == field)
{
return CORINFO_FIELD_INTRINSIC_ISLITTLEENDIAN;
}
return (CORINFO_FIELD_ACCESSOR)-1;
}
static CorInfoHelpFunc getGenericStaticsHelper(FieldDesc * pField)
{
STANDARD_VM_CONTRACT;
int helper = CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE;
if (pField->GetFieldType() == ELEMENT_TYPE_CLASS ||
pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
{
helper = CORINFO_HELP_GETGENERICS_GCSTATIC_BASE;
}
if (pField->IsThreadStatic())
{
const int delta = CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE - CORINFO_HELP_GETGENERICS_GCSTATIC_BASE;
static_assert_no_msg(CORINFO_HELP_GETGENERICS_NONGCTHREADSTATIC_BASE
== CORINFO_HELP_GETGENERICS_NONGCSTATIC_BASE + delta);
helper += (CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE - CORINFO_HELP_GETGENERICS_GCSTATIC_BASE);
}
return (CorInfoHelpFunc)helper;
}
CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable * pFieldMT)
{
STANDARD_VM_CONTRACT;
int helper = CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE;
if (pField->GetFieldType() == ELEMENT_TYPE_CLASS ||
pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
{
helper = CORINFO_HELP_GETSHARED_GCSTATIC_BASE;
}
if (pFieldMT->IsDynamicStatics())
{
const int delta = CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS - CORINFO_HELP_GETSHARED_GCSTATIC_BASE;
static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS
== CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta);
helper += delta;
}
else
if (!pFieldMT->HasClassConstructor() && !pFieldMT->HasBoxedRegularStatics())
{
const int delta = CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR - CORINFO_HELP_GETSHARED_GCSTATIC_BASE;
static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR
== CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta);
helper += delta;
}
if (pField->IsThreadStatic())
{
const int delta = CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE - CORINFO_HELP_GETSHARED_GCSTATIC_BASE;
static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE
== CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + delta);
static_assert_no_msg(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR
== CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR + delta);
static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR
== CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR + delta);
static_assert_no_msg(CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS
== CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS + delta);
static_assert_no_msg(CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_DYNAMICCLASS
== CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_DYNAMICCLASS + delta);
helper += delta;
}
return (CorInfoHelpFunc)helper;
}
static CorInfoHelpFunc getInstanceFieldHelper(FieldDesc * pField, CORINFO_ACCESS_FLAGS flags)
{
STANDARD_VM_CONTRACT;
int helper;
CorElementType type = pField->GetFieldType();
if (CorTypeInfo::IsObjRef(type))
helper = CORINFO_HELP_GETFIELDOBJ;
else
switch (type)
{
case ELEMENT_TYPE_VALUETYPE:
helper = CORINFO_HELP_GETFIELDSTRUCT;
break;
case ELEMENT_TYPE_I1:
case ELEMENT_TYPE_BOOLEAN:
case ELEMENT_TYPE_U1:
helper = CORINFO_HELP_GETFIELD8;
break;
case ELEMENT_TYPE_I2:
case ELEMENT_TYPE_CHAR:
case ELEMENT_TYPE_U2:
helper = CORINFO_HELP_GETFIELD16;
break;
case ELEMENT_TYPE_I4:
case ELEMENT_TYPE_U4:
IN_TARGET_32BIT(default:)
helper = CORINFO_HELP_GETFIELD32;
break;
case ELEMENT_TYPE_I8:
case ELEMENT_TYPE_U8:
IN_TARGET_64BIT(default:)
helper = CORINFO_HELP_GETFIELD64;
break;
case ELEMENT_TYPE_R4:
helper = CORINFO_HELP_GETFIELDFLOAT;
break;
case ELEMENT_TYPE_R8:
helper = CORINFO_HELP_GETFIELDDOUBLE;
break;
}
if (flags & CORINFO_ACCESS_SET)
{
const int delta = CORINFO_HELP_SETFIELDOBJ - CORINFO_HELP_GETFIELDOBJ;
static_assert_no_msg(CORINFO_HELP_SETFIELD8 == CORINFO_HELP_GETFIELD8 + delta);
static_assert_no_msg(CORINFO_HELP_SETFIELD16 == CORINFO_HELP_GETFIELD16 + delta);
static_assert_no_msg(CORINFO_HELP_SETFIELD32 == CORINFO_HELP_GETFIELD32 + delta);
static_assert_no_msg(CORINFO_HELP_SETFIELD64 == CORINFO_HELP_GETFIELD64 + delta);
static_assert_no_msg(CORINFO_HELP_SETFIELDSTRUCT == CORINFO_HELP_GETFIELDSTRUCT + delta);
static_assert_no_msg(CORINFO_HELP_SETFIELDFLOAT == CORINFO_HELP_GETFIELDFLOAT + delta);
static_assert_no_msg(CORINFO_HELP_SETFIELDDOUBLE == CORINFO_HELP_GETFIELDDOUBLE + delta);
helper += delta;
}
return (CorInfoHelpFunc)helper;
}
/*********************************************************************/
void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
CORINFO_METHOD_HANDLE callerHandle,
CORINFO_ACCESS_FLAGS flags,
CORINFO_FIELD_INFO *pResult
)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
JIT_TO_EE_TRANSITION();
_ASSERTE((flags & (CORINFO_ACCESS_GET | CORINFO_ACCESS_SET | CORINFO_ACCESS_ADDRESS | CORINFO_ACCESS_INIT_ARRAY)) != 0);
INDEBUG(memset(pResult, 0xCC, sizeof(*pResult)));
FieldDesc * pField = (FieldDesc*)pResolvedToken->hField;
MethodTable * pFieldMT = pField->GetApproxEnclosingMethodTable();
// Helper to use if the field access requires it
CORINFO_FIELD_ACCESSOR fieldAccessor = (CORINFO_FIELD_ACCESSOR)-1;
DWORD fieldFlags = 0;
pResult->offset = pField->GetOffset();
if (pField->IsStatic())
{
fieldFlags |= CORINFO_FLG_FIELD_STATIC;
if (pField->IsRVA())
{
fieldFlags |= CORINFO_FLG_FIELD_UNMANAGED;
Module* module = pFieldMT->GetModule();
if (module->IsRvaFieldTls(pResult->offset))
{
fieldAccessor = CORINFO_FIELD_STATIC_TLS;
// Provide helper to use if the JIT is not able to emit the TLS access
// as intrinsic
pResult->helper = CORINFO_HELP_GETSTATICFIELDADDR_TLS;
pResult->offset = module->GetFieldTlsOffset(pResult->offset);
}
else
{
fieldAccessor = CORINFO_FIELD_STATIC_RVA_ADDRESS;
}
// We are not going through a helper. The constructor has to be triggered explicitly.
if (!pFieldMT->IsClassPreInited())
fieldFlags |= CORINFO_FLG_FIELD_INITCLASS;
}
else
{
// Regular or thread static
CORINFO_FIELD_ACCESSOR intrinsicAccessor;
if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
fieldFlags |= CORINFO_FLG_FIELD_STATIC_IN_HEAP;
if (pFieldMT->IsSharedByGenericInstantiations())
{
fieldAccessor = CORINFO_FIELD_STATIC_GENERICS_STATIC_HELPER;
pResult->helper = getGenericStaticsHelper(pField);
}
else
if (pFieldMT->GetModule()->IsSystem() && (flags & CORINFO_ACCESS_GET) &&
(intrinsicAccessor = getFieldIntrinsic(pField)) != (CORINFO_FIELD_ACCESSOR)-1)
{
// Intrinsics
fieldAccessor = intrinsicAccessor;
}
else
if (// Domain neutral access.
m_pMethodBeingCompiled->IsDomainNeutral() || m_pMethodBeingCompiled->IsZapped() || IsCompilingForNGen() ||
// Static fields are not pinned in collectible types. We will always access
// them using a helper since the address cannot be embeded into the code.
pFieldMT->Collectible() ||
// We always treat accessing thread statics as if we are in domain neutral code.
pField->IsThreadStatic()
)
{
fieldAccessor = CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER;
pResult->helper = getSharedStaticsHelper(pField, pFieldMT);
}
else
{
fieldAccessor = CORINFO_FIELD_STATIC_ADDRESS;
// We are not going through a helper. The constructor has to be triggered explicitly.
if (!pFieldMT->IsClassPreInited())
fieldFlags |= CORINFO_FLG_FIELD_INITCLASS;
}
}
//
// Currently, we only this optimization for regular statics, but it
// looks like it may be permissible to do this optimization for
// thread statics as well.
//
if ((flags & CORINFO_ACCESS_ADDRESS) &&
!pField->IsThreadStatic() &&
(fieldAccessor != CORINFO_FIELD_STATIC_TLS))
{
fieldFlags |= CORINFO_FLG_FIELD_SAFESTATIC_BYREF_RETURN;
}
}
else
{
BOOL fInstanceHelper = FALSE;
if (fInstanceHelper)
{
if (flags & CORINFO_ACCESS_ADDRESS)
{
fieldAccessor = CORINFO_FIELD_INSTANCE_ADDR_HELPER;
pResult->helper = CORINFO_HELP_GETFIELDADDR;
}
else
{
fieldAccessor = CORINFO_FIELD_INSTANCE_HELPER;
pResult->helper = getInstanceFieldHelper(pField, flags);
}
}
else
if (pField->IsEnCNew())
{
fieldAccessor = CORINFO_FIELD_INSTANCE_ADDR_HELPER;
pResult->helper = CORINFO_HELP_GETFIELDADDR;
}
else
{
fieldAccessor = CORINFO_FIELD_INSTANCE;
}
// FieldDesc::GetOffset() does not include the size of Object
if (!pFieldMT->IsValueType())
{
pResult->offset += OBJECT_SIZE;
}
}
// TODO: This is touching metadata. Can we avoid it?
DWORD fieldAttribs = pField->GetAttributes();
if (IsFdFamily(fieldAttribs))
fieldFlags |= CORINFO_FLG_FIELD_PROTECTED;
if (IsFdInitOnly(fieldAttribs))
fieldFlags |= CORINFO_FLG_FIELD_FINAL;
pResult->fieldAccessor = fieldAccessor;
pResult->fieldFlags = fieldFlags;
if (!(flags & CORINFO_ACCESS_INLINECHECK))
{
//get the field's type. Grab the class for structs.
pResult->fieldType = getFieldTypeInternal(pResolvedToken->hField, &pResult->structType, pResolvedToken->hClass);
MethodDesc * pCallerForSecurity = GetMethodForSecurity(callerHandle);
//
//Since we can't get the special verify-only instantiated FD like we can with MDs, go back to the parent
//of the memberRef and load that one. That should give us the open instantiation.
//
//If the field we found is owned by a generic type, you have to go back to the signature and reload.
//Otherwise we filled in !0.
TypeHandle fieldTypeForSecurity = TypeHandle(pResolvedToken->hClass);
if (pResolvedToken->pTypeSpec != NULL)
{
SigTypeContext typeContext;
SigTypeContext::InitTypeContext(pCallerForSecurity, &typeContext);
SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
fieldTypeForSecurity = sigptr.GetTypeHandleThrowing((Module *)pResolvedToken->tokenScope, &typeContext);
// typeHnd can be a variable type
if (fieldTypeForSecurity.GetMethodTable() == NULL)
{
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_METHODDEF_PARENT_NO_MEMBERS);
}
}
BOOL doAccessCheck = TRUE;
AccessCheckOptions::AccessCheckType accessCheckType = AccessCheckOptions::kNormalAccessibilityChecks;
DynamicResolver * pAccessContext = NULL;
//More in code:CEEInfo::getCallInfo, but the short version is that the caller and callee Descs do
//not completely describe the type.
TypeHandle callerTypeForSecurity = TypeHandle(pCallerForSecurity->GetMethodTable());
if (IsDynamicScope(pResolvedToken->tokenScope))
{
doAccessCheck = ModifyCheckForDynamicMethod(GetDynamicResolver(pResolvedToken->tokenScope), &callerTypeForSecurity,
&accessCheckType, &pAccessContext);
}
//Now for some link time checks.
//Um... where are the field link demands?
pResult->accessAllowed = CORINFO_ACCESS_ALLOWED;
if (doAccessCheck)
{
//Well, let's check some visibility at least.
AccessCheckOptions accessCheckOptions(accessCheckType,
pAccessContext,
FALSE,
pField);
_ASSERTE(pCallerForSecurity != NULL && callerTypeForSecurity != NULL);
StaticAccessCheckContext accessContext(pCallerForSecurity, callerTypeForSecurity.GetMethodTable());
BOOL canAccess = ClassLoader::CanAccess(
&accessContext,
fieldTypeForSecurity.GetMethodTable(),
fieldTypeForSecurity.GetAssembly(),
fieldAttribs,
NULL,
(flags & CORINFO_ACCESS_INIT_ARRAY) ? NULL : pField, // For InitializeArray, we don't need tocheck the type of the field.
accessCheckOptions);
if (!canAccess)
{
//Set up the throw helper
pResult->accessAllowed = CORINFO_ACCESS_ILLEGAL;
pResult->accessCalloutHelper.helperNum = CORINFO_HELP_FIELD_ACCESS_EXCEPTION;
pResult->accessCalloutHelper.numArgs = 2;
pResult->accessCalloutHelper.args[0].Set(CORINFO_METHOD_HANDLE(pCallerForSecurity));
pResult->accessCalloutHelper.args[1].Set(CORINFO_FIELD_HANDLE(pField));
if (IsCompilingForNGen())
{
//see code:CEEInfo::getCallInfo for more information.
if (pCallerForSecurity->ContainsGenericVariables())
COMPlusThrowNonLocalized(kNotSupportedException, W("Cannot embed generic MethodDesc"));
}
}
}
}
EE_TO_JIT_TRANSITION();
}
//---------------------------------------------------------------------------------------
//
bool CEEInfo::isFieldStatic(CORINFO_FIELD_HANDLE fldHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
bool res = false;
JIT_TO_EE_TRANSITION_LEAF();
FieldDesc* field = (FieldDesc*)fldHnd;
res = (field->IsStatic() != 0);
EE_TO_JIT_TRANSITION_LEAF();
return res;
}
//---------------------------------------------------------------------------------------
//
void
CEEInfo::findCallSiteSig(
CORINFO_MODULE_HANDLE scopeHnd,
unsigned sigMethTok,
CORINFO_CONTEXT_HANDLE context,
CORINFO_SIG_INFO * sigRet)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
JIT_TO_EE_TRANSITION();
PCCOR_SIGNATURE pSig = NULL;
DWORD cbSig = 0;
if (IsDynamicScope(scopeHnd))
{
DynamicResolver * pResolver = GetDynamicResolver(scopeHnd);
SigPointer sig;
if (TypeFromToken(sigMethTok) == mdtMemberRef)
{
sig = pResolver->ResolveSignatureForVarArg(sigMethTok);
}
else
{
_ASSERTE(TypeFromToken(sigMethTok) == mdtMethodDef);
TypeHandle classHandle;
MethodDesc * pMD = NULL;
FieldDesc * pFD = NULL;
// in this case a method is asked for its sig. Resolve the method token and get the sig
pResolver->ResolveToken(sigMethTok, &classHandle, &pMD, &pFD);
if (pMD == NULL)
COMPlusThrow(kInvalidProgramException);
PCCOR_SIGNATURE pSig = NULL;
DWORD cbSig;
pMD->GetSig(&pSig, &cbSig);
sig = SigPointer(pSig, cbSig);
context = MAKE_METHODCONTEXT(pMD);
scopeHnd = GetScopeHandle(pMD->GetModule());
}
sig.GetSignature(&pSig, &cbSig);
sigMethTok = mdTokenNil;
}
else
{
Module * module = (Module *)scopeHnd;
LPCUTF8 szName;
if (TypeFromToken(sigMethTok) == mdtMemberRef)
{
IfFailThrow(module->GetMDImport()->GetNameAndSigOfMemberRef(sigMethTok, &pSig, &cbSig, &szName));
}
else if (TypeFromToken(sigMethTok) == mdtMethodDef)
{
IfFailThrow(module->GetMDImport()->GetSigOfMethodDef(sigMethTok, &cbSig, &pSig));
}
}
CEEInfo::ConvToJitSig(
pSig,
cbSig,
scopeHnd,
sigMethTok,
sigRet,
GetMethodFromContext(context),
false,
GetTypeFromContext(context));
EE_TO_JIT_TRANSITION();
} // CEEInfo::findCallSiteSig
//---------------------------------------------------------------------------------------
//
void
CEEInfo::findSig(
CORINFO_MODULE_HANDLE scopeHnd,
unsigned sigTok,
CORINFO_CONTEXT_HANDLE context,
CORINFO_SIG_INFO * sigRet)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
JIT_TO_EE_TRANSITION();
PCCOR_SIGNATURE pSig = NULL;
DWORD cbSig = 0;
if (IsDynamicScope(scopeHnd))
{
SigPointer sig = GetDynamicResolver(scopeHnd)->ResolveSignature(sigTok);
sig.GetSignature(&pSig, &cbSig);
sigTok = mdTokenNil;
}
else
{
Module * module = (Module *)scopeHnd;
// We need to resolve this stand alone sig
IfFailThrow(module->GetMDImport()->GetSigFromToken(
(mdSignature)sigTok,
&cbSig,
&pSig));
}
CEEInfo::ConvToJitSig(
pSig,
cbSig,
scopeHnd,
sigTok,
sigRet,
GetMethodFromContext(context),
false,
GetTypeFromContext(context));
EE_TO_JIT_TRANSITION();
} // CEEInfo::findSig
//---------------------------------------------------------------------------------------
//
unsigned
CEEInfo::getClassSize(
CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
unsigned result = 0;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle VMClsHnd(clsHnd);
result = VMClsHnd.GetSize();
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
//---------------------------------------------------------------------------------------
//
// Get the size of a reference type as allocated on the heap. This includes the size of the fields
// (and any padding between the fields) and the size of a method table pointer but doesn't include
// object header size or any padding for minimum size.
unsigned
CEEInfo::getHeapClassSize(
CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL{
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
unsigned result = 0;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle VMClsHnd(clsHnd);
MethodTable* pMT = VMClsHnd.GetMethodTable();
_ASSERTE(pMT);
_ASSERTE(!pMT->IsValueType());
_ASSERTE(!pMT->HasComponentSize());
#ifdef FEATURE_READYTORUN_COMPILER
_ASSERTE(!IsReadyToRunCompilation() || pMT->IsInheritanceChainLayoutFixedInCurrentVersionBubble());
#endif
// Add OBJECT_SIZE to account for method table pointer.
result = pMT->GetNumInstanceFieldBytes() + OBJECT_SIZE;
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
//---------------------------------------------------------------------------------------
//
// Return TRUE if an object of this type can be allocated on the stack.
BOOL CEEInfo::canAllocateOnStack(CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL{
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
BOOL result = FALSE;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle VMClsHnd(clsHnd);
MethodTable* pMT = VMClsHnd.GetMethodTable();
_ASSERTE(pMT);
_ASSERTE(!pMT->IsValueType());
result = !pMT->HasFinalizer();
#ifdef FEATURE_READYTORUN_COMPILER
if (IsReadyToRunCompilation() && !pMT->IsInheritanceChainLayoutFixedInCurrentVersionBubble())
{
result = false;
}
#endif
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
unsigned CEEInfo::getClassAlignmentRequirement(CORINFO_CLASS_HANDLE type, BOOL fDoubleAlignHint)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
// Default alignment is sizeof(void*)
unsigned result = TARGET_POINTER_SIZE;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle clsHnd(type);
#ifdef FEATURE_DOUBLE_ALIGNMENT_HINT
if (fDoubleAlignHint)
{
MethodTable* pMT = clsHnd.GetMethodTable();
if (pMT != NULL)
{
// Return the size of the double align hint. Ignore the actual alignment info account
// so that structs with 64-bit integer fields do not trigger double aligned frames on x86.
if (pMT->GetClass()->IsAlign8Candidate())
result = 8;
}
}
else
#endif
{
result = getClassAlignmentRequirementStatic(clsHnd);
}
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
unsigned CEEInfo::getClassAlignmentRequirementStatic(TypeHandle clsHnd)
{
LIMITED_METHOD_CONTRACT;
// Default alignment is sizeof(void*)
unsigned result = TARGET_POINTER_SIZE;
MethodTable * pMT = clsHnd.GetMethodTable();
if (pMT == NULL)
return result;
if (pMT->HasLayout())
{
EEClassLayoutInfo* pInfo = pMT->GetLayoutInfo();
if (clsHnd.IsNativeValueType())
{
// if it's the unmanaged view of the managed type, we always use the unmanaged alignment requirement
result = pInfo->m_LargestAlignmentRequirementOfAllMembers;
}
else
if (pInfo->IsManagedSequential())
{
_ASSERTE(!pMT->ContainsPointers());
// if it's managed sequential, we use the managed alignment requirement
result = pInfo->m_ManagedLargestAlignmentRequirementOfAllMembers;
}
else if (pInfo->IsBlittable())
{
_ASSERTE(!pMT->ContainsPointers());
// if it's blittable, we use the unmanaged alignment requirement
result = pInfo->m_LargestAlignmentRequirementOfAllMembers;
}
}
#ifdef FEATURE_64BIT_ALIGNMENT
if (result < 8 && pMT->RequiresAlign8())
{
// If the structure contains 64-bit primitive fields and the platform requires 8-byte alignment for
// such fields then make sure we return at least 8-byte alignment. Note that it's technically possible
// to create unmanaged APIs that take unaligned structures containing such fields and this
// unconditional alignment bump would cause us to get the calling convention wrong on platforms such
// as ARM. If we see such cases in the future we'd need to add another control (such as an alignment
// property for the StructLayout attribute or a marshaling directive attribute for p/invoke arguments)
// that allows more precise control. For now we'll go with the likely scenario.
result = 8;
}
#endif // FEATURE_64BIT_ALIGNMENT
return result;
}
CORINFO_FIELD_HANDLE
CEEInfo::getFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CORINFO_FIELD_HANDLE result = NULL;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle VMClsHnd(clsHnd);
MethodTable* pMT= VMClsHnd.AsMethodTable();
result = (CORINFO_FIELD_HANDLE) ((pMT->GetApproxFieldDescListRaw()) + num);
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
mdMethodDef
CEEInfo::getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
mdMethodDef result = 0;
JIT_TO_EE_TRANSITION_LEAF();
MethodDesc* pMD = GetMethod(hMethod);
if (pMD->IsDynamicMethod())
{
// Dynamic methods do not have tokens
result = mdMethodDefNil;
}
else
{
result = pMD->GetMemberDef();
}
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
BOOL CEEInfo::checkMethodModifier(CORINFO_METHOD_HANDLE hMethod,
LPCSTR modifier,
BOOL fOptional)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
BOOL result = FALSE;
JIT_TO_EE_TRANSITION();
MethodDesc* pMD = GetMethod(hMethod);
Module* pModule = pMD->GetModule();
MetaSig sig(pMD);
CorElementType eeType = fOptional ? ELEMENT_TYPE_CMOD_OPT : ELEMENT_TYPE_CMOD_REQD;
// modopts/modreqs for the method are by convention stored on the return type
result = sig.GetReturnProps().HasCustomModifier(pModule, modifier, eeType);
EE_TO_JIT_TRANSITION();
return result;
}
/*********************************************************************/
static unsigned ComputeGCLayout(MethodTable * pMT, BYTE* gcPtrs)
{
STANDARD_VM_CONTRACT;
unsigned result = 0;
_ASSERTE(pMT->IsValueType());
// TODO: TypedReference should ideally be implemented as a by-ref-like struct containing a ByReference<T> field, in which
// case the check for g_TypedReferenceMT below would not be necessary
if (pMT == g_TypedReferenceMT || pMT->HasSameTypeDefAs(g_pByReferenceClass))
{
if (gcPtrs[0] == TYPE_GC_NONE)
{
gcPtrs[0] = TYPE_GC_BYREF;
result++;
}
else if (gcPtrs[0] != TYPE_GC_BYREF)
{
COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
}
return result;
}
ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS);
for (FieldDesc *pFD = fieldIterator.Next(); pFD != NULL; pFD = fieldIterator.Next())
{
int fieldStartIndex = pFD->GetOffset() / TARGET_POINTER_SIZE;
if (pFD->GetFieldType() != ELEMENT_TYPE_VALUETYPE)
{
if (pFD->IsObjRef())
{
if (gcPtrs[fieldStartIndex] == TYPE_GC_NONE)
{
gcPtrs[fieldStartIndex] = TYPE_GC_REF;
result++;
}
else if (gcPtrs[fieldStartIndex] != TYPE_GC_REF)
{
COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
}
}
}
else
{
MethodTable * pFieldMT = pFD->GetApproxFieldTypeHandleThrowing().AsMethodTable();
result += ComputeGCLayout(pFieldMT, gcPtrs + fieldStartIndex);
}
}
return result;
}
unsigned CEEInfo::getClassGClayout (CORINFO_CLASS_HANDLE clsHnd, BYTE* gcPtrs)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
unsigned result = 0;
JIT_TO_EE_TRANSITION();
TypeHandle VMClsHnd(clsHnd);
MethodTable* pMT = VMClsHnd.GetMethodTable();
if (VMClsHnd.IsNativeValueType())
{
// native value types have no GC pointers
result = 0;
memset(gcPtrs, TYPE_GC_NONE,
(VMClsHnd.GetSize() + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE);
}
else if (pMT->IsByRefLike())
{
// TODO: TypedReference should ideally be implemented as a by-ref-like struct containing a ByReference<T> field, in
// which case the check for g_TypedReferenceMT below would not be necessary
if (pMT == g_TypedReferenceMT)
{
gcPtrs[0] = TYPE_GC_BYREF;
gcPtrs[1] = TYPE_GC_NONE;
result = 1;
}
else
{
memset(gcPtrs, TYPE_GC_NONE,
(VMClsHnd.GetSize() + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE);
// Note: This case is more complicated than the TypedReference case
// due to ByRefLike structs being included as fields in other value
// types (TypedReference can not be.)
result = ComputeGCLayout(VMClsHnd.AsMethodTable(), gcPtrs);
}
}
else
{
_ASSERTE(sizeof(BYTE) == 1);
BOOL isValueClass = pMT->IsValueType();
#ifdef FEATURE_READYTORUN_COMPILER
_ASSERTE(isValueClass || !IsReadyToRunCompilation() || pMT->IsInheritanceChainLayoutFixedInCurrentVersionBubble());
#endif
unsigned int size = isValueClass ? VMClsHnd.GetSize() : pMT->GetNumInstanceFieldBytes() + OBJECT_SIZE;
// assume no GC pointers at first
result = 0;
memset(gcPtrs, TYPE_GC_NONE,
(size + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE);
// walk the GC descriptors, turning on the correct bits
if (pMT->ContainsPointers())
{
CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT);
CGCDescSeries * pByValueSeries = map->GetLowestSeries();
for (SIZE_T i = 0; i < map->GetNumSeries(); i++)
{
// Get offset into the value class of the first pointer field (includes a +Object)
size_t cbSeriesSize = pByValueSeries->GetSeriesSize() + pMT->GetBaseSize();
size_t cbSeriesOffset = pByValueSeries->GetSeriesOffset();
size_t cbOffset = isValueClass ? cbSeriesOffset - OBJECT_SIZE : cbSeriesOffset;
_ASSERTE (cbOffset % TARGET_POINTER_SIZE == 0);
_ASSERTE (cbSeriesSize % TARGET_POINTER_SIZE == 0);
result += (unsigned) (cbSeriesSize / TARGET_POINTER_SIZE);
memset(&gcPtrs[cbOffset / TARGET_POINTER_SIZE], TYPE_GC_REF, cbSeriesSize / TARGET_POINTER_SIZE);
pByValueSeries++;
}
}
}
EE_TO_JIT_TRANSITION();
return result;
}
// returns the enregister info for a struct based on type of fields, alignment, etc.
bool CEEInfo::getSystemVAmd64PassStructInRegisterDescriptor(
/*IN*/ CORINFO_CLASS_HANDLE structHnd,
/*OUT*/ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
#if defined(UNIX_AMD64_ABI_ITF)
JIT_TO_EE_TRANSITION();
_ASSERTE(structPassInRegDescPtr != nullptr);
TypeHandle th(structHnd);
structPassInRegDescPtr->passedInRegisters = false;
// Make sure this is a value type.
if (th.IsValueType())
{
_ASSERTE((CorInfoType2UnixAmd64Classification(th.GetInternalCorElementType()) == SystemVClassificationTypeStruct) ||
(CorInfoType2UnixAmd64Classification(th.GetInternalCorElementType()) == SystemVClassificationTypeTypedReference));
// The useNativeLayout in this case tracks whether the classification
// is for a native layout of the struct or not.
// If the struct has special marshaling it has a native layout.
// In such cases the classifier needs to use the native layout.
// For structs with no native layout, the managed layout should be used
// even if classified for the purposes of marshaling/PInvoke passing.
bool useNativeLayout = false;
MethodTable* methodTablePtr = nullptr;
if (!th.IsTypeDesc())
{
methodTablePtr = th.AsMethodTable();
}
else
{
_ASSERTE(th.IsNativeValueType());
useNativeLayout = true;
methodTablePtr = th.AsNativeValueType();
}
_ASSERTE(methodTablePtr != nullptr);
// If we have full support for UNIX_AMD64_ABI, and not just the interface,
// then we've cached whether this is a reg passed struct in the MethodTable, computed during
// MethodTable construction. Otherwise, we are just building in the interface, and we haven't
// computed or cached anything, so we need to compute it now.
#if defined(UNIX_AMD64_ABI)
bool canPassInRegisters = useNativeLayout ? methodTablePtr->GetLayoutInfo()->IsNativeStructPassedInRegisters()
: methodTablePtr->IsRegPassedStruct();
#else // !defined(UNIX_AMD64_ABI)
bool canPassInRegisters = false;
SystemVStructRegisterPassingHelper helper((unsigned int)th.GetSize());
if (th.GetSize() <= CLR_SYSTEMV_MAX_STRUCT_BYTES_TO_PASS_IN_REGISTERS)
{
canPassInRegisters = methodTablePtr->ClassifyEightBytes(&helper, 0, 0, useNativeLayout);
}
#endif // !defined(UNIX_AMD64_ABI)
if (canPassInRegisters)
{
#if defined(UNIX_AMD64_ABI)
SystemVStructRegisterPassingHelper helper((unsigned int)th.GetSize());
bool result = methodTablePtr->ClassifyEightBytes(&helper, 0, 0, useNativeLayout);
// The answer must be true at this point.
_ASSERTE(result);
#endif // UNIX_AMD64_ABI
structPassInRegDescPtr->passedInRegisters = true;
structPassInRegDescPtr->eightByteCount = helper.eightByteCount;
_ASSERTE(structPassInRegDescPtr->eightByteCount <= CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS);
for (unsigned int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
{
structPassInRegDescPtr->eightByteClassifications[i] = helper.eightByteClassifications[i];
structPassInRegDescPtr->eightByteSizes[i] = helper.eightByteSizes[i];
structPassInRegDescPtr->eightByteOffsets[i] = helper.eightByteOffsets[i];
}
}
_ASSERTE(structPassInRegDescPtr->passedInRegisters == canPassInRegisters);
}
EE_TO_JIT_TRANSITION();
return true;
#else // !defined(UNIX_AMD64_ABI_ITF)
return false;
#endif // !defined(UNIX_AMD64_ABI_ITF)
}
/*********************************************************************/
unsigned CEEInfo::getClassNumInstanceFields (CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
unsigned result = 0;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle th(clsHnd);
if (!th.IsTypeDesc())
{
result = th.AsMethodTable()->GetNumInstanceFields();
}
else
{
// native value types are opaque aggregates with explicit size
result = 0;
}
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
CorInfoType CEEInfo::asCorInfoType (CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CorInfoType result = CORINFO_TYPE_UNDEF;
JIT_TO_EE_TRANSITION();
TypeHandle VMClsHnd(clsHnd);
result = toJitType(VMClsHnd);
EE_TO_JIT_TRANSITION();
return result;
}
CORINFO_LOOKUP_KIND CEEInfo::getLocationOfThisType(CORINFO_METHOD_HANDLE context)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CORINFO_LOOKUP_KIND result;
/* Initialize fields of result for debug build warning */
result.needsRuntimeLookup = false;
result.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ;
JIT_TO_EE_TRANSITION();
MethodDesc *pContextMD = GetMethod(context);
// If the method table is not shared, then return CONST
if (!pContextMD->GetMethodTable()->IsSharedByGenericInstantiations())
{
result.needsRuntimeLookup = false;
}
else
{
result.needsRuntimeLookup = true;
// If we've got a vtable extra argument, go through that
if (pContextMD->RequiresInstMethodTableArg())
{
result.runtimeLookupKind = CORINFO_LOOKUP_CLASSPARAM;
}
// If we've got an object, go through its vtable
else if (pContextMD->AcquiresInstMethodTableFromThis())
{
result.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ;
}
// Otherwise go through the method-desc argument
else
{
_ASSERTE(pContextMD->RequiresInstMethodDescArg());
result.runtimeLookupKind = CORINFO_LOOKUP_METHODPARAM;
}
}
EE_TO_JIT_TRANSITION();
return result;
}
CORINFO_METHOD_HANDLE CEEInfo::GetDelegateCtor(
CORINFO_METHOD_HANDLE methHnd,
CORINFO_CLASS_HANDLE clsHnd,
CORINFO_METHOD_HANDLE targetMethodHnd,
DelegateCtorArgs *pCtorData)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
if (isVerifyOnly())
{
// No sense going through the optimized case just for verification and it can cause issues parsing
// uninstantiated generic signatures.
return methHnd;
}
CORINFO_METHOD_HANDLE result = NULL;
JIT_TO_EE_TRANSITION();
MethodDesc *pCurrentCtor = (MethodDesc*)methHnd;
if (!pCurrentCtor->IsFCall())
{
result = methHnd;
}
else
{
MethodDesc *pTargetMethod = (MethodDesc*)targetMethodHnd;
TypeHandle delegateType = (TypeHandle)clsHnd;
MethodDesc *pDelegateCtor = COMDelegate::GetDelegateCtor(delegateType, pTargetMethod, pCtorData);
if (!pDelegateCtor)
pDelegateCtor = pCurrentCtor;
result = (CORINFO_METHOD_HANDLE)pDelegateCtor;
}
EE_TO_JIT_TRANSITION();
return result;
}
void CEEInfo::MethodCompileComplete(CORINFO_METHOD_HANDLE methHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
JIT_TO_EE_TRANSITION();
MethodDesc* pMD = GetMethod(methHnd);
if (pMD->IsDynamicMethod())
{
pMD->AsDynamicMethodDesc()->GetResolver()->FreeCompileTimeState();
}
EE_TO_JIT_TRANSITION();
}
// Given a module scope (scopeHnd), a method handle (context) and an metadata token,
// attempt to load the handle (type, field or method) associated with the token.
// If this is not possible at compile-time (because the method code is shared and the token contains type parameters)
// then indicate how the handle should be looked up at run-time.
//
// See corinfo.h for more details
//
void CEEInfo::embedGenericHandle(
CORINFO_RESOLVED_TOKEN * pResolvedToken,
BOOL fEmbedParent,
CORINFO_GENERICHANDLE_RESULT *pResult)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
INDEBUG(memset(pResult, 0xCC, sizeof(*pResult)));
JIT_TO_EE_TRANSITION();
BOOL fRuntimeLookup;
MethodDesc * pTemplateMD = NULL;
if (!fEmbedParent && pResolvedToken->hMethod != NULL)
{
MethodDesc * pMD = (MethodDesc *)pResolvedToken->hMethod;
TypeHandle th(pResolvedToken->hClass);
pResult->handleType = CORINFO_HANDLETYPE_METHOD;
Instantiation methodInst = pMD->GetMethodInstantiation();
pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pMD, th.GetMethodTable(), FALSE, methodInst, FALSE);
// Normalize the method handle for reflection
if (pResolvedToken->tokenType == CORINFO_TOKENKIND_Ldtoken)
pMD = MethodDesc::FindOrCreateAssociatedMethodDescForReflection(pMD, th, methodInst);
pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)pMD;
pTemplateMD = pMD;
// Runtime lookup is only required for stubs. Regular entrypoints are always the same shared MethodDescs.
fRuntimeLookup = pMD->IsWrapperStub() &&
(pMD->GetMethodTable()->IsSharedByGenericInstantiations() || TypeHandle::IsCanonicalSubtypeInstantiation(methodInst));
}
else
if (!fEmbedParent && pResolvedToken->hField != NULL)
{
FieldDesc * pFD = (FieldDesc *)pResolvedToken->hField;
TypeHandle th(pResolvedToken->hClass);
pResult->handleType = CORINFO_HANDLETYPE_FIELD;
pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)pFD;
fRuntimeLookup = th.IsSharedByGenericInstantiations() && pFD->IsStatic();
}
else
{
TypeHandle th(pResolvedToken->hClass);
pResult->handleType = CORINFO_HANDLETYPE_CLASS;
if (pResolvedToken->tokenType == CORINFO_TOKENKIND_Newarr)
{
pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)th.AsArray()->GetTemplateMethodTable();
}
else
{
pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)th.AsPtr();
}
if (fEmbedParent && pResolvedToken->hMethod != NULL)
{
MethodDesc * pDeclaringMD = (MethodDesc *)pResolvedToken->hMethod;
if (!pDeclaringMD->GetMethodTable()->HasSameTypeDefAs(th.GetMethodTable()))
{
//
// The method type may point to a sub-class of the actual class that declares the method.
// It is important to embed the declaring type in this case.
//
pTemplateMD = pDeclaringMD;
pResult->compileTimeHandle = (CORINFO_GENERIC_HANDLE)pDeclaringMD->GetMethodTable();
}
}
// IsSharedByGenericInstantiations would not work here. The runtime lookup is required
// even for standalone generic variables that show up as __Canon here.
fRuntimeLookup = th.IsCanonicalSubtype();
}
_ASSERTE(pResult->compileTimeHandle);
if (fRuntimeLookup
// Handle invalid IL - see comment in code:CEEInfo::ComputeRuntimeLookupForSharedGenericToken
&& ContextIsShared(pResolvedToken->tokenContext))
{
DictionaryEntryKind entryKind = EmptySlot;
switch (pResult->handleType)
{
case CORINFO_HANDLETYPE_CLASS:
entryKind = (pTemplateMD != NULL) ? DeclaringTypeHandleSlot : TypeHandleSlot;
break;
case CORINFO_HANDLETYPE_METHOD:
entryKind = MethodDescSlot;
break;
case CORINFO_HANDLETYPE_FIELD:
entryKind = FieldDescSlot;
break;
default:
_ASSERTE(false);
}
ComputeRuntimeLookupForSharedGenericToken(entryKind,
pResolvedToken,
NULL,
pTemplateMD,
&pResult->lookup);
}
else
{
// If the target is not shared then we've already got our result and
// can simply do a static look up
pResult->lookup.lookupKind.needsRuntimeLookup = false;
pResult->lookup.constLookup.handle = pResult->compileTimeHandle;
pResult->lookup.constLookup.accessType = IAT_VALUE;
}
EE_TO_JIT_TRANSITION();
}
void CEEInfo::ScanForModuleDependencies(Module* pModule, SigPointer psig)
{
STANDARD_VM_CONTRACT;
_ASSERTE(pModule && !pModule->IsSystem());
CorElementType eType;
IfFailThrow(psig.GetElemType(&eType));
switch (eType)
{
case ELEMENT_TYPE_GENERICINST:
{
ScanForModuleDependencies(pModule,psig);
IfFailThrow(psig.SkipExactlyOne());
ULONG ntypars;
IfFailThrow(psig.GetData(&ntypars));
for (ULONG i = 0; i < ntypars; i++)
{
ScanForModuleDependencies(pModule,psig);
IfFailThrow(psig.SkipExactlyOne());
}
break;
}
case ELEMENT_TYPE_VALUETYPE:
case ELEMENT_TYPE_CLASS:
{
mdToken tk;
IfFailThrow(psig.GetToken(&tk));
if (TypeFromToken(tk) == mdtTypeRef)
{
Module * pTypeDefModule;
mdToken tkTypeDef;
if (ClassLoader::ResolveTokenToTypeDefThrowing(pModule, tk, &pTypeDefModule, &tkTypeDef))
break;
if (!pTypeDefModule->IsSystem() && (pModule != pTypeDefModule))
{
m_pOverride->addActiveDependency((CORINFO_MODULE_HANDLE)pModule, (CORINFO_MODULE_HANDLE)pTypeDefModule);
}
}
break;
}
default:
break;
}
}
void CEEInfo::ScanMethodSpec(Module * pModule, PCCOR_SIGNATURE pMethodSpec, ULONG cbMethodSpec)
{
STANDARD_VM_CONTRACT;
SigPointer sp(pMethodSpec, cbMethodSpec);
BYTE etype;
IfFailThrow(sp.GetByte(&etype));
_ASSERT(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST);
ULONG nGenericMethodArgs;
IfFailThrow(sp.GetData(&nGenericMethodArgs));
for (ULONG i = 0; i < nGenericMethodArgs; i++)
{
ScanForModuleDependencies(pModule,sp);
IfFailThrow(sp.SkipExactlyOne());
}
}
BOOL CEEInfo::ScanTypeSpec(Module * pModule, PCCOR_SIGNATURE pTypeSpec, ULONG cbTypeSpec)
{
STANDARD_VM_CONTRACT;
SigPointer sp(pTypeSpec, cbTypeSpec);
CorElementType eType;
IfFailThrow(sp.GetElemType(&eType));
// Filter out non-instantiated types and typedescs (typevars, arrays, ...)
if (eType != ELEMENT_TYPE_GENERICINST)
{
// Scanning of the parent chain is required for reference types only.
// Note that the parent chain MUST NOT be scanned for instantiated
// generic variables because of they are not a real dependencies.
return (eType == ELEMENT_TYPE_CLASS);
}
IfFailThrow(sp.SkipExactlyOne());
ULONG ntypars;
IfFailThrow(sp.GetData(&ntypars));
for (ULONG i = 0; i < ntypars; i++)
{
ScanForModuleDependencies(pModule,sp);
IfFailThrow(sp.SkipExactlyOne());
}
return TRUE;
}
void CEEInfo::ScanInstantiation(Module * pModule, Instantiation inst)
{
STANDARD_VM_CONTRACT;
for (DWORD i = 0; i < inst.GetNumArgs(); i++)
{
TypeHandle th = inst[i];
if (th.IsTypeDesc())
continue;
MethodTable * pMT = th.AsMethodTable();
Module * pDefModule = pMT->GetModule();
if (!pDefModule->IsSystem() && (pModule != pDefModule))
{
m_pOverride->addActiveDependency((CORINFO_MODULE_HANDLE)pModule, (CORINFO_MODULE_HANDLE)pDefModule);
}
if (pMT->HasInstantiation())
{
ScanInstantiation(pModule, pMT->GetInstantiation());
}
}
}
//
// ScanToken is used to track triggers for creation of per-AppDomain state instead, including allocations required for statics and
// triggering of module cctors.
//
// The basic rule is: There should be no possibility of a shared module that is "active" to have a direct call into a module that
// is not "active". And we don't want to intercept every call during runtime, so during compile time we track static calls and
// everything that can result in new virtual calls.
//
// The current algorithm (scan the parent type chain and instantiation variables) is more than enough to maintain this invariant.
// One could come up with a more efficient algorithm that still maintains the invariant, but it may introduce backward compatibility
// issues.
//
// For efficiency, the implementation leverages the loaded types as much as possible. Unfortunately, we still have to go back to
// metadata when the generic variables could have been substituted via generic context.
//
void CEEInfo::ScanToken(Module * pModule, CORINFO_RESOLVED_TOKEN * pResolvedToken, TypeHandle th, MethodDesc * pMD)
{
STANDARD_VM_CONTRACT;
if (pModule->IsSystem())
return;
if (isVerifyOnly())
return;
//
// Scan method instantiation
//
if (pMD != NULL && pResolvedToken->pMethodSpec != NULL)
{
if (ContextIsInstantiated(pResolvedToken->tokenContext))
{
ScanMethodSpec(pModule, pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
}
else
{
ScanInstantiation(pModule, pMD->GetMethodInstantiation());
}
}
if (th.IsTypeDesc())
return;
MethodTable * pMT = th.AsMethodTable();
//
// Scan type instantiation
//
if (pResolvedToken->pTypeSpec != NULL)
{
if (ContextIsInstantiated(pResolvedToken->tokenContext))
{
if (!ScanTypeSpec(pModule, pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec))
return;
}
else
{
ScanInstantiation(pModule, pMT->GetInstantiation());
}
}
//
// Scan chain of parent types
//
for (;;)
{
Module * pDefModule = pMT->GetModule();
if (pDefModule->IsSystem())
break;
if (pModule != pDefModule)
{
m_pOverride->addActiveDependency((CORINFO_MODULE_HANDLE)pModule, (CORINFO_MODULE_HANDLE)pDefModule);
}
MethodTable * pParentMT = pMT->GetParentMethodTable();
if (pParentMT == NULL)
break;
if (pParentMT->HasInstantiation())
{
IMDInternalImport* pInternalImport = pDefModule->GetMDImport();
mdToken tkParent;
IfFailThrow(pInternalImport->GetTypeDefProps(pMT->GetCl(), NULL, &tkParent));
if (TypeFromToken(tkParent) == mdtTypeSpec)
{
PCCOR_SIGNATURE pTypeSpec;
ULONG cbTypeSpec;
IfFailThrow(pInternalImport->GetTypeSpecFromToken(tkParent, &pTypeSpec, &cbTypeSpec));
ScanTypeSpec(pDefModule, pTypeSpec, cbTypeSpec);
}
}
pMT = pParentMT;
}
}
void CEEInfo::ScanTokenForDynamicScope(CORINFO_RESOLVED_TOKEN * pResolvedToken, TypeHandle th, MethodDesc * pMD)
{
STANDARD_VM_CONTRACT;
if (m_pMethodBeingCompiled->IsLCGMethod())
{
// The dependency tracking for LCG is irrelevant. Perform immediate activation.
if (pMD != NULL && pMD->HasMethodInstantiation())
pMD->EnsureActive();
if (!th.IsTypeDesc())
th.AsMethodTable()->EnsureInstanceActive();
return;
}
// Stubs-as-IL have to do regular dependency tracking because they can be shared cross-domain.
Module * pModule = GetDynamicResolver(pResolvedToken->tokenScope)->GetDynamicMethod()->GetModule();
ScanToken(pModule, pResolvedToken, th, pMD);
}
MethodDesc * CEEInfo::GetMethodForSecurity(CORINFO_METHOD_HANDLE callerHandle)
{
STANDARD_VM_CONTRACT;
// Cache the cast lookup
if (callerHandle == m_hMethodForSecurity_Key)
{
return m_pMethodForSecurity_Value;
}
MethodDesc * pCallerMethod = (MethodDesc *)callerHandle;
//If the caller is generic, load the open type and then load the field again, This allows us to
//differentiate between BadGeneric<T> containing a memberRef for a field of type InaccessibleClass and
//GoodGeneric<T> containing a memberRef for a field of type T instantiated over InaccessibleClass.
MethodDesc * pMethodForSecurity = pCallerMethod->IsILStub() ?
pCallerMethod : pCallerMethod->LoadTypicalMethodDefinition();
m_hMethodForSecurity_Key = callerHandle;
m_pMethodForSecurity_Value = pMethodForSecurity;
return pMethodForSecurity;
}
// Check that the instantation is <!/!!0, ..., !/!!(n-1)>
static BOOL IsSignatureForTypicalInstantiation(SigPointer sigptr, CorElementType varType, ULONG ntypars)
{
STANDARD_VM_CONTRACT;
for (ULONG i = 0; i < ntypars; i++)
{
CorElementType type;
IfFailThrow(sigptr.GetElemType(&type));
if (type != varType)
return FALSE;
ULONG data;
IfFailThrow(sigptr.GetData(&data));
if (data != i)
return FALSE;
}
return TRUE;
}
// Check that methodSpec instantiation is <!!0, ..., !!(n-1)>
static BOOL IsMethodSpecForTypicalInstantation(SigPointer sigptr)
{
STANDARD_VM_CONTRACT;
BYTE etype;
IfFailThrow(sigptr.GetByte(&etype));
_ASSERTE(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST);
ULONG ntypars;
IfFailThrow(sigptr.GetData(&ntypars));
return IsSignatureForTypicalInstantiation(sigptr, ELEMENT_TYPE_MVAR, ntypars);
}
// Check that typeSpec instantiation is <!0, ..., !(n-1)>
static BOOL IsTypeSpecForTypicalInstantiation(SigPointer sigptr)
{
STANDARD_VM_CONTRACT;
CorElementType type;
IfFailThrow(sigptr.GetElemType(&type));
if (type != ELEMENT_TYPE_GENERICINST)
return FALSE;
IfFailThrow(sigptr.SkipExactlyOne());
ULONG ntypars;
IfFailThrow(sigptr.GetData(&ntypars));
return IsSignatureForTypicalInstantiation(sigptr, ELEMENT_TYPE_VAR, ntypars);
}
void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entryKind,
CORINFO_RESOLVED_TOKEN * pResolvedToken,
CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
MethodDesc * pTemplateMD /* for method-based slots */,
CORINFO_LOOKUP *pResultLookup)
{
CONTRACTL{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pResultLookup));
} CONTRACTL_END;
// We should never get here when we are only verifying
_ASSERTE(!isVerifyOnly());
pResultLookup->lookupKind.needsRuntimeLookup = true;
pResultLookup->lookupKind.runtimeLookupFlags = 0;
CORINFO_RUNTIME_LOOKUP *pResult = &pResultLookup->runtimeLookup;
pResult->signature = NULL;
pResult->indirectFirstOffset = 0;
pResult->indirectSecondOffset = 0;
// Unless we decide otherwise, just do the lookup via a helper function
pResult->indirections = CORINFO_USEHELPER;
MethodDesc *pContextMD = GetMethodFromContext(pResolvedToken->tokenContext);
MethodTable *pContextMT = pContextMD->GetMethodTable();
// Do not bother computing the runtime lookup if we are inlining. The JIT is going
// to abort the inlining attempt anyway.
if (pContextMD != m_pMethodBeingCompiled)
{
return;
}
// There is a pathological case where invalid IL refereces __Canon type directly, but there is no dictionary availabled to store the lookup.
// All callers of ComputeRuntimeLookupForSharedGenericToken have to filter out this case. We can't do much about it here.
_ASSERTE(pContextMD->IsSharedByGenericInstantiations());
BOOL fInstrument = FALSE;
#ifdef FEATURE_NATIVE_IMAGE_GENERATION
// This will make sure that when IBC logging is turned on we will go through a version
// of JIT_GenericHandle which logs the access. Note that we still want the dictionaries
// to be populated to prepopulate the types at NGen time.
if (IsCompilingForNGen() &&
GetAppDomain()->ToCompilationDomain()->m_fForceInstrument)
{
fInstrument = TRUE;
}
#endif // FEATURE_NATIVE_IMAGE_GENERATION
if (pContextMD->RequiresInstMethodDescArg())
{
pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_METHODPARAM;
}
else
{
if (pContextMD->RequiresInstMethodTableArg())
pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_CLASSPARAM;
else
pResultLookup->lookupKind.runtimeLookupKind = CORINFO_LOOKUP_THISOBJ;
}
#ifdef FEATURE_READYTORUN_COMPILER
if (IsReadyToRunCompilation())
{
pResultLookup->lookupKind.runtimeLookupArgs = NULL;
switch (entryKind)
{
case DeclaringTypeHandleSlot:
_ASSERTE(pTemplateMD != NULL);
pResultLookup->lookupKind.runtimeLookupArgs = pTemplateMD->GetMethodTable();
pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_DeclaringTypeHandle;
break;
case TypeHandleSlot:
pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_TypeHandle;
break;
case MethodDescSlot:
case MethodEntrySlot:
case ConstrainedMethodEntrySlot:
case DispatchStubAddrSlot:
{
if (pTemplateMD != (MethodDesc*)pResolvedToken->hMethod)
ThrowHR(E_NOTIMPL);
if (entryKind == MethodDescSlot)
pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_MethodHandle;
else if (entryKind == MethodEntrySlot || entryKind == ConstrainedMethodEntrySlot)
pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_MethodEntry;
else
pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_VirtualEntry;
pResultLookup->lookupKind.runtimeLookupArgs = pConstrainedResolvedToken;
break;
}
case FieldDescSlot:
pResultLookup->lookupKind.runtimeLookupFlags = READYTORUN_FIXUP_FieldHandle;
break;
default:
_ASSERTE(!"Unknown dictionary entry kind!");
IfFailThrow(E_FAIL);
}
// For R2R compilations, we don't generate the dictionary lookup signatures (dictionary lookups are done in a
// different way that is more version resilient... plus we can't have pointers to existing MTs/MDs in the sigs)
return;
}
#endif
// If we've got a method type parameter of any kind then we must look in the method desc arg
if (pContextMD->RequiresInstMethodDescArg())
{
pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_METHOD_LOG : CORINFO_HELP_RUNTIMEHANDLE_METHOD;
if (fInstrument)
goto NoSpecialCase;
// Special cases:
// (1) Naked method type variable: look up directly in instantiation hanging off runtime md
// (2) Reference to method-spec of current method (e.g. a recursive call) i.e. currentmeth<!0,...,!(n-1)>
if ((entryKind == TypeHandleSlot) && (pResolvedToken->tokenType != CORINFO_TOKENKIND_Newarr))
{
SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
CorElementType type;
IfFailThrow(sigptr.GetElemType(&type));
if (type == ELEMENT_TYPE_MVAR)
{
pResult->indirections = 2;
pResult->testForNull = 0;
#ifdef FEATURE_PREJIT
pResult->testForFixup = 1;
#else
pResult->testForFixup = 0;
#endif
pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo);
if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative)
{
pResult->indirectFirstOffset = 1;
}
ULONG data;
IfFailThrow(sigptr.GetData(&data));
pResult->offsets[1] = sizeof(TypeHandle) * data;
return;
}
}
else if (entryKind == MethodDescSlot)
{
// It's the context itself (i.e. a recursive call)
if (!pTemplateMD->HasSameMethodDefAs(pContextMD))
goto NoSpecialCase;
// Now just check that the instantiation is (!!0, ..., !!(n-1))
if (!IsMethodSpecForTypicalInstantation(SigPointer(pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec)))
goto NoSpecialCase;
// Type instantiation has to match too if there is one
if (pContextMT->HasInstantiation())
{
TypeHandle thTemplate(pResolvedToken->hClass);
if (thTemplate.IsTypeDesc() || !thTemplate.AsMethodTable()->HasSameTypeDefAs(pContextMT))
goto NoSpecialCase;
// This check filters out method instantiation on generic type definition, like G::M<!!0>()
// We may not ever get it here. Filter it out just to be sure...
if (pResolvedToken->pTypeSpec == NULL)
goto NoSpecialCase;
if (!IsTypeSpecForTypicalInstantiation(SigPointer(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec)))
goto NoSpecialCase;
}
// Just use the method descriptor that was passed in!
pResult->indirections = 0;
pResult->testForNull = 0;
pResult->testForFixup = 0;
return;
}
}
// Otherwise we must just have class type variables
else
{
_ASSERTE(pContextMT->GetNumGenericArgs() > 0);
if (pContextMD->RequiresInstMethodTableArg())
{
// If we've got a vtable extra argument, go through that
pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG : CORINFO_HELP_RUNTIMEHANDLE_CLASS;
}
// If we've got an object, go through its vtable
else
{
_ASSERTE(pContextMD->AcquiresInstMethodTableFromThis());
pResult->helper = fInstrument ? CORINFO_HELP_RUNTIMEHANDLE_CLASS_LOG : CORINFO_HELP_RUNTIMEHANDLE_CLASS;
}
if (fInstrument)
goto NoSpecialCase;
// Special cases:
// (1) Naked class type variable: look up directly in instantiation hanging off vtable
// (2) C<!0,...,!(n-1)> where C is the context's class and C is sealed: just return vtable ptr
if ((entryKind == TypeHandleSlot) && (pResolvedToken->tokenType != CORINFO_TOKENKIND_Newarr))
{
SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
CorElementType type;
IfFailThrow(sigptr.GetElemType(&type));
if (type == ELEMENT_TYPE_VAR)
{
pResult->indirections = 3;
pResult->testForNull = 0;
#ifdef FEATURE_PREJIT
pResult->testForFixup = 1;
#else
pResult->testForFixup = 0;
#endif
pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo();
pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1);
ULONG data;
IfFailThrow(sigptr.GetData(&data));
pResult->offsets[2] = sizeof(TypeHandle) * data;
if (MethodTable::IsPerInstInfoRelative())
{
pResult->indirectFirstOffset = 1;
pResult->indirectSecondOffset = 1;
}
return;
}
else if (type == ELEMENT_TYPE_GENERICINST &&
(pContextMT->IsSealed() || pResultLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_CLASSPARAM))
{
TypeHandle thTemplate(pResolvedToken->hClass);
if (thTemplate.IsTypeDesc() || !thTemplate.AsMethodTable()->HasSameTypeDefAs(pContextMT))
goto NoSpecialCase;
if (!IsTypeSpecForTypicalInstantiation(SigPointer(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec)))
goto NoSpecialCase;
// Just use the vtable pointer itself!
pResult->indirections = 0;
pResult->testForNull = 0;
pResult->testForFixup = 0;
return;
}
}
}
NoSpecialCase:
SigBuilder sigBuilder;
sigBuilder.AppendData(entryKind);
if (pResultLookup->lookupKind.runtimeLookupKind != CORINFO_LOOKUP_METHODPARAM)
{
_ASSERTE(pContextMT->GetNumDicts() > 0);
sigBuilder.AppendData(pContextMT->GetNumDicts() - 1);
}
Module * pModule = (Module *)pResolvedToken->tokenScope;
switch (entryKind)
{
case DeclaringTypeHandleSlot:
_ASSERTE(pTemplateMD != NULL);
sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
sigBuilder.AppendPointer(pTemplateMD->GetMethodTable());
// fall through
case TypeHandleSlot:
{
if (pResolvedToken->tokenType == CORINFO_TOKENKIND_Newarr)
{
if (!IsReadyToRunCompilation())
{
sigBuilder.AppendElementType((CorElementType)ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG);
}
sigBuilder.AppendElementType(ELEMENT_TYPE_SZARRAY);
}
// Note that we can come here with pResolvedToken->pTypeSpec == NULL for invalid IL that
// directly references __Canon
if (pResolvedToken->pTypeSpec != NULL)
{
SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder);
}
else
{
sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
sigBuilder.AppendPointer(pResolvedToken->hClass);
}
}
break;
case ConstrainedMethodEntrySlot:
// Encode constrained type token
if (pConstrainedResolvedToken->pTypeSpec != NULL)
{
SigPointer sigptr(pConstrainedResolvedToken->pTypeSpec, pConstrainedResolvedToken->cbTypeSpec);
sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder);
}
else
{
sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
sigBuilder.AppendPointer(pConstrainedResolvedToken->hClass);
}
// fall through
case MethodDescSlot:
case MethodEntrySlot:
case DispatchStubAddrSlot:
{
// Encode containing type
if (pResolvedToken->pTypeSpec != NULL)
{
SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder);
}
else
{
sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
sigBuilder.AppendPointer(pResolvedToken->hClass);
}
// Encode method
_ASSERTE(pTemplateMD != NULL);
mdMethodDef methodToken = pTemplateMD->GetMemberDef_NoLogging();
DWORD methodFlags = 0;
// Check for non-NULL method spec first. We can encode the method instantiation only if we have one in method spec to start with. Note that there are weird cases
// like instantiating stub for generic method definition that do not have method spec but that won't be caught by the later conditions either.
BOOL fMethodNeedsInstantiation = (pResolvedToken->pMethodSpec != NULL) && pTemplateMD->HasMethodInstantiation() && !pTemplateMD->IsGenericMethodDefinition();
if (pTemplateMD->IsUnboxingStub())
methodFlags |= ENCODE_METHOD_SIG_UnboxingStub;
// Always create instantiating stub for method entry points even if the template does not ask for it. It saves caller
// from creating throw-away instantiating stub.
if (pTemplateMD->IsInstantiatingStub() || (entryKind == MethodEntrySlot))
methodFlags |= ENCODE_METHOD_SIG_InstantiatingStub;
if (fMethodNeedsInstantiation)
methodFlags |= ENCODE_METHOD_SIG_MethodInstantiation;
if (IsNilToken(methodToken))
{
methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken;
}
else
if (entryKind == DispatchStubAddrSlot && pTemplateMD->IsVtableMethod())
{
// Encode the method for dispatch stub using slot to avoid touching the interface method MethodDesc at runtime
// There should be no other flags set if we are encoding the method using slot for virtual stub dispatch
_ASSERTE(methodFlags == 0);
methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken;
}
else
if (!pTemplateMD->GetModule()->IsInCurrentVersionBubble())
{
// Using a method defined in another version bubble. We can assume the slot number is stable only for real interface methods.
if (!pTemplateMD->GetMethodTable()->IsInterface() || pTemplateMD->IsStatic() || pTemplateMD->HasMethodInstantiation())
{
_ASSERTE(!"References to non-interface methods not yet supported in version resilient images");
IfFailThrow(E_FAIL);
}
methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken;
}
sigBuilder.AppendData(methodFlags);
if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0)
{
// Encode method token and its module context (as method's type)
sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
sigBuilder.AppendPointer(pTemplateMD->GetMethodTable());
sigBuilder.AppendData(RidFromToken(methodToken));
}
else
{
sigBuilder.AppendData(pTemplateMD->GetSlot());
}
if (fMethodNeedsInstantiation)
{
SigPointer sigptr(pResolvedToken->pMethodSpec, pResolvedToken->cbMethodSpec);
BYTE etype;
IfFailThrow(sigptr.GetByte(&etype));
// Load the generic method instantiation
THROW_BAD_FORMAT_MAYBE(etype == (BYTE)IMAGE_CEE_CS_CALLCONV_GENERICINST, 0, pModule);
DWORD nGenericMethodArgs;
IfFailThrow(sigptr.GetData(&nGenericMethodArgs));
sigBuilder.AppendData(nGenericMethodArgs);
_ASSERTE(nGenericMethodArgs == pTemplateMD->GetNumGenericMethodArgs());
for (DWORD i = 0; i < nGenericMethodArgs; i++)
{
sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder);
}
}
}
break;
case FieldDescSlot:
{
if (pResolvedToken->pTypeSpec != NULL)
{
// Encode containing type
SigPointer sigptr(pResolvedToken->pTypeSpec, pResolvedToken->cbTypeSpec);
sigptr.ConvertToInternalExactlyOne(pModule, NULL, &sigBuilder);
}
else
{
sigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
sigBuilder.AppendPointer(pResolvedToken->hClass);
}
FieldDesc * pField = (FieldDesc *)pResolvedToken->hField;
_ASSERTE(pField != NULL);
DWORD fieldIndex = pField->GetApproxEnclosingMethodTable()->GetIndexForFieldDesc(pField);
sigBuilder.AppendData(fieldIndex);
}
break;
default:
_ASSERTE(false);
}
DictionaryEntrySignatureSource signatureSource = (IsCompilationProcess() ? FromZapImage : FromJIT);
// It's a method dictionary lookup
if (pResultLookup->lookupKind.runtimeLookupKind == CORINFO_LOOKUP_METHODPARAM)
{
_ASSERTE(pContextMD != NULL);
_ASSERTE(pContextMD->HasMethodInstantiation());
if (DictionaryLayout::FindToken(pContextMD->GetLoaderAllocator(), pContextMD->GetNumGenericMethodArgs(), pContextMD->GetDictionaryLayout(), pResult, &sigBuilder, 1, signatureSource))
{
pResult->testForNull = 1;
pResult->testForFixup = 0;
// Indirect through dictionary table pointer in InstantiatedMethodDesc
pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo);
if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative)
{
pResult->indirectFirstOffset = 1;
}
}
}
// It's a class dictionary lookup (CORINFO_LOOKUP_CLASSPARAM or CORINFO_LOOKUP_THISOBJ)
else
{
if (DictionaryLayout::FindToken(pContextMT->GetLoaderAllocator(), pContextMT->GetNumGenericArgs(), pContextMT->GetClass()->GetDictionaryLayout(), pResult, &sigBuilder, 2, signatureSource))
{
pResult->testForNull = 1;
pResult->testForFixup = 0;
// Indirect through dictionary table pointer in vtable
pResult->offsets[0] = MethodTable::GetOffsetOfPerInstInfo();
// Next indirect through the dictionary appropriate to this instantiated type
pResult->offsets[1] = sizeof(TypeHandle*) * (pContextMT->GetNumDicts() - 1);
if (MethodTable::IsPerInstInfoRelative())
{
pResult->indirectFirstOffset = 1;
pResult->indirectSecondOffset = 1;
}
}
}
}
/*********************************************************************/
const char* CEEInfo::getClassName (CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
const char* result = NULL;
JIT_TO_EE_TRANSITION();
TypeHandle VMClsHnd(clsHnd);
MethodTable* pMT = VMClsHnd.GetMethodTable();
if (pMT == NULL)
{
result = "";
}
else
{
#ifdef _DEBUG
result = pMT->GetDebugClassName();
#else // !_DEBUG
// since this is for diagnostic purposes only,
// give up on the namespace, as we don't have a buffer to concat it
// also note this won't show array class names.
LPCUTF8 nameSpace;
result = pMT->GetFullyQualifiedNameInfo(&nameSpace);
#endif
}
EE_TO_JIT_TRANSITION();
return result;
}
/***********************************************************************/
const char* CEEInfo::getHelperName (CorInfoHelpFunc ftnNum)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
PRECONDITION(ftnNum >= 0 && ftnNum < CORINFO_HELP_COUNT);
} CONTRACTL_END;
const char* result = NULL;
JIT_TO_EE_TRANSITION_LEAF();
#ifdef CROSSGEN_COMPILE
result = hlpNameTable[ftnNum];
#else
#ifdef _DEBUG
result = hlpFuncTable[ftnNum].name;
#else
result = "AnyJITHelper";
#endif
#endif
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
/*********************************************************************/
int CEEInfo::appendClassName(__deref_inout_ecount(*pnBufLen) WCHAR** ppBuf,
int* pnBufLen,
CORINFO_CLASS_HANDLE clsHnd,
BOOL fNamespace,
BOOL fFullInst,
BOOL fAssembly)
{
CONTRACTL {
SO_TOLERANT;
MODE_PREEMPTIVE;
THROWS;
GC_TRIGGERS;
} CONTRACTL_END;
int nLen = 0;
JIT_TO_EE_TRANSITION();
TypeHandle th(clsHnd);
StackSString ss;
TypeString::AppendType(ss,th,
(fNamespace ? TypeString::FormatNamespace : 0) |
(fFullInst ? TypeString::FormatFullInst : 0) |
(fAssembly ? TypeString::FormatAssembly : 0));
const WCHAR* szString = ss.GetUnicode();
nLen = (int)wcslen(szString);
if (*pnBufLen > 0)
{
wcscpy_s(*ppBuf, *pnBufLen, szString );
(*ppBuf)[(*pnBufLen) - 1] = W('\0');
(*ppBuf) += nLen;
(*pnBufLen) -= nLen;
}
EE_TO_JIT_TRANSITION();
return nLen;
}
/*********************************************************************/
CORINFO_MODULE_HANDLE CEEInfo::getClassModule(CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CORINFO_MODULE_HANDLE result = NULL;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle VMClsHnd(clsHnd);
result = CORINFO_MODULE_HANDLE(VMClsHnd.GetModule());
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
/*********************************************************************/
CORINFO_ASSEMBLY_HANDLE CEEInfo::getModuleAssembly(CORINFO_MODULE_HANDLE modHnd)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CORINFO_ASSEMBLY_HANDLE result = NULL;
JIT_TO_EE_TRANSITION_LEAF();
result = CORINFO_ASSEMBLY_HANDLE(GetModule(modHnd)->GetAssembly());
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
/*********************************************************************/
const char* CEEInfo::getAssemblyName(CORINFO_ASSEMBLY_HANDLE asmHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
const char* result = NULL;
JIT_TO_EE_TRANSITION();
result = ((Assembly*)asmHnd)->GetSimpleName();
EE_TO_JIT_TRANSITION();
return result;
}
/*********************************************************************/
void* CEEInfo::LongLifetimeMalloc(size_t sz)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
void* result = NULL;
JIT_TO_EE_TRANSITION_LEAF();
result = new (nothrow) char[sz];
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
/*********************************************************************/
void CEEInfo::LongLifetimeFree(void* obj)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
JIT_TO_EE_TRANSITION_LEAF();
(operator delete)(obj);
EE_TO_JIT_TRANSITION_LEAF();
}
/*********************************************************************/
size_t CEEInfo::getClassModuleIdForStatics(CORINFO_CLASS_HANDLE clsHnd, CORINFO_MODULE_HANDLE *pModuleHandle, void **ppIndirection)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
size_t result = 0;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle VMClsHnd(clsHnd);
Module *pModule = VMClsHnd.AsMethodTable()->GetModuleForStatics();
if (ppIndirection != NULL)
*ppIndirection = NULL;
// The zapper needs the module handle. The jit should not use it at all.
if (pModuleHandle)
*pModuleHandle = CORINFO_MODULE_HANDLE(pModule);
result = pModule->GetModuleID();
_ASSERTE(result);
EE_TO_JIT_TRANSITION_LEAF();
return result;
}
/*********************************************************************/
BOOL CEEInfo::isValueClass(CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
BOOL ret = FALSE;
JIT_TO_EE_TRANSITION_LEAF();
_ASSERTE(clsHnd);
// Note that clsHnd.IsValueType() would not return what the JIT expects
// for corner cases like ELEMENT_TYPE_FNPTR
TypeHandle VMClsHnd(clsHnd);
MethodTable * pMT = VMClsHnd.GetMethodTable();
ret = (pMT != NULL) ? pMT->IsValueType() : 0;
EE_TO_JIT_TRANSITION_LEAF();
return ret;
}
/*********************************************************************/
// Decides how the JIT should do the optimization to inline the check for
// GetTypeFromHandle(handle) == obj.GetType() (for CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE)
// GetTypeFromHandle(X) == GetTypeFromHandle(Y) (for CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
//
// This will enable to use directly the typehandle instead of going through getClassByHandle
CorInfoInlineTypeCheck CEEInfo::canInlineTypeCheck(CORINFO_CLASS_HANDLE clsHnd, CorInfoInlineTypeCheckSource source)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
CorInfoInlineTypeCheck ret;
JIT_TO_EE_TRANSITION_LEAF();
if (source == CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN)
{
// It's always okay to compare type handles coming from IL tokens
ret = CORINFO_INLINE_TYPECHECK_PASS;
}
else
{
_ASSERTE(source == CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE);
ret = canInlineTypeCheckWithObjectVTable(clsHnd) ?
CORINFO_INLINE_TYPECHECK_PASS : CORINFO_INLINE_TYPECHECK_NONE;
}
EE_TO_JIT_TRANSITION_LEAF();
return(ret);
}
/*********************************************************************/
// If this method returns true, JIT will do optimization to inline the check for
// GetTypeFromHandle(handle) == obj.GetType()
//
// This will enable to use directly the typehandle instead of going through getClassByHandle
BOOL CEEInfo::canInlineTypeCheckWithObjectVTable (CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
} CONTRACTL_END;
BOOL ret = FALSE;
JIT_TO_EE_TRANSITION_LEAF();
_ASSERTE(clsHnd);
TypeHandle VMClsHnd(clsHnd);
if (VMClsHnd.IsTypeDesc())
{
// We can't do this optimization for arrays because of the object methodtable is template methodtable
ret = FALSE;
}
else
if (VMClsHnd.AsMethodTable()->IsMarshaledByRef())
{
// We can't do this optimization for marshalbyrefs because of the object methodtable can be transparent proxy
ret = FALSE;
}
else
if (VMClsHnd.AsMethodTable()->IsInterface())
{
// Object.GetType() should not ever return interface. However, WCF custom remoting proxy does it. Disable this
// optimization for interfaces so that (autogenerated) code that compares Object.GetType() with interface type works
// as expected for WCF custom remoting proxy. Note that this optimization is still not going to work well for custom
// remoting proxies that are even more broken than the WCF one, e.g. returning random non-marshalbyref types
// from Object.GetType().
ret = FALSE;
}
else
if (VMClsHnd == TypeHandle(g_pCanonMethodTableClass))
{
// We can't do this optimization in shared generics code because of we do not know what the actual type is going to be.
// (It can be array, marshalbyref, etc.)
ret = FALSE;
}
else
{
// It is safe to perform this optimization
ret = TRUE;
}
EE_TO_JIT_TRANSITION_LEAF();
return(ret);
}
/*********************************************************************/
DWORD CEEInfo::getClassAttribs (CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
// <REVISIT_TODO>@todo FIX need to really fetch the class atributes. at present
// we don't need to because the JIT only cares in the case of COM classes</REVISIT_TODO>
DWORD ret = 0;
JIT_TO_EE_TRANSITION();
ret = getClassAttribsInternal(clsHnd);
EE_TO_JIT_TRANSITION();
return ret;
}
/*********************************************************************/
BOOL CEEInfo::isStructRequiringStackAllocRetBuf(CORINFO_CLASS_HANDLE clsHnd)
{
CONTRACTL {
SO_TOLERANT;
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
} CONTRACTL_END;
BOOL ret = 0;
JIT_TO_EE_TRANSITION_LEAF();
TypeHandle VMClsHnd(clsHnd);
MethodTable * pMT = VMClsHnd.GetMethodTable();
ret = (pMT != NULL && pMT->IsStructRequiringStackAllocRetBuf());
EE_TO_JIT_TRANSITION_LEAF();
return ret;
}
/*********************************************************************/
DWORD CEEInfo::getClassAttribsInternal (CORINFO_CLASS_HANDLE clsHnd)
{
STANDARD_VM_CONTRACT;
DWORD ret = 0;
_ASSERTE(clsHnd);
TypeHandle VMClsHnd(clsHnd);