Merge branch 'rm-interpreter'
This commit is contained in:
		
						commit
						afbcdc8a20
					
				@ -42,7 +42,6 @@ library.sources += [
 | 
			
		||||
  'environment.cpp',
 | 
			
		||||
  'scripted-invoker.cpp',
 | 
			
		||||
  'opcodes.cpp',
 | 
			
		||||
  'interpreter.cpp',
 | 
			
		||||
  'watchdog_timer.cpp',
 | 
			
		||||
  'x86/code-stubs-x86.cpp',
 | 
			
		||||
  'x86/jit_x86.cpp',
 | 
			
		||||
 | 
			
		||||
@ -90,7 +90,6 @@ class Environment : public ISourcePawnEnvironment
 | 
			
		||||
  void DisableProfiling();
 | 
			
		||||
 | 
			
		||||
  void SetJitEnabled(bool enabled) {
 | 
			
		||||
    jit_enabled_ = enabled;
 | 
			
		||||
  }
 | 
			
		||||
  bool IsJitEnabled() const {
 | 
			
		||||
    return jit_enabled_;
 | 
			
		||||
 | 
			
		||||
@ -1,857 +0,0 @@
 | 
			
		||||
// vim: set ts=8 sts=2 sw=2 tw=99 et:
 | 
			
		||||
//
 | 
			
		||||
// This file is part of SourcePawn.
 | 
			
		||||
// 
 | 
			
		||||
// SourcePawn is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// (at your option) any later version.
 | 
			
		||||
// 
 | 
			
		||||
// SourcePawn is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
// 
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "interpreter.h"
 | 
			
		||||
#include "opcodes.h"
 | 
			
		||||
#include "watchdog_timer.h"
 | 
			
		||||
#include "environment.h"
 | 
			
		||||
 | 
			
		||||
#define STACK_MARGIN 64
 | 
			
		||||
 | 
			
		||||
using namespace sp;
 | 
			
		||||
using namespace SourcePawn;
 | 
			
		||||
 | 
			
		||||
static inline bool
 | 
			
		||||
IsValidOffset(uint32_t cip)
 | 
			
		||||
{
 | 
			
		||||
  return cip % 4 == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline cell_t
 | 
			
		||||
Read(const sp_plugin_t *plugin, cell_t offset)
 | 
			
		||||
{
 | 
			
		||||
  return *reinterpret_cast<cell_t *>(plugin->memory + offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
Write(const sp_plugin_t *plugin, cell_t offset, cell_t value)
 | 
			
		||||
{
 | 
			
		||||
  *reinterpret_cast<cell_t *>(plugin->memory + offset) = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline cell_t *
 | 
			
		||||
Jump(const sp_plugin_t *plugin, cell_t target)
 | 
			
		||||
{
 | 
			
		||||
  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size)
 | 
			
		||||
    return NULL;
 | 
			
		||||
  return reinterpret_cast<cell_t *>(plugin->pcode + target);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline cell_t *
 | 
			
		||||
JumpTarget(const sp_plugin_t *plugin, cell_t *cip, bool cond, int *errp)
 | 
			
		||||
{
 | 
			
		||||
  if (!cond)
 | 
			
		||||
    return cip + 1;
 | 
			
		||||
 | 
			
		||||
  cell_t target = *cip;
 | 
			
		||||
  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
 | 
			
		||||
    *errp = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cell_t *next = reinterpret_cast<cell_t *>(plugin->pcode + target);
 | 
			
		||||
  if (next < cip && !Environment::get()->watchdog()->HandleInterrupt()) {
 | 
			
		||||
    *errp = SP_ERROR_TIMEOUT;
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return next;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval)
 | 
			
		||||
{
 | 
			
		||||
  const sp_plugin_t *plugin = rt->plugin();
 | 
			
		||||
  cell_t *code = reinterpret_cast<cell_t *>(plugin->pcode);
 | 
			
		||||
  cell_t *codeend = reinterpret_cast<cell_t *>(plugin->pcode + plugin->pcode_size);
 | 
			
		||||
 | 
			
		||||
  if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size)
 | 
			
		||||
    return SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
 | 
			
		||||
  PluginContext *cx = rt->GetBaseContext();
 | 
			
		||||
 | 
			
		||||
  int err = SP_ERROR_NONE;
 | 
			
		||||
 | 
			
		||||
  // Save the original frm. BaseContext won't, and if we error, we won't hit
 | 
			
		||||
  // the stack unwinding code.
 | 
			
		||||
  cell_t orig_frm = cx->frm();
 | 
			
		||||
 | 
			
		||||
  cell_t &frm = *cx->addressOfFrm();
 | 
			
		||||
  cell_t &sp = *cx->addressOfSp();
 | 
			
		||||
 | 
			
		||||
  cell_t pri = 0;
 | 
			
		||||
  cell_t alt = 0;
 | 
			
		||||
  cell_t *cip = code + (aCodeStart / 4);
 | 
			
		||||
  cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + sp);
 | 
			
		||||
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    if (cip >= codeend) {
 | 
			
		||||
      err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
      goto error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    SpewOpcode(plugin, reinterpret_cast<cell_t *>(plugin->pcode + aCodeStart), cip);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    OPCODE op = (OPCODE)*cip++;
 | 
			
		||||
 | 
			
		||||
    switch (op) {
 | 
			
		||||
      case OP_MOVE_PRI:
 | 
			
		||||
        pri = alt;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_MOVE_ALT:
 | 
			
		||||
        alt = pri;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_XCHG:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t tmp = pri;
 | 
			
		||||
        pri = alt;
 | 
			
		||||
        alt = tmp;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_ZERO:
 | 
			
		||||
        Write(plugin, *cip++, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_ZERO_S:
 | 
			
		||||
        Write(plugin, frm + *cip++, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_PUSH_PRI:
 | 
			
		||||
        *--stk = pri;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_PUSH_ALT:
 | 
			
		||||
        *--stk = alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_PUSH_C:
 | 
			
		||||
      case OP_PUSH2_C:
 | 
			
		||||
      case OP_PUSH3_C:
 | 
			
		||||
      case OP_PUSH4_C:
 | 
			
		||||
      case OP_PUSH5_C:
 | 
			
		||||
      {
 | 
			
		||||
        int n = 1;
 | 
			
		||||
        if (op >= OP_PUSH2_C)
 | 
			
		||||
          n = ((op - OP_PUSH2_C) / 4) + 2;
 | 
			
		||||
 | 
			
		||||
        int i = 1;
 | 
			
		||||
        do {
 | 
			
		||||
          *--stk = *cip++;
 | 
			
		||||
        } while (i++ < n);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_PUSH_ADR:
 | 
			
		||||
      case OP_PUSH2_ADR:
 | 
			
		||||
      case OP_PUSH3_ADR:
 | 
			
		||||
      case OP_PUSH4_ADR:
 | 
			
		||||
      case OP_PUSH5_ADR:
 | 
			
		||||
      {
 | 
			
		||||
        int n = 1;
 | 
			
		||||
        if (op >= OP_PUSH2_ADR)
 | 
			
		||||
          n = ((op - OP_PUSH2_ADR) / 4) + 2;
 | 
			
		||||
 | 
			
		||||
        int i = 1;
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
          cell_t addr = frm + *cip++;
 | 
			
		||||
          *--stk = addr;
 | 
			
		||||
        } while (i++ < n);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_PUSH_S:
 | 
			
		||||
      case OP_PUSH2_S:
 | 
			
		||||
      case OP_PUSH3_S:
 | 
			
		||||
      case OP_PUSH4_S:
 | 
			
		||||
      case OP_PUSH5_S:
 | 
			
		||||
      {
 | 
			
		||||
        int n = 1;
 | 
			
		||||
        if (op >= OP_PUSH2_S)
 | 
			
		||||
          n = ((op - OP_PUSH2_S) / 4) + 2;
 | 
			
		||||
 | 
			
		||||
        int i = 1;
 | 
			
		||||
        do {
 | 
			
		||||
          cell_t value = Read(plugin, frm + *cip++);
 | 
			
		||||
          *--stk = value;
 | 
			
		||||
        } while (i++ < n);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_PUSH:
 | 
			
		||||
      case OP_PUSH2:
 | 
			
		||||
      case OP_PUSH3:
 | 
			
		||||
      case OP_PUSH4:
 | 
			
		||||
      case OP_PUSH5:
 | 
			
		||||
      {
 | 
			
		||||
        int n = 1;
 | 
			
		||||
        if (op >= OP_PUSH2)
 | 
			
		||||
          n = ((op - OP_PUSH2) / 4) + 2;
 | 
			
		||||
 | 
			
		||||
        int i = 1;
 | 
			
		||||
        do {
 | 
			
		||||
          cell_t value = Read(plugin, *cip++);
 | 
			
		||||
          *--stk = value;
 | 
			
		||||
        } while (i++ < n);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_ZERO_PRI:
 | 
			
		||||
        pri = 0;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_ZERO_ALT:
 | 
			
		||||
        alt = 0;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_ADD:
 | 
			
		||||
        pri += alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SUB:
 | 
			
		||||
        pri -= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SUB_ALT:
 | 
			
		||||
        pri = alt - pri;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_PROC:
 | 
			
		||||
      {
 | 
			
		||||
        *--stk = frm;
 | 
			
		||||
        *--stk = 0;
 | 
			
		||||
        frm = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_IDXADDR_B:
 | 
			
		||||
        pri <<= *cip++;
 | 
			
		||||
        pri += alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SHL:
 | 
			
		||||
        pri <<= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SHR:
 | 
			
		||||
        pri = unsigned(pri) >> unsigned(alt);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SSHR:
 | 
			
		||||
        pri >>= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SHL_C_PRI:
 | 
			
		||||
        pri <<= *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_SHL_C_ALT:
 | 
			
		||||
        alt <<= *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SHR_C_PRI:
 | 
			
		||||
        pri >>= *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_SHR_C_ALT:
 | 
			
		||||
        alt >>= *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SMUL:
 | 
			
		||||
        pri *= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_NOT:
 | 
			
		||||
        pri = pri ? 0 : 1;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_NEG:
 | 
			
		||||
        pri = -pri;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_XOR:
 | 
			
		||||
        pri ^= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_OR:
 | 
			
		||||
        pri |= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_AND:
 | 
			
		||||
        pri &= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_INVERT:
 | 
			
		||||
        pri = ~pri;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_ADD_C:
 | 
			
		||||
        pri += *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SMUL_C:
 | 
			
		||||
        pri *= *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_EQ:
 | 
			
		||||
        pri = pri == alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_NEQ:
 | 
			
		||||
        pri = pri != alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SLESS:
 | 
			
		||||
        pri = pri < alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SLEQ:
 | 
			
		||||
        pri = pri <= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SGRTR:
 | 
			
		||||
        pri = pri > alt;
 | 
			
		||||
        break;
 | 
			
		||||
        
 | 
			
		||||
      case OP_SGEQ:
 | 
			
		||||
        pri = pri >= alt;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_EQ_C_PRI:
 | 
			
		||||
        pri = pri == *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_EQ_C_ALT:
 | 
			
		||||
        pri = alt == *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_INC_PRI:
 | 
			
		||||
        pri++;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_INC_ALT:
 | 
			
		||||
        alt++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_INC:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        Write(plugin, offset, Read(plugin, offset) + 1);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_INC_S:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t value = Read(plugin, frm + offset);
 | 
			
		||||
        Write(plugin, frm + offset, value + 1);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_INC_I:
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        Write(plugin, pri, Read(plugin, pri) + 1);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_DEC_PRI:
 | 
			
		||||
        pri--;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_DEC_ALT:
 | 
			
		||||
        alt--;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_DEC:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        Write(plugin, offset, Read(plugin, offset) - 1);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_DEC_S:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t value = Read(plugin, frm + offset);
 | 
			
		||||
        Write(plugin, frm + offset, value - 1);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_DEC_I:
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        Write(plugin, pri, Read(plugin, pri) - 1);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_LOAD_PRI:
 | 
			
		||||
        pri = Read(plugin, *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_LOAD_ALT:
 | 
			
		||||
        alt = Read(plugin, *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_LOAD_S_PRI:
 | 
			
		||||
        pri = Read(plugin, frm + *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_LOAD_S_ALT:
 | 
			
		||||
        alt = Read(plugin, frm + *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_LOAD_S_BOTH:
 | 
			
		||||
        pri = Read(plugin, frm + *cip++);
 | 
			
		||||
        alt = Read(plugin, frm + *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_LREF_S_PRI:
 | 
			
		||||
      {
 | 
			
		||||
        pri = Read(plugin, frm + *cip++);
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_LREF_S_ALT:
 | 
			
		||||
      {
 | 
			
		||||
        alt = Read(plugin, frm + *cip++);
 | 
			
		||||
        alt = Read(plugin, alt);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_CONST_PRI:
 | 
			
		||||
        pri = *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_CONST_ALT:
 | 
			
		||||
        alt = *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_ADDR_PRI:
 | 
			
		||||
        pri = frm + *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_ADDR_ALT:
 | 
			
		||||
        alt = frm + *cip++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_STOR_PRI:
 | 
			
		||||
        Write(plugin, *cip++, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_STOR_ALT:
 | 
			
		||||
        Write(plugin, *cip++, alt);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_STOR_S_PRI:
 | 
			
		||||
        Write(plugin, frm + *cip++, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_STOR_S_ALT:
 | 
			
		||||
        Write(plugin, frm +*cip++, alt);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_IDXADDR:
 | 
			
		||||
        pri = alt + pri * 4;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SREF_S_PRI:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t addr = Read(plugin, frm + offset);
 | 
			
		||||
        Write(plugin, addr, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_SREF_S_ALT:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t addr = Read(plugin, frm + offset);
 | 
			
		||||
        Write(plugin, addr, alt);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_POP_PRI:
 | 
			
		||||
        pri = *stk++;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_POP_ALT:
 | 
			
		||||
        alt = *stk++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SWAP_PRI:
 | 
			
		||||
      case OP_SWAP_ALT:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t reg = (op == OP_SREF_S_PRI) ? pri : alt;
 | 
			
		||||
        cell_t temp = *stk;
 | 
			
		||||
        *stk = reg;
 | 
			
		||||
        reg = temp;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_LIDX:
 | 
			
		||||
        pri = alt + pri * 4;
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_LIDX_B:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t val = *cip++;
 | 
			
		||||
        pri = alt + (pri << val);
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_CONST:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t value = *cip++;
 | 
			
		||||
        Write(plugin, offset, value);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_CONST_S:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t value = *cip++;
 | 
			
		||||
        Write(plugin, frm + offset, value);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_LOAD_I:
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_STOR_I:
 | 
			
		||||
        if (!cx->checkAddress(stk, alt)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        Write(plugin, alt, pri);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_SDIV:
 | 
			
		||||
      case OP_SDIV_ALT:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t dividend = (op == OP_SDIV) ? pri : alt;
 | 
			
		||||
        cell_t divisor = (op == OP_SDIV) ? alt : pri;
 | 
			
		||||
        if (divisor == 0) {
 | 
			
		||||
          err = SP_ERROR_DIVIDE_BY_ZERO;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        if (dividend == INT_MIN && divisor == -1) {
 | 
			
		||||
          err = SP_ERROR_INTEGER_OVERFLOW;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = dividend / divisor;
 | 
			
		||||
        alt = dividend % divisor;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      case OP_LODB_I:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t val = *cip++;
 | 
			
		||||
        if (!cx->checkAddress(stk, pri)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        pri = Read(plugin, pri);
 | 
			
		||||
        if (val == 1)
 | 
			
		||||
          pri &= 0xff;
 | 
			
		||||
        else if (val == 2)
 | 
			
		||||
          pri &= 0xffff;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_STRB_I:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t val = *cip++;
 | 
			
		||||
        if (!cx->checkAddress(stk, alt)) {
 | 
			
		||||
          err = SP_ERROR_MEMACCESS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        if (val == 1)
 | 
			
		||||
          *reinterpret_cast<int8_t *>(plugin->memory + alt) = pri;
 | 
			
		||||
        else if (val == 2)
 | 
			
		||||
          *reinterpret_cast<int16_t *>(plugin->memory + alt) = pri;
 | 
			
		||||
        else if (val == 4)
 | 
			
		||||
          *reinterpret_cast<int32_t *>(plugin->memory + alt) = pri;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_RETN:
 | 
			
		||||
      {
 | 
			
		||||
        stk++;
 | 
			
		||||
        frm = *stk++;
 | 
			
		||||
        stk += *stk + 1;
 | 
			
		||||
        *rval = pri;
 | 
			
		||||
        err = SP_ERROR_NONE;
 | 
			
		||||
        goto done;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_MOVS:
 | 
			
		||||
      {
 | 
			
		||||
        uint8_t *src = plugin->memory + pri;
 | 
			
		||||
        uint8_t *dest = plugin->memory + alt;
 | 
			
		||||
        memcpy(dest, src, *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_FILL:
 | 
			
		||||
      {
 | 
			
		||||
        uint8_t *dest = plugin->memory + alt;
 | 
			
		||||
        memset(dest, pri, *cip++);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_STRADJUST_PRI:
 | 
			
		||||
        pri += 4;
 | 
			
		||||
        pri >>= 2;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_STACK:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t amount = *cip++;
 | 
			
		||||
        if (!IsValidOffset(amount)) {
 | 
			
		||||
          err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stk += amount / 4;
 | 
			
		||||
        if (amount > 0) {
 | 
			
		||||
          if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) {
 | 
			
		||||
            err = SP_ERROR_STACKMIN;
 | 
			
		||||
            goto error;
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          if (uintptr_t(stk) < uintptr_t(plugin->memory + cx->hp() + STACK_MARGIN)) {
 | 
			
		||||
            err = SP_ERROR_STACKLOW;
 | 
			
		||||
            goto error;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_HEAP:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t amount = *cip++;
 | 
			
		||||
 | 
			
		||||
        alt = cx->hp();
 | 
			
		||||
        *cx->addressOfHp() += amount;
 | 
			
		||||
 | 
			
		||||
        if (amount > 0) {
 | 
			
		||||
          if (uintptr_t(plugin->memory + cx->hp()) > uintptr_t(stk)) {
 | 
			
		||||
            err = SP_ERROR_HEAPLOW;
 | 
			
		||||
            goto error;
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          if (uint32_t(cx->hp()) < plugin->data_size) {
 | 
			
		||||
            err = SP_ERROR_HEAPMIN;
 | 
			
		||||
            goto error;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_JUMP:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, true, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_JZER:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri == 0, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JNZ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri != 0, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_JEQ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri == alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JNEQ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri != alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JSLESS:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri < alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JSLEQ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri <= alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JSGRTR:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri > alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      case OP_JSGEQ:
 | 
			
		||||
        if ((cip = JumpTarget(plugin, cip, pri >= alt, &err)) == NULL)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_TRACKER_PUSH_C:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t amount = *cip++;
 | 
			
		||||
        int error = cx->pushTracker(amount * 4);
 | 
			
		||||
        if (error != SP_ERROR_NONE) {
 | 
			
		||||
          err = error;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_TRACKER_POP_SETHEAP:
 | 
			
		||||
      {
 | 
			
		||||
        int error = cx->popTrackerAndSetHeap();
 | 
			
		||||
        if (error != SP_ERROR_NONE) {
 | 
			
		||||
          err = error;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_BREAK:
 | 
			
		||||
        *cx->addressOfCip() = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case OP_BOUNDS:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t value = *cip++;
 | 
			
		||||
        if (uint32_t(pri) > uint32_t(value)) {
 | 
			
		||||
          err = SP_ERROR_ARRAY_BOUNDS;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_CALL:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
 | 
			
		||||
        if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) {
 | 
			
		||||
          err = SP_ERROR_INSTRUCTION_PARAM;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // For debugging.
 | 
			
		||||
        uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode);
 | 
			
		||||
        if (!cx->pushReturnCip(rcip)) {
 | 
			
		||||
          err = SP_ERROR_STACKLOW;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        *cx->addressOfCip() = offset;
 | 
			
		||||
        sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
 | 
			
		||||
        int err = Interpret(rt, offset, &pri);
 | 
			
		||||
 | 
			
		||||
        stk = reinterpret_cast<cell_t *>(plugin->memory + sp);
 | 
			
		||||
        *cx->addressOfCip() = rcip;
 | 
			
		||||
        cx->popReturnCip();
 | 
			
		||||
 | 
			
		||||
        if (err != SP_ERROR_NONE)
 | 
			
		||||
          goto error;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_GENARRAY:
 | 
			
		||||
      case OP_GENARRAY_Z:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t val = *cip++;
 | 
			
		||||
        if ((err = cx->generateArray(val, stk, op == OP_GENARRAY_Z)) != SP_ERROR_NONE)
 | 
			
		||||
          goto error;
 | 
			
		||||
 | 
			
		||||
        stk += (val - 1) * 4;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_SYSREQ_C:
 | 
			
		||||
      case OP_SYSREQ_N:
 | 
			
		||||
      {
 | 
			
		||||
        uint32_t native_index = *cip++;
 | 
			
		||||
 | 
			
		||||
        if (native_index >= plugin->num_natives) {
 | 
			
		||||
          err = SP_ERROR_INSTRUCTION_PARAM;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint32_t num_params;
 | 
			
		||||
        if (op == OP_SYSREQ_N) {
 | 
			
		||||
          num_params = *cip++;
 | 
			
		||||
          *--stk = num_params;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
        pri = cx->invokeNative(native_index, stk);
 | 
			
		||||
        if (cx->GetLastNativeError() != SP_ERROR_NONE) {
 | 
			
		||||
          err = cx->GetLastNativeError();
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (op == OP_SYSREQ_N)
 | 
			
		||||
          stk += num_params + 1;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case OP_SWITCH:
 | 
			
		||||
      {
 | 
			
		||||
        cell_t offset = *cip++;
 | 
			
		||||
        cell_t *table = reinterpret_cast<cell_t *>(plugin->pcode + offset + sizeof(cell_t));
 | 
			
		||||
 | 
			
		||||
        size_t ncases = *table++;
 | 
			
		||||
        cell_t target = *table++;   // default case
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < ncases; i++) {
 | 
			
		||||
          if (table[i * 2] == pri) {
 | 
			
		||||
            target = table[i * 2 + 1];
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((cip = Jump(plugin, target)) == NULL) {
 | 
			
		||||
          err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
          goto error;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
      {
 | 
			
		||||
        err = SP_ERROR_INVALID_INSTRUCTION;
 | 
			
		||||
        goto error;
 | 
			
		||||
      }
 | 
			
		||||
    } // switch
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 done:
 | 
			
		||||
  assert(orig_frm == frm);
 | 
			
		||||
  sp = uintptr_t(stk) - uintptr_t(plugin->memory);
 | 
			
		||||
  return err;
 | 
			
		||||
 | 
			
		||||
 error:
 | 
			
		||||
  frm = orig_frm;
 | 
			
		||||
  goto done;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,27 +0,0 @@
 | 
			
		||||
// vim: set ts=8 sts=2 sw=2 tw=99 et:
 | 
			
		||||
//
 | 
			
		||||
// This file is part of SourcePawn.
 | 
			
		||||
// 
 | 
			
		||||
// SourcePawn is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// (at your option) any later version.
 | 
			
		||||
// 
 | 
			
		||||
// SourcePawn is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
// 
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#ifndef _include_sourcepawn_interpreter_h_
 | 
			
		||||
#define _include_sourcepawn_interpreter_h_
 | 
			
		||||
 | 
			
		||||
#include <sp_vm_types.h>
 | 
			
		||||
#include <sp_vm_api.h>
 | 
			
		||||
#include "plugin-runtime.h"
 | 
			
		||||
#include "plugin-context.h"
 | 
			
		||||
 | 
			
		||||
int Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval);
 | 
			
		||||
 | 
			
		||||
#endif // _include_sourcepawn_interpreter_h_
 | 
			
		||||
@ -18,7 +18,6 @@
 | 
			
		||||
#include "plugin-context.h"
 | 
			
		||||
#include "watchdog_timer.h"
 | 
			
		||||
#include "x86/jit_x86.h"
 | 
			
		||||
#include "interpreter.h"
 | 
			
		||||
#include "environment.h"
 | 
			
		||||
 | 
			
		||||
using namespace SourcePawn;
 | 
			
		||||
@ -593,10 +592,7 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne
 | 
			
		||||
 | 
			
		||||
  // Enter the execution engine.
 | 
			
		||||
  Environment *env = Environment::get();
 | 
			
		||||
  if (env->IsJitEnabled())
 | 
			
		||||
    ir = env->Invoke(m_pRuntime, fn, result);
 | 
			
		||||
  else
 | 
			
		||||
    ir = Interpret(m_pRuntime, cfun->Public()->code_offs, result);
 | 
			
		||||
  ir = env->Invoke(m_pRuntime, fn, result);
 | 
			
		||||
 | 
			
		||||
  /* Restore some states, stop the frame tracer */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,17 +32,11 @@ IsPointerCellAligned(void *p)
 | 
			
		||||
 | 
			
		||||
PluginRuntime::PluginRuntime()
 | 
			
		||||
  : m_Debug(&m_plugin),
 | 
			
		||||
    m_pCtx(NULL), 
 | 
			
		||||
    m_PubFuncs(NULL),
 | 
			
		||||
    m_CompSerial(0)
 | 
			
		||||
{
 | 
			
		||||
  memset(&m_plugin, 0, sizeof(m_plugin));
 | 
			
		||||
 | 
			
		||||
  m_MaxFuncs = 0;
 | 
			
		||||
  m_NumFuncs = 0;
 | 
			
		||||
  float_table_ = NULL;
 | 
			
		||||
  alt_pcode_ = NULL;
 | 
			
		||||
  
 | 
			
		||||
  memset(m_CodeHash, 0, sizeof(m_CodeHash));
 | 
			
		||||
  memset(m_DataHash, 0, sizeof(m_DataHash));
 | 
			
		||||
 | 
			
		||||
@ -63,14 +57,10 @@ PluginRuntime::~PluginRuntime()
 | 
			
		||||
  for (uint32_t i = 0; i < m_plugin.num_publics; i++)
 | 
			
		||||
    delete m_PubFuncs[i];
 | 
			
		||||
  delete [] m_PubFuncs;
 | 
			
		||||
  delete [] float_table_;
 | 
			
		||||
  delete [] alt_pcode_;
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < m_JitFunctions.length(); i++)
 | 
			
		||||
    delete m_JitFunctions[i];
 | 
			
		||||
 | 
			
		||||
  delete m_pCtx;
 | 
			
		||||
 | 
			
		||||
  free(m_plugin.base);
 | 
			
		||||
  delete [] m_plugin.memory;
 | 
			
		||||
  delete [] m_plugin.publics;
 | 
			
		||||
 | 
			
		||||
@ -107,10 +107,8 @@ class PluginRuntime
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  sp_plugin_t m_plugin;
 | 
			
		||||
  uint8_t *alt_pcode_;
 | 
			
		||||
  unsigned int m_NumFuncs;
 | 
			
		||||
  unsigned int m_MaxFuncs;
 | 
			
		||||
  floattbl_t *float_table_;
 | 
			
		||||
  ke::AutoArray<uint8_t> alt_pcode_;
 | 
			
		||||
  ke::AutoArray<floattbl_t> float_table_;
 | 
			
		||||
 | 
			
		||||
  struct FunctionMapPolicy {
 | 
			
		||||
    static inline uint32_t hash(ucell_t value) {
 | 
			
		||||
@ -127,7 +125,7 @@ class PluginRuntime
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  DebugInfo m_Debug;
 | 
			
		||||
  PluginContext *m_pCtx;
 | 
			
		||||
  ke::AutoPtr<PluginContext> m_pCtx;
 | 
			
		||||
  ScriptedInvoker **m_PubFuncs;
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,6 @@
 | 
			
		||||
#include "plugin-runtime.h"
 | 
			
		||||
#include "plugin-context.h"
 | 
			
		||||
#include "watchdog_timer.h"
 | 
			
		||||
#include "interpreter.h"
 | 
			
		||||
#include "environment.h"
 | 
			
		||||
#include "code-stubs.h"
 | 
			
		||||
#include "x86-utils.h"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user