core/vm/sp_vm_basecontext.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Jul 15 08:00:26 2007 +0000 (2007-07-15)
changeset 1116 40cc5e9f0f1b
parent 1000 aefbd36eaf91
child 1231 17badaf66ce4
permissions -rw-r--r--
Removed strange debugging code
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourcePawn (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ================================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include <string.h>
    16 #include <stdarg.h>
    17 #include <assert.h>
    18 #include <limits.h>
    19 #include "sp_vm_api.h"
    20 #include "sp_vm_basecontext.h"
    21 #include "sp_vm_engine.h"
    22 
    23 #ifdef SOURCEMOD_BUILD
    24 #include "Logger.h"
    25 #endif
    26 
    27 using namespace SourcePawn;
    28 
    29 extern SourcePawnEngine g_SourcePawn;
    30 
    31 #define CELLBOUNDMAX	(INT_MAX/sizeof(cell_t))
    32 #define STACKMARGIN		((cell_t)(16*sizeof(cell_t)))
    33 
    34 int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip)
    35 {
    36 	g_SourcePawn.RunTracer(ctx, frm, cip);
    37 
    38 	return SP_ERROR_NONE;
    39 }
    40 
    41 BaseContext::BaseContext(sp_context_t *_ctx)
    42 {
    43 	ctx = _ctx;
    44 	ctx->context = this;
    45 	ctx->dbreak = GlobalDebugBreak;
    46 	m_InExec = false;
    47 	m_CustomMsg = false;
    48 	m_funcsnum = ctx->vmbase->FunctionCount(ctx);
    49 #if 0
    50 	m_priv_funcs = NULL;
    51 #endif
    52 	m_pub_funcs = NULL;
    53 
    54 #if 0
    55 	/**
    56 	 * Note: Since the m_plugin member will never change,
    57 	 * it is safe to assume the function count will never change
    58 	 */
    59 	if (m_funcsnum && m_priv_funcs == NULL)
    60 	{
    61 		m_priv_funcs = new CFunction *[m_funcsnum];
    62 		memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
    63 	} else {
    64 		m_priv_funcs = NULL;
    65 	}
    66 #endif
    67 
    68 	if (ctx->plugin->info.publics_num && m_pub_funcs == NULL)
    69 	{
    70 		m_pub_funcs = new CFunction *[ctx->plugin->info.publics_num];
    71 		memset(m_pub_funcs, 0, sizeof(CFunction *) * ctx->plugin->info.publics_num);
    72 	} else {
    73 		m_pub_funcs = NULL;
    74 	}
    75 
    76 	/* Initialize the null references */
    77 	uint32_t index;
    78 	if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE)
    79 	{
    80 		sp_pubvar_t *pubvar;
    81 		GetPubvarByIndex(index, &pubvar);
    82 		m_pNullVec = pubvar->offs;
    83 	} else {
    84 		m_pNullVec = NULL;
    85 	}
    86 
    87 	if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE)
    88 	{
    89 		sp_pubvar_t *pubvar;
    90 		GetPubvarByIndex(index, &pubvar);
    91 		m_pNullString = pubvar->offs;
    92 	} else {
    93 		m_pNullString = NULL;
    94 	}
    95 }
    96 
    97 void BaseContext::FlushFunctionCache()
    98 {
    99 	if (m_pub_funcs)
   100 	{
   101 		for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
   102 		{
   103 			delete m_pub_funcs[i];
   104 			m_pub_funcs[i] = NULL;
   105 		}
   106 	}
   107 
   108 #if 0
   109 	if (m_priv_funcs)
   110 	{
   111 		for (unsigned int i=0; i<m_funcsnum; i++)
   112 		{
   113 			delete m_priv_funcs[i];
   114 			m_priv_funcs[i] = NULL;
   115 		}
   116 	}
   117 #endif
   118 }
   119 
   120 void BaseContext::RefreshFunctionCache()
   121 {
   122 	if (m_pub_funcs)
   123 	{
   124 		sp_public_t *pub;
   125 		for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
   126 		{
   127 			if (!m_pub_funcs[i])
   128 			{
   129 				continue;
   130 			}
   131 			if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE)
   132 			{
   133 				continue;
   134 			}
   135 			m_pub_funcs[i]->Set(pub->code_offs, this);
   136 		}
   137 	}
   138 
   139 #if 0
   140 	if (m_priv_funcs)
   141 	{
   142 		for (unsigned int i=0; i<m_funcsnum; i++)
   143 		{
   144 			if (!m_priv_funcs[i])
   145 			{
   146 				continue;
   147 			}
   148 			g_pVM->
   149 		}
   150 	}
   151 #endif
   152 }
   153 
   154 BaseContext::~BaseContext()
   155 {
   156 	FlushFunctionCache();
   157 	delete [] m_pub_funcs;
   158 	m_pub_funcs = NULL;
   159 #if 0
   160 	delete [] m_priv_funcs;
   161 	m_priv_funcs = NULL;
   162 #endif
   163 }
   164 
   165 void BaseContext::SetContext(sp_context_t *_ctx)
   166 {
   167 	if (!_ctx)
   168 	{
   169 		return;
   170 	}
   171 	ctx = _ctx;
   172 	ctx->context = this;
   173 	ctx->dbreak = GlobalDebugBreak;
   174 	RefreshFunctionCache();
   175 }
   176 
   177 IVirtualMachine *BaseContext::GetVirtualMachine()
   178 {
   179 	return (IVirtualMachine *)ctx->vmbase;
   180 }
   181 
   182 sp_context_t *BaseContext::GetContext()
   183 {
   184 	return ctx;
   185 }
   186 
   187 bool BaseContext::IsDebugging()
   188 {
   189 	return (ctx->flags & SPFLAG_PLUGIN_DEBUG);
   190 }
   191 
   192 int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn)
   193 {
   194 	if (!IsDebugging())
   195 	{
   196 		return SP_ERROR_NOTDEBUGGING;
   197 	}
   198 
   199 	*oldpfn = ctx->dbreak;
   200 	ctx->dbreak = newpfn;
   201 
   202 	return SP_ERROR_NONE;
   203 }
   204 
   205 IPluginDebugInfo *BaseContext::GetDebugInfo()
   206 {
   207 	return this;
   208 }
   209 
   210 int BaseContext::Execute(uint32_t code_addr, cell_t *result)
   211 {
   212 	if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
   213 	{
   214 		return SP_ERROR_NOT_RUNNABLE;
   215 	}
   216 
   217 	/* tada, prevent a crash */
   218 	cell_t _ignore_result;
   219 	if (!result)
   220 	{
   221 		result = &_ignore_result;
   222 	}
   223 
   224 	IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase;
   225 
   226 	uint32_t pushcount = ctx->pushcount;
   227 	int err;
   228 
   229 	PushCell(pushcount++);
   230 	ctx->pushcount = 0;
   231 
   232 	cell_t save_sp = ctx->sp;
   233 	cell_t save_hp = ctx->hp;
   234 	uint32_t n_idx = ctx->n_idx;
   235 
   236 	bool wasExec = m_InExec;
   237 
   238 	/* Clear the error state, if any */
   239 	ctx->n_err = SP_ERROR_NONE;
   240 	ctx->n_idx = 0;
   241 	m_InExec = true;
   242 	m_MsgCache[0] = '\0';
   243 	m_CustomMsg = false;
   244 
   245 	g_SourcePawn.PushTracer(ctx);
   246 
   247 	err = vm->ContextExecute(ctx, code_addr, result);
   248 
   249 	m_InExec = wasExec;
   250 
   251 	/**
   252 	 * :TODO: Calling from a plugin in here will erase the cached message...
   253 	 * Should that be documented?
   254 	 */
   255 	g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL);
   256 
   257 #if 1//defined _DEBUG
   258 	//:TODO: debug code for leak detection, remove before the release?
   259 	if (err == SP_ERROR_NONE)
   260 	{
   261 		if ((ctx->sp - (cell_t)(pushcount * sizeof(cell_t))) != save_sp)
   262 		{
   263 			const char *name;
   264 			ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
   265 			g_Logger.LogError("Stack leak detected: sp:%d should be %d on function %s", ctx->sp, save_sp, name);
   266 		}
   267 		if (ctx->hp != save_hp)
   268 		{
   269 			const char *name;
   270 			ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
   271 			g_Logger.LogError("Heap leak detected: hp:%d should be %d on function %s", ctx->hp, save_hp, name);
   272 		}
   273 		//assert(ctx->sp - pushcount * sizeof(cell_t) == save_sp);
   274 		//assert(ctx->hp == save_hp);
   275 	}
   276 #endif
   277 	if (err != SP_ERROR_NONE)
   278 	{
   279 		ctx->sp = save_sp;
   280 		ctx->hp = save_hp;
   281 	}
   282 
   283 	ctx->n_idx = n_idx;
   284 
   285 	return err;
   286 }
   287 
   288 void BaseContext::SetErrorMessage(const char *msg, va_list ap)
   289 {
   290 	m_CustomMsg = true;
   291 
   292 	vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
   293 }
   294 
   295 cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
   296 {
   297 	if (!m_InExec)
   298 	{
   299 		return 0;
   300 	}
   301 
   302 	ctx->n_err = error;
   303 	
   304 	if (msg)
   305 	{
   306 		va_list ap;
   307 		va_start(ap, msg);
   308 		SetErrorMessage(msg, ap);
   309 		va_end(ap);
   310 	}
   311 
   312 	return 0;
   313 }
   314 
   315 cell_t BaseContext::ThrowNativeError(const char *msg, ...)
   316 {
   317 	if (!m_InExec)
   318 	{
   319 		return 0;
   320 	}
   321 
   322 	ctx->n_err = SP_ERROR_NATIVE;
   323 
   324 	if (msg)
   325 	{
   326 		va_list ap;
   327 		va_start(ap, msg);
   328 		SetErrorMessage(msg, ap);
   329 		va_end(ap);
   330 	}
   331 
   332 	return 0;
   333 }
   334 
   335 int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
   336 {
   337 	cell_t *addr;
   338 	ucell_t realmem;
   339 
   340 #if 0
   341 	if (cells > CELLBOUNDMAX)
   342 	{
   343 		return SP_ERROR_ARAM;
   344 	}
   345 #else
   346 	assert(cells < CELLBOUNDMAX);
   347 #endif
   348 
   349 	realmem = cells * sizeof(cell_t);
   350 
   351 	/**
   352 	 * Check if the space between the heap and stack is sufficient.
   353 	 */
   354 	if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN)
   355 	{
   356 		return SP_ERROR_HEAPLOW;
   357 	}
   358 
   359 	addr = (cell_t *)(ctx->memory + ctx->hp);
   360 	/* store size of allocation in cells */
   361 	*addr = (cell_t)cells;
   362 	addr++;
   363 	ctx->hp += sizeof(cell_t);
   364 
   365 	*local_addr = ctx->hp;
   366 
   367 	if (phys_addr)
   368 	{
   369 		*phys_addr = addr;
   370 	}
   371 
   372 	ctx->hp += realmem;
   373 
   374 	return SP_ERROR_NONE;
   375 }
   376 
   377 int BaseContext::HeapPop(cell_t local_addr)
   378 {
   379 	cell_t cellcount;
   380 	cell_t *addr;
   381 
   382 	/* check the bounds of this address */
   383 	local_addr -= sizeof(cell_t);
   384 	if (local_addr < ctx->heap_base || local_addr >= ctx->sp)
   385 	{
   386 		return SP_ERROR_INVALID_ADDRESS;
   387 	}
   388 
   389 	addr = (cell_t *)(ctx->memory + local_addr);
   390 	cellcount = (*addr) * sizeof(cell_t);
   391 	/* check if this memory count looks valid */
   392 	if ((signed)(ctx->hp - cellcount - sizeof(cell_t)) != local_addr)
   393 	{
   394 		return SP_ERROR_INVALID_ADDRESS;
   395 	}
   396 
   397 	ctx->hp = local_addr;
   398 
   399 	return SP_ERROR_NONE;
   400 }
   401 
   402 
   403 int BaseContext::HeapRelease(cell_t local_addr)
   404 {
   405 	if (local_addr < ctx->heap_base)
   406 	{
   407 		return SP_ERROR_INVALID_ADDRESS;
   408 	}
   409 
   410 	ctx->hp = local_addr - sizeof(cell_t);
   411 
   412 	return SP_ERROR_NONE;
   413 }
   414 
   415 int BaseContext::FindNativeByName(const char *name, uint32_t *index)
   416 {
   417 	int high;
   418 
   419 	high = ctx->plugin->info.natives_num - 1;
   420 
   421 	for (uint32_t i=0; i<ctx->plugin->info.natives_num; i++)
   422 	{
   423 		if (strcmp(ctx->natives[i].name, name) == 0)
   424 		{
   425 			if (index)
   426 			{
   427 				*index = i;
   428 			}
   429 			return SP_ERROR_NONE;
   430 		}
   431 	}
   432 
   433 	return SP_ERROR_NOT_FOUND;
   434 }
   435 
   436 int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native)
   437 {
   438 	if (index >= ctx->plugin->info.natives_num)
   439 	{
   440 		return SP_ERROR_INDEX;
   441 	}
   442 
   443 	if (native)
   444 	{
   445 		*native = &(ctx->natives[index]);
   446 	}
   447 
   448 	return SP_ERROR_NONE;
   449 }
   450 
   451 
   452 uint32_t BaseContext::GetNativesNum()
   453 {
   454 	return ctx->plugin->info.natives_num;
   455 }
   456 
   457 int BaseContext::FindPublicByName(const char *name, uint32_t *index)
   458 {
   459 	int diff, high, low;
   460 	uint32_t mid;
   461 
   462 	high = ctx->plugin->info.publics_num - 1;
   463 	low = 0;
   464 
   465 	while (low <= high)
   466 	{
   467 		mid = (low + high) / 2;
   468 		diff = strcmp(ctx->publics[mid].name, name);
   469 		if (diff == 0)
   470 		{
   471 			if (index)
   472 			{
   473 				*index = mid;
   474 			}
   475 			return SP_ERROR_NONE;
   476 		} else if (diff < 0) {
   477 			low = mid + 1;
   478 		} else {
   479 			high = mid - 1;
   480 		}
   481 	}
   482 
   483 	return SP_ERROR_NOT_FOUND;
   484 }
   485 
   486 int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
   487 {
   488 	if (index >= ctx->plugin->info.publics_num)
   489 	{
   490 		return SP_ERROR_INDEX;
   491 	}
   492 
   493 	if (pblic)
   494 	{
   495 		*pblic = &(ctx->publics[index]);
   496 	}
   497 
   498 	return SP_ERROR_NONE;
   499 }
   500 
   501 uint32_t BaseContext::GetPublicsNum()
   502 {
   503 	return ctx->plugin->info.publics_num;
   504 }
   505 
   506 int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
   507 {
   508 	if (index >= ctx->plugin->info.pubvars_num)
   509 	{
   510 		return SP_ERROR_INDEX;
   511 	}
   512 
   513 	if (pubvar)
   514 	{
   515 		*pubvar = &(ctx->pubvars[index]);
   516 	}
   517 
   518 	return SP_ERROR_NONE;
   519 }
   520 
   521 int BaseContext::FindPubvarByName(const char *name, uint32_t *index)
   522 {
   523 	int diff, high, low;
   524 	uint32_t mid;
   525 
   526 	high = ctx->plugin->info.pubvars_num - 1;
   527 	low = 0;
   528 
   529 	while (low <= high)
   530 	{
   531 		mid = (low + high) / 2;
   532 		diff = strcmp(ctx->pubvars[mid].name, name);
   533 		if (diff == 0)
   534 		{
   535 			if (index)
   536 			{
   537 				*index = mid;
   538 			}
   539 			return SP_ERROR_NONE;
   540 		} else if (diff < 0) {
   541 			low = mid + 1;
   542 		} else {
   543 			high = mid - 1;
   544 		}
   545 	}
   546 
   547 	return SP_ERROR_NOT_FOUND;
   548 }
   549 
   550 int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
   551 {
   552 	if (index >= ctx->plugin->info.pubvars_num)
   553 	{
   554 		return SP_ERROR_INDEX;
   555 	}
   556 
   557 	*local_addr = ctx->plugin->info.pubvars[index].address;
   558 	*phys_addr = ctx->pubvars[index].offs;
   559 
   560 	return SP_ERROR_NONE;
   561 }
   562 
   563 uint32_t BaseContext::GetPubVarsNum()
   564 {
   565 	return ctx->plugin->info.pubvars_num;
   566 }
   567 
   568 int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite)
   569 {
   570 	uint32_t i, j, max;
   571 
   572 	max = ctx->plugin->info.natives_num;
   573 
   574 	for (i=0; i<max; i++)
   575 	{
   576 		if ((ctx->natives[i].status == SP_NATIVE_BOUND) && !overwrite)
   577 		{
   578 			continue;
   579 		}
   580 
   581 		for (j=0; (natives[j].name) && (!num || j<num); j++)
   582 		{
   583 			if (!strcmp(ctx->natives[i].name, natives[j].name))
   584 			{
   585 				ctx->natives[i].pfn = natives[j].func;
   586 				ctx->natives[i].status = SP_NATIVE_BOUND;
   587 			}
   588 		}
   589 	}
   590 
   591 	return SP_ERROR_NONE;
   592 }
   593 
   594 int BaseContext::BindNative(const sp_nativeinfo_t *native)
   595 {
   596 	uint32_t index;
   597 	int err;
   598 
   599 	if ((err = FindNativeByName(native->name, &index)) != SP_ERROR_NONE)
   600 	{
   601 		return err;
   602 	}
   603 
   604 	ctx->natives[index].pfn = native->func;
   605 	ctx->natives[index].status = SP_NATIVE_BOUND;
   606 
   607 	return SP_ERROR_NONE;
   608 }
   609 
   610 int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
   611 {
   612 	uint32_t nativesnum, i;
   613 
   614 	nativesnum = ctx->plugin->info.natives_num;
   615 
   616 	for (i=0; i<nativesnum; i++)
   617 	{
   618 		if (ctx->natives[i].status == SP_NATIVE_UNBOUND)
   619 		{
   620 			ctx->natives[i].pfn = native;
   621 			ctx->natives[i].status = SP_NATIVE_BOUND;
   622 		}
   623 	}
   624 
   625 	return SP_ERROR_NONE;
   626 }
   627 
   628 int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
   629 {
   630 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   631 	{
   632 		return SP_ERROR_INVALID_ADDRESS;
   633 	}
   634 
   635 	if (phys_addr)
   636 	{
   637 		*phys_addr = (cell_t *)(ctx->memory + local_addr);
   638 	}
   639 
   640 	return SP_ERROR_NONE;
   641 }
   642 
   643 int BaseContext::PushCell(cell_t value)
   644 {
   645 	if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t)))
   646 	{
   647 		return SP_ERROR_STACKLOW;
   648 	}
   649 
   650 	ctx->sp -= sizeof(cell_t);
   651 	*(cell_t *)(ctx->memory + ctx->sp) = value;
   652 	ctx->pushcount++;
   653 
   654 	return SP_ERROR_NONE;
   655 }
   656 
   657 int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
   658 {
   659 	unsigned int i;
   660 	int err;
   661 
   662 	for (i=0; i<numcells; i++)
   663 	{
   664 		if ((err = PushCell(array[i])) != SP_ERROR_NONE)
   665 		{
   666 			ctx->sp += (cell_t)(i * sizeof(cell_t));
   667 			ctx->pushcount -= i;
   668 			return err;
   669 		}
   670 	}
   671 
   672 	return SP_ERROR_NONE;
   673 }
   674 
   675 int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
   676 {
   677 	cell_t *ph_addr;
   678 	int err;
   679 
   680 	if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE)
   681 	{
   682 		return err;
   683 	}
   684 
   685 	memcpy(ph_addr, array, numcells * sizeof(cell_t));
   686 
   687 	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
   688 	{
   689 		HeapRelease(*local_addr);
   690 		return err;
   691 	}
   692 
   693 	if (phys_addr)
   694 	{
   695 		*phys_addr = ph_addr;
   696 	}
   697 
   698 	return SP_ERROR_NONE;
   699 }
   700 
   701 int BaseContext::LocalToString(cell_t local_addr, char **addr)
   702 {
   703 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   704 	{
   705 		return SP_ERROR_INVALID_ADDRESS;
   706 	}
   707 	*addr = (char *)(ctx->memory + local_addr);
   708 
   709 	return SP_ERROR_NONE;
   710 }
   711 
   712 int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
   713 {
   714 	char *ph_addr;
   715 	int err;
   716 	unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t);
   717 
   718 	if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE)
   719 	{
   720 		return err;
   721 	}
   722 
   723 	memcpy(ph_addr, string, len);
   724 	ph_addr[len] = '\0';
   725 
   726 	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
   727 	{
   728 		HeapRelease(*local_addr);
   729 		return err;
   730 	}
   731 
   732 	if (phys_addr)
   733 	{
   734 		*phys_addr = ph_addr;
   735 	}
   736 
   737 	return SP_ERROR_NONE;
   738 }
   739 
   740 int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
   741 {
   742 	char *dest;
   743 	size_t len;
   744 
   745 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   746 	{
   747 		return SP_ERROR_INVALID_ADDRESS;
   748 	}
   749 
   750 	if (bytes == 0)
   751 	{
   752 		return SP_ERROR_NONE;
   753 	}
   754 
   755 	len = strlen(source);
   756 	dest = (char *)(ctx->memory + local_addr);
   757 
   758 	if (len >= bytes)
   759 	{
   760 		len = bytes - 1;
   761 	}
   762 
   763 	memcpy(dest, source, len);
   764 	dest[len] = '\0';
   765 
   766 	return SP_ERROR_NONE;
   767 }
   768 
   769 inline int __CheckValidChar(char *c)
   770 {
   771 	int count;
   772 	int bytecount = 0;
   773 
   774 	for (count=1; (*c & 0xC0) == 0x80; count++)
   775 	{
   776 		c--;
   777 	}
   778 
   779 	switch (*c & 0xF0)
   780 	{
   781 	case 0xC0:
   782 	case 0xD0:
   783 		{
   784 			bytecount = 2;
   785 			break;
   786 		}
   787 	case 0xE0:
   788 		{
   789 			bytecount = 3;
   790 			break;
   791 		}
   792 	case 0xF0:
   793 		{
   794 			bytecount = 4;
   795 			break;
   796 		}
   797 	}
   798 
   799 	if (bytecount != count)
   800 	{
   801 		return count;
   802 	}
   803 
   804 	return 0;
   805 }
   806 
   807 int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
   808 {
   809 	char *dest;
   810 	size_t len;
   811 	bool needtocheck = false;
   812 
   813 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   814 	{
   815 		return SP_ERROR_INVALID_ADDRESS;
   816 	}
   817 	
   818 	if (maxbytes == 0)
   819 	{
   820 		return SP_ERROR_NONE;
   821 	}
   822 
   823 	len = strlen(source);
   824 	dest = (char *)(ctx->memory + local_addr);
   825 
   826 	if ((size_t)len >= maxbytes)
   827 	{
   828 		len = maxbytes - 1;
   829 		needtocheck = true;
   830 	}
   831 
   832 	memcpy(dest, source, len);
   833 	if ((dest[len-1] & 1<<7) && needtocheck)
   834 	{
   835 		len -= __CheckValidChar(dest+len-1);
   836 	}
   837 	dest[len] = '\0';
   838 
   839 	if (wrtnbytes)
   840 	{
   841 		*wrtnbytes = len;
   842 	}
   843 
   844 	return SP_ERROR_NONE;
   845 }
   846 
   847 #define USHR(x) ((unsigned int)(x)>>1)
   848 
   849 int BaseContext::LookupFile(ucell_t addr, const char **filename)
   850 {
   851 	int high, low, mid;
   852 
   853 	high = ctx->plugin->debug.files_num;
   854 	low = -1;
   855 
   856 	while (high - low > 1)
   857 	{
   858 		mid = USHR(low + high);
   859 		if (ctx->files[mid].addr <= addr)
   860 		{
   861 			low = mid;
   862 		} else {
   863 			high = mid;
   864 		}
   865 	}
   866 
   867 	if (low == -1)
   868 	{
   869 		return SP_ERROR_NOT_FOUND;
   870 	}
   871 
   872 	*filename = ctx->files[low].name;
   873 
   874 	return SP_ERROR_NONE;
   875 }
   876 
   877 int BaseContext::LookupFunction(ucell_t addr, const char **name)
   878 {
   879 	uint32_t iter, max = ctx->plugin->debug.syms_num;
   880 
   881 	for (iter=0; iter<max; iter++)
   882 	{
   883 		if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION) 
   884 			&& (ctx->symbols[iter].codestart <= addr) 
   885 			&& (ctx->symbols[iter].codeend > addr))
   886 		{
   887 			break;
   888 		}
   889 	}
   890 
   891 	if (iter >= max)
   892 	{
   893 		return SP_ERROR_NOT_FOUND;
   894 	}
   895 
   896 	*name = ctx->symbols[iter].name;
   897 
   898 	return SP_ERROR_NONE;
   899 }
   900 
   901 int BaseContext::LookupLine(ucell_t addr, uint32_t *line)
   902 {
   903 	int high, low, mid;
   904 
   905 	high = ctx->plugin->debug.lines_num;
   906 	low = -1;
   907 
   908 	while (high - low > 1)
   909 	{
   910 		mid = USHR(low + high);
   911 		if (ctx->lines[mid].addr <= addr)
   912 		{
   913 			low = mid;
   914 		} else {
   915 			high = mid;
   916 		}
   917 	}
   918 
   919 	if (low == -1)
   920 	{
   921 		return SP_ERROR_NOT_FOUND;
   922 	}
   923 
   924 	/* Since the CIP occurs BEFORE the line, we have to add one */
   925 	*line = ctx->lines[low].line + 1;
   926 
   927 	return SP_ERROR_NONE;
   928 }
   929 
   930 IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
   931 {
   932 	CFunction *pFunc = NULL;
   933 
   934 	if (func_id & 1)
   935 	{
   936 		func_id >>= 1;
   937 		if (func_id >= ctx->plugin->info.publics_num)
   938 		{
   939 			return NULL;
   940 		}
   941 		pFunc = m_pub_funcs[func_id];
   942 		if (!pFunc)
   943 		{
   944 			m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this);
   945 			pFunc = m_pub_funcs[func_id];
   946 		} else if (pFunc->IsInvalidated()) {
   947 			pFunc->Set(ctx->publics[func_id].code_offs, this);
   948 		}
   949 	} else {
   950 		/* :TODO: currently not used */
   951 #if 0
   952 		func_id >>= 1;
   953 		unsigned int index;
   954 		if (!g_pVM->FunctionLookup(ctx, func_id, &index))
   955 		{
   956 			return NULL;
   957 		}
   958 		pFunc = m_priv_funcs[func_id];
   959 		if (!pFunc)
   960 		{
   961 			m_priv_funcs[func_id] = new CFunction(save, this);
   962 			pFunc = m_priv_funcs[func_id];
   963 		}
   964 #endif
   965 		assert(false);
   966 	}
   967 
   968 	return pFunc;
   969 }
   970 
   971 IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
   972 {
   973 	uint32_t index;
   974 
   975 	if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
   976 	{
   977 		return NULL;
   978 	}
   979 
   980 	CFunction *pFunc = m_pub_funcs[index];
   981 	if (!pFunc)
   982 	{
   983 		sp_public_t *pub = NULL;
   984 		GetPublicByIndex(index, &pub);
   985 		if (pub)
   986 		{
   987 			m_pub_funcs[index] = new CFunction(pub->code_offs, this);
   988 		}
   989 		pFunc = m_pub_funcs[index];
   990 	} else if (pFunc->IsInvalidated()) {
   991 		sp_public_t *pub = NULL;
   992 		GetPublicByIndex(index, &pub);
   993 		if (pub)
   994 		{
   995 			pFunc->Set(pub->code_offs, this);
   996 		} else {
   997 			pFunc = NULL;
   998 		}
   999 	}
  1000 
  1001 	return pFunc;
  1002 }
  1003 
  1004 int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr)
  1005 {
  1006 	int err;
  1007 	if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE)
  1008 	{
  1009 		return err;
  1010 	}
  1011 
  1012 	if ((cell_t *)*addr == m_pNullString)
  1013 	{
  1014 		*addr = NULL;
  1015 	}
  1016 
  1017 	return SP_ERROR_NONE;
  1018 }
  1019 
  1020 #if defined SOURCEMOD_BUILD
  1021 SourceMod::IdentityToken_t *BaseContext::GetIdentity()
  1022 {
  1023 	return m_pToken;
  1024 }
  1025 
  1026 void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token)
  1027 {
  1028 	m_pToken = token;
  1029 }
  1030 
  1031 cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
  1032 {
  1033 	if (type == SP_NULL_VECTOR)
  1034 	{
  1035 		return m_pNullVec;
  1036 	}
  1037 
  1038 	return NULL;
  1039 }
  1040 #endif