core/vm/sp_vm_function.cpp
author Scott Ehlert <ds@alliedmods.net>
Thu Mar 27 04:54:56 2008 +0000 (2008-03-27)
changeset 1947 e73a1e51833b
parent 1926 295f585cc4f5
permissions -rw-r--r--
Updated copyright year
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourcePawn
     5  * Copyright (C) 2004-2008 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 #include "sm_stringutil.h"
    36 
    37 /********************
    38 * FUNCTION CALLING *
    39 ********************/
    40 
    41 void CFunction::Set(uint32_t code_addr, IPluginContext *plugin, funcid_t id, uint32_t pub_id)
    42 {
    43 	m_codeaddr = code_addr;
    44 	m_pContext = plugin;
    45 	m_curparam = 0;
    46 	m_errorstate = SP_ERROR_NONE;
    47 	m_Invalid = false;
    48 	m_pCtx = plugin ? plugin->GetContext() : NULL;
    49 	m_FnId = id;
    50 	
    51 	m_pContext->GetPublicByIndex(pub_id, &m_pPublic);
    52 }
    53 
    54 bool CFunction::IsRunnable()
    55 {
    56 	return ((m_pCtx->flags & SPFLAG_PLUGIN_PAUSED) != SPFLAG_PLUGIN_PAUSED);
    57 }
    58 
    59 int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
    60 {
    61 	int ir, serial;
    62 
    63 	if (!IsRunnable())
    64 	{
    65 		return SP_ERROR_NOT_RUNNABLE;
    66 	}
    67 
    68 	if ((m_pCtx->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS
    69 		&& m_pPublic != NULL)
    70 	{
    71 		serial = m_pCtx->profiler->OnCallbackBegin(m_pContext, m_pPublic);
    72 	}
    73 
    74 	while (num_params--)
    75 	{
    76 		m_pContext->PushCell(params[num_params]);
    77 	}
    78 
    79 	ir = m_pContext->Execute(m_codeaddr, result);
    80 
    81 	if ((m_pCtx->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS
    82 		&& m_pPublic != NULL)
    83 	{
    84 		m_pCtx->profiler->OnCallbackEnd(serial);
    85 	}
    86 
    87 	return ir;
    88 }
    89 
    90 IPluginContext *CFunction::GetParentContext()
    91 {
    92 	return m_pContext;
    93 }
    94 
    95 CFunction::CFunction(uint32_t code_addr, IPluginContext *plugin, funcid_t id, uint32_t pub_id) : 
    96 	m_codeaddr(code_addr), m_pContext(plugin), m_curparam(0), 
    97 	m_errorstate(SP_ERROR_NONE), m_FnId(id)
    98 {
    99 	m_Invalid = false;
   100 	if (plugin)
   101 	{
   102 		m_pCtx = plugin->GetContext();
   103 	}
   104 	m_pContext->GetPublicByIndex(pub_id, &m_pPublic);
   105 }
   106 
   107 int CFunction::PushCell(cell_t cell)
   108 {
   109 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   110 	{
   111 		return SetError(SP_ERROR_PARAMS_MAX);
   112 	}
   113 
   114 	m_info[m_curparam].marked = false;
   115 	m_params[m_curparam] = cell;
   116 	m_curparam++;
   117 
   118 	return SP_ERROR_NONE;
   119 }
   120 
   121 int CFunction::PushCellByRef(cell_t *cell, int flags)
   122 {
   123 	return PushArray(cell, 1, flags);
   124 }
   125 
   126 int CFunction::PushFloat(float number)
   127 {
   128 	cell_t val = *(cell_t *)&number;
   129 
   130 	return PushCell(val);
   131 }
   132 
   133 int CFunction::PushFloatByRef(float *number, int flags)
   134 {
   135 	return PushCellByRef((cell_t *)number, flags);
   136 }
   137 
   138 int CFunction::PushArray(cell_t *inarray, unsigned int cells, int copyback)
   139 {
   140 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   141 	{
   142 		return SetError(SP_ERROR_PARAMS_MAX);
   143 	}
   144 
   145 	ParamInfo *info = &m_info[m_curparam];
   146 
   147 	info->flags = inarray ? copyback : 0;
   148 	info->marked = true;
   149 	info->size = cells;
   150 	info->str.is_sz = false;
   151 	info->orig_addr = inarray;
   152 
   153 	m_curparam++;
   154 
   155 	return SP_ERROR_NONE;
   156 }
   157 
   158 int CFunction::PushString(const char *string)
   159 {
   160 	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
   161 }
   162 
   163 int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
   164 {
   165 	return _PushString(buffer, sz_flags, cp_flags, length);
   166 }
   167 
   168 int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
   169 {
   170 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   171 	{
   172 		return SetError(SP_ERROR_PARAMS_MAX);
   173 	}
   174 
   175 	ParamInfo *info = &m_info[m_curparam];
   176 
   177 	info->marked = true;
   178 	info->orig_addr = (cell_t *)string;
   179 	info->flags = cp_flags;
   180 	info->size = len;
   181 	info->str.sz_flags = sz_flags;
   182 	info->str.is_sz = true;
   183 
   184 	m_curparam++;
   185 
   186 	return SP_ERROR_NONE;
   187 }
   188 
   189 void CFunction::Cancel()
   190 {
   191 	if (!m_curparam)
   192 	{
   193 		return;
   194 	}
   195 
   196 	m_errorstate = SP_ERROR_NONE;
   197 	m_curparam = 0;
   198 }
   199 
   200 int CFunction::Execute(cell_t *result)
   201 {
   202 	int err = SP_ERROR_NONE;
   203 
   204 	if (!IsRunnable())
   205 	{
   206 		m_errorstate = SP_ERROR_NOT_RUNNABLE;
   207 	}
   208 
   209 	if (m_errorstate != SP_ERROR_NONE)
   210 	{
   211 		err = m_errorstate;
   212 		Cancel();
   213 		return err;
   214 	}
   215 
   216 	//This is for re-entrancy!
   217 	cell_t temp_params[SP_MAX_EXEC_PARAMS];
   218 	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
   219 	unsigned int numparams = m_curparam;
   220 	unsigned int i;
   221 	bool docopies = true;
   222 
   223 	if (numparams)
   224 	{
   225 		//Save the info locally, then reset it for re-entrant calls.
   226 		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
   227 	}
   228 	m_curparam = 0;
   229 
   230 	/* Browse the parameters and build arrays */
   231 	for (i=0; i<numparams; i++)
   232 	{
   233 		/* Is this marked as an array? */
   234 		if (temp_info[i].marked)
   235 		{
   236 			if (!temp_info[i].str.is_sz)
   237 			{
   238 				/* Allocate a normal/generic array */
   239 				if ((err=m_pContext->HeapAlloc(temp_info[i].size, 
   240 											   &(temp_info[i].local_addr),
   241 											   &(temp_info[i].phys_addr)))
   242 					!= SP_ERROR_NONE)
   243 				{
   244 					break;
   245 				}
   246 				if (temp_info[i].orig_addr)
   247 				{
   248 					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
   249 				}
   250 			}
   251 			else
   252 			{
   253 				/* Calculate cells required for the string */
   254 				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
   255 
   256 				/* Allocate the buffer */
   257 				if ((err=m_pContext->HeapAlloc(cells,
   258 												&(temp_info[i].local_addr),
   259 												&(temp_info[i].phys_addr)))
   260 					!= SP_ERROR_NONE)
   261 				{
   262 					break;
   263 				}
   264 				/* Copy original string if necessary */
   265 				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
   266 				{
   267 					/* Cut off UTF-8 properly */
   268 					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
   269 					{
   270 						if ((err=m_pContext->StringToLocalUTF8(temp_info[i].local_addr, 
   271 																temp_info[i].size, 
   272 																(const char *)temp_info[i].orig_addr,
   273 																NULL))
   274 							!= SP_ERROR_NONE)
   275 						{
   276 							break;
   277 						}
   278 					}
   279 					/* Copy a binary blob */
   280 					else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
   281 					{
   282 						memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
   283 					}
   284 					/* Copy ASCII characters */
   285 					else
   286 					{
   287 						if ((err=m_pContext->StringToLocal(temp_info[i].local_addr,
   288 															temp_info[i].size,
   289 															(const char *)temp_info[i].orig_addr))
   290 							!= SP_ERROR_NONE)
   291 						{
   292 							break;
   293 						}
   294 					}
   295 				}
   296 			} /* End array/string calculation */
   297 			/* Update the pushed parameter with the byref local address */
   298 			temp_params[i] = temp_info[i].local_addr;
   299 		}
   300 		else
   301 		{
   302 			/* Just copy the value normally */
   303 			temp_params[i] = m_params[i];
   304 		}
   305 	}
   306 
   307 	/* Make the call if we can */
   308 	if (err == SP_ERROR_NONE)
   309 	{
   310 		if ((err = CallFunction(temp_params, numparams, result)) != SP_ERROR_NONE)
   311 		{
   312 			docopies = false;
   313 		}
   314 	}
   315 	else
   316 	{
   317 		docopies = false;
   318 	}
   319 
   320 	/* i should be equal to the last valid parameter + 1 */
   321 	while (i--)
   322 	{
   323 		if (!temp_info[i].marked)
   324 		{
   325 			continue;
   326 		}
   327 
   328 		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
   329 		{
   330 			if (temp_info[i].orig_addr)
   331 			{
   332 				if (temp_info[i].str.is_sz)
   333 				{
   334 					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
   335 				
   336 				}
   337 				else
   338 				{
   339 					if (temp_info[i].size == 1)
   340 					{
   341 						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
   342 					}
   343 					else
   344 					{
   345 						memcpy(temp_info[i].orig_addr, 
   346 								temp_info[i].phys_addr, 
   347 								temp_info[i].size * sizeof(cell_t));
   348 					}
   349 				}
   350 			}
   351 		}
   352 
   353 		if ((err=m_pContext->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
   354 		{
   355 			return err;
   356 		}
   357 	}
   358 
   359 	return err;
   360 }
   361 
   362 funcid_t CFunction::GetFunctionID()
   363 {
   364 	return m_FnId;
   365 }
   366