Пример #1
0
        /// <summary>
        /// Call to a DNA method given it's method def (with a byref this).
        /// </summary>
        /// <param name="methodDef">The method def</param>
        /// <param name="_this">The this object (or null if method is static)</param>
        /// <param name="args">The arguments to pass (or null for no arguments)</param>
        /// <returns>The value returned by the method.</returns>
        public static object CallRefThis(ulong methodDef, ref object _this, object[] args = null)
        {
            tMD_MethodDef *pMethodDef = (tMD_MethodDef *)methodDef;
            uint           start      = 0;
            uint           byRefSize  = 0;
            bool           byRefThis  = false;
            bool           byRefArgs  = false;
            uint           paramsSize = 0;
            uint           retValSize = 0;
            byte *         pByRefPtr;
            byte *         pStackPtr;
            object         ret = null;

            byte *pBuf    = stackalloc byte[256];
            uint  bufSize = 256;

            tMD_TypeDef *pThisType       = null;
            tMD_TypeDef *pMethodThisType = null;

            if (args == null)
            {
                args = noArgs;
            }

            // First pass.. check types and get by ref buf size..

            if (!MetaData.METHOD_ISSTATIC(pMethodDef))
            {
                if (_this == null)
                {
                    throw new System.NullReferenceException("Call to member method can not have null this");
                }
                pThisType       = MonoType.GetTypeForMonoObject(_this, null, null);
                pMethodThisType = MetaData.PARAM_ACTUAL_TYPE(&pMethodDef->pParams[0]);
                if (!MetaData.TYPE_ISASSIGNABLE_TO(pThisType, pMethodThisType))
                {
                    throw new System.InvalidOperationException("This type is not compatible with method this type");
                }
                if (MetaData.PARAM_ISBYREF(&pMethodDef->pParams[0]))
                {
                    byRefSize  += pMethodThisType->stackSize;
                    paramsSize += (uint)sizeof(byte *);
                    byRefThis   = true;
                }
                else
                {
                    paramsSize += pMethodDef->pParams[0].pStackTypeDef->stackSize;
                }
                start = 1;
            }

            if (args.Length != pMethodDef->numberOfParameters - start)
            {
                throw new System.InvalidOperationException("Wrong number of parameters for this method");
            }

            for (uint i = start; i < args.Length; i++)
            {
                tMD_TypeDef *pParamType = MetaData.PARAM_ACTUAL_TYPE(&pMethodDef->pParams[i]);
                tMD_TypeDef *pArgType   = args[i - start] != null?MonoType.GetTypeForMonoObject(args[i - start], null, null) : null;

                if (pArgType == null && pParamType->isValueType == 0)
                {
                    pArgType = pParamType;
                }
                if (!MetaData.TYPE_ISASSIGNABLE_TO(pArgType, pParamType))
                {
                    throw new System.InvalidOperationException("Argument types are not compatible");
                }
                if (MetaData.PARAM_ISBYREF(&pMethodDef->pParams[i]))
                {
                    byRefSize  += pParamType->stackSize;
                    paramsSize += (uint)sizeof(byte *);
                    byRefArgs   = true;
                }
                else
                {
                    paramsSize += pMethodDef->pParams[i].pStackTypeDef->stackSize;
                }
            }

            if (pMethodDef->pReturnType != null)
            {
                retValSize = pMethodDef->pReturnType->stackSize;
            }

            if (byRefSize + paramsSize + retValSize > bufSize)
            {
                throw new System.InvalidOperationException("Buffer overflow for parameter stack");
            }

            // Now actually marshal params to the param buffer

            pByRefPtr = pBuf;
            pStackPtr = pBuf + byRefSize;

            if (!MetaData.METHOD_ISSTATIC(pMethodDef))
            {
                if (MetaData.PARAM_ISBYREF(&pMethodDef->pParams[0]))
                {
                    MonoType.MarshalFromObject(pByRefPtr, ref _this);
                    *(byte **)pStackPtr = pByRefPtr;
                    pByRefPtr          += pMethodDef->pParams[0].pByRefTypeDef->stackSize;
                    pStackPtr          += sizeof(byte *);
                }
                else
                {
                    MonoType.MarshalFromObject(pStackPtr, ref _this);
                    pStackPtr += pMethodDef->pParams[0].pStackTypeDef->stackSize;
                }
            }

            for (uint i = start; i < args.Length; i++)
            {
                if (MetaData.PARAM_ISBYREF(&pMethodDef->pParams[i]))
                {
                    MonoType.MarshalFromObject(pByRefPtr, ref args[i - start]);
                    *(byte **)pStackPtr = pByRefPtr;
                    pByRefPtr          += pMethodDef->pParams[i].pByRefTypeDef->stackSize;
                    pStackPtr          += sizeof(byte *);
                }
                else
                {
                    MonoType.MarshalFromObject(pStackPtr, ref args[i - start]);
                    pStackPtr += pMethodDef->pParams[i].pStackTypeDef->stackSize;
                }
            }

            // Call the method (usuing reusable call thread)

            int status = Thread.Call(pMethodDef, pBuf + byRefSize, pBuf + byRefSize + paramsSize);

            if (status != Thread.THREAD_STATUS_EXIT)
            {
                throw new System.InvalidOperationException("Thread blocked in call to method");
            }

            // Marshal back any byref values

            if (byRefArgs || byRefThis)
            {
                pByRefPtr = pBuf;
                if (byRefThis)
                {
                    object refThis;
                    MonoType.MarshalToObject(pByRefPtr, out refThis);
                    _this      = refThis;
                    pByRefPtr += pMethodDef->pParams[0].pByRefTypeDef->stackSize;
                }
                if (byRefArgs)
                {
                    for (uint i = start; i < args.Length; i++)
                    {
                        if (MetaData.PARAM_ISBYREF(&pMethodDef->pParams[i]))
                        {
                            object refArg;
                            MonoType.MarshalToObject(pByRefPtr, out refArg);
                            args[i - start] = refArg;
                            pByRefPtr      += pMethodDef->pParams[i].pByRefTypeDef->stackSize;
                        }
                    }
                }
            }

            // Marshal the return value

            if (pMethodDef->pReturnType != null)
            {
                MonoType.MarshalToObject(pBuf + byRefSize + paramsSize, out ret);
            }

            return(ret);
        }