core/logic/NativeInvoker.cpp
author David Anderson <dvander@alliedmods.net>
Fri Aug 30 10:16:28 2013 -0700 (2013-08-30)
changeset 4109 10eb68d668af
parent 4108 dabaea5ab45c
permissions -rw-r--r--
Use Refcounted to manage to NativeEntry (bug 5852 part 4, r=ds).
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourcePawn
     5  * Copyright (C) 2004-2009 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 "NativeInvoker.h"
    33 #include "ShareSys.h"
    34 
    35 NativeInterface g_NInvoke;
    36 
    37 NativeInvoker::NativeInvoker()
    38 {
    39 }
    40 
    41 NativeInvoker::~NativeInvoker()
    42 {
    43 }
    44 
    45 const char *NativeInterface::GetInterfaceName()
    46 {
    47 	return SMINTERFACE_NINVOKE_NAME;
    48 }
    49 
    50 unsigned int NativeInterface::GetInterfaceVersion()
    51 {
    52 	return SMINTERFACE_NINVOKE_VERSION;
    53 }
    54 
    55 void NativeInterface::OnSourceModAllInitialized()
    56 {
    57 	sharesys->AddInterface(NULL, &g_NInvoke);
    58 }
    59 
    60 IPluginRuntime *NativeInterface::CreateRuntime(const char *name, size_t bytes)
    61 {
    62 	return g_pSourcePawn2->CreateEmptyRuntime(name, bytes);
    63 }
    64 
    65 INativeInvoker *NativeInterface::CreateInvoker()
    66 {
    67 	return new NativeInvoker();
    68 }
    69 
    70 bool NativeInvoker::Start(IPluginContext *pContext, const char *name)
    71 {
    72 	ke::Ref<Native> entry = g_ShareSys.FindNative(name);
    73 	if (!entry)
    74 		return false;
    75 
    76 	if (!entry->owner)
    77 		return false;
    78 
    79 	native_ = entry->func();
    80 	context_ = pContext;
    81 
    82 	m_curparam = 0;
    83 	m_errorstate = SP_ERROR_NONE;
    84 
    85 	return true;
    86 }
    87 
    88 cell_t NativeInvoker::PushCell(cell_t cell)
    89 {
    90 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
    91 	{
    92 		return SetError(SP_ERROR_PARAMS_MAX);
    93 	}
    94 
    95 	m_info[m_curparam].marked = false;
    96 	m_params[m_curparam] = cell;
    97 	m_curparam++;
    98 
    99 	return SP_ERROR_NONE;
   100 }
   101 
   102 int NativeInvoker::PushCellByRef(cell_t *cell, int flags)
   103 {
   104 	return PushArray(cell, 1, flags);
   105 }
   106 
   107 int NativeInvoker::PushFloat(float number)
   108 {
   109 	cell_t val = *(cell_t *)&number;
   110 
   111 	return PushCell(val);
   112 }
   113 
   114 int NativeInvoker::PushFloatByRef(float *number, int flags)
   115 {
   116 	return PushCellByRef((cell_t *)number, flags);
   117 }
   118 
   119 int NativeInvoker::PushArray(cell_t *inarray, unsigned int cells, int copyback)
   120 {
   121 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   122 	{
   123 		return SetError(SP_ERROR_PARAMS_MAX);
   124 	}
   125 
   126 	ParamInfo *info = &m_info[m_curparam];
   127 
   128 	info->flags = inarray ? copyback : 0;
   129 	info->marked = true;
   130 	info->size = cells;
   131 	info->str.is_sz = false;
   132 	info->orig_addr = inarray;
   133 
   134 	m_curparam++;
   135 
   136 	return SP_ERROR_NONE;
   137 }
   138 
   139 int NativeInvoker::PushString(const char *string)
   140 {
   141 	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
   142 }
   143 
   144 int NativeInvoker::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
   145 {
   146 	return _PushString(buffer, sz_flags, cp_flags, length);
   147 }
   148 
   149 int NativeInvoker::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
   150 {
   151 	if (m_curparam >= SP_MAX_EXEC_PARAMS)
   152 	{
   153 		return SetError(SP_ERROR_PARAMS_MAX);
   154 	}
   155 
   156 	ParamInfo *info = &m_info[m_curparam];
   157 
   158 	info->marked = true;
   159 	info->orig_addr = (cell_t *)string;
   160 	info->flags = cp_flags;
   161 	info->size = len;
   162 	info->str.sz_flags = sz_flags;
   163 	info->str.is_sz = true;
   164 
   165 	m_curparam++;
   166 
   167 	return SP_ERROR_NONE;
   168 }
   169 
   170 void NativeInvoker::Cancel()
   171 {
   172 	if (context_ == NULL)
   173 		return;
   174 
   175 	m_errorstate = SP_ERROR_NONE;
   176 	m_curparam = 0;
   177 	context_ = NULL;
   178 	native_ = NULL;
   179 }
   180 
   181 int NativeInvoker::SetError(int err)
   182 {
   183 	m_errorstate = err;
   184 	return err;
   185 }
   186 
   187 int NativeInvoker::Invoke(cell_t *result)
   188 {
   189 	int err = SP_ERROR_NONE;
   190 
   191 	if (context_ == NULL)
   192 		return SP_ERROR_INVALID_NATIVE;
   193 
   194 	if (m_errorstate != SP_ERROR_NONE)
   195 	{
   196 		err = m_errorstate;
   197 		Cancel();
   198 		return err;
   199 	}
   200 
   201 	cell_t tresult;
   202 
   203 	if (result == NULL)
   204 	{
   205 		result = &tresult;
   206 	}
   207 
   208 	//This is for re-entrancy!
   209 	IPluginContext *ctx = context_;
   210 	cell_t _temp_params[SP_MAX_EXEC_PARAMS + 1];
   211 	cell_t *temp_params = &_temp_params[1];
   212 	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
   213 	unsigned int numparams = m_curparam;
   214 	unsigned int i;
   215 	bool docopies = true;
   216 
   217 	if (numparams)
   218 	{
   219 		//Save the info locally, then reset it for re-entrant calls.
   220 		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
   221 	}
   222 	m_curparam = 0;
   223 	context_ = NULL;
   224 
   225 	/* Initialize 0th parameter */
   226 	_temp_params[0] = numparams;
   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 = ctx->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 			}
   249 			else
   250 			{
   251 				/* Calculate cells required for the string */
   252 				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
   253 
   254 				/* Allocate the buffer */
   255 				if ((err = ctx->HeapAlloc(cells,
   256 										  &temp_info[i].local_addr,
   257 										  &temp_info[i].phys_addr))
   258 					!= SP_ERROR_NONE)
   259 				{
   260 					break;
   261 				}
   262 				/* Copy original string if necessary */
   263 				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
   264 				{
   265 					/* Cut off UTF-8 properly */
   266 					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
   267 					{
   268 						if ((err = ctx->StringToLocalUTF8(temp_info[i].local_addr, 
   269 														  temp_info[i].size, 
   270 														  (const char *)temp_info[i].orig_addr,
   271 														  NULL))
   272 							!= SP_ERROR_NONE)
   273 						{
   274 							break;
   275 						}
   276 					}
   277 					/* Copy a binary blob */
   278 					else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
   279 					{
   280 						memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
   281 					}
   282 					/* Copy ASCII characters */
   283 					else
   284 					{
   285 						if ((err = ctx->StringToLocal(temp_info[i].local_addr,
   286 													  temp_info[i].size,
   287 													  (const char *)temp_info[i].orig_addr))
   288 							!= SP_ERROR_NONE)
   289 						{
   290 							break;
   291 						}
   292 					}
   293 				}
   294 			} /* End array/string calculation */
   295 			/* Update the pushed parameter with the byref local address */
   296 			temp_params[i] = temp_info[i].local_addr;
   297 		}
   298 		else
   299 		{
   300 			/* Just copy the value normally */
   301 			temp_params[i] = m_params[i];
   302 		}
   303 	}
   304 
   305 	/* Make the call if we can */
   306 	if (err == SP_ERROR_NONE)
   307 	{
   308 		*result = native_(ctx, _temp_params);
   309 		if (ctx->GetLastNativeError() != SP_ERROR_NONE)
   310 		{
   311 			docopies = false;
   312 			ctx->ClearLastNativeError();
   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 = ctx->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
   354 		{
   355 			return err;
   356 		}
   357 	}
   358 
   359 	return err;
   360 }