* Add STL-standard type traits and functions to TMap to enable for loop iteration * add third-party range-map library I have e-mailed the author for clarifcation on the license, will update this when they respond * vcpkg: Add cppdap and eventpp libraries, update baseline * DAP implementation * Add `FileSystem::FileHash()` to get the CRC32 hash * add `starting_offset` param to `VMDisasm()` for debugger disassembly view Defaults to `0`, should not change output if it's not set * Add `VMFrameStack::HasFrames()` to prevent assertions when inspecting in the debugger * Add `PC` field to VMFrame, ensure that it is updated whenever vm increments/decrements the pc Does not change alignment, the offsets used in VMFrame still work We need this for the debugger because we otherwise have no way to get the pc; it was a local in `ExecScriptFunc()` * `ZCCCompiler::CreateClassTypes()`: ensure SourceLumpName is set for classes derived from non-native classes * start debug server in d_main, add `vm_debug` cvars and `-debug` CLI arg * Add documentation for `vm_debug` cvars * vm_exec: Add debugger hooks for instruction execution events * c_console: Add debugger hooks for logging events in `PrintString` * add `.cache/` to gitignore * vendor cppdap on main @ 6464cd7 Patches: removed .gitmodules (submodules were thirdparty/json, thirdparty/googletest) removed thirdparty/googletest, not needed removed thirdparty/json/docs, thirdparty/json/test, thirdparty/json/benchmarks to prevent massive bloat * vendor eventpp on master @ 1224dd6 * build: use internal cppdap and eventpp by default * dap: fix Binary::GetFunctionLineRange() * fix bug in range_map::find_ranges * make Binary dap::Source dynamic * cache source code upon retrieval * refactor Binary into a class, cleanup PexCache * fix ending session gracefully * d_main: Stop debug server in D_Cleanup() * Fix connecting to debugger when session already started * cleanup unused stuff in ZScriptDebugger * always send TerminateEvents on disconnect if initialized * tweak color display * Don't cache disassembly lines when scanning scripts * Cache nodes when getting runtime state * WIP display locals * Fix display of static arrays * Ensure names display in proper order * dap: Fix struct locals display, add args display * Support `start` parameter * Support `filter` parameter * remove struct unbound native data display Practically useless for debugging zscript and didn't work properly anyway * d_main: fix vm_jit and vm_jit not being disabled soon enough * support breaking on abort exceptions * dap: refactor game event emitter functions into seperate header * dap: show native functions on stack * dap: Remove "Native" from exception handling, simplify exception event emit * dap: add instruction breakpoints * dap: fix display of locals not in scope yet * dap: Make disassembly view display invalid instructions for non-code addresses * dap: remove dot initializers * dap: fix local structs in scope * dap: don't parse the non-used options in the launch/attach requests * dap: fix local struct view * dap: Fix displaying objects that aren't their actual types * dap: Fix action and state handling * dap: stack display view * dap: fix object display view * d_dehacked: set qualified name in addition to the printable name * dap: fix displaying breakpoint errors when script isn't loaded * dap: remove debug print * dap: Display parameter names * dap: Turn down verbosity of logging * dap: fix disassembly view * dap: fix performance problems with arrays * c_console: emit event only if not PRINT_NODAPEVENT * dap: improve logging * dap: update upstream cppdap library to fix deadlocks on no bind * dap: Fix ending session on client socket closed * dap: prevent DebugServer.h from pulling in `dap` and `ZScriptDebugger.h` * dap: fix pause event not being emitted on pause * dap: remove eventpp emitters, way too slow * Remove eventpp dependency * dap: Display correct register names * dap: Show special inits in registers * dap: Add stack offset to VMLocalVariable * dap: fix display of static arrays and local variables on stack vs. registers * dap: fix displaying function pointers * dap: tweak color display * dap: fix scalar display < 4 * dap: add Globals display to debugger * dap: unify methods to get vmvalue * dap: rename free method to freeValue to avoid running afoul of macro defs * fix windows builds * fix compile on linux * cleanup * Fix display of function breakpoints * dap: Don't send back binary files * dap: include sbarinfo in script types (no debug support for anything but zscript yet, this is just for returning source info) * dap: don't show ending session message unless initialized * fix erroneous commit * dap: handle evaluate requests * Fix getting bitfield values * Add CVars Scope and evaluation * add running console commands from repl * dap: disable commands via repl for now * fix loading functions DECORATE scripts * Add source information to Dehacked VM functions, add debugging support * dap: cleanup * fix resolving archive paths * don't send source back on native stack frames * handle `modules` request * cleanup * allow evaluating cvars on hover * fix oob bpinfos * fix restarting the game blowing out the debug server * dap: process input events while paused to prevent deadlocks * fix getting local state * dap: fix LocalState alignment * dap: fix DumpStateHelper * update cppdap protocol version to 1.68.0 * remove cppdap from vcpkg deps We can't use the upstream version anyway because the maintainers are not merging our patches * dap: make named variable nodes derive from the same class * dap: make cvar scope available in native stack frames * handle local variables with conflicting names * add I_GetWindowEvent() to win32 to only process window events when debugging is paused * dap: fix evaluate * dap: fix display of `out` variables
258 lines
6.8 KiB
C++
258 lines
6.8 KiB
C++
/*
|
|
** vmexec.cpp
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright -2016 Randy Heit
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
#include "v_video.h"
|
|
#include "s_soundinternal.h"
|
|
#include "basics.h"
|
|
//#include "r_state.h"
|
|
#include "stats.h"
|
|
#include "vmintern.h"
|
|
#include "types.h"
|
|
#include "basics.h"
|
|
#include "texturemanager.h"
|
|
#include "palutil.h"
|
|
#include "common/scripting/dap/GameEventEmit.h"
|
|
|
|
extern cycle_t VMCycles[10];
|
|
extern int VMCalls[10];
|
|
|
|
// THe sprite ID to string cast is game specific so let's do it with a callback to remove the dependency and allow easier reuse.
|
|
void (*VM_CastSpriteIDToString)(FString* a, unsigned int b) = [](FString* a, unsigned int b) { a->Format("%d", b); };
|
|
|
|
// intentionally implemented in a different source file to prevent inlining.
|
|
#if 0
|
|
void ThrowVMException(VMException *x);
|
|
#endif
|
|
|
|
#define IMPLEMENT_VMEXEC
|
|
|
|
#if !defined(COMPGOTO) && defined(__GNUC__)
|
|
#define COMPGOTO 1
|
|
#endif
|
|
|
|
#if COMPGOTO
|
|
#define OP(x) x
|
|
#define NEXTOP do { pc++; DebugServer::RuntimeEvents::EmitInstructionExecutionEvent(stack, ret, numret, pc); unsigned op = pc->op; a = pc->a; goto *ops[op]; } while(0)
|
|
#else
|
|
#define OP(x) case OP_##x
|
|
#define NEXTOP pc++; break
|
|
#endif
|
|
|
|
#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b))
|
|
|
|
#define A (pc[0].a)
|
|
#define B (pc[0].b)
|
|
#define C (pc[0].c)
|
|
#define Cs (pc[0].cs)
|
|
#define BC (pc[0].i16u)
|
|
#define BCs (pc[0].i16)
|
|
#define ABCs (pc[0].i24)
|
|
#define JMPOFS(x) ((x)->i24)
|
|
|
|
#define KC (konstd[C])
|
|
#define RC (reg.d[C])
|
|
|
|
#define PA (reg.a[A])
|
|
#define PB (reg.a[B])
|
|
|
|
#define ASSERTD(x) assert((unsigned)(x) < f->NumRegD)
|
|
#define ASSERTF(x) assert((unsigned)(x) < f->NumRegF)
|
|
#define ASSERTA(x) assert((unsigned)(x) < f->NumRegA)
|
|
#define ASSERTS(x) assert((unsigned)(x) < f->NumRegS)
|
|
|
|
#define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD)
|
|
#define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF)
|
|
#define ASSERTKA(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstA)
|
|
#define ASSERTKS(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstS)
|
|
|
|
#define CMPJMP(test) \
|
|
if ((test) == (a & CMP_CHECK)) { \
|
|
assert(pc[1].op == OP_JMP); \
|
|
pc += 1 + JMPOFS(pc+1); \
|
|
} else { \
|
|
pc += 1; \
|
|
}
|
|
|
|
#define GETADDR(a,o,x) \
|
|
if (a == NULL) { ThrowAbortException(x, nullptr); return 0; } \
|
|
ptr = (VM_SBYTE *)a + o
|
|
|
|
#ifdef NDEBUG
|
|
#define WAS_NDEBUG 1
|
|
#else
|
|
#define WAS_NDEBUG 0
|
|
#endif
|
|
|
|
#if WAS_NDEBUG
|
|
#undef NDEBUG
|
|
#endif
|
|
#undef assert
|
|
#include <assert.h>
|
|
struct VMExec_Checked
|
|
{
|
|
#include "vmexec.h"
|
|
};
|
|
#if WAS_NDEBUG
|
|
#define NDEBUG
|
|
#endif
|
|
|
|
#if !WAS_NDEBUG
|
|
#define NDEBUG
|
|
#endif
|
|
#undef assert
|
|
#include <assert.h>
|
|
struct VMExec_Unchecked
|
|
{
|
|
#include "vmexec.h"
|
|
};
|
|
#if !WAS_NDEBUG
|
|
#undef NDEBUG
|
|
#endif
|
|
#undef assert
|
|
#include <assert.h>
|
|
|
|
int (*VMExec)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) =
|
|
#ifdef NDEBUG
|
|
VMExec_Unchecked::Exec
|
|
#else
|
|
VMExec_Checked::Exec
|
|
#endif
|
|
;
|
|
|
|
// Note: If the VM is being used in multiple threads, this should be declared as thread_local.
|
|
// ZDoom doesn't need this at the moment so this is disabled.
|
|
|
|
thread_local VMFrameStack GlobalVMStack;
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// VMSelectEngine
|
|
//
|
|
// Selects the VM engine, either checked or unchecked. Default will decide
|
|
// based on the NDEBUG preprocessor definition.
|
|
//
|
|
//===========================================================================
|
|
|
|
void VMSelectEngine(EVMEngine engine)
|
|
{
|
|
switch (engine)
|
|
{
|
|
case VMEngine_Default:
|
|
#ifdef NDEBUG
|
|
VMExec = VMExec_Unchecked::Exec;
|
|
#else
|
|
#endif
|
|
VMExec = VMExec_Checked::Exec;
|
|
break;
|
|
case VMEngine_Unchecked:
|
|
VMExec = VMExec_Unchecked::Exec;
|
|
break;
|
|
case VMEngine_Checked:
|
|
VMExec = VMExec_Checked::Exec;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// VMFillParams
|
|
//
|
|
// Takes parameters from the parameter stack and stores them in the callee's
|
|
// registers.
|
|
//
|
|
//===========================================================================
|
|
|
|
void VMFillParams(VMValue *params, VMFrame *callee, int numparam)
|
|
{
|
|
unsigned int regd, regf, regs, rega;
|
|
VMScriptFunction *calleefunc = static_cast<VMScriptFunction *>(callee->Func);
|
|
const VMRegisters calleereg(callee);
|
|
|
|
assert(calleefunc != NULL && !(calleefunc->VarFlags & VARF_Native));
|
|
assert(numparam == calleefunc->NumArgs);
|
|
assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3);
|
|
|
|
regd = regf = regs = rega = 0;
|
|
const uint8_t *reginfo = calleefunc->RegTypes;
|
|
assert(reginfo != nullptr);
|
|
for (int i = 0; i < calleefunc->NumArgs; ++i, reginfo++)
|
|
{
|
|
// copy all parameters to the local registers.
|
|
VMValue &p = params[i];
|
|
if (*reginfo < REGT_STRING)
|
|
{
|
|
if (*reginfo == REGT_INT)
|
|
{
|
|
calleereg.d[regd++] = p.i;
|
|
}
|
|
else // p.Type == REGT_FLOAT
|
|
{
|
|
calleereg.f[regf++] = p.f;
|
|
}
|
|
}
|
|
else if (*reginfo == REGT_STRING)
|
|
{
|
|
calleereg.s[regs++] = p.s();
|
|
}
|
|
else
|
|
{
|
|
assert(*reginfo == REGT_POINTER);
|
|
calleereg.a[rega++] = p.a;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef NDEBUG
|
|
bool AssertObject(void * ob)
|
|
{
|
|
auto obj = (DObject*)ob;
|
|
if (obj == nullptr) return true;
|
|
#ifdef _MSC_VER
|
|
__try
|
|
{
|
|
return obj->MagicID == DObject::MAGIC_ID;
|
|
}
|
|
__except (1)
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
// No SEH on non-Microsoft compilers. :(
|
|
return obj->MagicID == DObject::MAGIC_ID;
|
|
#endif
|
|
}
|
|
#endif
|