diff options
Diffstat (limited to 'src/classlibnative/bcltype/varargsnative.cpp')
-rw-r--r-- | src/classlibnative/bcltype/varargsnative.cpp | 638 |
1 files changed, 638 insertions, 0 deletions
diff --git a/src/classlibnative/bcltype/varargsnative.cpp b/src/classlibnative/bcltype/varargsnative.cpp new file mode 100644 index 0000000000..33dd816477 --- /dev/null +++ b/src/classlibnative/bcltype/varargsnative.cpp @@ -0,0 +1,638 @@ +// 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: VarArgsNative.cpp +// + +// +// This module contains the implementation of the native methods for the +// varargs class(es).. +// + + +#include "common.h" +#include "object.h" +#include "excep.h" +#include "frames.h" +#include "vars.hpp" +#include "varargsnative.h" + +// Some platforms have additional alignment requirements for arguments. This function adjusts the given arg +// pointer to achieve such an alignment for the next argument on those platforms (otherwise it is a no-op). +// NOTE: the debugger has its own implementation of this algorithm in Debug\DI\RsType.cpp, CordbType::RequiresAlign8() +// so if you change this implementation be sure to update the debugger's version as well. +static void AdjustArgPtrForAlignment(VARARGS *pData, size_t cbArg) +{ +#ifdef _TARGET_ARM_ + // Only 64-bit primitives or value types with embedded 64-bit primitives are aligned on 64-bit boundaries. + if (cbArg < 8) + return; + + // For the value type case we have to dig deeper and ask the typeloader whether the type requires + // alignment. + SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic. + CorElementType et = pData->SigPtr.PeekElemTypeClosed(pData->ArgCookie->pModule, &typeContext); + if (et == ELEMENT_TYPE_TYPEDBYREF) + { + return; + } + else + if (et == ELEMENT_TYPE_VALUETYPE) + { + SigPointer tempSig(pData->SigPtr); + TypeHandle valueType = tempSig.GetTypeHandleThrowing(pData->ArgCookie->pModule, &typeContext); + if (!valueType.AsMethodTable()->RequiresAlign8()) + return; + } + else + { + // One of the primitive 64-bit types + } + pData->ArgPtr = (BYTE*)ALIGN_UP(pData->ArgPtr, 8); +#endif // _TARGET_ARM_ +} + +//////////////////////////////////////////////////////////////////////////////// +// Initialize the basic info for processing a varargs parameter list. +//////////////////////////////////////////////////////////////////////////////// +static void InitCommon(VARARGS *data, VASigCookie** cookie) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(data)); + PRECONDITION(CheckPointer(cookie)); + } CONTRACTL_END; + + // Save the cookie and a copy of the signature. + data->ArgCookie = *cookie; + data->SigPtr = data->ArgCookie->signature.CreateSigPointer(); + + // Skip the calling convention, get the # of args and skip the return type. + ULONG callConv; + IfFailThrow(data->SigPtr.GetCallingConvInfo(&callConv)); + + ULONG sigData; + IfFailThrow(data->SigPtr.GetData(&sigData)); + data->RemainingArgs = sigData; + + IfFailThrow(data->SigPtr.SkipExactlyOne()); + + // Get a pointer to the cookie arg. + data->ArgPtr = (BYTE *) cookie; + +#if defined(_TARGET_X86_) + // STACK_GROWS_DOWN_ON_ARGS_WALK + + // <return address> ;; lower memory + // <varargs_cookie> \ + // <argN> \ + // += sizeOfArgs + // / + // <arg1> / + // * <this> ;; if an instance method (note: <this> is usally passed in + // ;; a register and wouldn't appear on the stack frame) + // ;; higher memory + // + // When the stack grows down, ArgPtr always points to the end of the next + // argument passed. So we initialize it to the address that is the + // end of the first fixed arg (arg1) (marked with a '*'). + + data->ArgPtr += data->ArgCookie->sizeOfArgs; +#else + // STACK_GROWS_UP_ON_ARGS_WALK + + // <this> ;; lower memory + // <varargs_cookie> ;; if an instance method + // * <arg1> + // + // <argN> ;; higher memory + // + // When the stack grows up, ArgPtr always points to the start of the next + // argument passed. So we initialize it to the address marked with a '*', + // which is the start of the first fixed arg (arg1). + + // Always skip over the varargs_cookie. + data->ArgPtr += StackElemSize(sizeof(LPVOID)); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// After initialization advance the next argument pointer to the first optional +// argument. +//////////////////////////////////////////////////////////////////////////////// +void AdvanceArgPtr(VARARGS *data) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(data)); + } CONTRACTL_END; + + // Advance to the first optional arg. + while (data->RemainingArgs > 0) + { + if (data->SigPtr.AtSentinel()) + break; + + SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic + SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule, &typeContext); + SIZE_T cbArg = StackElemSize(cbRaw); + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (ArgIterator::IsVarArgPassedByRef(cbRaw)) + cbArg = sizeof(void*); +#endif + + // Adjust the frame pointer and the signature info. + AdjustArgPtrForAlignment(data, cbArg); +#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK + data->ArgPtr -= cbArg; +#else // STACK_GROWS_UP_ON_ARGS_WALK + data->ArgPtr += cbArg; +#endif // STACK_GROWS_**_ON_ARGS_WALK + + IfFailThrow(data->SigPtr.SkipExactlyOne()); + --data->RemainingArgs; + } +} // AdvanceArgPtr + +//////////////////////////////////////////////////////////////////////////////// +// ArgIterator constructor that initializes the state to support iteration +// of the args starting at the first optional argument. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL2(void, VarArgsNative::Init, VARARGS* _this, LPVOID cookie) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + + _ASSERTE(_this != NULL); + VARARGS* data = _this; + if (cookie == 0) + COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized")); + + VASigCookie* pCookie = *(VASigCookie**)(cookie); + + if (pCookie->signature.IsEmpty()) + { + data->SigPtr = SigPointer(NULL, 0); + data->ArgCookie = NULL; + data->ArgPtr = (BYTE*)((VASigCookieEx*)pCookie)->m_pArgs; + } + else + { + // Use common code to pick the cookie apart and advance to the ... + InitCommon(data, (VASigCookie**)cookie); + AdvanceArgPtr(data); + } + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +//////////////////////////////////////////////////////////////////////////////// +// ArgIterator constructor that initializes the state to support iteration +// of the args starting at the argument following the supplied argument pointer. +// Specifying NULL as the firstArg parameter causes it to start at the first +// argument to the call. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL3( +void, +VarArgsNative::Init2, + VARARGS * _this, + LPVOID cookie, + LPVOID firstArg) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + + _ASSERTE(_this != NULL); + VARARGS* data = _this; + if (cookie == 0) + COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized")); + + // Init most of the structure. + InitCommon(data, (VASigCookie**)cookie); + + // If it is NULL, start at the first arg. + if (firstArg != NULL) + { + // + // The expectation made by VarArgsNative is that: +#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK + // data->ArgPtr points to the end of the next argument. + // <varargs_cookie> + // <argN> <-- data->ArgPtr after InitCommon + // + // <argM 1st optional arg> + // *@ <arg2> <-- firstArg, data->ArgPtr leaving Init2() + // <arg1> + // <this> ;; if an instance method + // ;; higher memory + // +#else // STACK_GROWS_UP_ON_ARGS_WALK + // data->ArgPtr points to the beginning of the next argument + // <this> ;; if an instance method + // <varargs_cookie> + // <arg1> <-- data->ArgPtr after InitCommon + // * <arg2> <-- firstArg + // @ <argM - 1st optional arg> <-- data->ArgPtr leaving Init2() + // + // <argN> + // ;; higher memory +#endif // STACK_GROWS_**_ON_ARGS_WALK + // where * indicates the place on the stack that firstArg points upon + // entry to Init2. We need to correct in the STACK_GROWS_UP... case since + // we actually want to point at the argument after firstArg. This confusion + // comes from the difference in expectation of whether ArgPtr is pointing + // at the beginning or end of the argument on the stack. + // + // @ indicates where we want data->ArgPtr to point to after we're done with Init2 + // + + // Advance to the specified arg. + while (data->RemainingArgs > 0) + { + if (data->SigPtr.AtSentinel()) + { + COMPlusThrow(kArgumentException); + } + + SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic + SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule,&typeContext); + SIZE_T cbArg = StackElemSize(cbRaw); + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (ArgIterator::IsVarArgPassedByRef(cbRaw)) + cbArg = sizeof(void*); +#endif + + // Adjust the frame pointer and the signature info. + AdjustArgPtrForAlignment(data, cbArg); + IfFailThrow(data->SigPtr.SkipExactlyOne()); + data->RemainingArgs--; + +#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK + data->ArgPtr -= cbArg; + bool atFirstArg = (data->ArgPtr == firstArg); +#else // STACK_GROWS_UP_ON_ARGS_WALK + bool atFirstArg = (data->ArgPtr == firstArg); + data->ArgPtr += cbArg; +#endif // STACK_GROWS_**_ON_ARGS_WALK + + if (atFirstArg) + break; + } + } + HELPER_METHOD_FRAME_END(); +} // VarArgsNative::Init2 +FCIMPLEND + + +//////////////////////////////////////////////////////////////////////////////// +// Return the number of unprocessed args in the argument iterator. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL1(int, VarArgsNative::GetRemainingCount, VARARGS* _this) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_RET_0(); + + _ASSERTE(_this != NULL); + if (!(_this->ArgCookie)) + { + // this argiterator was created by marshaling from an unmanaged va_list - + // can't do this operation + COMPlusThrow(kNotSupportedException); + } + HELPER_METHOD_FRAME_END(); + return (_this->RemainingArgs); +} +FCIMPLEND + + +//////////////////////////////////////////////////////////////////////////////// +// Retrieve the type of the next argument without consuming it. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL1(void*, VarArgsNative::GetNextArgType, VARARGS* _this) +{ + FCALL_CONTRACT; + + TypedByRef value; + + HELPER_METHOD_FRAME_BEGIN_RET_0(); + + PREFIX_ASSUME(_this != NULL); + VARARGS data = *_this; + + if (!(_this->ArgCookie)) + { + // this argiterator was created by marshaling from an unmanaged va_list - + // can't do this operation + COMPlusThrow(kNotSupportedException); + } + + + // Make sure there are remaining args. + if (data.RemainingArgs == 0) + COMPlusThrow(kInvalidOperationException, W("InvalidOperation_EnumEnded")); + + GetNextArgHelper(&data, &value, FALSE); + HELPER_METHOD_FRAME_END(); + return value.type.AsPtr(); +} +FCIMPLEND + +//////////////////////////////////////////////////////////////////////////////// +// Retrieve the next argument and return it in a TypedByRef and advance the +// next argument pointer. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL2(void, VarArgsNative::DoGetNextArg, VARARGS* _this, void * value) +{ + FCALL_CONTRACT; + + TypedByRef * result = (TypedByRef *)value; + HELPER_METHOD_FRAME_BEGIN_0(); + GCPROTECT_BEGININTERIOR (result); + + _ASSERTE(_this != NULL); + if (!(_this->ArgCookie)) + { + // this argiterator was created by marshaling from an unmanaged va_list - + // can't do this operation + COMPlusThrow(kInvalidOperationException); + } + + // Make sure there are remaining args. + if (_this->RemainingArgs == 0) + COMPlusThrow(kInvalidOperationException, W("InvalidOperation_EnumEnded")); + + GetNextArgHelper(_this, result, TRUE); + GCPROTECT_END (); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + + +//////////////////////////////////////////////////////////////////////////////// +// Retrieve the next argument and return it in a TypedByRef and advance the +// next argument pointer. +//////////////////////////////////////////////////////////////////////////////// +FCIMPL3(void, VarArgsNative::GetNextArg2, VARARGS* _this, void * value, ReflectClassBaseObject *pTypeUNSAFE) +{ + FCALL_CONTRACT; + + TypedByRef * result = (TypedByRef *)value; + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); + + if (refType == NULL) + FCThrowResVoid(kArgumentNullException, W("Arg_InvalidHandle")); + + HELPER_METHOD_FRAME_BEGIN_1(refType); + GCPROTECT_BEGININTERIOR (result); + + // IJW + + TypeHandle typehandle = refType->GetType(); + + _ASSERTE(_this != NULL); + UINT size = 0; + + CorElementType typ = typehandle.GetInternalCorElementType(); + if (CorTypeInfo::IsPrimitiveType(typ)) + { + size = CorTypeInfo::Size(typ); + } + else if (typ == ELEMENT_TYPE_PTR) + { + size = sizeof(LPVOID); + } + else if (typ == ELEMENT_TYPE_VALUETYPE) + { + size = typehandle.AsMethodTable()->GetNativeSize(); + } + else + { + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } + + size = StackElemSize(size); + + AdjustArgPtrForAlignment(_this, size); + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (ArgIterator::IsVarArgPassedByRef(size)) + { + result->data = *(void**)_this->ArgPtr; + size = sizeof(void*); + } + else +#endif + { + result->data = (void*)_this->ArgPtr; + } + + result->type = typehandle; + _this->ArgPtr += size; + + GCPROTECT_END (); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + + + +//////////////////////////////////////////////////////////////////////////////// +// This is a helper that uses a VARARGS tracking data structure to retrieve +// the next argument out of a varargs function call. This does not check if +// there are any args remaining (it assumes it has been checked). +//////////////////////////////////////////////////////////////////////////////// +void +VarArgsNative::GetNextArgHelper( + VARARGS * data, + TypedByRef * value, + BOOL fData) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(data)); + PRECONDITION(CheckPointer(value)); + } CONTRACTL_END; + + GCPROTECT_BEGININTERIOR (value); + CorElementType elemType; + + _ASSERTE(data->RemainingArgs != 0); + + SigTypeContext typeContext; // This is an empty type context. This is OK because the vararg methods may not be generic + SIZE_T cbRaw = data->SigPtr.SizeOf(data->ArgCookie->pModule,&typeContext); + SIZE_T cbArg = StackElemSize(cbRaw); + + AdjustArgPtrForAlignment(data, cbArg); + + // Get a pointer to the beginning of the argument. +#ifdef STACK_GROWS_DOWN_ON_ARGS_WALK + data->ArgPtr -= cbArg; +#endif + + // Assume the ref pointer points directly at the arg on the stack. + void* origArgPtr = data->ArgPtr; + value->data = origArgPtr; + +#ifndef STACK_GROWS_DOWN_ON_ARGS_WALK + data->ArgPtr += cbArg; +#endif // STACK_GROWS_**_ON_ARGS_WALK + + +TryAgain: + switch (elemType = data->SigPtr.PeekElemTypeClosed(data->ArgCookie->pModule, &typeContext)) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: +#if BIGENDIAN + if ( origArgPtr == value->data ) { + value->data = (BYTE*)origArgPtr + (sizeof(void*)-1); + } +#endif + value->type = MscorlibBinder::GetElementType(elemType); + break; + + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_CHAR: +#if BIGENDIAN + if ( origArgPtr == value->data ) { + value->data = (BYTE*)origArgPtr + (sizeof(void*)-2); + } +#endif + value->type = MscorlibBinder::GetElementType(elemType); + break; + + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_U: + value->type = MscorlibBinder::GetElementType(elemType); + break; + + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R8: + value->type = MscorlibBinder::GetElementType(elemType); +#if !defined(_WIN64) && (DATA_ALIGNMENT > 4) + if ( fData && origArgPtr == value->data ) { + // allocate an aligned copy of the value + value->data = value->type.GetMethodTable()->Box(origArgPtr, FALSE)->UnBox(); + } +#endif + break; + + case ELEMENT_TYPE_PTR: + value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule, &typeContext); + break; + + case ELEMENT_TYPE_BYREF: + // Check if we have already processed a by-ref. + if (value->data != origArgPtr) + { + _ASSERTE(!"Can't have a ByRef of a ByRef"); + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } + + // Dereference the argument to remove the indirection of the ByRef. + value->data = *((void **) value->data); + + // Consume and discard the element type. + IfFailThrow(data->SigPtr.GetElemType(NULL)); + goto TryAgain; + + case ELEMENT_TYPE_VALUETYPE: + +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (origArgPtr == value->data && ArgIterator::IsVarArgPassedByRef(cbRaw)) + { + // Adjust the arg pointer so only one word has been skipped + data->ArgPtr = (BYTE *)origArgPtr + sizeof(void*); + // Dereference the argument because the valuetype is passed by reference + value->data = *((void **) origArgPtr); + } +#endif + +#if BIGENDIAN + // Adjust the pointer for small valuetypes + if (origArgPtr == value->data) { + value->data = StackElemEndianessFixup(origArgPtr, cbRaw); + } +#endif + + // fall through + case ELEMENT_TYPE_CLASS: { + value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule, &typeContext); + + if (value->type.GetMethodTable()->IsByRefLike()) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } + + if (elemType == ELEMENT_TYPE_CLASS && value->type.GetMethodTable()->IsValueType()) + value->type = g_pObjectClass; + } break; + + case ELEMENT_TYPE_TYPEDBYREF: + if (value->data != origArgPtr) + { + //<TODO>@todo: Is this really an error?</TODO> + _ASSERTE(!"Can't have a ByRef of a TypedByRef"); + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } +#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE + if (ArgIterator::IsVarArgPassedByRef(sizeof(TypedByRef))) + { + // Adjust the arg pointer so only one word has been skipped + data->ArgPtr = (BYTE *)origArgPtr + sizeof(void *); + // Dereference the argument because the valuetypes are always passed by reference + value->data = *((void **)origArgPtr); + } +#endif // ENREGISTERED_PARAMTYPE_MAXSIZE + // Load the TypedByRef + value->type = ((TypedByRef*)value->data)->type; + value->data = ((TypedByRef*)value->data)->data; + break; + + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_ARRAY: + { + value->type = data->SigPtr.GetTypeHandleThrowing(data->ArgCookie->pModule,&typeContext); + + break; + } + + case ELEMENT_TYPE_FNPTR: + case ELEMENT_TYPE_OBJECT: + _ASSERTE(!"Not implemented"); + COMPlusThrow(kNotSupportedException); + break; + + case ELEMENT_TYPE_SENTINEL: + default: + _ASSERTE(!"Should be unreachable"); + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + break; + } + + // Update the tracking stuff to move past the argument. + --data->RemainingArgs; + IfFailThrow(data->SigPtr.SkipExactlyOne()); + GCPROTECT_END (); +} // VarArgsNative::GetNextArgHelper |