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