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