commit
						9a37b94f4d
					
				@ -18,7 +18,10 @@ def setup(binary):
 | 
			
		||||
  compiler = binary.compiler
 | 
			
		||||
  compiler.includes += Includes
 | 
			
		||||
  if compiler.vendor == 'gcc' or compiler.vendor == 'clang':
 | 
			
		||||
    compiler.cxxflags += ['-fno-rtti']
 | 
			
		||||
    compiler.cxxflags += [
 | 
			
		||||
      '-fno-rtti',
 | 
			
		||||
      '-Wno-invalid-offsetof',
 | 
			
		||||
    ]
 | 
			
		||||
  elif binary.compiler.vendor == 'msvc':
 | 
			
		||||
    compiler.cxxflags += ['/GR-']
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
@ -16,13 +16,13 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sp_vm_api.h>
 | 
			
		||||
 | 
			
		||||
typedef struct sp_context_s sp_context_t;
 | 
			
		||||
class PluginContext;
 | 
			
		||||
 | 
			
		||||
namespace sp {
 | 
			
		||||
 | 
			
		||||
class Environment;
 | 
			
		||||
 | 
			
		||||
typedef int (*InvokeStubFn)(sp_context_t *ctx, uint8_t *memory, void *code);
 | 
			
		||||
typedef int (*InvokeStubFn)(PluginContext *cx, void *code, cell_t *rval);
 | 
			
		||||
 | 
			
		||||
class CodeStubs
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -20,12 +20,12 @@ using namespace SourcePawn;
 | 
			
		||||
 | 
			
		||||
CContextTrace::CContextTrace(PluginRuntime *pRuntime, int err, const char *errstr, cell_t start_rp) 
 | 
			
		||||
 : m_pRuntime(pRuntime),
 | 
			
		||||
   context_(pRuntime->GetBaseContext()),
 | 
			
		||||
   m_Error(err),
 | 
			
		||||
   m_pMsg(errstr),
 | 
			
		||||
   m_StartRp(start_rp),
 | 
			
		||||
   m_Level(0)
 | 
			
		||||
{
 | 
			
		||||
  m_ctx = pRuntime->m_pCtx->GetCtx();
 | 
			
		||||
  m_pDebug = m_pRuntime->GetDebugInfo();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -65,21 +65,19 @@ CContextTrace::GetTraceInfo(CallStackInfo *trace)
 | 
			
		||||
  cell_t cip;
 | 
			
		||||
 | 
			
		||||
  if (m_Level == 0) {
 | 
			
		||||
    cip = m_ctx->cip;
 | 
			
		||||
  } else if (m_ctx->rp > 0) {
 | 
			
		||||
    cip = context_->cip();
 | 
			
		||||
  } else if (context_->rp() > 0) {
 | 
			
		||||
    /* Entries go from ctx.rp - 1 to m_StartRp */
 | 
			
		||||
    cell_t offs, start, end;
 | 
			
		||||
 | 
			
		||||
    offs = m_Level - 1;
 | 
			
		||||
    start = m_ctx->rp - 1;
 | 
			
		||||
    start = context_->rp() - 1;
 | 
			
		||||
    end = m_StartRp;
 | 
			
		||||
 | 
			
		||||
    if (start - offs < end)
 | 
			
		||||
    {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cip = m_ctx->rstk_cips[start - offs];
 | 
			
		||||
    cip = context_->getReturnStackCip(start - offs);
 | 
			
		||||
  } else {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
@ -106,15 +104,19 @@ CContextTrace::GetTraceInfo(CallStackInfo *trace)
 | 
			
		||||
const char *
 | 
			
		||||
CContextTrace::GetLastNative(uint32_t *index)
 | 
			
		||||
{
 | 
			
		||||
  if (m_ctx->n_err == SP_ERROR_NONE)
 | 
			
		||||
  if (context_->GetLastNativeError() == SP_ERROR_NONE)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  int lastNative = context_->lastNative();
 | 
			
		||||
  if (lastNative < 0)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  sp_native_t *native;
 | 
			
		||||
  if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE)
 | 
			
		||||
  if (m_pRuntime->GetNativeByIndex(lastNative, &native) != SP_ERROR_NONE)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  if (index)
 | 
			
		||||
    *index = m_ctx->n_idx;
 | 
			
		||||
    *index = lastNative;
 | 
			
		||||
 | 
			
		||||
  return native->name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@
 | 
			
		||||
#include <sp_vm_api.h>
 | 
			
		||||
 | 
			
		||||
class PluginRuntime;
 | 
			
		||||
class PluginContext;
 | 
			
		||||
 | 
			
		||||
namespace sp {
 | 
			
		||||
 | 
			
		||||
@ -37,7 +38,7 @@ class CContextTrace : public IContextTrace
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  PluginRuntime *m_pRuntime;
 | 
			
		||||
  sp_context_t *m_ctx;
 | 
			
		||||
  PluginContext *context_;
 | 
			
		||||
  int m_Error;
 | 
			
		||||
  const char *m_pMsg;
 | 
			
		||||
  cell_t m_StartRp;
 | 
			
		||||
 | 
			
		||||
@ -238,17 +238,16 @@ Environment::UnpatchAllJumpsFromTimeout()
 | 
			
		||||
int
 | 
			
		||||
Environment::Invoke(PluginRuntime *runtime, CompiledFunction *fn, cell_t *result)
 | 
			
		||||
{
 | 
			
		||||
  sp_context_t *ctx = runtime->GetBaseContext()->GetCtx();
 | 
			
		||||
  PluginContext *cx = runtime->GetBaseContext();
 | 
			
		||||
 | 
			
		||||
  // Note that cip, hp, sp are saved and restored by Execute2().
 | 
			
		||||
  ctx->cip = fn->GetCodeOffset();
 | 
			
		||||
  *cx->addressOfCip() = fn->GetCodeOffset();
 | 
			
		||||
 | 
			
		||||
  InvokeStubFn invoke = code_stubs_->InvokeStub();
 | 
			
		||||
 | 
			
		||||
  EnterInvoke();
 | 
			
		||||
  int err = invoke(ctx, runtime->plugin()->memory, fn->GetEntryAddress());
 | 
			
		||||
  int err = invoke(cx, fn->GetEntryAddress(), result);
 | 
			
		||||
  LeaveInvoke();
 | 
			
		||||
 | 
			
		||||
  *result = ctx->rval;
 | 
			
		||||
  return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -45,187 +45,34 @@ Write(const sp_plugin_t *plugin, cell_t offset, cell_t value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline cell_t *
 | 
			
		||||
Jump(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t target)
 | 
			
		||||
Jump(const sp_plugin_t *plugin, cell_t target)
 | 
			
		||||
{
 | 
			
		||||
  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
 | 
			
		||||
    ctx->err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size)
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return reinterpret_cast<cell_t *>(plugin->pcode + target);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline cell_t *
 | 
			
		||||
JumpTarget(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *cip, bool cond)
 | 
			
		||||
JumpTarget(const sp_plugin_t *plugin, cell_t *cip, bool cond, int *errp)
 | 
			
		||||
{
 | 
			
		||||
  if (!cond)
 | 
			
		||||
    return cip + 1;
 | 
			
		||||
 | 
			
		||||
  cell_t target = *cip;
 | 
			
		||||
  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
 | 
			
		||||
    ctx->err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
    *errp = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cell_t *next = reinterpret_cast<cell_t *>(plugin->pcode + target);
 | 
			
		||||
  if (next < cip && !Environment::get()->watchdog()->HandleInterrupt()) {
 | 
			
		||||
    ctx->err = SP_ERROR_TIMEOUT;
 | 
			
		||||
    *errp = SP_ERROR_TIMEOUT;
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return next;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool
 | 
			
		||||
CheckAddress(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *stk, cell_t addr)
 | 
			
		||||
{
 | 
			
		||||
  if (uint32_t(addr) >= plugin->mem_size) {
 | 
			
		||||
    ctx->err = SP_ERROR_MEMACCESS;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (addr < ctx->hp)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  if (reinterpret_cast<cell_t *>(plugin->memory + addr) < stk) {
 | 
			
		||||
    ctx->err = SP_ERROR_MEMACCESS;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
PopTrackerAndSetHeap(PluginRuntime *rt)
 | 
			
		||||
{
 | 
			
		||||
  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
 | 
			
		||||
  tracker_t *trk = ctx->tracker;
 | 
			
		||||
  assert(trk->pCur > trk->pBase);
 | 
			
		||||
 | 
			
		||||
  trk->pCur--;
 | 
			
		||||
  if (trk->pCur < trk->pBase)
 | 
			
		||||
    return SP_ERROR_TRACKER_BOUNDS;
 | 
			
		||||
 | 
			
		||||
  ucell_t amt = *trk->pCur;
 | 
			
		||||
  if (amt > (ctx->hp - rt->plugin()->data_size))
 | 
			
		||||
    return SP_ERROR_HEAPMIN;
 | 
			
		||||
 | 
			
		||||
  ctx->hp -= amt;
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
PushTracker(sp_context_t *ctx, size_t amount)
 | 
			
		||||
{
 | 
			
		||||
  tracker_t *trk = ctx->tracker;
 | 
			
		||||
 | 
			
		||||
  if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
 | 
			
		||||
    return SP_ERROR_TRACKER_BOUNDS;
 | 
			
		||||
 | 
			
		||||
  if (trk->pCur + 1 - (trk->pBase + trk->size) == 0) {
 | 
			
		||||
    size_t disp = trk->size - 1;
 | 
			
		||||
    trk->size *= 2;
 | 
			
		||||
    trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
 | 
			
		||||
 | 
			
		||||
    if (!trk->pBase)
 | 
			
		||||
      return SP_ERROR_TRACKER_BOUNDS;
 | 
			
		||||
 | 
			
		||||
    trk->pCur = trk->pBase + disp;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  *trk->pCur++ = amount;
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell_t
 | 
			
		||||
NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
  cell_t save_sp = ctx->sp;
 | 
			
		||||
  cell_t save_hp = ctx->hp;
 | 
			
		||||
 | 
			
		||||
  ctx->n_idx = native_idx;
 | 
			
		||||
 | 
			
		||||
  sp_native_t *native = &ctx->plugin->natives[native_idx];
 | 
			
		||||
 | 
			
		||||
  if (native->status == SP_NATIVE_UNBOUND) {
 | 
			
		||||
    ctx->n_err = SP_ERROR_INVALID_NATIVE;
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cell_t result = native->pfn(ctx->basecx, params);
 | 
			
		||||
 | 
			
		||||
  if (ctx->n_err != SP_ERROR_NONE)
 | 
			
		||||
    return result;
 | 
			
		||||
 | 
			
		||||
  if (save_sp != ctx->sp) {
 | 
			
		||||
    ctx->n_err = SP_ERROR_STACKLEAK;
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
  if (save_hp != ctx->hp) {
 | 
			
		||||
    ctx->n_err = SP_ERROR_HEAPLEAK;
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell_t
 | 
			
		||||
BoundNativeCallback(sp_context_t *ctx, SPVM_NATIVE_FUNC pfn, cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
  cell_t save_sp = ctx->sp;
 | 
			
		||||
  cell_t save_hp = ctx->hp;
 | 
			
		||||
 | 
			
		||||
  cell_t result = pfn(ctx->basecx, params);
 | 
			
		||||
 | 
			
		||||
  if (ctx->n_err != SP_ERROR_NONE)
 | 
			
		||||
    return result;
 | 
			
		||||
 | 
			
		||||
  if (save_sp != ctx->sp) {
 | 
			
		||||
    ctx->n_err = SP_ERROR_STACKLEAK;
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
  if (save_hp != ctx->hp) {
 | 
			
		||||
    ctx->n_err = SP_ERROR_HEAPLEAK;
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool
 | 
			
		||||
GenerateArray(PluginRuntime *rt, sp_context_t *ctx, cell_t dims, cell_t *stk, bool autozero)
 | 
			
		||||
{
 | 
			
		||||
  if (dims == 1) {
 | 
			
		||||
    uint32_t size = *stk;
 | 
			
		||||
    if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) {
 | 
			
		||||
      ctx->err = SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    *stk = ctx->hp;
 | 
			
		||||
 | 
			
		||||
    uint32_t bytes = size * 4;
 | 
			
		||||
 | 
			
		||||
    ctx->hp += bytes;
 | 
			
		||||
    if (uintptr_t(ctx->plugin->memory + ctx->hp) >= uintptr_t(stk)) {
 | 
			
		||||
      ctx->err = SP_ERROR_HEAPLOW;
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((ctx->err = PushTracker(ctx, bytes)) != SP_ERROR_NONE)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if (autozero)
 | 
			
		||||
      memset(ctx->plugin->memory + ctx->hp, 0, bytes);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((ctx->err = GenerateFullArray(rt, dims, stk, autozero)) != SP_ERROR_NONE)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
{
 | 
			
		||||
@ -236,21 +83,25 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
  if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size)
 | 
			
		||||
    return SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
 | 
			
		||||
  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
 | 
			
		||||
  ctx->err = SP_ERROR_NONE;
 | 
			
		||||
  PluginContext *cx = rt->GetBaseContext();
 | 
			
		||||
 | 
			
		||||
  int err = SP_ERROR_NONE;
 | 
			
		||||
 | 
			
		||||
  // Save the original frm. BaseContext won't, and if we error, we won't hit
 | 
			
		||||
  // the stack unwinding code.
 | 
			
		||||
  cell_t orig_frm = ctx->frm;
 | 
			
		||||
  cell_t orig_frm = cx->frm();
 | 
			
		||||
 | 
			
		||||
  cell_t &frm = *cx->addressOfFrm();
 | 
			
		||||
  cell_t &sp = *cx->addressOfSp();
 | 
			
		||||
 | 
			
		||||
  cell_t pri = 0;
 | 
			
		||||
  cell_t alt = 0;
 | 
			
		||||
  cell_t *cip = code + (aCodeStart / 4);
 | 
			
		||||
  cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
 | 
			
		||||
  cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + sp);
 | 
			
		||||
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    if (cip >= codeend) {
 | 
			
		||||
      ctx->err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
      err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
      goto error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -281,7 +132,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_ZERO_S:
 | 
			
		||||
        Write(plugin, ctx->frm + *cip++, 0);
 | 
			
		||||
        Write(plugin, frm + *cip++, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_PUSH_PRI:
 | 
			
		||||
@ -321,7 +172,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
        int i = 1;
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
          cell_t addr = ctx->frm + *cip++;
 | 
			
		||||
          cell_t addr = frm + *cip++;
 | 
			
		||||
          *--stk = addr;
 | 
			
		||||
        } while (i++ < n);
 | 
			
		||||
        break;
 | 
			
		||||
@ -339,7 +190,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
 | 
			
		||||
        int i = 1;
 | 
			
		||||
        do {
 | 
			
		||||
          cell_t value = Read(plugin, ctx->frm + *cip++);
 | 
			
		||||
          cell_t value = Read(plugin, frm + *cip++);
 | 
			
		||||
          *--stk = value;
 | 
			
		||||
        } while (i++ < n);
 | 
			
		||||
        break;
 | 
			
		||||
@ -384,9 +235,9 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
 | 
			
		||||
      case OP_PROC:
 | 
			
		||||
      {
 | 
			
		||||
        *--stk = ctx->frm;
 | 
			
		||||
        *--stk = frm;
 | 
			
		||||
        *--stk = 0;
 | 
			
		||||
        ctx->frm = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
        frm = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -505,14 +356,16 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      case OP_INC_S:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t value = Read(plugin, ctx->frm + offset);
 | 
			
		||||
        Write(plugin, ctx->frm + offset, value + 1);
 | 
			
		||||
        cell_t value = Read(plugin, frm + offset);
 | 
			
		||||
        Write(plugin, frm + offset, value + 1);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_INC_I:
 | 
			
		||||
        if (!CheckAddress(plugin, ctx, stk, pri))
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        Write(plugin, pri, Read(plugin, pri) + 1);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
@ -533,14 +386,16 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      case OP_DEC_S:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t value = Read(plugin, ctx->frm + offset);
 | 
			
		||||
        Write(plugin, ctx->frm + offset, value - 1);
 | 
			
		||||
        cell_t value = Read(plugin, frm + offset);
 | 
			
		||||
        Write(plugin, frm + offset, value - 1);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_DEC_I:
 | 
			
		||||
        if (!CheckAddress(plugin, ctx, stk, pri))
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        Write(plugin, pri, Read(plugin, pri) - 1);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
@ -552,27 +407,27 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_LOAD_S_PRI:
 | 
			
		||||
        pri = Read(plugin, ctx->frm + *cip++);
 | 
			
		||||
        pri = Read(plugin, frm + *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_LOAD_S_ALT:
 | 
			
		||||
        alt = Read(plugin, ctx->frm + *cip++);
 | 
			
		||||
        alt = Read(plugin, frm + *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_LOAD_S_BOTH:
 | 
			
		||||
        pri = Read(plugin, ctx->frm + *cip++);
 | 
			
		||||
        alt = Read(plugin, ctx->frm + *cip++);
 | 
			
		||||
        pri = Read(plugin, frm + *cip++);
 | 
			
		||||
        alt = Read(plugin, frm + *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_LREF_S_PRI:
 | 
			
		||||
      {
 | 
			
		||||
        pri = Read(plugin, ctx->frm + *cip++);
 | 
			
		||||
        pri = Read(plugin, frm + *cip++);
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_LREF_S_ALT:
 | 
			
		||||
      {
 | 
			
		||||
        alt = Read(plugin, ctx->frm + *cip++);
 | 
			
		||||
        alt = Read(plugin, frm + *cip++);
 | 
			
		||||
        alt = Read(plugin, alt);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
@ -585,10 +440,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_ADDR_PRI:
 | 
			
		||||
        pri = ctx->frm + *cip++;
 | 
			
		||||
        pri = frm + *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_ADDR_ALT:
 | 
			
		||||
        alt = ctx->frm + *cip++;
 | 
			
		||||
        alt = frm + *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_STOR_PRI:
 | 
			
		||||
@ -599,10 +454,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_STOR_S_PRI:
 | 
			
		||||
        Write(plugin, ctx->frm + *cip++, pri);
 | 
			
		||||
        Write(plugin, frm + *cip++, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_STOR_S_ALT:
 | 
			
		||||
        Write(plugin, ctx->frm +*cip++, alt);
 | 
			
		||||
        Write(plugin, frm +*cip++, alt);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_IDXADDR:
 | 
			
		||||
@ -612,7 +467,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      case OP_SREF_S_PRI:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t addr = Read(plugin, ctx->frm + offset);
 | 
			
		||||
        cell_t addr = Read(plugin, frm + offset);
 | 
			
		||||
        Write(plugin, addr, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
@ -620,7 +475,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      case OP_SREF_S_ALT:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t addr = Read(plugin, ctx->frm + offset);
 | 
			
		||||
        cell_t addr = Read(plugin, frm + offset);
 | 
			
		||||
        Write(plugin, addr, alt);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
@ -644,8 +499,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
 | 
			
		||||
      case OP_LIDX:
 | 
			
		||||
        pri = alt + pri * 4;
 | 
			
		||||
        if (!CheckAddress(plugin, ctx, stk, pri))
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
@ -653,8 +510,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      {
 | 
			
		||||
        cell_t val = *cip++;
 | 
			
		||||
        pri = alt + (pri << val);
 | 
			
		||||
        if (!CheckAddress(plugin, ctx, stk, pri))
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
@ -671,19 +530,23 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t value = *cip++;
 | 
			
		||||
        Write(plugin, ctx->frm + offset, value);
 | 
			
		||||
        Write(plugin, frm + offset, value);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_LOAD_I:
 | 
			
		||||
        if (!CheckAddress(plugin, ctx, stk, pri))
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_STOR_I:
 | 
			
		||||
        if (!CheckAddress(plugin, ctx, stk, alt))
 | 
			
		||||
        if (!cx->checkAddress(stk, alt)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        Write(plugin, alt, pri);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
@ -693,11 +556,11 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
        cell_t dividend = (op == OP_SDIV) ? pri : alt;
 | 
			
		||||
        cell_t divisor = (op == OP_SDIV) ? alt : pri;
 | 
			
		||||
        if (divisor == 0) {
 | 
			
		||||
          ctx->err = SP_ERROR_DIVIDE_BY_ZERO;
 | 
			
		||||
          err = SP_ERROR_DIVIDE_BY_ZERO;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        if (dividend == INT_MIN && divisor == -1) {
 | 
			
		||||
          ctx->err = SP_ERROR_INTEGER_OVERFLOW;
 | 
			
		||||
          err = SP_ERROR_INTEGER_OVERFLOW;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = dividend / divisor;
 | 
			
		||||
@ -708,8 +571,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      case OP_LODB_I:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t val = *cip++;
 | 
			
		||||
        if (!CheckAddress(plugin, ctx, stk, pri))
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        if (val == 1)
 | 
			
		||||
          pri &= 0xff;
 | 
			
		||||
@ -721,8 +586,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      case OP_STRB_I:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t val = *cip++;
 | 
			
		||||
        if (!CheckAddress(plugin, ctx, stk, alt))
 | 
			
		||||
        if (!cx->checkAddress(stk, alt)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        if (val == 1)
 | 
			
		||||
          *reinterpret_cast<int8_t *>(plugin->memory + alt) = pri;
 | 
			
		||||
        else if (val == 2)
 | 
			
		||||
@ -735,10 +602,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      case OP_RETN:
 | 
			
		||||
      {
 | 
			
		||||
        stk++;
 | 
			
		||||
        ctx->frm = *stk++;
 | 
			
		||||
        frm = *stk++;
 | 
			
		||||
        stk += *stk + 1;
 | 
			
		||||
        *rval = pri;
 | 
			
		||||
        ctx->err = SP_ERROR_NONE;
 | 
			
		||||
        err = SP_ERROR_NONE;
 | 
			
		||||
        goto done;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -766,19 +633,19 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      {
 | 
			
		||||
        cell_t amount = *cip++;
 | 
			
		||||
        if (!IsValidOffset(amount)) {
 | 
			
		||||
          ctx->err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
          err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stk += amount / 4;
 | 
			
		||||
        if (amount > 0) {
 | 
			
		||||
          if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) {
 | 
			
		||||
            ctx->err = SP_ERROR_STACKMIN;
 | 
			
		||||
            err = SP_ERROR_STACKMIN;
 | 
			
		||||
            goto error;
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          if (uintptr_t(stk) < uintptr_t(plugin->memory + ctx->hp + STACK_MARGIN)) {
 | 
			
		||||
            ctx->err = SP_ERROR_STACKLOW;
 | 
			
		||||
          if (uintptr_t(stk) < uintptr_t(plugin->memory + cx->hp() + STACK_MARGIN)) {
 | 
			
		||||
            err = SP_ERROR_STACKLOW;
 | 
			
		||||
            goto error;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
@ -789,17 +656,17 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      {
 | 
			
		||||
        cell_t amount = *cip++;
 | 
			
		||||
 | 
			
		||||
        alt = ctx->hp;
 | 
			
		||||
        ctx->hp += amount;
 | 
			
		||||
        alt = cx->hp();
 | 
			
		||||
        *cx->addressOfHp() += amount;
 | 
			
		||||
 | 
			
		||||
        if (amount > 0) {
 | 
			
		||||
          if (uintptr_t(plugin->memory + ctx->hp) > uintptr_t(stk)) {
 | 
			
		||||
            ctx->err = SP_ERROR_HEAPLOW;
 | 
			
		||||
          if (uintptr_t(plugin->memory + cx->hp()) > uintptr_t(stk)) {
 | 
			
		||||
            err = SP_ERROR_HEAPLOW;
 | 
			
		||||
            goto error;
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          if (uint32_t(ctx->hp) < plugin->data_size) {
 | 
			
		||||
            ctx->err = SP_ERROR_HEAPMIN;
 | 
			
		||||
          if (uint32_t(cx->hp()) < plugin->data_size) {
 | 
			
		||||
            err = SP_ERROR_HEAPMIN;
 | 
			
		||||
            goto error;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
@ -807,50 +674,50 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_JUMP:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, true)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, true, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_JZER:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, pri == 0)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri == 0, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JNZ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, pri != 0)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri != 0, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_JEQ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, pri == alt)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri == alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JNEQ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, pri != alt)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri != alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JSLESS:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, pri < alt)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri < alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JSLEQ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, pri <= alt)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri <= alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JSGRTR:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, pri > alt)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri > alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JSGEQ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, ctx, cip, pri >= alt)) == NULL)
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri >= alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_TRACKER_PUSH_C:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t amount = *cip++;
 | 
			
		||||
        int error = PushTracker(ctx, amount * 4);
 | 
			
		||||
        int error = cx->pushTracker(amount * 4);
 | 
			
		||||
        if (error != SP_ERROR_NONE) {
 | 
			
		||||
          ctx->err = error;
 | 
			
		||||
          err = error;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
@ -858,23 +725,23 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
 | 
			
		||||
      case OP_TRACKER_POP_SETHEAP:
 | 
			
		||||
      {
 | 
			
		||||
        int error = PopTrackerAndSetHeap(rt);
 | 
			
		||||
        int error = cx->popTrackerAndSetHeap();
 | 
			
		||||
        if (error != SP_ERROR_NONE) {
 | 
			
		||||
          ctx->err = error;
 | 
			
		||||
          err = error;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_BREAK:
 | 
			
		||||
        ctx->cip = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
 | 
			
		||||
        *cx->addressOfCip() = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_BOUNDS:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t value = *cip++;
 | 
			
		||||
        if (uint32_t(pri) > uint32_t(value)) {
 | 
			
		||||
          ctx->err = SP_ERROR_ARRAY_BOUNDS;
 | 
			
		||||
          err = SP_ERROR_ARRAY_BOUNDS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
@ -885,26 +752,24 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
 | 
			
		||||
        if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) {
 | 
			
		||||
          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ctx->rp >= SP_MAX_RETURN_STACK) {
 | 
			
		||||
          ctx->err = SP_ERROR_STACKLOW;
 | 
			
		||||
          err = SP_ERROR_INSTRUCTION_PARAM;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // For debugging.
 | 
			
		||||
        uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode);
 | 
			
		||||
        ctx->rstk_cips[ctx->rp++] = rcip;
 | 
			
		||||
        ctx->cip = offset;
 | 
			
		||||
        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
        if (!cx->pushReturnCip(rcip)) {
 | 
			
		||||
          err = SP_ERROR_STACKLOW;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        *cx->addressOfCip() = offset;
 | 
			
		||||
        sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
 | 
			
		||||
        int err = Interpret(rt, offset, &pri);
 | 
			
		||||
 | 
			
		||||
        stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
 | 
			
		||||
        ctx->cip = rcip;
 | 
			
		||||
        ctx->rp--;
 | 
			
		||||
        stk = reinterpret_cast<cell_t *>(plugin->memory + sp);
 | 
			
		||||
        *cx->addressOfCip() = rcip;
 | 
			
		||||
        cx->popReturnCip();
 | 
			
		||||
 | 
			
		||||
        if (err != SP_ERROR_NONE)
 | 
			
		||||
          goto error;
 | 
			
		||||
@ -915,7 +780,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
      case OP_GENARRAY_Z:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t val = *cip++;
 | 
			
		||||
        if (!GenerateArray(rt, ctx, val, stk, op == OP_GENARRAY_Z))
 | 
			
		||||
        if ((err = cx->generateArray(val, stk, op == OP_GENARRAY_Z)) != SP_ERROR_NONE)
 | 
			
		||||
          goto error;
 | 
			
		||||
 | 
			
		||||
        stk += (val - 1) * 4;
 | 
			
		||||
@ -928,7 +793,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
        uint32_t native_index = *cip++;
 | 
			
		||||
 | 
			
		||||
        if (native_index >= plugin->num_natives) {
 | 
			
		||||
          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
 | 
			
		||||
          err = SP_ERROR_INSTRUCTION_PARAM;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -938,10 +803,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
          *--stk = num_params;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
        pri = NativeCallback(ctx, native_index, stk);
 | 
			
		||||
        if (ctx->n_err != SP_ERROR_NONE) {
 | 
			
		||||
          ctx->err = ctx->n_err;
 | 
			
		||||
        sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
        pri = cx->invokeNative(native_index, stk);
 | 
			
		||||
        if (cx->GetLastNativeError() != SP_ERROR_NONE) {
 | 
			
		||||
          err = cx->GetLastNativeError();
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -965,26 +830,28 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((cip = Jump(plugin, ctx, target)) == NULL)
 | 
			
		||||
        if ((cip = Jump(plugin, target)) == NULL) {
 | 
			
		||||
          err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
      {
 | 
			
		||||
        ctx->err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
        err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
        goto error;
 | 
			
		||||
      }
 | 
			
		||||
    } // switch
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 done:
 | 
			
		||||
  assert(orig_frm == ctx->frm);
 | 
			
		||||
  ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
  return ctx->err;
 | 
			
		||||
  assert(orig_frm == frm);
 | 
			
		||||
  sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
  return err;
 | 
			
		||||
 | 
			
		||||
 error:
 | 
			
		||||
  ctx->frm = orig_frm;
 | 
			
		||||
  frm = orig_frm;
 | 
			
		||||
  goto done;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,19 +22,6 @@
 | 
			
		||||
#include "plugin-runtime.h"
 | 
			
		||||
#include "plugin-context.h"
 | 
			
		||||
 | 
			
		||||
struct tracker_t
 | 
			
		||||
{
 | 
			
		||||
  size_t size; 
 | 
			
		||||
  ucell_t *pBase; 
 | 
			
		||||
  ucell_t *pCur;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval);
 | 
			
		||||
 | 
			
		||||
int GenerateFullArray(PluginRuntime *rt, uint32_t argc, cell_t *argv, int autozero);
 | 
			
		||||
cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params);
 | 
			
		||||
cell_t BoundNativeCallback(sp_context_t *ctx, SPVM_NATIVE_FUNC pfn, cell_t *params);
 | 
			
		||||
int PopTrackerAndSetHeap(PluginRuntime *rt);
 | 
			
		||||
int PushTracker(sp_context_t *ctx, size_t amount);
 | 
			
		||||
 | 
			
		||||
#endif // _include_sourcepawn_interpreter_h_
 | 
			
		||||
 | 
			
		||||
@ -68,27 +68,8 @@ namespace SourcePawn
 | 
			
		||||
	} sp_plugin_t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct tracker_t;
 | 
			
		||||
class PluginContext;
 | 
			
		||||
 | 
			
		||||
typedef struct sp_context_s
 | 
			
		||||
{
 | 
			
		||||
	cell_t			hp;				/**< Heap pointer */
 | 
			
		||||
	cell_t			sp;				/**< Stack pointer */
 | 
			
		||||
	cell_t			frm;			/**< Frame pointer */
 | 
			
		||||
	cell_t			rval;			/**< Return value from InvokeFunction() */
 | 
			
		||||
	int32_t			cip;			/**< Code pointer last error occurred in */
 | 
			
		||||
	int32_t			err;			/**< Error last set by interpreter */
 | 
			
		||||
	int32_t			n_err;			/**< Error code set by a native */
 | 
			
		||||
	uint32_t		n_idx;			/**< Current native index being executed */
 | 
			
		||||
	tracker_t 		*tracker;
 | 
			
		||||
	sp_plugin_t 	*plugin;
 | 
			
		||||
	PluginContext	*basecx;
 | 
			
		||||
	void *			vm[8];			/**< VM-specific pointers */
 | 
			
		||||
	cell_t			rp;				/**< Return stack pointer */
 | 
			
		||||
	cell_t			rstk_cips[SP_MAX_RETURN_STACK];
 | 
			
		||||
} sp_context_t;
 | 
			
		||||
 | 
			
		||||
//#define SPFLAG_PLUGIN_DEBUG			(1<<0)
 | 
			
		||||
#define SPFLAG_PLUGIN_PAUSED		(1<<1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -51,25 +51,21 @@ PluginContext::PluginContext(PluginRuntime *pRuntime)
 | 
			
		||||
    m_pNullString = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  m_ctx.hp = m_pRuntime->plugin()->data_size;
 | 
			
		||||
  m_ctx.sp = m_pRuntime->plugin()->mem_size - sizeof(cell_t);
 | 
			
		||||
  m_ctx.frm = m_ctx.sp;
 | 
			
		||||
  m_ctx.n_err = SP_ERROR_NONE;
 | 
			
		||||
  m_ctx.n_idx = SP_ERROR_NONE;
 | 
			
		||||
  m_ctx.rp = 0;
 | 
			
		||||
  hp_ = m_pRuntime->plugin()->data_size;
 | 
			
		||||
  sp_ = m_pRuntime->plugin()->mem_size - sizeof(cell_t);
 | 
			
		||||
  frm_ = sp_;
 | 
			
		||||
  rp_ = 0;
 | 
			
		||||
  last_native_ = -1;
 | 
			
		||||
  native_error_ = SP_ERROR_NONE;
 | 
			
		||||
 | 
			
		||||
  m_ctx.tracker = new tracker_t;
 | 
			
		||||
  m_ctx.tracker->pBase = (ucell_t *)malloc(1024);
 | 
			
		||||
  m_ctx.tracker->pCur = m_ctx.tracker->pBase;
 | 
			
		||||
  m_ctx.tracker->size = 1024 / sizeof(cell_t);
 | 
			
		||||
  m_ctx.basecx = this;
 | 
			
		||||
  m_ctx.plugin = const_cast<sp_plugin_t *>(pRuntime->plugin());
 | 
			
		||||
  tracker_.pBase = (ucell_t *)malloc(1024);
 | 
			
		||||
  tracker_.pCur = tracker_.pBase;
 | 
			
		||||
  tracker_.size = 1024 / sizeof(cell_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PluginContext::~PluginContext()
 | 
			
		||||
{
 | 
			
		||||
  free(m_ctx.tracker->pBase);
 | 
			
		||||
  delete m_ctx.tracker;
 | 
			
		||||
  free(tracker_.pBase);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IVirtualMachine *
 | 
			
		||||
@ -84,12 +80,6 @@ PluginContext::GetContext()
 | 
			
		||||
  return reinterpret_cast<sp_context_t *>((IPluginContext * )this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sp_context_t *
 | 
			
		||||
PluginContext::GetCtx()
 | 
			
		||||
{
 | 
			
		||||
  return &m_ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
PluginContext::IsDebugging()
 | 
			
		||||
{
 | 
			
		||||
@ -137,7 +127,7 @@ PluginContext::ThrowNativeErrorEx(int error, const char *msg, ...)
 | 
			
		||||
  if (!m_InExec)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  m_ctx.n_err = error;
 | 
			
		||||
  native_error_ = error;
 | 
			
		||||
  
 | 
			
		||||
  if (msg) {
 | 
			
		||||
    va_list ap;
 | 
			
		||||
@ -155,7 +145,7 @@ PluginContext::ThrowNativeError(const char *msg, ...)
 | 
			
		||||
  if (!m_InExec)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  m_ctx.n_err = SP_ERROR_NATIVE;
 | 
			
		||||
  native_error_ = SP_ERROR_NATIVE;
 | 
			
		||||
 | 
			
		||||
  if (msg) {
 | 
			
		||||
    va_list ap;
 | 
			
		||||
@ -187,21 +177,21 @@ PluginContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_a
 | 
			
		||||
  /**
 | 
			
		||||
   * Check if the space between the heap and stack is sufficient.
 | 
			
		||||
   */
 | 
			
		||||
  if ((cell_t)(m_ctx.sp - m_ctx.hp - realmem) < STACKMARGIN)
 | 
			
		||||
  if ((cell_t)(sp_ - hp_ - realmem) < STACKMARGIN)
 | 
			
		||||
    return SP_ERROR_HEAPLOW;
 | 
			
		||||
 | 
			
		||||
  addr = (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.hp);
 | 
			
		||||
  addr = (cell_t *)(m_pRuntime->plugin()->memory + hp_);
 | 
			
		||||
  /* store size of allocation in cells */
 | 
			
		||||
  *addr = (cell_t)cells;
 | 
			
		||||
  addr++;
 | 
			
		||||
  m_ctx.hp += sizeof(cell_t);
 | 
			
		||||
  hp_ += sizeof(cell_t);
 | 
			
		||||
 | 
			
		||||
  *local_addr = m_ctx.hp;
 | 
			
		||||
  *local_addr = hp_;
 | 
			
		||||
 | 
			
		||||
  if (phys_addr)
 | 
			
		||||
    *phys_addr = addr;
 | 
			
		||||
 | 
			
		||||
  m_ctx.hp += realmem;
 | 
			
		||||
  hp_ += realmem;
 | 
			
		||||
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
@ -214,16 +204,16 @@ PluginContext::HeapPop(cell_t local_addr)
 | 
			
		||||
 | 
			
		||||
  /* check the bounds of this address */
 | 
			
		||||
  local_addr -= sizeof(cell_t);
 | 
			
		||||
  if (local_addr < (cell_t)m_pRuntime->plugin()->data_size || local_addr >= m_ctx.sp)
 | 
			
		||||
  if (local_addr < (cell_t)m_pRuntime->plugin()->data_size || local_addr >= sp_)
 | 
			
		||||
    return SP_ERROR_INVALID_ADDRESS;
 | 
			
		||||
 | 
			
		||||
  addr = (cell_t *)(m_pRuntime->plugin()->memory + local_addr);
 | 
			
		||||
  cellcount = (*addr) * sizeof(cell_t);
 | 
			
		||||
  /* check if this memory count looks valid */
 | 
			
		||||
  if ((signed)(m_ctx.hp - cellcount - sizeof(cell_t)) != local_addr)
 | 
			
		||||
  if ((signed)(hp_ - cellcount - sizeof(cell_t)) != local_addr)
 | 
			
		||||
    return SP_ERROR_INVALID_ADDRESS;
 | 
			
		||||
 | 
			
		||||
  m_ctx.hp = local_addr;
 | 
			
		||||
  hp_ = local_addr;
 | 
			
		||||
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
@ -235,7 +225,7 @@ PluginContext::HeapRelease(cell_t local_addr)
 | 
			
		||||
  if (local_addr < (cell_t)m_pRuntime->plugin()->data_size)
 | 
			
		||||
    return SP_ERROR_INVALID_ADDRESS;
 | 
			
		||||
 | 
			
		||||
  m_ctx.hp = local_addr - sizeof(cell_t);
 | 
			
		||||
  hp_ = local_addr - sizeof(cell_t);
 | 
			
		||||
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
@ -327,7 +317,7 @@ PluginContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
 | 
			
		||||
int
 | 
			
		||||
PluginContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
 | 
			
		||||
{
 | 
			
		||||
  if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) ||
 | 
			
		||||
  if (((local_addr >= hp_) && (local_addr < sp_)) ||
 | 
			
		||||
      (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
 | 
			
		||||
  {
 | 
			
		||||
    return SP_ERROR_INVALID_ADDRESS;
 | 
			
		||||
@ -360,7 +350,7 @@ PluginContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t arra
 | 
			
		||||
int
 | 
			
		||||
PluginContext::LocalToString(cell_t local_addr, char **addr)
 | 
			
		||||
{
 | 
			
		||||
  if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) ||
 | 
			
		||||
  if (((local_addr >= hp_) && (local_addr < sp_)) ||
 | 
			
		||||
      (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
 | 
			
		||||
  {
 | 
			
		||||
    return SP_ERROR_INVALID_ADDRESS;
 | 
			
		||||
@ -382,7 +372,7 @@ PluginContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source
 | 
			
		||||
  char *dest;
 | 
			
		||||
  size_t len;
 | 
			
		||||
 | 
			
		||||
  if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) ||
 | 
			
		||||
  if (((local_addr >= hp_) && (local_addr < sp_)) ||
 | 
			
		||||
      (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
 | 
			
		||||
  {
 | 
			
		||||
    return SP_ERROR_INVALID_ADDRESS;
 | 
			
		||||
@ -445,7 +435,7 @@ PluginContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char
 | 
			
		||||
  size_t len;
 | 
			
		||||
  bool needtocheck = false;
 | 
			
		||||
 | 
			
		||||
  if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) ||
 | 
			
		||||
  if (((local_addr >= hp_) && (local_addr < sp_)) ||
 | 
			
		||||
      (local_addr < 0) ||
 | 
			
		||||
      ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
 | 
			
		||||
  {
 | 
			
		||||
@ -550,7 +540,7 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne
 | 
			
		||||
  if (m_pRuntime->IsPaused())
 | 
			
		||||
    return SP_ERROR_NOT_RUNNABLE;
 | 
			
		||||
 | 
			
		||||
  if ((cell_t)(m_ctx.hp + 16*sizeof(cell_t)) > (cell_t)(m_ctx.sp - (sizeof(cell_t) * (num_params + 1))))
 | 
			
		||||
  if ((cell_t)(hp_ + 16*sizeof(cell_t)) > (cell_t)(sp_ - (sizeof(cell_t) * (num_params + 1))))
 | 
			
		||||
    return SP_ERROR_STACKLOW;
 | 
			
		||||
 | 
			
		||||
  if (result == NULL)
 | 
			
		||||
@ -580,25 +570,25 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne
 | 
			
		||||
  uint32_t save_n_idx;
 | 
			
		||||
  cell_t save_sp, save_hp, save_rp, save_cip;
 | 
			
		||||
 | 
			
		||||
  save_sp = m_ctx.sp;
 | 
			
		||||
  save_hp = m_ctx.hp;
 | 
			
		||||
  save_sp = sp_;
 | 
			
		||||
  save_hp = hp_;
 | 
			
		||||
  save_exec = m_InExec;
 | 
			
		||||
  save_n_idx = m_ctx.n_idx;
 | 
			
		||||
  save_rp = m_ctx.rp;
 | 
			
		||||
  save_cip = m_ctx.cip;
 | 
			
		||||
  save_n_idx = last_native_;
 | 
			
		||||
  save_rp = rp_;
 | 
			
		||||
  save_cip = cip_;
 | 
			
		||||
 | 
			
		||||
  /* Push parameters */
 | 
			
		||||
 | 
			
		||||
  m_ctx.sp -= sizeof(cell_t) * (num_params + 1);
 | 
			
		||||
  sp = (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.sp);
 | 
			
		||||
  sp_ -= sizeof(cell_t) * (num_params + 1);
 | 
			
		||||
  sp = (cell_t *)(m_pRuntime->plugin()->memory + sp_);
 | 
			
		||||
 | 
			
		||||
  sp[0] = num_params;
 | 
			
		||||
  for (unsigned int i = 0; i < num_params; i++)
 | 
			
		||||
    sp[i + 1] = params[i];
 | 
			
		||||
 | 
			
		||||
  /* Clear internal state */
 | 
			
		||||
  m_ctx.n_err = SP_ERROR_NONE;
 | 
			
		||||
  m_ctx.n_idx = 0;
 | 
			
		||||
  native_error_ = SP_ERROR_NONE;
 | 
			
		||||
  last_native_ = -1;
 | 
			
		||||
  m_MsgCache[0] = '\0';
 | 
			
		||||
  m_CustomMsg = false;
 | 
			
		||||
  m_InExec = true;
 | 
			
		||||
@ -615,23 +605,23 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne
 | 
			
		||||
  m_InExec = save_exec;
 | 
			
		||||
 | 
			
		||||
  if (ir == SP_ERROR_NONE) {
 | 
			
		||||
    m_ctx.n_err = SP_ERROR_NONE;
 | 
			
		||||
    if (m_ctx.sp != save_sp) {
 | 
			
		||||
    native_error_ = SP_ERROR_NONE;
 | 
			
		||||
    if (sp_ != save_sp) {
 | 
			
		||||
      ir = SP_ERROR_STACKLEAK;
 | 
			
		||||
      _SetErrorMessage("Stack leak detected: sp:%d should be %d!", 
 | 
			
		||||
        m_ctx.sp, 
 | 
			
		||||
        sp_, 
 | 
			
		||||
        save_sp);
 | 
			
		||||
    }
 | 
			
		||||
    if (m_ctx.hp != save_hp) {
 | 
			
		||||
    if (hp_ != save_hp) {
 | 
			
		||||
      ir = SP_ERROR_HEAPLEAK;
 | 
			
		||||
      _SetErrorMessage("Heap leak detected: hp:%d should be %d!", 
 | 
			
		||||
        m_ctx.hp, 
 | 
			
		||||
        hp_, 
 | 
			
		||||
        save_hp);
 | 
			
		||||
    }
 | 
			
		||||
    if (m_ctx.rp != save_rp) {
 | 
			
		||||
    if (rp_ != save_rp) {
 | 
			
		||||
      ir = SP_ERROR_STACKLEAK;
 | 
			
		||||
      _SetErrorMessage("Return stack leak detected: rp:%d should be %d!",
 | 
			
		||||
        m_ctx.rp,
 | 
			
		||||
        rp_,
 | 
			
		||||
        save_rp);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -642,13 +632,13 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne
 | 
			
		||||
  if (ir != SP_ERROR_NONE)
 | 
			
		||||
    Environment::get()->ReportError(m_pRuntime, ir, m_MsgCache, save_rp);
 | 
			
		||||
 | 
			
		||||
  m_ctx.sp = save_sp;
 | 
			
		||||
  m_ctx.hp = save_hp;
 | 
			
		||||
  m_ctx.rp = save_rp;
 | 
			
		||||
  sp_ = save_sp;
 | 
			
		||||
  hp_ = save_hp;
 | 
			
		||||
  rp_ = save_rp;
 | 
			
		||||
  
 | 
			
		||||
  m_ctx.cip = save_cip;
 | 
			
		||||
  m_ctx.n_idx = save_n_idx;
 | 
			
		||||
  m_ctx.n_err = SP_ERROR_NONE;
 | 
			
		||||
  cip_ = save_cip;
 | 
			
		||||
  last_native_ = save_n_idx;
 | 
			
		||||
  native_error_ = SP_ERROR_NONE;
 | 
			
		||||
  m_MsgCache[0] = '\0';
 | 
			
		||||
  m_CustomMsg = false;
 | 
			
		||||
 | 
			
		||||
@ -780,13 +770,13 @@ DebugInfo::LookupLine(ucell_t addr, uint32_t *line)
 | 
			
		||||
int
 | 
			
		||||
PluginContext::GetLastNativeError()
 | 
			
		||||
{
 | 
			
		||||
  return m_ctx.n_err;
 | 
			
		||||
  return native_error_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell_t *
 | 
			
		||||
PluginContext::GetLocalParams()
 | 
			
		||||
{
 | 
			
		||||
  return (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.frm + (2 * sizeof(cell_t)));
 | 
			
		||||
  return (cell_t *)(m_pRuntime->plugin()->memory + frm_ + (2 * sizeof(cell_t)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@ -812,5 +802,250 @@ PluginContext::GetKey(int k, void **value)
 | 
			
		||||
void
 | 
			
		||||
PluginContext::ClearLastNativeError()
 | 
			
		||||
{
 | 
			
		||||
  m_ctx.n_err = SP_ERROR_NONE;
 | 
			
		||||
  native_error_ = SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
PluginContext::popTrackerAndSetHeap()
 | 
			
		||||
{
 | 
			
		||||
  assert(tracker_.pCur > tracker_.pBase);
 | 
			
		||||
 | 
			
		||||
  tracker_.pCur--;
 | 
			
		||||
  if (tracker_.pCur < tracker_.pBase)
 | 
			
		||||
    return SP_ERROR_TRACKER_BOUNDS;
 | 
			
		||||
 | 
			
		||||
  ucell_t amt = *tracker_.pCur;
 | 
			
		||||
  if (amt > (hp_ - m_pRuntime->plugin()->data_size))
 | 
			
		||||
    return SP_ERROR_HEAPMIN;
 | 
			
		||||
 | 
			
		||||
  hp_ -= amt;
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
PluginContext::pushTracker(uint32_t amount)
 | 
			
		||||
{
 | 
			
		||||
  if ((size_t)(tracker_.pCur - tracker_.pBase) >= tracker_.size)
 | 
			
		||||
    return SP_ERROR_TRACKER_BOUNDS;
 | 
			
		||||
 | 
			
		||||
  if (tracker_.pCur + 1 - (tracker_.pBase + tracker_.size) == 0) {
 | 
			
		||||
    size_t disp = tracker_.size - 1;
 | 
			
		||||
    tracker_.size *= 2;
 | 
			
		||||
    tracker_.pBase = (ucell_t *)realloc(tracker_.pBase, tracker_.size * sizeof(cell_t));
 | 
			
		||||
 | 
			
		||||
    if (!tracker_.pBase)
 | 
			
		||||
      return SP_ERROR_TRACKER_BOUNDS;
 | 
			
		||||
 | 
			
		||||
    tracker_.pCur = tracker_.pBase + disp;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  *tracker_.pCur++ = amount;
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell_t
 | 
			
		||||
PluginContext::invokeNative(ucell_t native_idx, cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
  cell_t save_sp = sp_;
 | 
			
		||||
  cell_t save_hp = hp_;
 | 
			
		||||
 | 
			
		||||
  // Note: Invoke() saves the last native, so we don't need to here.
 | 
			
		||||
  last_native_ = native_idx;
 | 
			
		||||
 | 
			
		||||
  sp_native_t *native = &m_pRuntime->plugin()->natives[native_idx];
 | 
			
		||||
 | 
			
		||||
  if (native->status == SP_NATIVE_UNBOUND) {
 | 
			
		||||
    native_error_ = SP_ERROR_INVALID_NATIVE;
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cell_t result = native->pfn(this, params);
 | 
			
		||||
 | 
			
		||||
  if (native_error_ != SP_ERROR_NONE)
 | 
			
		||||
    return result;
 | 
			
		||||
 | 
			
		||||
  if (save_sp != sp_) {
 | 
			
		||||
    native_error_ = SP_ERROR_STACKLEAK;
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
  if (save_hp != hp_) {
 | 
			
		||||
    native_error_ = SP_ERROR_HEAPLEAK;
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell_t
 | 
			
		||||
PluginContext::invokeBoundNative(SPVM_NATIVE_FUNC pfn, cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
  cell_t save_sp = sp_;
 | 
			
		||||
  cell_t save_hp = hp_;
 | 
			
		||||
 | 
			
		||||
  cell_t result = pfn(this, params);
 | 
			
		||||
 | 
			
		||||
  if (native_error_ != SP_ERROR_NONE)
 | 
			
		||||
    return result;
 | 
			
		||||
 | 
			
		||||
  if (save_sp != sp_) {
 | 
			
		||||
    native_error_ = SP_ERROR_STACKLEAK;
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
  if (save_hp != hp_) {
 | 
			
		||||
    native_error_ = SP_ERROR_HEAPLEAK;
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct array_creation_t
 | 
			
		||||
{
 | 
			
		||||
  const cell_t *dim_list;     /* Dimension sizes */
 | 
			
		||||
  cell_t dim_count;           /* Number of dimensions */
 | 
			
		||||
  cell_t *data_offs;          /* Current offset AFTER the indirection vectors (data) */
 | 
			
		||||
  cell_t *base;               /* array base */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static cell_t
 | 
			
		||||
GenerateInnerArrayIndirectionVectors(array_creation_t *ar, int dim, cell_t cur_offs)
 | 
			
		||||
{
 | 
			
		||||
  cell_t write_offs = cur_offs;
 | 
			
		||||
  cell_t *data_offs = ar->data_offs;
 | 
			
		||||
 | 
			
		||||
  cur_offs += ar->dim_list[dim];
 | 
			
		||||
 | 
			
		||||
  // Dimension n-x where x > 2 will have sub-vectors.  
 | 
			
		||||
  // Otherwise, we just need to reference the data section.
 | 
			
		||||
  if (ar->dim_count > 2 && dim < ar->dim_count - 2) {
 | 
			
		||||
    // For each index at this dimension, write offstes to our sub-vectors.
 | 
			
		||||
    // After we write one sub-vector, we generate its sub-vectors recursively.
 | 
			
		||||
    // At the end, we're given the next offset we can use.
 | 
			
		||||
    for (int i = 0; i < ar->dim_list[dim]; i++) {
 | 
			
		||||
      ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell_t);
 | 
			
		||||
      write_offs++;
 | 
			
		||||
      cur_offs = GenerateInnerArrayIndirectionVectors(ar, dim + 1, cur_offs);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // In this section, there are no sub-vectors, we need to write offsets 
 | 
			
		||||
    // to the data.  This is separate so the data stays in one big chunk.
 | 
			
		||||
    // The data offset will increment by the size of the last dimension, 
 | 
			
		||||
    // because that is where the data is finally computed as. 
 | 
			
		||||
    for (int i = 0; i < ar->dim_list[dim]; i++) {
 | 
			
		||||
      ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell_t);
 | 
			
		||||
      write_offs++;
 | 
			
		||||
      *data_offs = *data_offs + ar->dim_list[dim + 1];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return cur_offs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t
 | 
			
		||||
calc_indirection(const array_creation_t *ar, cell_t dim)
 | 
			
		||||
{
 | 
			
		||||
  cell_t size = ar->dim_list[dim];
 | 
			
		||||
  if (dim < ar->dim_count - 2)
 | 
			
		||||
    size += ar->dim_list[dim] * calc_indirection(ar, dim + 1);
 | 
			
		||||
  return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t
 | 
			
		||||
GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _dimcount, bool autozero)
 | 
			
		||||
{
 | 
			
		||||
  array_creation_t ar;
 | 
			
		||||
  cell_t data_offs;
 | 
			
		||||
 | 
			
		||||
  /* Reverse the dimensions */
 | 
			
		||||
  cell_t dim_list[sDIMEN_MAX];
 | 
			
		||||
  int cur_dim = 0;
 | 
			
		||||
  for (int i = _dimcount - 1; i >= 0; i--)
 | 
			
		||||
    dim_list[cur_dim++] = dims[i];
 | 
			
		||||
  
 | 
			
		||||
  ar.base = arraybase;
 | 
			
		||||
  ar.dim_list = dim_list;
 | 
			
		||||
  ar.dim_count = _dimcount;
 | 
			
		||||
  ar.data_offs = &data_offs;
 | 
			
		||||
 | 
			
		||||
  data_offs = calc_indirection(&ar, 0);
 | 
			
		||||
  GenerateInnerArrayIndirectionVectors(&ar, 0, 0);
 | 
			
		||||
  return data_offs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
PluginContext::generateFullArray(uint32_t argc, cell_t *argv, int autozero)
 | 
			
		||||
{
 | 
			
		||||
  // Calculate how many cells are needed.
 | 
			
		||||
  if (argv[0] <= 0)
 | 
			
		||||
    return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
 | 
			
		||||
  uint32_t cells = argv[0];
 | 
			
		||||
 | 
			
		||||
  for (uint32_t dim = 1; dim < argc; dim++) {
 | 
			
		||||
    cell_t dimsize = argv[dim];
 | 
			
		||||
    if (dimsize <= 0)
 | 
			
		||||
      return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
    if (!ke::IsUint32MultiplySafe(cells, dimsize))
 | 
			
		||||
      return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
    cells *= uint32_t(dimsize);
 | 
			
		||||
    if (!ke::IsUint32AddSafe(cells, dimsize))
 | 
			
		||||
      return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
    cells += uint32_t(dimsize);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!ke::IsUint32MultiplySafe(cells, 4))
 | 
			
		||||
    return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
 | 
			
		||||
  uint32_t bytes = cells * 4;
 | 
			
		||||
  if (!ke::IsUint32AddSafe(hp_, bytes))
 | 
			
		||||
    return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
 | 
			
		||||
  uint32_t new_hp = hp_ + bytes;
 | 
			
		||||
  cell_t *dat_hp = reinterpret_cast<cell_t *>(m_pRuntime->plugin()->memory + new_hp);
 | 
			
		||||
 | 
			
		||||
  // argv, coincidentally, is STK.
 | 
			
		||||
  if (dat_hp >= argv - STACK_MARGIN)
 | 
			
		||||
    return SP_ERROR_HEAPLOW;
 | 
			
		||||
 | 
			
		||||
  if (int err = pushTracker(bytes))
 | 
			
		||||
    return err;
 | 
			
		||||
 | 
			
		||||
  cell_t *base = reinterpret_cast<cell_t *>(m_pRuntime->plugin()->memory + hp_);
 | 
			
		||||
  cell_t offs = GenerateArrayIndirectionVectors(base, argv, argc, !!autozero);
 | 
			
		||||
  assert(size_t(offs) == cells);
 | 
			
		||||
 | 
			
		||||
  argv[argc - 1] = hp_;
 | 
			
		||||
  hp_ = new_hp;
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
PluginContext::generateArray(cell_t dims, cell_t *stk, bool autozero)
 | 
			
		||||
{
 | 
			
		||||
  if (dims == 1) {
 | 
			
		||||
    uint32_t size = *stk;
 | 
			
		||||
    if (size == 0 || !ke::IsUint32MultiplySafe(size, 4))
 | 
			
		||||
      return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
    *stk = hp_;
 | 
			
		||||
 | 
			
		||||
    uint32_t bytes = size * 4;
 | 
			
		||||
 | 
			
		||||
    hp_ += bytes;
 | 
			
		||||
    if (uintptr_t(m_pRuntime->plugin()->memory + hp_) >= uintptr_t(stk))
 | 
			
		||||
      return SP_ERROR_HEAPLOW;
 | 
			
		||||
 | 
			
		||||
    if (int err = pushTracker(bytes))
 | 
			
		||||
      return err;
 | 
			
		||||
 | 
			
		||||
    if (autozero)
 | 
			
		||||
      memset(m_pRuntime->plugin()->memory + hp_, 0, bytes);
 | 
			
		||||
 | 
			
		||||
    return SP_ERROR_NONE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (int err = generateFullArray(dims, stk, autozero))
 | 
			
		||||
    return err;
 | 
			
		||||
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,18 @@
 | 
			
		||||
#include "plugin-runtime.h"
 | 
			
		||||
#include "jit_shared.h"
 | 
			
		||||
 | 
			
		||||
struct HeapTracker
 | 
			
		||||
{
 | 
			
		||||
  HeapTracker()
 | 
			
		||||
   : size(0),
 | 
			
		||||
     pBase(nullptr),
 | 
			
		||||
     pCur(nullptr)
 | 
			
		||||
  {}
 | 
			
		||||
  size_t size; 
 | 
			
		||||
  ucell_t *pBase; 
 | 
			
		||||
  ucell_t *pCur;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PluginContext : public IPluginContext
 | 
			
		||||
{
 | 
			
		||||
 public:
 | 
			
		||||
@ -27,7 +39,6 @@ class PluginContext : public IPluginContext
 | 
			
		||||
 public: //IPluginContext
 | 
			
		||||
  IVirtualMachine *GetVirtualMachine();
 | 
			
		||||
  sp_context_t *GetContext();
 | 
			
		||||
  sp_context_t *GetCtx();
 | 
			
		||||
  bool IsDebugging();
 | 
			
		||||
  int SetDebugBreak(void *newpfn, void *oldpfn);
 | 
			
		||||
  IPluginDebugInfo *GetDebugInfo();
 | 
			
		||||
@ -76,6 +87,94 @@ class PluginContext : public IPluginContext
 | 
			
		||||
 public:
 | 
			
		||||
  bool IsInExec();
 | 
			
		||||
 | 
			
		||||
  static inline size_t offsetOfRp() {
 | 
			
		||||
    return offsetof(PluginContext, rp_);
 | 
			
		||||
  }
 | 
			
		||||
  static inline size_t offsetOfRstkCips() {
 | 
			
		||||
    return offsetof(PluginContext, rstk_cips_);
 | 
			
		||||
  }
 | 
			
		||||
  static inline size_t offsetOfTracker() {
 | 
			
		||||
    return offsetof(PluginContext, tracker_);
 | 
			
		||||
  }
 | 
			
		||||
  static inline size_t offsetOfLastNative() {
 | 
			
		||||
    return offsetof(PluginContext, last_native_);
 | 
			
		||||
  }
 | 
			
		||||
  static inline size_t offsetOfNativeError() {
 | 
			
		||||
    return offsetof(PluginContext, native_error_);
 | 
			
		||||
  }
 | 
			
		||||
  static inline size_t offsetOfSp() {
 | 
			
		||||
    return offsetof(PluginContext, sp_);
 | 
			
		||||
  }
 | 
			
		||||
  static inline size_t offsetOfRuntime() {
 | 
			
		||||
    return offsetof(PluginContext, m_pRuntime);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int32_t *addressOfCip() {
 | 
			
		||||
    return &cip_;
 | 
			
		||||
  }
 | 
			
		||||
  int32_t *addressOfSp() {
 | 
			
		||||
    return &sp_;
 | 
			
		||||
  }
 | 
			
		||||
  cell_t *addressOfFrm() {
 | 
			
		||||
    return &frm_;
 | 
			
		||||
  }
 | 
			
		||||
  cell_t *addressOfHp() {
 | 
			
		||||
    return &hp_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int32_t cip() const {
 | 
			
		||||
    return cip_;
 | 
			
		||||
  }
 | 
			
		||||
  cell_t frm() const {
 | 
			
		||||
    return frm_;
 | 
			
		||||
  }
 | 
			
		||||
  cell_t hp() const {
 | 
			
		||||
    return hp_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Return stack logic.
 | 
			
		||||
  bool pushReturnCip(cell_t cip) {
 | 
			
		||||
    if (rp_ >= SP_MAX_RETURN_STACK)
 | 
			
		||||
      return false;
 | 
			
		||||
    rstk_cips_[rp_++] = cip;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  void popReturnCip() {
 | 
			
		||||
    assert(rp_ > 0);
 | 
			
		||||
    rp_--;
 | 
			
		||||
  }
 | 
			
		||||
  cell_t rp() const {
 | 
			
		||||
    return rp_;
 | 
			
		||||
  }
 | 
			
		||||
  cell_t getReturnStackCip(int index) {
 | 
			
		||||
    assert(index >= 0 && index < SP_MAX_RETURN_STACK);
 | 
			
		||||
    return rstk_cips_[index];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int popTrackerAndSetHeap();
 | 
			
		||||
  int pushTracker(uint32_t amount);
 | 
			
		||||
 | 
			
		||||
  int generateArray(cell_t dims, cell_t *stk, bool autozero);
 | 
			
		||||
  int generateFullArray(uint32_t argc, cell_t *argv, int autozero);
 | 
			
		||||
  cell_t invokeNative(ucell_t native_idx, cell_t *params);
 | 
			
		||||
  cell_t invokeBoundNative(SPVM_NATIVE_FUNC pfn, cell_t *params);
 | 
			
		||||
  int lastNative() const {
 | 
			
		||||
    return last_native_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inline bool checkAddress(cell_t *stk, cell_t addr) {
 | 
			
		||||
    if (uint32_t(addr) >= m_pRuntime->plugin()->mem_size)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if (addr < hp_)
 | 
			
		||||
      return true;
 | 
			
		||||
 | 
			
		||||
    if (reinterpret_cast<cell_t *>(m_pRuntime->plugin()->memory + addr) < stk)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  void SetErrorMessage(const char *msg, va_list ap);
 | 
			
		||||
  void _SetErrorMessage(const char *msg, ...);
 | 
			
		||||
@ -87,9 +186,27 @@ class PluginContext : public IPluginContext
 | 
			
		||||
  bool m_CustomMsg;
 | 
			
		||||
  bool m_InExec;
 | 
			
		||||
  PluginRuntime *m_pRuntime;
 | 
			
		||||
  sp_context_t m_ctx;
 | 
			
		||||
  void *m_keys[4];
 | 
			
		||||
  bool m_keys_set[4];
 | 
			
		||||
 | 
			
		||||
  // Tracker for local HEA growth.
 | 
			
		||||
  HeapTracker tracker_;
 | 
			
		||||
 | 
			
		||||
  // Return stack.
 | 
			
		||||
  cell_t rp_;
 | 
			
		||||
  cell_t rstk_cips_[SP_MAX_RETURN_STACK];
 | 
			
		||||
 | 
			
		||||
  // Track the currently executing native index, and any error it throws.
 | 
			
		||||
  int32_t last_native_;
 | 
			
		||||
  int native_error_;
 | 
			
		||||
 | 
			
		||||
  // Most recent CIP.
 | 
			
		||||
  int32_t cip_;
 | 
			
		||||
 | 
			
		||||
  // Stack, heap, and frame pointer.
 | 
			
		||||
  cell_t sp_;
 | 
			
		||||
  cell_t hp_;
 | 
			
		||||
  cell_t frm_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif //_INCLUDE_SOURCEPAWN_BASECONTEXT_H_
 | 
			
		||||
 | 
			
		||||
@ -96,6 +96,10 @@ class PluginRuntime
 | 
			
		||||
    return m_JitFunctions[i];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static inline size_t offsetToPlugin() {
 | 
			
		||||
    return offsetof(PluginRuntime, m_plugin);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  void SetupFloatNativeRemapping();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -47,12 +47,19 @@ CodeStubs::CompileInvokeStub()
 | 
			
		||||
  __ push(ebx);   // ebp - 12
 | 
			
		||||
  __ push(esp);   // ebp - 16
 | 
			
		||||
 | 
			
		||||
  // ebx = cx
 | 
			
		||||
  __ movl(ebx, Operand(ebp, 8 + 4 * 0));
 | 
			
		||||
  __ movl(eax, Operand(ebp, 8 + 4 * 1));
 | 
			
		||||
  __ movl(ecx, Operand(ebp, 8 + 4 * 2));
 | 
			
		||||
 | 
			
		||||
  // ecx = code
 | 
			
		||||
  __ movl(ecx, Operand(ebp, 8 + 4 * 1));
 | 
			
		||||
 | 
			
		||||
  // eax = cx->m_pRuntime->m_plugin.memory
 | 
			
		||||
  __ movl(eax, Operand(ebx, PluginContext::offsetOfRuntime()));
 | 
			
		||||
  __ addl(eax, PluginRuntime::offsetToPlugin());
 | 
			
		||||
  __ movl(eax, Operand(eax, offsetof(sp_plugin_t, memory)));
 | 
			
		||||
 | 
			
		||||
  // Set up run-time registers.
 | 
			
		||||
  __ movl(edi, Operand(ebx, offsetof(sp_context_t, sp)));
 | 
			
		||||
  __ movl(edi, Operand(ebx, PluginContext::offsetOfSp()));
 | 
			
		||||
  __ addl(edi, eax);
 | 
			
		||||
  __ movl(esi, eax);
 | 
			
		||||
  __ movl(ebx, edi);
 | 
			
		||||
@ -64,8 +71,8 @@ CodeStubs::CompileInvokeStub()
 | 
			
		||||
  __ call(ecx);
 | 
			
		||||
 | 
			
		||||
  // Get input context, store rval.
 | 
			
		||||
  __ movl(ecx, Operand(ebp, 8 + 4 * 0));
 | 
			
		||||
  __ movl(Operand(ecx, offsetof(sp_context_t, rval)), pri);
 | 
			
		||||
  __ movl(ecx, Operand(ebp, 8 + 4 * 2));
 | 
			
		||||
  __ movl(Operand(ecx, 0), pri);
 | 
			
		||||
 | 
			
		||||
  // Set no error.
 | 
			
		||||
  __ movl(eax, SP_ERROR_NONE);
 | 
			
		||||
@ -75,7 +82,8 @@ CodeStubs::CompileInvokeStub()
 | 
			
		||||
  Label ret;
 | 
			
		||||
  __ bind(&ret);
 | 
			
		||||
  __ subl(stk, dat);
 | 
			
		||||
  __ movl(Operand(ecx, offsetof(sp_context_t, sp)), stk);
 | 
			
		||||
  __ movl(ecx, Operand(ebp, 8 + 4 * 0));
 | 
			
		||||
  __ movl(Operand(ecx, PluginContext::offsetOfSp()), stk);
 | 
			
		||||
 | 
			
		||||
  // Restore stack.
 | 
			
		||||
  __ movl(esp, Operand(ebp, -16));
 | 
			
		||||
 | 
			
		||||
@ -77,128 +77,6 @@ OpToCondition(OPCODE op)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct array_creation_t
 | 
			
		||||
{
 | 
			
		||||
  const cell_t *dim_list;     /* Dimension sizes */
 | 
			
		||||
  cell_t dim_count;           /* Number of dimensions */
 | 
			
		||||
  cell_t *data_offs;          /* Current offset AFTER the indirection vectors (data) */
 | 
			
		||||
  cell_t *base;               /* array base */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static cell_t
 | 
			
		||||
GenerateInnerArrayIndirectionVectors(array_creation_t *ar, int dim, cell_t cur_offs)
 | 
			
		||||
{
 | 
			
		||||
  cell_t write_offs = cur_offs;
 | 
			
		||||
  cell_t *data_offs = ar->data_offs;
 | 
			
		||||
 | 
			
		||||
  cur_offs += ar->dim_list[dim];
 | 
			
		||||
 | 
			
		||||
  // Dimension n-x where x > 2 will have sub-vectors.  
 | 
			
		||||
  // Otherwise, we just need to reference the data section.
 | 
			
		||||
  if (ar->dim_count > 2 && dim < ar->dim_count - 2) {
 | 
			
		||||
    // For each index at this dimension, write offstes to our sub-vectors.
 | 
			
		||||
    // After we write one sub-vector, we generate its sub-vectors recursively.
 | 
			
		||||
    // At the end, we're given the next offset we can use.
 | 
			
		||||
    for (int i = 0; i < ar->dim_list[dim]; i++) {
 | 
			
		||||
      ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell_t);
 | 
			
		||||
      write_offs++;
 | 
			
		||||
      cur_offs = GenerateInnerArrayIndirectionVectors(ar, dim + 1, cur_offs);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // In this section, there are no sub-vectors, we need to write offsets 
 | 
			
		||||
    // to the data.  This is separate so the data stays in one big chunk.
 | 
			
		||||
    // The data offset will increment by the size of the last dimension, 
 | 
			
		||||
    // because that is where the data is finally computed as. 
 | 
			
		||||
    for (int i = 0; i < ar->dim_list[dim]; i++) {
 | 
			
		||||
      ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell_t);
 | 
			
		||||
      write_offs++;
 | 
			
		||||
      *data_offs = *data_offs + ar->dim_list[dim + 1];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return cur_offs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t
 | 
			
		||||
calc_indirection(const array_creation_t *ar, cell_t dim)
 | 
			
		||||
{
 | 
			
		||||
  cell_t size = ar->dim_list[dim];
 | 
			
		||||
  if (dim < ar->dim_count - 2)
 | 
			
		||||
    size += ar->dim_list[dim] * calc_indirection(ar, dim + 1);
 | 
			
		||||
  return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t
 | 
			
		||||
GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _dimcount, bool autozero)
 | 
			
		||||
{
 | 
			
		||||
  array_creation_t ar;
 | 
			
		||||
  cell_t data_offs;
 | 
			
		||||
 | 
			
		||||
  /* Reverse the dimensions */
 | 
			
		||||
  cell_t dim_list[sDIMEN_MAX];
 | 
			
		||||
  int cur_dim = 0;
 | 
			
		||||
  for (int i = _dimcount - 1; i >= 0; i--)
 | 
			
		||||
    dim_list[cur_dim++] = dims[i];
 | 
			
		||||
  
 | 
			
		||||
  ar.base = arraybase;
 | 
			
		||||
  ar.dim_list = dim_list;
 | 
			
		||||
  ar.dim_count = _dimcount;
 | 
			
		||||
  ar.data_offs = &data_offs;
 | 
			
		||||
 | 
			
		||||
  data_offs = calc_indirection(&ar, 0);
 | 
			
		||||
  GenerateInnerArrayIndirectionVectors(&ar, 0, 0);
 | 
			
		||||
  return data_offs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
GenerateFullArray(PluginRuntime *rt, uint32_t argc, cell_t *argv, int autozero)
 | 
			
		||||
{
 | 
			
		||||
  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
 | 
			
		||||
 | 
			
		||||
  // Calculate how many cells are needed.
 | 
			
		||||
  if (argv[0] <= 0)
 | 
			
		||||
    return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
 | 
			
		||||
  uint32_t cells = argv[0];
 | 
			
		||||
 | 
			
		||||
  for (uint32_t dim = 1; dim < argc; dim++) {
 | 
			
		||||
    cell_t dimsize = argv[dim];
 | 
			
		||||
    if (dimsize <= 0)
 | 
			
		||||
      return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
    if (!ke::IsUint32MultiplySafe(cells, dimsize))
 | 
			
		||||
      return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
    cells *= uint32_t(dimsize);
 | 
			
		||||
    if (!ke::IsUint32AddSafe(cells, dimsize))
 | 
			
		||||
      return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
    cells += uint32_t(dimsize);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!ke::IsUint32MultiplySafe(cells, 4))
 | 
			
		||||
    return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
 | 
			
		||||
  uint32_t bytes = cells * 4;
 | 
			
		||||
  if (!ke::IsUint32AddSafe(ctx->hp, bytes))
 | 
			
		||||
    return SP_ERROR_ARRAY_TOO_BIG;
 | 
			
		||||
 | 
			
		||||
  uint32_t new_hp = ctx->hp + bytes;
 | 
			
		||||
  cell_t *dat_hp = reinterpret_cast<cell_t *>(rt->plugin()->memory + new_hp);
 | 
			
		||||
 | 
			
		||||
  // argv, coincidentally, is STK.
 | 
			
		||||
  if (dat_hp >= argv - STACK_MARGIN)
 | 
			
		||||
    return SP_ERROR_HEAPLOW;
 | 
			
		||||
 | 
			
		||||
  if (int err = PushTracker(rt->GetBaseContext()->GetCtx(), bytes))
 | 
			
		||||
    return err;
 | 
			
		||||
 | 
			
		||||
  cell_t *base = reinterpret_cast<cell_t *>(rt->plugin()->memory + ctx->hp);
 | 
			
		||||
  cell_t offs = GenerateArrayIndirectionVectors(base, argv, argc, !!autozero);
 | 
			
		||||
  assert(size_t(offs) == cells);
 | 
			
		||||
 | 
			
		||||
  argv[argc - 1] = ctx->hp;
 | 
			
		||||
  ctx->hp = new_hp;
 | 
			
		||||
  return SP_ERROR_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !defined NDEBUG
 | 
			
		||||
static const char *
 | 
			
		||||
GetFunctionName(const sp_plugin_t *plugin, uint32_t offs)
 | 
			
		||||
@ -303,6 +181,7 @@ CompileFromThunk(PluginRuntime *runtime, cell_t pcode_offs, void **addrp, char *
 | 
			
		||||
Compiler::Compiler(PluginRuntime *rt, cell_t pcode_offs)
 | 
			
		||||
  : env_(Environment::get()),
 | 
			
		||||
    rt_(rt),
 | 
			
		||||
    context_(rt->GetBaseContext()),
 | 
			
		||||
    plugin_(rt->plugin()),
 | 
			
		||||
    error_(SP_ERROR_NONE),
 | 
			
		||||
    pcode_start_(pcode_offs),
 | 
			
		||||
@ -384,6 +263,37 @@ Compiler::emit(int *errp)
 | 
			
		||||
  return new CompiledFunction(code, pcode_start_, edges.take());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helpers for invoking context members.
 | 
			
		||||
static int
 | 
			
		||||
InvokePushTracker(PluginContext *cx, uint32_t amount)
 | 
			
		||||
{
 | 
			
		||||
  return cx->pushTracker(amount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
InvokePopTrackerAndSetHeap(PluginContext *cx)
 | 
			
		||||
{
 | 
			
		||||
  return cx->popTrackerAndSetHeap();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t
 | 
			
		||||
InvokeNativeHelper(PluginContext *cx, ucell_t native_idx, cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
  return cx->invokeNative(native_idx, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t
 | 
			
		||||
InvokeBoundNativeHelper(PluginContext *cx, SPVM_NATIVE_FUNC fn, cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
  return cx->invokeBoundNative(fn, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
InvokeGenerateFullArray(PluginContext *cx, uint32_t argc, cell_t *argv, int autozero)
 | 
			
		||||
{
 | 
			
		||||
  return cx->generateFullArray(argc, argv, autozero);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
Compiler::emitOp(OPCODE op)
 | 
			
		||||
{
 | 
			
		||||
@ -1258,8 +1168,8 @@ Compiler::emitOp(OPCODE op)
 | 
			
		||||
      __ push(alt);
 | 
			
		||||
 | 
			
		||||
      __ push(amount * 4);
 | 
			
		||||
      __ push(intptr_t(rt_->GetBaseContext()->GetCtx()));
 | 
			
		||||
      __ call(ExternalAddress((void *)PushTracker));
 | 
			
		||||
      __ push(intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
      __ call(ExternalAddress((void *)InvokePushTracker));
 | 
			
		||||
      __ addl(esp, 8);
 | 
			
		||||
      __ testl(eax, eax);
 | 
			
		||||
      __ j(not_zero, &extern_error_);
 | 
			
		||||
@ -1276,8 +1186,8 @@ Compiler::emitOp(OPCODE op)
 | 
			
		||||
      __ push(alt);
 | 
			
		||||
 | 
			
		||||
      // Get the context pointer and call the sanity checker.
 | 
			
		||||
      __ push(intptr_t(rt_));
 | 
			
		||||
      __ call(ExternalAddress((void *)PopTrackerAndSetHeap));
 | 
			
		||||
      __ push(intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
      __ call(ExternalAddress((void *)InvokePopTrackerAndSetHeap));
 | 
			
		||||
      __ addl(esp, 4);
 | 
			
		||||
      __ testl(eax, eax);
 | 
			
		||||
      __ j(not_zero, &extern_error_);
 | 
			
		||||
@ -1296,8 +1206,6 @@ Compiler::emitOp(OPCODE op)
 | 
			
		||||
 | 
			
		||||
    case OP_HALT:
 | 
			
		||||
      __ align(16);
 | 
			
		||||
      __ movl(tmp, intptr_t(rt_->GetBaseContext()->GetCtx()));
 | 
			
		||||
      __ movl(Operand(tmp, offsetof(sp_context_t, rval)), pri);
 | 
			
		||||
      __ movl(pri, readCell());
 | 
			
		||||
      __ jmp(&extern_error_);
 | 
			
		||||
      break;
 | 
			
		||||
@ -1405,8 +1313,8 @@ Compiler::emitGenArray(bool autozero)
 | 
			
		||||
 | 
			
		||||
    __ shll(tmp, 2);
 | 
			
		||||
    __ push(tmp);
 | 
			
		||||
    __ push(intptr_t(rt_->GetBaseContext()->GetCtx()));
 | 
			
		||||
    __ call(ExternalAddress((void *)PushTracker));
 | 
			
		||||
    __ push(intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
    __ call(ExternalAddress((void *)InvokePushTracker));
 | 
			
		||||
    __ addl(esp, 4);
 | 
			
		||||
    __ pop(tmp);
 | 
			
		||||
    __ shrl(tmp, 2);
 | 
			
		||||
@ -1432,8 +1340,8 @@ Compiler::emitGenArray(bool autozero)
 | 
			
		||||
    __ push(autozero ? 1 : 0);
 | 
			
		||||
    __ push(stk);
 | 
			
		||||
    __ push(val);
 | 
			
		||||
    __ push(intptr_t(rt_));
 | 
			
		||||
    __ call(ExternalAddress((void *)GenerateFullArray));
 | 
			
		||||
    __ push(intptr_t(context_));
 | 
			
		||||
    __ call(ExternalAddress((void *)InvokeGenerateFullArray));
 | 
			
		||||
    __ addl(esp, 4 * sizeof(void *));
 | 
			
		||||
 | 
			
		||||
    // restore pri to tmp
 | 
			
		||||
@ -1462,8 +1370,8 @@ Compiler::emitCall()
 | 
			
		||||
 | 
			
		||||
  // eax = context
 | 
			
		||||
  // ecx = rp
 | 
			
		||||
  __ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx()));
 | 
			
		||||
  __ movl(ecx, Operand(eax, offsetof(sp_context_t, rp)));
 | 
			
		||||
  __ movl(eax, intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
  __ movl(ecx, Operand(eax, PluginContext::offsetOfRp()));
 | 
			
		||||
 | 
			
		||||
  // Check if the return stack is used up.
 | 
			
		||||
  __ cmpl(ecx, SP_MAX_RETURN_STACK);
 | 
			
		||||
@ -1471,10 +1379,10 @@ Compiler::emitCall()
 | 
			
		||||
 | 
			
		||||
  // Add to the return stack.
 | 
			
		||||
  uintptr_t cip = uintptr_t(cip_ - 2) - uintptr_t(plugin_->pcode);
 | 
			
		||||
  __ movl(Operand(eax, ecx, ScaleFour, offsetof(sp_context_t, rstk_cips)), cip);
 | 
			
		||||
  __ movl(Operand(eax, ecx, ScaleFour, PluginContext::offsetOfRstkCips()), cip);
 | 
			
		||||
 | 
			
		||||
  // Increment the return stack pointer.
 | 
			
		||||
  __ addl(Operand(eax, offsetof(sp_context_t, rp)), 1);
 | 
			
		||||
  __ addl(Operand(eax, PluginContext::offsetOfRp()), 1);
 | 
			
		||||
 | 
			
		||||
  // Store the CIP of the function we're about to call.
 | 
			
		||||
  __ movl(Operand(cipAddr()), offset);
 | 
			
		||||
@ -1495,8 +1403,8 @@ Compiler::emitCall()
 | 
			
		||||
  __ movl(Operand(cipAddr()), cip);
 | 
			
		||||
 | 
			
		||||
  // Mark us as leaving the last frame.
 | 
			
		||||
  __ movl(tmp, intptr_t(rt_->GetBaseContext()->GetCtx()));
 | 
			
		||||
  __ subl(Operand(tmp, offsetof(sp_context_t, rp)), 1);
 | 
			
		||||
  __ movl(tmp, intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
  __ subl(Operand(tmp, PluginContext::offsetOfRp()), 1);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1581,13 +1489,13 @@ Compiler::emitNativeCall(OPCODE op)
 | 
			
		||||
  // Push the last parameter for the C++ function.
 | 
			
		||||
  __ push(stk);
 | 
			
		||||
 | 
			
		||||
  __ movl(eax, intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
  __ movl(Operand(eax, PluginContext::offsetOfLastNative()), native_index);
 | 
			
		||||
 | 
			
		||||
  // Relocate our absolute stk to be dat-relative, and update the context's
 | 
			
		||||
  // view.
 | 
			
		||||
  __ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx()));
 | 
			
		||||
  __ subl(stk, dat);
 | 
			
		||||
  __ movl(Operand(eax, offsetof(sp_context_t, sp)), stk);
 | 
			
		||||
 | 
			
		||||
  __ movl(Operand(eax, offsetof(sp_context_t, n_idx)), native_index);
 | 
			
		||||
  __ movl(Operand(eax, PluginContext::offsetOfSp()), stk);
 | 
			
		||||
 | 
			
		||||
  sp_native_t *native = rt_->GetNativeByIndex(native_index);
 | 
			
		||||
  if ((native->status != SP_NATIVE_BOUND) ||
 | 
			
		||||
@ -1596,18 +1504,18 @@ Compiler::emitNativeCall(OPCODE op)
 | 
			
		||||
    // The native is either unbound, or it could become unbound in the
 | 
			
		||||
    // future. Invoke the slower native callback.
 | 
			
		||||
    __ push(native_index);
 | 
			
		||||
    __ push(eax);
 | 
			
		||||
    __ call(ExternalAddress((void *)NativeCallback));
 | 
			
		||||
    __ push(intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
    __ call(ExternalAddress((void *)InvokeNativeHelper));
 | 
			
		||||
  } else {
 | 
			
		||||
    // The native is bound so we have a few more guarantees.
 | 
			
		||||
    __ push(intptr_t(native->pfn));
 | 
			
		||||
    __ push(eax);
 | 
			
		||||
    __ call(ExternalAddress((void *)BoundNativeCallback));
 | 
			
		||||
    __ push(intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
    __ call(ExternalAddress((void *)InvokeBoundNativeHelper));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check for errors.
 | 
			
		||||
  __ movl(ecx, intptr_t(rt_->GetBaseContext()->GetCtx()));
 | 
			
		||||
  __ movl(ecx, Operand(ecx, offsetof(sp_context_t, n_err)));
 | 
			
		||||
  __ movl(ecx, intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
  __ movl(ecx, Operand(ecx, PluginContext::offsetOfNativeError()));
 | 
			
		||||
  __ testl(ecx, ecx);
 | 
			
		||||
  __ j(not_zero, &extern_error_);
 | 
			
		||||
  
 | 
			
		||||
@ -1798,8 +1706,8 @@ Compiler::emitErrorPaths()
 | 
			
		||||
 | 
			
		||||
  if (extern_error_.used()) {
 | 
			
		||||
    __ bind(&extern_error_);
 | 
			
		||||
    __ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx()));
 | 
			
		||||
    __ movl(eax, Operand(eax, offsetof(sp_context_t, n_err)));
 | 
			
		||||
    __ movl(eax, intptr_t(rt_->GetBaseContext()));
 | 
			
		||||
    __ movl(eax, Operand(eax, PluginContext::offsetOfNativeError()));
 | 
			
		||||
    __ jmp(ExternalAddress(env_->stubs()->ReturnStub()));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -91,22 +91,20 @@ class Compiler
 | 
			
		||||
  void emitFloatCmp(ConditionCode cc);
 | 
			
		||||
 | 
			
		||||
  ExternalAddress cipAddr() {
 | 
			
		||||
    sp_context_t *ctx = rt_->GetBaseContext()->GetCtx();
 | 
			
		||||
    return ExternalAddress(&ctx->cip);
 | 
			
		||||
    return ExternalAddress(context_->addressOfCip());
 | 
			
		||||
  }
 | 
			
		||||
  ExternalAddress hpAddr() {
 | 
			
		||||
    sp_context_t *ctx = rt_->GetBaseContext()->GetCtx();
 | 
			
		||||
    return ExternalAddress(&ctx->hp);
 | 
			
		||||
    return ExternalAddress(context_->addressOfHp());
 | 
			
		||||
  }
 | 
			
		||||
  ExternalAddress frmAddr() {
 | 
			
		||||
    sp_context_t *ctx = rt_->GetBaseContext()->GetCtx();
 | 
			
		||||
    return ExternalAddress(&ctx->frm);
 | 
			
		||||
    return ExternalAddress(context_->addressOfFrm());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  AssemblerX86 masm;
 | 
			
		||||
  sp::Environment *env_;
 | 
			
		||||
  PluginRuntime *rt_;
 | 
			
		||||
  PluginContext *context_;
 | 
			
		||||
  const sp_plugin_t *plugin_;
 | 
			
		||||
  int error_;
 | 
			
		||||
  uint32_t pcode_start_;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user