core/vm/sp_vm_function.cpp
author Borja Ferrer <faluco@alliedmods.net>
Thu May 10 22:51:56 2007 +0000 (2007-05-10)
changeset 762 e82fc0f89dc1
parent 653 ef60870067ff
child 857 11471aa66e06
permissions -rw-r--r--
added debug code for leak detection on plugins
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourcePawn (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ================================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include <stdio.h>
    16 #include <string.h>
    17 #include "PluginSys.h"
    18 
    19 /********************
    20 * FUNCTION CALLING *
    21 ********************/
    22 
    23 void CFunction::Set(uint32_t code_addr, IPluginContext *plugin)
    24 {
    25 	m_codeaddr = code_addr;
    26 	m_pContext = plugin;
    27 	m_curparam = 0;
    28 	m_errorstate = SP_ERROR_NONE;
    29 	m_Invalid = false;
    30 	m_pCtx = plugin ? plugin->GetContext() : NULL;
    31 }
    32 
    33 bool CFunction::IsRunnable()
    34 {
    35 	return ((m_pCtx->flags & SPFLAG_PLUGIN_PAUSED) != SPFLAG_PLUGIN_PAUSED);
    36 }
    37 
    38 int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
    39 {
    40 	if (!IsRunnable())
    41 	{
    42 		return SP_ERROR_NOT_RUNNABLE;
    43 	}
    44 
    45 	while (num_params--)
    46 	{
    47 		m_pContext->PushCell(params[num_params]);
    48 	}
    49 
    50 	return m_pContext->Execute(m_codeaddr, result);
    51 }
    52 
    53 IPluginContext *CFunction::GetParentContext()
    54 {
    55 	return m_pContext;
    56 }
    57 
    58 CFunction::CFunction(uint32_t code_addr, IPluginContext *plugin) : 
    59 	m_codeaddr(code_addr), m_pContext(plugin), m_curparam(0), 
    60 	m_errorstate(SP_ERROR_NONE)
    61 {
    62 	m_Invalid = false;
    63 	if (plugin)
    64 	{
    65 		m_pCtx = plugin->GetContext();
    66 	}
    67 }
    68 
    69 int CFunction::PushCell(cell_t cell)
    70 {
    71 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
    72 	{
    73 		return SetError(SP_ERROR_PARAMS_MAX);
    74 	}
    75 
    76 	m_info[m_curparam].marked = false;
    77 	m_params[m_curparam] = cell;
    78 	m_curparam++;
    79 
    80 	return SP_ERROR_NONE;
    81 }
    82 
    83 int CFunction::PushCellByRef(cell_t *cell, int flags)
    84 {
    85 	return PushArray(cell, 1, flags);
    86 }
    87 
    88 int CFunction::PushFloat(float number)
    89 {
    90 	cell_t val = *(cell_t *)&number;
    91 
    92 	return PushCell(val);
    93 }
    94 
    95 int CFunction::PushFloatByRef(float *number, int flags)
    96 {
    97 	return PushCellByRef((cell_t *)number, flags);
    98 }
    99 
   100 int CFunction::PushArray(cell_t *inarray, unsigned int cells, int copyback)
   101 {
   102 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   103 	{
   104 		return SetError(SP_ERROR_PARAMS_MAX);
   105 	}
   106 
   107 	ParamInfo *info = &m_info[m_curparam];
   108 
   109 	info->flags = inarray ? copyback : 0;
   110 	info->marked = true;
   111 	info->size = cells;
   112 	info->str.is_sz = false;
   113 	info->orig_addr = inarray;
   114 
   115 	m_curparam++;
   116 
   117 	return SP_ERROR_NONE;
   118 }
   119 
   120 int CFunction::PushString(const char *string)
   121 {
   122 	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
   123 }
   124 
   125 int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
   126 {
   127 	return _PushString(buffer, sz_flags, cp_flags, length);
   128 }
   129 
   130 int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
   131 {
   132 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   133 	{
   134 		return SetError(SP_ERROR_PARAMS_MAX);
   135 	}
   136 
   137 	ParamInfo *info = &m_info[m_curparam];
   138 
   139 	info->marked = true;
   140 	info->orig_addr = (cell_t *)string;
   141 	info->flags = cp_flags;
   142 	info->size = len;
   143 	info->str.sz_flags = sz_flags;
   144 	info->str.is_sz = true;
   145 
   146 	m_curparam++;
   147 
   148 	return SP_ERROR_NONE;
   149 }
   150 
   151 void CFunction::Cancel()
   152 {
   153 	if (!m_curparam)
   154 	{
   155 		return;
   156 	}
   157 
   158 	m_errorstate = SP_ERROR_NONE;
   159 	m_curparam = 0;
   160 }
   161 
   162 int CFunction::Execute(cell_t *result)
   163 {
   164 	int err = SP_ERROR_NONE;
   165 	if (!IsRunnable())
   166 	{
   167 		m_errorstate = SP_ERROR_NOT_RUNNABLE;
   168 	}
   169 	if (m_errorstate != SP_ERROR_NONE)
   170 	{
   171 		err = m_errorstate;
   172 		Cancel();
   173 		return err;
   174 	}
   175 
   176 	//This is for re-entrancy!
   177 	cell_t temp_params[SP_MAX_EXEC_PARAMS];
   178 	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
   179 	unsigned int numparams = m_curparam;
   180 	unsigned int i;
   181 	bool docopies = true;
   182 
   183 	if (numparams)
   184 	{
   185 		//Save the info locally, then reset it for re-entrant calls.
   186 		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
   187 	}
   188 	m_curparam = 0;
   189 
   190 	/* Browse the parameters and build arrays */
   191 	for (i=0; i<numparams; i++)
   192 	{
   193 		/* Is this marked as an array? */
   194 		if (temp_info[i].marked)
   195 		{
   196 			if (!temp_info[i].str.is_sz)
   197 			{
   198 				/* Allocate a normal/generic array */
   199 				if ((err=m_pContext->HeapAlloc(temp_info[i].size, 
   200 											   &(temp_info[i].local_addr),
   201 											   &(temp_info[i].phys_addr)))
   202 					!= SP_ERROR_NONE)
   203 				{
   204 					break;
   205 				}
   206 				if (temp_info[i].orig_addr)
   207 				{
   208 					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
   209 				}
   210 			} else {
   211 				/* Calculate cells required for the string */
   212 				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
   213 
   214 				/* Allocate the buffer */
   215 				if ((err=m_pContext->HeapAlloc(cells,
   216 												&(temp_info[i].local_addr),
   217 												&(temp_info[i].phys_addr)))
   218 					!= SP_ERROR_NONE)
   219 				{
   220 					break;
   221 				}
   222 				/* Copy original string if necessary */
   223 				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
   224 				{
   225 					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
   226 					{
   227 						if ((err=m_pContext->StringToLocalUTF8(temp_info[i].local_addr, 
   228 																temp_info[i].size, 
   229 																(const char *)temp_info[i].orig_addr,
   230 																NULL))
   231 							!= SP_ERROR_NONE)
   232 						{
   233 							break;
   234 						}
   235 					} else {
   236 						if ((err=m_pContext->StringToLocal(temp_info[i].local_addr,
   237 															temp_info[i].size,
   238 															(const char *)temp_info[i].orig_addr))
   239 							!= SP_ERROR_NONE)
   240 						{
   241 							break;
   242 						}
   243 					}
   244 				}
   245 			} /* End array/string calculation */
   246 			/* Update the pushed parameter with the byref local address */
   247 			temp_params[i] = temp_info[i].local_addr;
   248 		} else {
   249 			/* Just copy the value normally */
   250 			temp_params[i] = m_params[i];
   251 		}
   252 	}
   253 
   254 	/* Make the call if we can */
   255 	if (err == SP_ERROR_NONE)
   256 	{
   257 		if ((err = CallFunction(temp_params, numparams, result)) != SP_ERROR_NONE)
   258 		{
   259 			docopies = false;
   260 		}
   261 	} else {
   262 		docopies = false;
   263 	}
   264 
   265 	/* i should be equal to the last valid parameter + 1 */
   266 	while (i--)
   267 	{
   268 		if (!temp_info[i].marked)
   269 		{
   270 			continue;
   271 		}
   272 		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
   273 		{
   274 			if (temp_info[i].orig_addr)
   275 			{
   276 				if (temp_info[i].str.is_sz)
   277 				{
   278 					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
   279 				} else {
   280 					if (temp_info[i].size == 1)
   281 					{
   282 						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
   283 					} else {
   284 						memcpy(temp_info[i].orig_addr, 
   285 								temp_info[i].phys_addr, 
   286 								temp_info[i].size * sizeof(cell_t));
   287 					}
   288 				}
   289 			}
   290 		}
   291 		if ((err=m_pContext->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
   292 		{
   293 			return err;
   294 		}
   295 	}
   296 
   297 	return err;
   298 }