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