core/vm/sp_vm_function.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 26 17:41:02 2007 +0000 (2007-08-26)
changeset 1369 bd9ccced1b1a
parent 1339 02e8f2c0f8af
child 1908 318e0a01aef9
permissions -rw-r--r--
fixed amb834 - command callbacks could potentially occur on invalid clients in very rare cases
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@327
    35
dvander@327
    36
/********************
dvander@327
    37
* FUNCTION CALLING *
dvander@327
    38
********************/
dvander@327
    39
dvander@1339
    40
void CFunction::Set(uint32_t code_addr, IPluginContext *plugin, funcid_t id)
dvander@327
    41
{
dvander@348
    42
	m_codeaddr = code_addr;
dvander@327
    43
	m_pContext = plugin;
dvander@327
    44
	m_curparam = 0;
dvander@327
    45
	m_errorstate = SP_ERROR_NONE;
dvander@502
    46
	m_Invalid = false;
dvander@525
    47
	m_pCtx = plugin ? plugin->GetContext() : NULL;
dvander@1339
    48
	m_FnId = id;
dvander@525
    49
}
dvander@525
    50
dvander@525
    51
bool CFunction::IsRunnable()
dvander@525
    52
{
dvander@525
    53
	return ((m_pCtx->flags & SPFLAG_PLUGIN_PAUSED) != SPFLAG_PLUGIN_PAUSED);
dvander@327
    54
}
dvander@327
    55
dvander@327
    56
int CFunction::CallFunction(const cell_t *params, unsigned int num_params, cell_t *result)
dvander@327
    57
{
dvander@525
    58
	if (!IsRunnable())
dvander@525
    59
	{
dvander@525
    60
		return SP_ERROR_NOT_RUNNABLE;
dvander@525
    61
	}
dvander@525
    62
dvander@327
    63
	while (num_params--)
dvander@327
    64
	{
dvander@327
    65
		m_pContext->PushCell(params[num_params]);
dvander@327
    66
	}
dvander@327
    67
dvander@348
    68
	return m_pContext->Execute(m_codeaddr, result);
dvander@327
    69
}
dvander@327
    70
dvander@327
    71
IPluginContext *CFunction::GetParentContext()
dvander@327
    72
{
dvander@327
    73
	return m_pContext;
dvander@327
    74
}
dvander@327
    75
dvander@1339
    76
CFunction::CFunction(uint32_t code_addr, IPluginContext *plugin, funcid_t id) : 
dvander@348
    77
	m_codeaddr(code_addr), m_pContext(plugin), m_curparam(0), 
dvander@1339
    78
	m_errorstate(SP_ERROR_NONE), m_FnId(id)
dvander@327
    79
{
dvander@502
    80
	m_Invalid = false;
dvander@525
    81
	if (plugin)
dvander@525
    82
	{
dvander@525
    83
		m_pCtx = plugin->GetContext();
dvander@525
    84
	}
dvander@327
    85
}
dvander@327
    86
dvander@327
    87
int CFunction::PushCell(cell_t cell)
dvander@327
    88
{
dvander@327
    89
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@327
    90
	{
dvander@327
    91
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@327
    92
	}
dvander@327
    93
dvander@327
    94
	m_info[m_curparam].marked = false;
dvander@327
    95
	m_params[m_curparam] = cell;
dvander@327
    96
	m_curparam++;
dvander@327
    97
dvander@327
    98
	return SP_ERROR_NONE;
dvander@327
    99
}
dvander@327
   100
ds@585
   101
int CFunction::PushCellByRef(cell_t *cell, int flags)
dvander@327
   102
{
dvander@589
   103
	return PushArray(cell, 1, flags);
dvander@327
   104
}
dvander@327
   105
dvander@327
   106
int CFunction::PushFloat(float number)
dvander@327
   107
{
dvander@327
   108
	cell_t val = *(cell_t *)&number;
dvander@327
   109
dvander@327
   110
	return PushCell(val);
dvander@327
   111
}
dvander@327
   112
ds@585
   113
int CFunction::PushFloatByRef(float *number, int flags)
dvander@327
   114
{
ds@585
   115
	return PushCellByRef((cell_t *)number, flags);
dvander@327
   116
}
dvander@327
   117
dvander@589
   118
int CFunction::PushArray(cell_t *inarray, unsigned int cells, int copyback)
dvander@327
   119
{
dvander@327
   120
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@327
   121
	{
dvander@327
   122
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@327
   123
	}
dvander@327
   124
dvander@327
   125
	ParamInfo *info = &m_info[m_curparam];
dvander@327
   126
dvander@327
   127
	info->flags = inarray ? copyback : 0;
dvander@327
   128
	info->marked = true;
dvander@589
   129
	info->size = cells;
dvander@589
   130
	info->str.is_sz = false;
dvander@589
   131
	info->orig_addr = inarray;
dvander@327
   132
dvander@589
   133
	m_curparam++;
dvander@327
   134
dvander@586
   135
	return SP_ERROR_NONE;
dvander@327
   136
}
dvander@327
   137
dvander@327
   138
int CFunction::PushString(const char *string)
dvander@327
   139
{
dvander@327
   140
	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
dvander@327
   141
}
dvander@327
   142
dvander@327
   143
int CFunction::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
dvander@327
   144
{
dvander@327
   145
	return _PushString(buffer, sz_flags, cp_flags, length);
dvander@327
   146
}
dvander@327
   147
dvander@327
   148
int CFunction::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
dvander@327
   149
{
dvander@327
   150
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@327
   151
	{
dvander@327
   152
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@327
   153
	}
dvander@327
   154
dvander@327
   155
	ParamInfo *info = &m_info[m_curparam];
dvander@327
   156
dvander@327
   157
	info->marked = true;
dvander@589
   158
	info->orig_addr = (cell_t *)string;
dvander@589
   159
	info->flags = cp_flags;
dvander@589
   160
	info->size = len;
dvander@589
   161
	info->str.sz_flags = sz_flags;
dvander@589
   162
	info->str.is_sz = true;
dvander@327
   163
dvander@589
   164
	m_curparam++;
dvander@327
   165
dvander@327
   166
	return SP_ERROR_NONE;
dvander@327
   167
}
dvander@327
   168
dvander@327
   169
void CFunction::Cancel()
dvander@327
   170
{
dvander@327
   171
	if (!m_curparam)
dvander@327
   172
	{
dvander@327
   173
		return;
dvander@327
   174
	}
dvander@327
   175
dvander@327
   176
	m_errorstate = SP_ERROR_NONE;
dvander@589
   177
	m_curparam = 0;
dvander@327
   178
}
dvander@327
   179
dvander@327
   180
int CFunction::Execute(cell_t *result)
dvander@327
   181
{
dvander@589
   182
	int err = SP_ERROR_NONE;
dvander@525
   183
	if (!IsRunnable())
dvander@525
   184
	{
dvander@525
   185
		m_errorstate = SP_ERROR_NOT_RUNNABLE;
dvander@525
   186
	}
dvander@327
   187
	if (m_errorstate != SP_ERROR_NONE)
dvander@327
   188
	{
dvander@327
   189
		err = m_errorstate;
dvander@327
   190
		Cancel();
dvander@327
   191
		return err;
dvander@327
   192
	}
dvander@327
   193
dvander@327
   194
	//This is for re-entrancy!
dvander@327
   195
	cell_t temp_params[SP_MAX_EXEC_PARAMS];
dvander@327
   196
	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
dvander@327
   197
	unsigned int numparams = m_curparam;
dvander@589
   198
	unsigned int i;
dvander@327
   199
	bool docopies = true;
dvander@327
   200
dvander@327
   201
	if (numparams)
dvander@327
   202
	{
dvander@327
   203
		//Save the info locally, then reset it for re-entrant calls.
dvander@327
   204
		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
dvander@327
   205
	}
dvander@327
   206
	m_curparam = 0;
dvander@327
   207
dvander@589
   208
	/* Browse the parameters and build arrays */
dvander@589
   209
	for (i=0; i<numparams; i++)
dvander@327
   210
	{
dvander@589
   211
		/* Is this marked as an array? */
dvander@589
   212
		if (temp_info[i].marked)
dvander@589
   213
		{
dvander@589
   214
			if (!temp_info[i].str.is_sz)
dvander@589
   215
			{
dvander@589
   216
				/* Allocate a normal/generic array */
dvander@589
   217
				if ((err=m_pContext->HeapAlloc(temp_info[i].size, 
dvander@589
   218
											   &(temp_info[i].local_addr),
dvander@589
   219
											   &(temp_info[i].phys_addr)))
dvander@589
   220
					!= SP_ERROR_NONE)
dvander@589
   221
				{
dvander@589
   222
					break;
dvander@589
   223
				}
dvander@589
   224
				if (temp_info[i].orig_addr)
dvander@589
   225
				{
dvander@589
   226
					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
dvander@589
   227
				}
dvander@589
   228
			} else {
dvander@589
   229
				/* Calculate cells required for the string */
dvander@589
   230
				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
dvander@589
   231
dvander@589
   232
				/* Allocate the buffer */
dvander@589
   233
				if ((err=m_pContext->HeapAlloc(cells,
dvander@589
   234
												&(temp_info[i].local_addr),
dvander@589
   235
												&(temp_info[i].phys_addr)))
dvander@589
   236
					!= SP_ERROR_NONE)
dvander@589
   237
				{
dvander@589
   238
					break;
dvander@589
   239
				}
dvander@589
   240
				/* Copy original string if necessary */
dvander@589
   241
				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
dvander@589
   242
				{
dvander@589
   243
					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
dvander@589
   244
					{
dvander@589
   245
						if ((err=m_pContext->StringToLocalUTF8(temp_info[i].local_addr, 
dvander@589
   246
																temp_info[i].size, 
dvander@589
   247
																(const char *)temp_info[i].orig_addr,
dvander@589
   248
																NULL))
dvander@589
   249
							!= SP_ERROR_NONE)
dvander@589
   250
						{
dvander@589
   251
							break;
dvander@589
   252
						}
dvander@589
   253
					} else {
dvander@589
   254
						if ((err=m_pContext->StringToLocal(temp_info[i].local_addr,
dvander@589
   255
															temp_info[i].size,
dvander@589
   256
															(const char *)temp_info[i].orig_addr))
dvander@589
   257
							!= SP_ERROR_NONE)
dvander@589
   258
						{
dvander@589
   259
							break;
dvander@589
   260
						}
dvander@589
   261
					}
dvander@589
   262
				}
dvander@589
   263
			} /* End array/string calculation */
dvander@589
   264
			/* Update the pushed parameter with the byref local address */
dvander@589
   265
			temp_params[i] = temp_info[i].local_addr;
dvander@589
   266
		} else {
dvander@589
   267
			/* Just copy the value normally */
dvander@589
   268
			temp_params[i] = m_params[i];
dvander@589
   269
		}
dvander@589
   270
	}
dvander@589
   271
dvander@589
   272
	/* Make the call if we can */
dvander@589
   273
	if (err == SP_ERROR_NONE)
dvander@589
   274
	{
dvander@589
   275
		if ((err = CallFunction(temp_params, numparams, result)) != SP_ERROR_NONE)
dvander@589
   276
		{
dvander@589
   277
			docopies = false;
dvander@589
   278
		}
dvander@589
   279
	} else {
dvander@327
   280
		docopies = false;
dvander@327
   281
	}
dvander@327
   282
dvander@589
   283
	/* i should be equal to the last valid parameter + 1 */
dvander@589
   284
	while (i--)
dvander@327
   285
	{
dvander@589
   286
		if (!temp_info[i].marked)
dvander@327
   287
		{
dvander@327
   288
			continue;
dvander@327
   289
		}
dvander@589
   290
		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
dvander@327
   291
		{
dvander@589
   292
			if (temp_info[i].orig_addr)
dvander@327
   293
			{
dvander@589
   294
				if (temp_info[i].str.is_sz)
dvander@327
   295
				{
dvander@589
   296
					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
dvander@327
   297
				} else {
dvander@589
   298
					if (temp_info[i].size == 1)
dvander@589
   299
					{
dvander@589
   300
						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
dvander@589
   301
					} else {
dvander@589
   302
						memcpy(temp_info[i].orig_addr, 
dvander@589
   303
								temp_info[i].phys_addr, 
dvander@589
   304
								temp_info[i].size * sizeof(cell_t));
dvander@589
   305
					}
dvander@327
   306
				}
dvander@327
   307
			}
dvander@327
   308
		}
dvander@589
   309
		if ((err=m_pContext->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
dvander@589
   310
		{
dvander@589
   311
			return err;
dvander@589
   312
		}
dvander@327
   313
	}
dvander@327
   314
dvander@327
   315
	return err;
dvander@327
   316
}
dvander@1339
   317
dvander@1339
   318
funcid_t CFunction::GetFunctionID()
dvander@1339
   319
{
dvander@1339
   320
	return m_FnId;
dvander@1339
   321
}