core/vm/sp_vm_function.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1231 17badaf66ce4
child 1339 02e8f2c0f8af
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

(This day shall henceforth be known as the Eighty Column Massacre of '07)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourcePawn
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <stdio.h>
    33 #include <string.h>
    34 #include "sp_vm_function.h"
    35 
    36 /********************
    37 * FUNCTION CALLING *
    38 ********************/
    39 
    40 void CFunction::Set(uint32_t code_addr, IPluginContext *plugin)
    41 {
    42 	m_codeaddr = code_addr;
    43 	m_pContext = plugin;
    44 	m_curparam = 0;
    45 	m_errorstate = SP_ERROR_NONE;
    46 	m_Invalid = false;
    47 	m_pCtx = plugin ? plugin->GetContext() : NULL;
    48 }
    49 
    50 bool CFunction::IsRunnable()
    51 {
    52 	return ((m_pCtx->flags & SPFLAG_PLUGIN_PAUSED) != SPFLAG_PLUGIN_PAUSED);
    53 }
    54 
    55 int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
    56 {
    57 	if (!IsRunnable())
    58 	{
    59 		return SP_ERROR_NOT_RUNNABLE;
    60 	}
    61 
    62 	while (num_params--)
    63 	{
    64 		m_pContext->PushCell(params[num_params]);
    65 	}
    66 
    67 	return m_pContext->Execute(m_codeaddr, result);
    68 }
    69 
    70 IPluginContext *CFunction::GetParentContext()
    71 {
    72 	return m_pContext;
    73 }
    74 
    75 CFunction::CFunction(uint32_t code_addr, IPluginContext *plugin) : 
    76 	m_codeaddr(code_addr), m_pContext(plugin), m_curparam(0), 
    77 	m_errorstate(SP_ERROR_NONE)
    78 {
    79 	m_Invalid = false;
    80 	if (plugin)
    81 	{
    82 		m_pCtx = plugin->GetContext();
    83 	}
    84 }
    85 
    86 int CFunction::PushCell(cell_t cell)
    87 {
    88 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
    89 	{
    90 		return SetError(SP_ERROR_PARAMS_MAX);
    91 	}
    92 
    93 	m_info[m_curparam].marked = false;
    94 	m_params[m_curparam] = cell;
    95 	m_curparam++;
    96 
    97 	return SP_ERROR_NONE;
    98 }
    99 
   100 int CFunction::PushCellByRef(cell_t *cell, int flags)
   101 {
   102 	return PushArray(cell, 1, flags);
   103 }
   104 
   105 int CFunction::PushFloat(float number)
   106 {
   107 	cell_t val = *(cell_t *)&number;
   108 
   109 	return PushCell(val);
   110 }
   111 
   112 int CFunction::PushFloatByRef(float *number, int flags)
   113 {
   114 	return PushCellByRef((cell_t *)number, flags);
   115 }
   116 
   117 int CFunction::PushArray(cell_t *inarray, unsigned int cells, int copyback)
   118 {
   119 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   120 	{
   121 		return SetError(SP_ERROR_PARAMS_MAX);
   122 	}
   123 
   124 	ParamInfo *info = &m_info[m_curparam];
   125 
   126 	info->flags = inarray ? copyback : 0;
   127 	info->marked = true;
   128 	info->size = cells;
   129 	info->str.is_sz = false;
   130 	info->orig_addr = inarray;
   131 
   132 	m_curparam++;
   133 
   134 	return SP_ERROR_NONE;
   135 }
   136 
   137 int CFunction::PushString(const char *string)
   138 {
   139 	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
   140 }
   141 
   142 int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
   143 {
   144 	return _PushString(buffer, sz_flags, cp_flags, length);
   145 }
   146 
   147 int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
   148 {
   149 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   150 	{
   151 		return SetError(SP_ERROR_PARAMS_MAX);
   152 	}
   153 
   154 	ParamInfo *info = &m_info[m_curparam];
   155 
   156 	info->marked = true;
   157 	info->orig_addr = (cell_t *)string;
   158 	info->flags = cp_flags;
   159 	info->size = len;
   160 	info->str.sz_flags = sz_flags;
   161 	info->str.is_sz = true;
   162 
   163 	m_curparam++;
   164 
   165 	return SP_ERROR_NONE;
   166 }
   167 
   168 void CFunction::Cancel()
   169 {
   170 	if (!m_curparam)
   171 	{
   172 		return;
   173 	}
   174 
   175 	m_errorstate = SP_ERROR_NONE;
   176 	m_curparam = 0;
   177 }
   178 
   179 int CFunction::Execute(cell_t *result)
   180 {
   181 	int err = SP_ERROR_NONE;
   182 	if (!IsRunnable())
   183 	{
   184 		m_errorstate = SP_ERROR_NOT_RUNNABLE;
   185 	}
   186 	if (m_errorstate != SP_ERROR_NONE)
   187 	{
   188 		err = m_errorstate;
   189 		Cancel();
   190 		return err;
   191 	}
   192 
   193 	//This is for re-entrancy!
   194 	cell_t temp_params[SP_MAX_EXEC_PARAMS];
   195 	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
   196 	unsigned int numparams = m_curparam;
   197 	unsigned int i;
   198 	bool docopies = true;
   199 
   200 	if (numparams)
   201 	{
   202 		//Save the info locally, then reset it for re-entrant calls.
   203 		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
   204 	}
   205 	m_curparam = 0;
   206 
   207 	/* Browse the parameters and build arrays */
   208 	for (i=0; i<numparams; i++)
   209 	{
   210 		/* Is this marked as an array? */
   211 		if (temp_info[i].marked)
   212 		{
   213 			if (!temp_info[i].str.is_sz)
   214 			{
   215 				/* Allocate a normal/generic array */
   216 				if ((err=m_pContext->HeapAlloc(temp_info[i].size, 
   217 											   &(temp_info[i].local_addr),
   218 											   &(temp_info[i].phys_addr)))
   219 					!= SP_ERROR_NONE)
   220 				{
   221 					break;
   222 				}
   223 				if (temp_info[i].orig_addr)
   224 				{
   225 					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
   226 				}
   227 			} else {
   228 				/* Calculate cells required for the string */
   229 				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
   230 
   231 				/* Allocate the buffer */
   232 				if ((err=m_pContext->HeapAlloc(cells,
   233 												&(temp_info[i].local_addr),
   234 												&(temp_info[i].phys_addr)))
   235 					!= SP_ERROR_NONE)
   236 				{
   237 					break;
   238 				}
   239 				/* Copy original string if necessary */
   240 				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
   241 				{
   242 					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
   243 					{
   244 						if ((err=m_pContext->StringToLocalUTF8(temp_info[i].local_addr, 
   245 																temp_info[i].size, 
   246 																(const char *)temp_info[i].orig_addr,
   247 																NULL))
   248 							!= SP_ERROR_NONE)
   249 						{
   250 							break;
   251 						}
   252 					} else {
   253 						if ((err=m_pContext->StringToLocal(temp_info[i].local_addr,
   254 															temp_info[i].size,
   255 															(const char *)temp_info[i].orig_addr))
   256 							!= SP_ERROR_NONE)
   257 						{
   258 							break;
   259 						}
   260 					}
   261 				}
   262 			} /* End array/string calculation */
   263 			/* Update the pushed parameter with the byref local address */
   264 			temp_params[i] = temp_info[i].local_addr;
   265 		} else {
   266 			/* Just copy the value normally */
   267 			temp_params[i] = m_params[i];
   268 		}
   269 	}
   270 
   271 	/* Make the call if we can */
   272 	if (err == SP_ERROR_NONE)
   273 	{
   274 		if ((err = CallFunction(temp_params, numparams, result)) != SP_ERROR_NONE)
   275 		{
   276 			docopies = false;
   277 		}
   278 	} else {
   279 		docopies = false;
   280 	}
   281 
   282 	/* i should be equal to the last valid parameter + 1 */
   283 	while (i--)
   284 	{
   285 		if (!temp_info[i].marked)
   286 		{
   287 			continue;
   288 		}
   289 		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
   290 		{
   291 			if (temp_info[i].orig_addr)
   292 			{
   293 				if (temp_info[i].str.is_sz)
   294 				{
   295 					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
   296 				} else {
   297 					if (temp_info[i].size == 1)
   298 					{
   299 						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
   300 					} else {
   301 						memcpy(temp_info[i].orig_addr, 
   302 								temp_info[i].phys_addr, 
   303 								temp_info[i].size * sizeof(cell_t));
   304 					}
   305 				}
   306 			}
   307 		}
   308 		if ((err=m_pContext->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
   309 		{
   310 			return err;
   311 		}
   312 	}
   313 
   314 	return err;
   315 }