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