core/vm/sp_vm_function.cpp
author Matt Woodrow <pred@alliedmods.net>
Tue Mar 11 23:52:56 2008 +0000 (2008-03-11)
changeset 1921 f993e100e5a3
parent 1908 318e0a01aef9
child 1926 295f585cc4f5
permissions -rw-r--r--
Fixed bug in GetAdjustedTime()
     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 #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 	if (!IsRunnable())
   204 	{
   205 		m_errorstate = SP_ERROR_NOT_RUNNABLE;
   206 	}
   207 	if (m_errorstate != SP_ERROR_NONE)
   208 	{
   209 		err = m_errorstate;
   210 		Cancel();
   211 		return err;
   212 	}
   213 
   214 	//This is for re-entrancy!
   215 	cell_t temp_params[SP_MAX_EXEC_PARAMS];
   216 	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
   217 	unsigned int numparams = m_curparam;
   218 	unsigned int i;
   219 	bool docopies = true;
   220 
   221 	if (numparams)
   222 	{
   223 		//Save the info locally, then reset it for re-entrant calls.
   224 		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
   225 	}
   226 	m_curparam = 0;
   227 
   228 	/* Browse the parameters and build arrays */
   229 	for (i=0; i<numparams; i++)
   230 	{
   231 		/* Is this marked as an array? */
   232 		if (temp_info[i].marked)
   233 		{
   234 			if (!temp_info[i].str.is_sz)
   235 			{
   236 				/* Allocate a normal/generic array */
   237 				if ((err=m_pContext->HeapAlloc(temp_info[i].size, 
   238 											   &(temp_info[i].local_addr),
   239 											   &(temp_info[i].phys_addr)))
   240 					!= SP_ERROR_NONE)
   241 				{
   242 					break;
   243 				}
   244 				if (temp_info[i].orig_addr)
   245 				{
   246 					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
   247 				}
   248 			} else {
   249 				/* Calculate cells required for the string */
   250 				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
   251 
   252 				/* Allocate the buffer */
   253 				if ((err=m_pContext->HeapAlloc(cells,
   254 												&(temp_info[i].local_addr),
   255 												&(temp_info[i].phys_addr)))
   256 					!= SP_ERROR_NONE)
   257 				{
   258 					break;
   259 				}
   260 				/* Copy original string if necessary */
   261 				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
   262 				{
   263 					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
   264 					{
   265 						if ((err=m_pContext->StringToLocalUTF8(temp_info[i].local_addr, 
   266 																temp_info[i].size, 
   267 																(const char *)temp_info[i].orig_addr,
   268 																NULL))
   269 							!= SP_ERROR_NONE)
   270 						{
   271 							break;
   272 						}
   273 					} else {
   274 						if ((err=m_pContext->StringToLocal(temp_info[i].local_addr,
   275 															temp_info[i].size,
   276 															(const char *)temp_info[i].orig_addr))
   277 							!= SP_ERROR_NONE)
   278 						{
   279 							break;
   280 						}
   281 					}
   282 				}
   283 			} /* End array/string calculation */
   284 			/* Update the pushed parameter with the byref local address */
   285 			temp_params[i] = temp_info[i].local_addr;
   286 		} else {
   287 			/* Just copy the value normally */
   288 			temp_params[i] = m_params[i];
   289 		}
   290 	}
   291 
   292 	/* Make the call if we can */
   293 	if (err == SP_ERROR_NONE)
   294 	{
   295 		if ((err = CallFunction(temp_params, numparams, result)) != SP_ERROR_NONE)
   296 		{
   297 			docopies = false;
   298 		}
   299 	} else {
   300 		docopies = false;
   301 	}
   302 
   303 	/* i should be equal to the last valid parameter + 1 */
   304 	while (i--)
   305 	{
   306 		if (!temp_info[i].marked)
   307 		{
   308 			continue;
   309 		}
   310 		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
   311 		{
   312 			if (temp_info[i].orig_addr)
   313 			{
   314 				if (temp_info[i].str.is_sz)
   315 				{
   316 					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
   317 				} else {
   318 					if (temp_info[i].size == 1)
   319 					{
   320 						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
   321 					} else {
   322 						memcpy(temp_info[i].orig_addr, 
   323 								temp_info[i].phys_addr, 
   324 								temp_info[i].size * sizeof(cell_t));
   325 					}
   326 				}
   327 			}
   328 		}
   329 		if ((err=m_pContext->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
   330 		{
   331 			return err;
   332 		}
   333 	}
   334 
   335 	return err;
   336 }
   337 
   338 funcid_t CFunction::GetFunctionID()
   339 {
   340 	return m_FnId;
   341 }
   342