348 lines
8.0 KiB
C++
348 lines
8.0 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "ScriptAPI/ScriptAPI.h"
|
|
#include "ScriptInstance.h"
|
|
#include "ScriptTypes.h"
|
|
#include <sys/stat.h>
|
|
|
|
extern "C"
|
|
{
|
|
int DukTimeoutCheck(void * udata)
|
|
{
|
|
CScriptInstance * inst = (CScriptInstance *)udata;
|
|
return (int)inst->IsTimedOut();
|
|
}
|
|
}
|
|
|
|
CScriptInstance::CScriptInstance(CScriptSystem * sys, const char * name) :
|
|
m_System(sys),
|
|
m_InstanceName(name),
|
|
m_Ctx(nullptr),
|
|
m_RefCount(0),
|
|
m_ExecTimeout(JS_EXEC_TIMEOUT),
|
|
m_ExecStartTime(0),
|
|
m_SourceCode(nullptr),
|
|
m_CurExecCallbackId(JS_INVALID_CALLBACK),
|
|
m_bStopping(false),
|
|
m_bAborting(false)
|
|
{
|
|
}
|
|
|
|
CScriptInstance::~CScriptInstance()
|
|
{
|
|
Cleanup();
|
|
}
|
|
|
|
std::string & CScriptInstance::Name()
|
|
{
|
|
return m_InstanceName;
|
|
}
|
|
|
|
CScriptSystem * CScriptInstance::System()
|
|
{
|
|
return m_System;
|
|
}
|
|
|
|
CDebuggerUI * CScriptInstance::Debugger()
|
|
{
|
|
return m_System->Debugger();
|
|
}
|
|
|
|
JSAppCallbackID CScriptInstance::CallbackId()
|
|
{
|
|
return m_CurExecCallbackId;
|
|
}
|
|
|
|
bool CScriptInstance::Run(const char * path)
|
|
{
|
|
if (m_Ctx != nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_Ctx = duk_create_heap(nullptr, nullptr, nullptr, this, nullptr);
|
|
|
|
if (m_Ctx == nullptr)
|
|
{
|
|
goto error_cleanup;
|
|
}
|
|
|
|
struct stat statBuf;
|
|
if (stat(path, &statBuf) != 0)
|
|
{
|
|
m_System->ConsoleLog("[SCRIPTSYS]: error: could not stat '%s'", path);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
m_SourceCode = new char[statBuf.st_size + 1];
|
|
m_SourceCode[statBuf.st_size] = '\0';
|
|
|
|
m_SourceFile.open(path, std::ios::in | std::ios::binary);
|
|
if (!m_SourceFile.is_open())
|
|
{
|
|
m_System->ConsoleLog("[SCRIPTSYS]: error: could not open '%s'", path);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
m_SourceFile.read(m_SourceCode, statBuf.st_size);
|
|
|
|
if ((size_t)m_SourceFile.tellg() != (size_t)statBuf.st_size)
|
|
{
|
|
m_System->ConsoleLog("[SCRIPTSYS]: error: could not read '%s'", path);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
m_ExecStartTime = Timestamp();
|
|
|
|
ScriptAPI::InitEnvironment(m_Ctx, this);
|
|
|
|
try
|
|
{
|
|
duk_push_string(m_Ctx, m_InstanceName.c_str());
|
|
if (duk_pcompile_string_filename(m_Ctx, DUK_COMPILE_STRICT, m_SourceCode) != 0 || duk_pcall(m_Ctx, 0) == DUK_EXEC_ERROR)
|
|
{
|
|
duk_get_prop_string(m_Ctx, -1, "stack");
|
|
m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1));
|
|
duk_pop_n(m_Ctx, 2);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
duk_pop(m_Ctx);
|
|
}
|
|
catch (std::runtime_error & exc)
|
|
{
|
|
FatalHandler(exc);
|
|
}
|
|
|
|
return true;
|
|
|
|
error_cleanup:
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
|
|
void CScriptInstance::IncRefCount()
|
|
{
|
|
m_RefCount++;
|
|
}
|
|
|
|
void CScriptInstance::DecRefCount()
|
|
{
|
|
if (m_RefCount > 0)
|
|
{
|
|
m_RefCount--;
|
|
}
|
|
}
|
|
|
|
void CScriptInstance::SetStopping(bool bStopping)
|
|
{
|
|
m_bStopping = bStopping;
|
|
}
|
|
|
|
bool CScriptInstance::PrepareAbort()
|
|
{
|
|
if (!m_bAborting)
|
|
{
|
|
m_bAborting = true;
|
|
m_RefCount = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CScriptInstance::RawCMethodCall(void * dukThisHeapPtr, duk_c_function func, JSDukArgSetupFunc argSetupFunc, void * argSetupParam)
|
|
{
|
|
try
|
|
{
|
|
m_ExecStartTime = Timestamp();
|
|
duk_push_c_function(m_Ctx, func, DUK_VARARGS);
|
|
duk_push_heapptr(m_Ctx, dukThisHeapPtr);
|
|
duk_idx_t nargs = argSetupFunc ? argSetupFunc(m_Ctx, argSetupParam) : 0;
|
|
|
|
if (duk_pcall_method(m_Ctx, nargs) == DUK_EXEC_ERROR)
|
|
{
|
|
duk_get_prop_string(m_Ctx, -1, "stack");
|
|
m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1));
|
|
duk_pop(m_Ctx);
|
|
}
|
|
|
|
duk_pop(m_Ctx);
|
|
}
|
|
catch (std::runtime_error & exc)
|
|
{
|
|
FatalHandler(exc);
|
|
}
|
|
}
|
|
|
|
void CScriptInstance::PostCMethodCall(void * dukThisHeapPtr, duk_c_function func, JSDukArgSetupFunc argSetupFunc,
|
|
void * argSetupParam, size_t argSetupParamSize)
|
|
{
|
|
m_System->PostCMethodCall(m_InstanceName.c_str(), dukThisHeapPtr, func, argSetupFunc, argSetupParam, argSetupParamSize);
|
|
}
|
|
|
|
void CScriptInstance::RawConsoleInput(const char * code)
|
|
{
|
|
m_System->ConsoleLog("> %s", code);
|
|
|
|
try
|
|
{
|
|
duk_get_global_string(m_Ctx, HS_gInputListener);
|
|
|
|
if (duk_is_function(m_Ctx, -1))
|
|
{
|
|
m_ExecStartTime = Timestamp();
|
|
duk_push_string(m_Ctx, code);
|
|
if (duk_pcall(m_Ctx, 1) != DUK_EXEC_SUCCESS)
|
|
{
|
|
duk_get_prop_string(m_Ctx, -1, "stack");
|
|
m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1));
|
|
duk_pop_n(m_Ctx, 2);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
duk_pop(m_Ctx);
|
|
return;
|
|
}
|
|
}
|
|
duk_pop(m_Ctx);
|
|
|
|
m_ExecStartTime = Timestamp();
|
|
|
|
duk_push_string(m_Ctx, stdstr_f("<input:%s>", m_InstanceName.c_str()).c_str());
|
|
if (duk_pcompile_string_filename(m_Ctx, DUK_COMPILE_STRICT, code) != 0 || duk_pcall(m_Ctx, 0) == DUK_EXEC_ERROR)
|
|
{
|
|
duk_get_prop_string(m_Ctx, -1, "stack");
|
|
m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1));
|
|
duk_pop(m_Ctx);
|
|
}
|
|
else
|
|
{
|
|
if (duk_is_string(m_Ctx, -1))
|
|
{
|
|
m_System->ConsoleLog("\"%s\"", duk_get_string(m_Ctx, -1));
|
|
}
|
|
else if (duk_is_object(m_Ctx, -1))
|
|
{
|
|
duk_dup(m_Ctx, -1);
|
|
duk_get_global_string(m_Ctx, "JSON");
|
|
duk_get_prop_string(m_Ctx, -1, "stringify");
|
|
duk_remove(m_Ctx, -2);
|
|
duk_pull(m_Ctx, -3);
|
|
duk_push_null(m_Ctx);
|
|
duk_push_int(m_Ctx, 2);
|
|
duk_pcall(m_Ctx, 3);
|
|
|
|
const char * str = duk_safe_to_string(m_Ctx, -2);
|
|
const char * res = duk_get_string(m_Ctx, -1);
|
|
|
|
m_System->ConsoleLog("%s %s", str, res);
|
|
duk_pop(m_Ctx);
|
|
}
|
|
else
|
|
{
|
|
m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1));
|
|
}
|
|
}
|
|
duk_pop(m_Ctx);
|
|
}
|
|
catch (std::runtime_error & exc)
|
|
{
|
|
FatalHandler(exc);
|
|
}
|
|
}
|
|
|
|
void CScriptInstance::SetExecTimeout(uint64_t timeout)
|
|
{
|
|
m_ExecTimeout = timeout;
|
|
}
|
|
|
|
bool CScriptInstance::IsTimedOut()
|
|
{
|
|
if (m_ExecStartTime == 0 || m_ExecTimeout == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint64_t timeElapsed = Timestamp() - m_ExecStartTime;
|
|
return (timeElapsed >= m_ExecTimeout);
|
|
}
|
|
|
|
uint64_t CScriptInstance::Timestamp()
|
|
{
|
|
FILETIME ft;
|
|
GetSystemTimeAsFileTime(&ft);
|
|
|
|
ULARGE_INTEGER li;
|
|
li.LowPart = ft.dwLowDateTime;
|
|
li.HighPart = ft.dwHighDateTime;
|
|
|
|
return li.QuadPart / 10000;
|
|
}
|
|
|
|
void CScriptInstance::Cleanup()
|
|
{
|
|
if (m_Ctx != nullptr)
|
|
{
|
|
duk_destroy_heap(m_Ctx);
|
|
m_Ctx = nullptr;
|
|
}
|
|
if (m_SourceCode != nullptr)
|
|
{
|
|
delete[] m_SourceCode;
|
|
m_SourceCode = nullptr;
|
|
}
|
|
if (m_SourceFile.is_open())
|
|
{
|
|
m_SourceFile.close();
|
|
}
|
|
}
|
|
|
|
bool CScriptInstance::RegisterWorker(CScriptWorker * worker)
|
|
{
|
|
if (IsStopping())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_Workers.push_back(worker);
|
|
return true;
|
|
}
|
|
|
|
void CScriptInstance::UnregisterWorker(CScriptWorker * worker)
|
|
{
|
|
std::vector<CScriptWorker *>::iterator it;
|
|
|
|
for (it = m_Workers.begin(); it != m_Workers.end(); it++)
|
|
{
|
|
if (*it == worker)
|
|
{
|
|
m_Workers.erase(it);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CScriptInstance::StopRegisteredWorkers()
|
|
{
|
|
std::vector<CScriptWorker *>::iterator it;
|
|
for (it = m_Workers.begin(); it != m_Workers.end(); it++)
|
|
{
|
|
CScriptWorker * worker = *it;
|
|
worker->StopWorkerProc();
|
|
}
|
|
}
|
|
|
|
void CScriptInstance::FatalHandler(std::runtime_error & exc)
|
|
{
|
|
if (m_bAborting)
|
|
{
|
|
m_System->ConsoleLog("[SCRIPTSYS]: '%s' aborted", m_InstanceName.c_str());
|
|
}
|
|
else
|
|
{
|
|
m_System->ConsoleLog("%s", exc.what());
|
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
|
}
|
|
}
|