#define _Outptr_opt_result_maybenull_
#define _Outptr_result_z_
#define _Outptr_result_buffer_(size)
+#define _Outptr_opt_result_buffer_(size)
#endif
#include "jiteeversionguid.h"
unsigned index
) = 0;
-
- // Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
- // If fNamespace=TRUE, include the namespace/enclosing classes
- // If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
- // If fAssembly=TRUE, suffix with a comma and the full assembly qualification
- // return size of representation
+ // Append a (possibly truncated) textual representation of the type `cls` to a preallocated buffer.
+ //
+ // Arguments:
+ // ppBuf - Pointer to buffer pointer. See below for details.
+ // pnBufLen - Pointer to buffer length. Must not be nullptr. See below for details.
+ // fNamespace - If true, include the namespace/enclosing classes.
+ // fFullInst - If true (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters.
+ // fAssembly - If true, suffix with a comma and the full assembly qualification.
+ //
+ // Returns the length of the representation, as a count of characters (but not including a terminating null character).
+ // Note that this will always be the actual number of characters required by the representation, even if the string
+ // was truncated when copied to the buffer.
+ //
+ // Operation:
+ //
+ // On entry, `*pnBufLen` specifies the size of the buffer pointed to by `*ppBuf` as a count of characters.
+ // There are two cases:
+ // 1. If the size is zero, the function computes the length of the representation and returns that.
+ // `ppBuf` is ignored (and may be nullptr) and `*ppBuf` and `*pnBufLen` are not updated.
+ // 2. If the size is non-zero, the buffer pointed to by `*ppBuf` is (at least) that size. The class name
+ // representation is copied to the buffer pointed to by `*ppBuf`. As many characters of the name as will fit in the
+ // buffer are copied. Thus, if the name is larger than the size of the buffer, the name will be truncated in the buffer.
+ // The buffer is guaranteed to be null terminated. Thus, the size must be large enough to include a terminating null
+ // character, or the string will be truncated to include one. On exit, `*pnBufLen` is updated by subtracting the
+ // number of characters that were actually copied to the buffer. Also, `*ppBuf` is updated to point at the null
+ // character that was added to the end of the name.
+ //
virtual int appendClassName(
- _Outptr_result_buffer_(*pnBufLen) char16_t** ppBuf,
- int* pnBufLen,
- CORINFO_CLASS_HANDLE cls,
- bool fNamespace,
- bool fFullInst,
- bool fAssembly
+ _Outptr_opt_result_buffer_(*pnBufLen) char16_t** ppBuf, /* IN OUT */
+ int* pnBufLen, /* IN OUT */
+ CORINFO_CLASS_HANDLE cls,
+ bool fNamespace,
+ bool fFullInst,
+ bool fAssembly
) = 0;
// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) & CORINFO_FLG_VALUECLASS, except faster.
#define GUID_DEFINED
#endif // !GUID_DEFINED
-constexpr GUID JITEEVersionIdentifier = { /* 398270b8-d474-428f-8113-3834281853cf */
- 0x398270b8,
- 0xd474,
- 0x428f,
- {0x81, 0x13, 0x38, 0x34, 0x28, 0x18, 0x53, 0xcf}
+constexpr GUID JITEEVersionIdentifier = { /* b2d3c86f-87fd-4724-9e5d-4c44905eba91 */
+ 0xb2d3c86f,
+ 0x87fd,
+ 0x4724,
+ {0x9e, 0x5d, 0x4c, 0x44, 0x90, 0x5e, 0xba, 0x91}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(DEBUG)
const WCHAR* eeGetCPString(size_t stringHandle);
+ unsigned eeTryGetClassSize(CORINFO_CLASS_HANDLE clsHnd);
const char16_t* eeGetShortClassName(CORINFO_CLASS_HANDLE clsHnd);
#endif
const char** classNamePtr;
const char* fieldOrMethodOrClassNamePtr;
char16_t* classNameWidePtr;
+ unsigned classSize;
EXCEPTION_POINTERS exceptionPointers;
};
#ifdef DEBUG
//------------------------------------------------------------------------
+// eeTryGetClassSize: wraps getClassSize but if doing SuperPMI replay
+// and the value isn't found, use a bogus size.
+//
+// NOTE: This is only allowed for JitDump output.
+//
+// Return value:
+// Either the actual class size, or (unsigned)-1 if SuperPMI didn't have it.
+//
+unsigned Compiler::eeTryGetClassSize(CORINFO_CLASS_HANDLE clsHnd)
+{
+ FilterSuperPMIExceptionsParam_ee_il param;
+
+ param.pThis = this;
+ param.pJitInfo = &info;
+ param.clazz = clsHnd;
+
+ bool success = eeRunWithSPMIErrorTrap<FilterSuperPMIExceptionsParam_ee_il>(
+ [](FilterSuperPMIExceptionsParam_ee_il* pParam) {
+ pParam->classSize = pParam->pJitInfo->compCompHnd->getClassSize(pParam->clazz);
+ },
+ ¶m);
+
+ if (!success)
+ {
+ param.classSize = (unsigned)-1; // Use the maximum unsigned value as the size
+ }
+ return param.classSize;
+}
+
+//------------------------------------------------------------------------
// eeGetShortClassName: wraps appendClassName to provide functionality
// similar to getClassName(), but returns a class name that is shortened,
// not using full assembly info.
bool success = eeRunWithSPMIErrorTrap<FilterSuperPMIExceptionsParam_ee_il>(
[](FilterSuperPMIExceptionsParam_ee_il* pParam) {
- char16_t dummyClassName[1] = {0};
- char16_t* pbuf = &dummyClassName[0];
- int len = 0;
- constexpr bool fNamespace = true;
- constexpr bool fFullInst = false;
- constexpr bool fAssembly = false;
+ int len = 0;
+ constexpr bool fNamespace = true;
+ constexpr bool fFullInst = false;
+ constexpr bool fAssembly = false;
// Warning: crossgen2 doesn't fully implement the `appendClassName` API.
- // If the size passed isn't large enough to the hold the entire string, crossgen2 will copy as much
- // as possible. However, the EE will assert. So we need to pass zero, get back the actual buffer size
- // required, allocate that space, and call the API again to get the full string. Both EE and crossgen2
- // will return the actual string size if *pnBufLen is zero.
- // The resulting string is guaranteed to be null-terminated.
- // Returned value is size of requested string, in characters, not including the terminating null
- // character. This could be larger than the buffer size and thus larger than the number of characters
- // actually copied to *pBuf (in the case of crossgen2, which doesn't assert on too-small buffers).
- int cchStrLen = pParam->pJitInfo->compCompHnd->appendClassName(&pbuf, &len, pParam->clazz, fNamespace,
+ // We need to pass size zero, get back the actual buffer size required, allocate that space,
+ // and call the API again to get the full string.
+ int cchStrLen = pParam->pJitInfo->compCompHnd->appendClassName(nullptr, &len, pParam->clazz, fNamespace,
fFullInst, fAssembly);
size_t cchBufLen = (size_t)cchStrLen + /* null terminator */ 1;
pParam->classNameWidePtr = pParam->pThis->getAllocator(CMK_DebugOnly).allocate<char16_t>(cchBufLen);
- pbuf = pParam->classNameWidePtr;
+ char16_t* pbuf = pParam->classNameWidePtr;
len = (int)cchBufLen;
int cchResultStrLen = pParam->pJitInfo->compCompHnd->appendClassName(&pbuf, &len, pParam->clazz, fNamespace,
param.classNameWidePtr = getAllocator(CMK_DebugOnly).allocate<char16_t>(cchLen);
memcpy(param.classNameWidePtr, substituteClassName, cchLen * sizeof(char16_t));
}
+
return param.classNameWidePtr;
}
#ifdef DEBUG
if (verbose)
{
- unsigned structSize =
- (callRetTyp == TYP_STRUCT) ? info.compCompHnd->getClassSize(calliSig.retTypeSigClass) : 0;
- printf("\nIn Compiler::impImportCall: opcode is %s, kind=%d, callRetType is %s, structSize is %d\n",
+ unsigned structSize = (callRetTyp == TYP_STRUCT) ? eeTryGetClassSize(calliSig.retTypeSigClass) : 0;
+ printf("\nIn Compiler::impImportCall: opcode is %s, kind=%d, callRetType is %s, structSize is %u\n",
opcodeNames[opcode], callInfo->kind, varTypeName(callRetTyp), structSize);
}
#endif
#ifdef DEBUG
if (verbose)
{
- unsigned structSize = (callRetTyp == TYP_STRUCT) ? info.compCompHnd->getClassSize(sig->retTypeSigClass) : 0;
- printf("\nIn Compiler::impImportCall: opcode is %s, kind=%d, callRetType is %s, structSize is %d\n",
+ unsigned structSize = (callRetTyp == TYP_STRUCT) ? eeTryGetClassSize(sig->retTypeSigClass) : 0;
+ printf("\nIn Compiler::impImportCall: opcode is %s, kind=%d, callRetType is %s, structSize is %u\n",
opcodeNames[opcode], callInfo->kind, varTypeName(callRetTyp), structSize);
}
#endif
WCHAR className[256] = {0};
WCHAR* pbuf = &className[0];
int len = ArrLen(className);
- info.compCompHnd->appendClassName((char16_t**)&pbuf, &len, typeHnd, true, false, false);
- noway_assert(pbuf < &className[256]);
+ int outlen = info.compCompHnd->appendClassName((char16_t**)&pbuf, &len, typeHnd, true, false, false);
+ noway_assert(outlen >= 0);
+ noway_assert((size_t)(outlen + 1) <= ArrLen(className));
JITDUMP("SIMD Candidate Type %S\n", className);
if (wcsncmp(className, W("System.Numerics."), 16) == 0)
if (pnBufLen > 0)
{
char* buffer = *ppBuf;
- for (int i = 0; i < Math.Min(name.Length, pnBufLen); i++)
+ int lengthToCopy = Math.Min(name.Length, pnBufLen);
+ for (int i = 0; i < lengthToCopy; i++)
buffer[i] = name[i];
if (name.Length < pnBufLen)
+ {
buffer[name.Length] = (char)0;
+ }
else
+ {
buffer[pnBufLen - 1] = (char)0;
- pnBufLen -= length;
- *ppBuf = buffer + length;
+ lengthToCopy -= 1;
+ }
+
+ pnBufLen -= lengthToCopy;
+ *ppBuf = buffer + lengthToCopy;
}
return length;
DWORD result;
};
-struct Agnostic_AppendClassName
+struct Agnostic_AppendClassNameIn
{
+ DWORD nBufLenIsZero;
DWORDLONG classHandle;
DWORD fNamespace;
DWORD fFullInst;
DWORD fAssembly;
};
+struct Agnostic_AppendClassNameOut
+{
+ DWORD nLen;
+ DWORD name_index;
+};
+
struct Agnostic_CheckMethodModifier
{
DWORDLONG hMethod;
LWM(AllocPgoInstrumentationBySchema, DWORDLONG, Agnostic_AllocPgoInstrumentationBySchema)
LWM(GetPgoInstrumentationResults, DWORDLONG, Agnostic_GetPgoInstrumentationResults)
-LWM(AppendClassName, Agnostic_AppendClassName, DWORD)
+LWM(AppendClassName, Agnostic_AppendClassNameIn, Agnostic_AppendClassNameOut)
LWM(AreTypesEquivalent, DLDL, DWORD)
LWM(AsCorInfoType, DWORDLONG, DWORD)
LWM(CanAccessClass, Agnostic_CanAccessClassIn, Agnostic_CanAccessClassOut)
}
void MethodContext::recAppendClassName(
- CORINFO_CLASS_HANDLE cls, bool fNamespace, bool fFullInst, bool fAssembly, const char16_t* result)
+ int nBufLenIn,
+ CORINFO_CLASS_HANDLE cls,
+ bool fNamespace,
+ bool fFullInst,
+ bool fAssembly,
+ int nLenOut,
+ const char16_t* result)
{
if (AppendClassName == nullptr)
- AppendClassName = new LightWeightMap<Agnostic_AppendClassName, DWORD>();
-
- Agnostic_AppendClassName key;
+ AppendClassName = new LightWeightMap<Agnostic_AppendClassNameIn, Agnostic_AppendClassNameOut>();
+
+ // The API has two different behaviors depending on whether the input specified length is zero or non-zero:
+ // (1) zero: returns the length of the string (which the caller can use to allocate a buffer)
+ // (2) non-zero: fill as much of the buffer with the name as there is space available.
+ // We don't want the input length to be part of the key, since the caller could potentially pass in a smaller
+ // or larger buffer, and we'll fill in as much of the buffer as possible. We do need to handle both the zero
+ // and non-zero cases, though, so we'll get one record for first call using zero (with the correct buffer size result),
+ // and another for the second call with a big-enough buffer. We could presumably just store the second case
+ // (and overwrite the first record), since it contains the same output length, but it is useful to store
+ // (and see) all the JIT-EE interface calls.
+
+ Agnostic_AppendClassNameIn key;
ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
- key.classHandle = CastHandle(cls);
- key.fNamespace = fNamespace;
- key.fFullInst = fFullInst;
- key.fAssembly = fAssembly;
+ key.nBufLenIsZero = (nBufLenIn == 0) ? 1 : 0;
+ key.classHandle = CastHandle(cls);
+ key.fNamespace = fNamespace;
+ key.fFullInst = fFullInst;
+ key.fAssembly = fAssembly;
- DWORD value = (DWORD)-1;
- if (result != nullptr)
- value = (DWORD)AppendClassName->AddBuffer((unsigned char*)result, (unsigned int)((wcslen((LPCWSTR)result) * 2) + 2));
+ Agnostic_AppendClassNameOut value;
+ value.nLen = nLenOut;
+ value.name_index = (DWORD)-1;
+
+ // Don't save the string buffer if the incoming buffer length is zero, even if the string buffer is non-null.
+ // In that case, the EE/crossgen2 don't zero-terminate the buffer (since they assume it is zero sized).
+ if ((result != nullptr) && (nBufLenIn > 0))
+ {
+ value.name_index = (DWORD)AppendClassName->AddBuffer(
+ (unsigned char*)result,
+ (unsigned int)((wcslen((LPCWSTR)result) + 1) * sizeof(char16_t)));
+ }
AppendClassName->Add(key, value);
DEBUG_REC(dmpAppendClassName(key, value));
}
-void MethodContext::dmpAppendClassName(const Agnostic_AppendClassName& key, DWORD value)
+void MethodContext::dmpAppendClassName(const Agnostic_AppendClassNameIn& key, const Agnostic_AppendClassNameOut& value)
{
- printf("AppendClassName key cls-%016llX ns-%u fi-%u as-%u, value %s", key.classHandle, key.fNamespace,
- key.fFullInst, key.fAssembly, AppendClassName->GetBuffer(value));
+ const char16_t* name = (const char16_t*)AppendClassName->GetBuffer(value.name_index);
+ printf("AppendClassName key lenzero-%s cls-%016llX ns-%u fi-%u as-%u, value len-%u ni-%u name-%S",
+ key.nBufLenIsZero ? "true" : "false", key.classHandle, key.fNamespace, key.fFullInst, key.fAssembly,
+ value.nLen, value.name_index, (const WCHAR*)name);
AppendClassName->Unlock();
}
-const WCHAR* MethodContext::repAppendClassName(CORINFO_CLASS_HANDLE cls,
- bool fNamespace,
- bool fFullInst,
- bool fAssembly)
+int MethodContext::repAppendClassName(char16_t** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ bool fNamespace,
+ bool fFullInst,
+ bool fAssembly)
{
- if (AppendClassName == nullptr)
- return W("hackishClassName");
+ static const char16_t unknownClass[] = u"hackishClassName";
+ static const int unknownClassLength = (int)(ArrLen(unknownClass) - 1); // Don't include null terminator in length.
- Agnostic_AppendClassName key;
- ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
- key.classHandle = CastHandle(cls);
- key.fNamespace = fNamespace;
- key.fFullInst = fFullInst;
- key.fAssembly = fAssembly;
+ // By default, at least return something.
+ const char16_t* name = unknownClass;
+ int nLen = unknownClassLength;
- int index = AppendClassName->GetIndex(key);
- if (index == -1)
- return W("hackishClassName");
+ if (AppendClassName != nullptr)
+ {
+ Agnostic_AppendClassNameIn key;
+ ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
+ key.nBufLenIsZero = (*pnBufLen == 0) ? 1 : 0;
+ key.classHandle = CastHandle(cls);
+ key.fNamespace = fNamespace;
+ key.fFullInst = fFullInst;
+ key.fAssembly = fAssembly;
- DWORD value = AppendClassName->Get(key);
- DEBUG_REP(dmpAppendClassName(key, value));
+ // First, see if we have an entry for this query.
+ int index = AppendClassName->GetIndex(key);
+ if (index != -1)
+ {
+ // Then, actually get the value.
+ Agnostic_AppendClassNameOut value = AppendClassName->Get(key);
+ DEBUG_REP(dmpAppendClassName(key, value));
- int offset = (int)value;
- const WCHAR* name = (const WCHAR*)AppendClassName->GetBuffer(offset);
- return name;
+ nLen = value.nLen;
+ name = (const char16_t*)AppendClassName->GetBuffer(value.name_index);
+ }
+ }
+
+ if ((ppBuf != nullptr) && (*ppBuf != nullptr) && (*pnBufLen > 0) && (name != nullptr))
+ {
+ // Copy as much as will fit.
+ char16_t* pBuf = *ppBuf;
+ int nLenToCopy = min(*pnBufLen, nLen + /* null terminator */ 1);
+ for (int i = 0; i < nLenToCopy - 1; i++)
+ {
+ pBuf[i] = name[i];
+ }
+ pBuf[nLenToCopy - 1] = 0; // null terminate the string if it wasn't already
+
+ // Update the buffer pointer and buffer size pointer based on the amount actually copied.
+ // Don't include the null terminator. `*ppBuf` will point at the added null terminator.
+ (*ppBuf) += nLenToCopy - 1;
+ (*pnBufLen) -= nLenToCopy - 1;
+ }
+
+ return nLen;
}
void MethodContext::recGetTailCallHelpers(
void dmpGetTypeInstantiationArgument(DWORDLONG key, DWORDLONG value);
CORINFO_CLASS_HANDLE repGetTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index);
- void recAppendClassName(
- CORINFO_CLASS_HANDLE cls, bool fNamespace, bool fFullInst, bool fAssembly, const char16_t* result);
- void dmpAppendClassName(const Agnostic_AppendClassName& key, DWORD value);
- const WCHAR* repAppendClassName(CORINFO_CLASS_HANDLE cls, bool fNamespace, bool fFullInst, bool fAssembly);
+ void recAppendClassName(int nBufLenIn,
+ CORINFO_CLASS_HANDLE cls,
+ bool fNamespace,
+ bool fFullInst,
+ bool fAssembly,
+ int nLenOut,
+ const char16_t* result);
+ void dmpAppendClassName(const Agnostic_AppendClassNameIn& key, const Agnostic_AppendClassNameOut& value);
+ int repAppendClassName(char16_t** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ bool fNamespace,
+ bool fFullInst,
+ bool fAssembly);
void recGetTailCallHelpers(
CORINFO_RESOLVED_TOKEN* callToken,
void PutThumb2Mov32(UINT16* p, UINT32 imm32);
void PutThumb2BlRel24(UINT16* p, INT32 imm24);
+template <typename T, int size>
+inline constexpr unsigned ArrLen(T (&)[size])
+{
+ return size;
+}
+
#endif // !_SPMIUtil
//#define USE_COREDISTOOLS
#endif // INTERNAL_BUILD
-#ifdef _MSC_VER
-// On Windows, we build against PAL macros that convert to Windows SEH. But we don't want all the
-// Contract stuff that normally gets pulled it. Defining JIT_BUILD prevents this, just as it does
-// when building the JIT using parts of utilcode.
+// JIT_BUILD disables certain PAL_TRY debugging and Contracts features. Set this just as the JIT sets it.
#define JIT_BUILD
+#ifdef _MSC_VER
// Defining this prevents:
// error C2338 : / RTCc rejects conformant code, so it isn't supported by the C++ Standard Library.
// Either remove this compiler option, or define _ALLOW_RTCc_IN_STL to acknowledge that you have received this
return temp;
}
-// Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
-// If fNamespace=TRUE, include the namespace/enclosing classes
-// If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
-// If fAssembly=TRUE, suffix with a comma and the full assembly qualification
-// return size of representation
-int interceptor_ICJI::appendClassName(_Outptr_result_buffer_(*pnBufLen) char16_t** ppBuf,
- int* pnBufLen,
- CORINFO_CLASS_HANDLE cls,
- bool fNamespace,
- bool fFullInst,
- bool fAssembly)
+// Append a (possibly truncated) textual representation of the type `cls` to a preallocated buffer.
+//
+// Arguments:
+// ppBuf - Pointer to buffer pointer. See below for details.
+// pnBufLen - Pointer to buffer length. Must not be nullptr. See below for details.
+// fNamespace - If true, include the namespace/enclosing classes.
+// fFullInst - If true (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters.
+// fAssembly - If true, suffix with a comma and the full assembly qualification.
+//
+// Returns the length of the representation, as a count of characters (but not including a terminating null character).
+// Note that this will always be the actual number of characters required by the representation, even if the string
+// was truncated when copied to the buffer.
+//
+// Operation:
+//
+// On entry, `*pnBufLen` specifies the size of the buffer pointed to by `*ppBuf` as a count of characters.
+// There are two cases:
+// 1. If the size is zero, the function computes the length of the representation and returns that.
+// `ppBuf` is ignored (and may be nullptr) and `*ppBuf` and `*pnBufLen` are not updated.
+// 2. If the size is non-zero, the buffer pointed to by `*ppBuf` is (at least) that size. The class name
+// representation is copied to the buffer pointed to by `*ppBuf`. As many characters of the name as will fit in the
+// buffer are copied. Thus, if the name is larger than the size of the buffer, the name will be truncated in the buffer.
+// The buffer is guaranteed to be null terminated. Thus, the size must be large enough to include a terminating null
+// character, or the string will be truncated to include one. On exit, `*pnBufLen` is updated by subtracting the
+// number of characters that were actually copied to the buffer. Also, `*ppBuf` is updated to point at the null
+// character that was added to the end of the name.
+//
+int interceptor_ICJI::appendClassName(_Outptr_opt_result_buffer_(*pnBufLen) char16_t** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ bool fNamespace,
+ bool fFullInst,
+ bool fAssembly)
{
mc->cr->AddCall("appendClassName");
- char16_t* pBuf = *ppBuf;
- int nLen = original_ICorJitInfo->appendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
- mc->recAppendClassName(cls, fNamespace, fFullInst, fAssembly, pBuf);
- return nLen;
+ char16_t* pBufIn = (ppBuf == nullptr) ? nullptr : *ppBuf;
+ int nBufLenIn = (pnBufLen == nullptr) ? 0 : *pnBufLen; // pnBufLen should never be nullptr, but don't crash if it is.
+ int nLenOut = original_ICorJitInfo->appendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
+ mc->recAppendClassName(nBufLenIn, cls, fNamespace, fFullInst, fAssembly, nLenOut, pBufIn);
+ return nLenOut;
}
// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) &
return result;
}
-// Append a (possibly truncated) representation of the type cls to the preallocated buffer ppBuf of length pnBufLen
-// If fNamespace=TRUE, include the namespace/enclosing classes
-// If fFullInst=TRUE (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters
-// If fAssembly=TRUE, suffix with a comma and the full assembly qualification
-// return size of representation
-int MyICJI::appendClassName(_Outptr_result_buffer_(*pnBufLen) char16_t** ppBuf,
- int* pnBufLen,
- CORINFO_CLASS_HANDLE cls,
- bool fNamespace,
- bool fFullInst,
- bool fAssembly)
+// Append a (possibly truncated) textual representation of the type `cls` to a preallocated buffer.
+//
+// Arguments:
+// ppBuf - Pointer to buffer pointer. See below for details.
+// pnBufLen - Pointer to buffer length. Must not be nullptr. See below for details.
+// fNamespace - If true, include the namespace/enclosing classes.
+// fFullInst - If true (regardless of fNamespace and fAssembly), include namespace and assembly for any type parameters.
+// fAssembly - If true, suffix with a comma and the full assembly qualification.
+//
+// Returns the length of the representation, as a count of characters (but not including a terminating null character).
+// Note that this will always be the actual number of characters required by the representation, even if the string
+// was truncated when copied to the buffer.
+//
+// Operation:
+//
+// On entry, `*pnBufLen` specifies the size of the buffer pointed to by `*ppBuf` as a count of characters.
+// There are two cases:
+// 1. If the size is zero, the function computes the length of the representation and returns that.
+// `ppBuf` is ignored (and may be nullptr) and `*ppBuf` and `*pnBufLen` are not updated.
+// 2. If the size is non-zero, the buffer pointed to by `*ppBuf` is (at least) that size. The class name
+// representation is copied to the buffer pointed to by `*ppBuf`. As many characters of the name as will fit in the
+// buffer are copied. Thus, if the name is larger than the size of the buffer, the name will be truncated in the buffer.
+// The buffer is guaranteed to be null terminated. Thus, the size must be large enough to include a terminating null
+// character, or the string will be truncated to include one. On exit, `*pnBufLen` is updated by subtracting the
+// number of characters that were actually copied to the buffer. Also, `*ppBuf` is updated to point at the null
+// character that was added to the end of the name.
+//
+int MyICJI::appendClassName(_Outptr_opt_result_buffer_(*pnBufLen) char16_t** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE cls,
+ bool fNamespace,
+ bool fFullInst,
+ bool fAssembly)
{
jitInstance->mc->cr->AddCall("appendClassName");
- const WCHAR* result = jitInstance->mc->repAppendClassName(cls, fNamespace, fFullInst, fAssembly);
- int nLen = 0;
- if (ppBuf != nullptr && result != nullptr)
- {
- nLen = (int)wcslen(result);
- if (*pnBufLen > nLen)
- {
- wcscpy_s((WCHAR*)*ppBuf, *pnBufLen, result);
- (*ppBuf) += nLen;
- (*pnBufLen) -= nLen;
- }
- }
- return nLen;
+ return jitInstance->mc->repAppendClassName(ppBuf, pnBufLen, cls, fNamespace, fFullInst, fAssembly);
}
// Quick check whether the type is a value class. Returns the same value as getClassAttribs(cls) &
/*********************************************************************/
-int CEEInfo::appendClassName(_Outptr_result_buffer_(*pnBufLen) char16_t** ppBuf,
- int* pnBufLen,
- CORINFO_CLASS_HANDLE clsHnd,
- bool fNamespace,
- bool fFullInst,
- bool fAssembly)
+int CEEInfo::appendClassName(_Outptr_opt_result_buffer_(*pnBufLen) char16_t** ppBuf,
+ int* pnBufLen,
+ CORINFO_CLASS_HANDLE clsHnd,
+ bool fNamespace,
+ bool fFullInst,
+ bool fAssembly)
{
CONTRACTL {
MODE_PREEMPTIVE;
nLen = (int)wcslen(szString);
if (*pnBufLen > 0)
{
- wcscpy_s((WCHAR*)*ppBuf, *pnBufLen, szString );
- (*ppBuf)[(*pnBufLen) - 1] = W('\0');
- (*ppBuf) += nLen;
- (*pnBufLen) -= nLen;
+ // Copy as much as will fit.
+ WCHAR* pBuf = (WCHAR*)*ppBuf;
+ int nLenToCopy = min(*pnBufLen, nLen + /* null terminator */ 1);
+ for (int i = 0; i < nLenToCopy - 1; i++)
+ {
+ pBuf[i] = szString[i];
+ }
+ pBuf[nLenToCopy - 1] = 0; // null terminate the string if it wasn't already
+
+ // Update the buffer pointer and buffer size pointer based on the amount actually copied.
+ // Don't include the null terminator. `*ppBuf` will point at the added null terminator.
+ (*ppBuf) += nLenToCopy - 1;
+ (*pnBufLen) -= nLenToCopy - 1;
}
EE_TO_JIT_TRANSITION();
+ // Return the actual length of the string, not including the null terminator.
return nLen;
}