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