1035 lines
30 KiB
C++
1035 lines
30 KiB
C++
#include <stdafx.h>
|
|
#include <dwrite.h>
|
|
#include "ScriptAPI.h"
|
|
#include "N64Image.h"
|
|
|
|
#pragma warning(disable: 4702) // disable unreachable code warning
|
|
|
|
void ScriptAPI::InitEnvironment(duk_context* ctx, CScriptInstance* inst)
|
|
{
|
|
duk_push_global_object(ctx);
|
|
duk_push_string(ctx, "global");
|
|
duk_dup(ctx, -2);
|
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
|
duk_pop(ctx);
|
|
|
|
duk_push_global_object(ctx);
|
|
duk_push_string(ctx, "PJ64_JSAPI_VERSION");
|
|
duk_push_string(ctx, PJ64_JSAPI_VERSION);
|
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
|
duk_pop(ctx);
|
|
|
|
duk_module_duktape_init(ctx);
|
|
duk_get_global_string(ctx, "Duktape");
|
|
duk_push_c_function(ctx, js_Duktape_modSearch, 4);
|
|
duk_put_prop_string(ctx, -2, "modSearch");
|
|
duk_pop(ctx);
|
|
|
|
duk_push_pointer(ctx, inst);
|
|
duk_put_global_string(ctx, HS_gInstancePtr);
|
|
|
|
duk_push_object(ctx); // callbackId => { hookId, callbackId, function }
|
|
duk_put_global_string(ctx, HS_gAppCallbacks);
|
|
|
|
duk_push_object(ctx); // fd => { fp }
|
|
duk_put_global_string(ctx, HS_gOpenFileDescriptors);
|
|
|
|
duk_push_array(ctx); // [{modPtr: hModule}, ...]
|
|
duk_put_global_string(ctx, HS_gNativeModules);
|
|
|
|
duk_push_int(ctx, 0);
|
|
duk_put_global_string(ctx, HS_gNextObjectRefId);
|
|
|
|
duk_push_object(ctx); // { refId: object, ... }
|
|
duk_put_global_string(ctx, HS_gObjectRefs);
|
|
|
|
duk_push_int(ctx, 0);
|
|
duk_put_global_string(ctx, HS_gNextInvervalId);
|
|
|
|
duk_push_object(ctx); // { intervalId: { func, worker }, ... }
|
|
duk_put_global_string(ctx, HS_gIntervals);
|
|
|
|
Define_asm(ctx);
|
|
Define_console(ctx);
|
|
Define_cpu(ctx);
|
|
Define_debug(ctx);
|
|
Define_events(ctx);
|
|
Define_fs(ctx);
|
|
Define_mem(ctx);
|
|
Define_pj64(ctx);
|
|
Define_script(ctx);
|
|
|
|
Define_alert(ctx);
|
|
Define_exec(ctx);
|
|
Define_interval(ctx);
|
|
|
|
Define_AddressRange(ctx);
|
|
Define_N64Image(ctx);
|
|
Define_Server(ctx);
|
|
Define_Socket(ctx);
|
|
|
|
Define_Number_prototype_hex(ctx);
|
|
DefineGlobalConstants(ctx);
|
|
|
|
if(duk_get_top(ctx) > 0)
|
|
{
|
|
inst->System()->ConsoleLog("[SCRIPTSYS]: warning: duktape stack is dirty after API init");
|
|
}
|
|
}
|
|
|
|
void ScriptAPI::DefineGlobalClass(duk_context* ctx, const char* className,
|
|
duk_c_function constructorFunc,
|
|
const DukPropListEntry* prototypeProps,
|
|
const DukPropListEntry* staticProps)
|
|
{
|
|
duk_push_global_object(ctx);
|
|
duk_push_string(ctx, className);
|
|
duk_push_c_function(ctx, constructorFunc, DUK_VARARGS);
|
|
|
|
duk_push_string(ctx, "name");
|
|
duk_push_string(ctx, className);
|
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE);
|
|
|
|
if (staticProps != nullptr)
|
|
{
|
|
DukPutPropList(ctx, -1, staticProps);
|
|
}
|
|
|
|
duk_push_object(ctx); // prototype
|
|
duk_push_string(ctx, className);
|
|
duk_put_prop_string(ctx, -2, DUK_WELLKNOWN_SYMBOL("Symbol.toStringTag"));
|
|
|
|
if (prototypeProps != nullptr)
|
|
{
|
|
DukPutPropList(ctx, -1, prototypeProps);
|
|
}
|
|
|
|
duk_freeze(ctx, -1);
|
|
duk_put_prop_string(ctx, -2, "prototype");
|
|
|
|
duk_freeze(ctx, -1);
|
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
void ScriptAPI::DefineGlobalInterface(duk_context* ctx, const char* name, const DukPropListEntry* props)
|
|
{
|
|
duk_push_global_object(ctx);
|
|
duk_push_string(ctx, name);
|
|
duk_push_object(ctx);
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
void ScriptAPI::DefineGlobalFunction(duk_context* ctx, const char* name, duk_c_function func)
|
|
{
|
|
duk_push_global_object(ctx);
|
|
duk_push_string(ctx, name);
|
|
duk_push_c_function(ctx, func, DUK_VARARGS);
|
|
duk_freeze(ctx, -1);
|
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
void ScriptAPI::DefineGlobalConstants(duk_context* ctx)
|
|
{
|
|
const duk_number_list_entry numbers[] = {
|
|
{ "u8", U8 },
|
|
{ "u16", U16 },
|
|
{ "u32", U32 },
|
|
{ "s8", S8 },
|
|
{ "s16", S16 },
|
|
{ "s32", S32 },
|
|
{ "f32", F32 },
|
|
{ "f64", F64 },
|
|
|
|
{ "s64", S64 },
|
|
{ "u64", U64 },
|
|
|
|
{ "GPR_R0", GPR_R0 },
|
|
{ "GPR_AT", GPR_AT },
|
|
{ "GPR_V0", GPR_V0 },
|
|
{ "GPR_V1", GPR_V1 },
|
|
{ "GPR_A0", GPR_A0 },
|
|
{ "GPR_A1", GPR_A1 },
|
|
{ "GPR_A2", GPR_A2 },
|
|
{ "GPR_A3", GPR_A3 },
|
|
{ "GPR_T0", GPR_T0 },
|
|
{ "GPR_T1", GPR_T1 },
|
|
{ "GPR_T2", GPR_T2 },
|
|
{ "GPR_T3", GPR_T3 },
|
|
{ "GPR_T4", GPR_T4 },
|
|
{ "GPR_T5", GPR_T5 },
|
|
{ "GPR_T6", GPR_T6 },
|
|
{ "GPR_T7", GPR_T7 },
|
|
{ "GPR_S0", GPR_S0 },
|
|
{ "GPR_S1", GPR_S1 },
|
|
{ "GPR_S2", GPR_S2 },
|
|
{ "GPR_S3", GPR_S3 },
|
|
{ "GPR_S4", GPR_S4 },
|
|
{ "GPR_S5", GPR_S5 },
|
|
{ "GPR_S6", GPR_S6 },
|
|
{ "GPR_S7", GPR_S7 },
|
|
{ "GPR_T8", GPR_T8 },
|
|
{ "GPR_T9", GPR_T9 },
|
|
{ "GPR_K0", GPR_K0 },
|
|
{ "GPR_K1", GPR_K1 },
|
|
{ "GPR_GP", GPR_GP },
|
|
{ "GPR_SP", GPR_SP },
|
|
{ "GPR_FP", GPR_FP },
|
|
{ "GPR_RA", GPR_RA },
|
|
//{ "GPR_S8", GPR_S8 },
|
|
{ "GPR_ANY", 0xFFFFFFFF },
|
|
|
|
{ "RDRAM_CONFIG_REG", 0xA3F00000 },
|
|
{ "RDRAM_DEVICE_TYPE_REG", 0xA3F00000 },
|
|
{ "RDRAM_DEVICE_ID_REG", 0xA3F00004 },
|
|
{ "RDRAM_DELAY_REG", 0xA3F00008 },
|
|
{ "RDRAM_MODE_REG", 0xA3F0000C },
|
|
{ "RDRAM_REF_INTERVAL_REG", 0xA3F00010 },
|
|
{ "RDRAM_REF_ROW_REG", 0xA3F00014 },
|
|
{ "RDRAM_RAS_INTERVAL_REG", 0xA3F00018 },
|
|
{ "RDRAM_MIN_INTERVAL_REG", 0xA3F0001C },
|
|
{ "RDRAM_ADDR_SELECT_REG", 0xA3F00020 },
|
|
{ "RDRAM_DEVICE_MANUF_REG", 0xA3F00024 },
|
|
{ "SP_MEM_ADDR_REG", 0xA4040000 },
|
|
{ "SP_DRAM_ADDR_REG", 0xA4040004 },
|
|
{ "SP_RD_LEN_REG", 0xA4040008 },
|
|
{ "SP_WR_LEN_REG", 0xA404000C },
|
|
{ "SP_STATUS_REG", 0xA4040010 },
|
|
{ "SP_DMA_FULL_REG", 0xA4040014 },
|
|
{ "SP_DMA_BUSY_REG", 0xA4040018 },
|
|
{ "SP_SEMAPHORE_REG", 0xA404001C },
|
|
{ "SP_PC_REG", 0xA4080000 },
|
|
{ "SP_IBIST_REG", 0xA4080004 },
|
|
{ "DPC_START_REG", 0xA4100000 },
|
|
{ "DPC_END_REG", 0xA4100004 },
|
|
{ "DPC_CURRENT_REG", 0xA4100008 },
|
|
{ "DPC_STATUS_REG", 0xA410000C },
|
|
{ "DPC_CLOCK_REG", 0xA4100010 },
|
|
{ "DPC_BUFBUSY_REG", 0xA4100014 },
|
|
{ "DPC_PIPEBUSY_REG", 0xA4100018 },
|
|
{ "DPC_TMEM_REG", 0xA410001C },
|
|
{ "DPS_TBIST_REG", 0xA4200000 },
|
|
{ "DPS_TEST_MODE_REG", 0xA4200004 },
|
|
{ "DPS_BUFTEST_ADDR_REG", 0xA4200008 },
|
|
{ "DPS_BUFTEST_DATA_REG", 0xA420000C },
|
|
{ "MI_INIT_MODE_REG", 0xA4300000 },
|
|
{ "MI_MODE_REG", 0xA4300000 },
|
|
{ "MI_VERSION_REG", 0xA4300004 },
|
|
{ "MI_NOOP_REG", 0xA4300004 },
|
|
{ "MI_INTR_REG", 0xA4300008 },
|
|
{ "MI_INTR_MASK_REG", 0xA430000C },
|
|
{ "VI_STATUS_REG", 0xA4400000 },
|
|
{ "VI_CONTROL_REG", 0xA4400000 },
|
|
{ "VI_ORIGIN_REG", 0xA4400004 },
|
|
{ "VI_DRAM_ADDR_REG", 0xA4400004 },
|
|
{ "VI_WIDTH_REG", 0xA4400008 },
|
|
{ "VI_H_WIDTH_REG", 0xA4400008 },
|
|
{ "VI_INTR_REG", 0xA440000C },
|
|
{ "VI_V_INTR_REG", 0xA440000C },
|
|
{ "VI_CURRENT_REG", 0xA4400010 },
|
|
{ "VI_V_CURRENT_LINE_REG", 0xA4400010 },
|
|
{ "VI_BURST_REG", 0xA4400014 },
|
|
{ "VI_TIMING_REG", 0xA4400014 },
|
|
{ "VI_V_SYNC_REG", 0xA4400018 },
|
|
{ "VI_H_SYNC_REG", 0xA440001C },
|
|
{ "VI_LEAP_REG", 0xA4400020 },
|
|
{ "VI_H_SYNC_LEAP_REG", 0xA4400020 },
|
|
{ "VI_H_START_REG", 0xA4400024 },
|
|
{ "VI_H_VIDEO_REG", 0xA4400024 },
|
|
{ "VI_V_START_REG", 0xA4400028 },
|
|
{ "VI_V_VIDEO_REG", 0xA4400028 },
|
|
{ "VI_V_BURST_REG", 0xA440002C },
|
|
{ "VI_X_SCALE_REG", 0xA4400030 },
|
|
{ "VI_Y_SCALE_REG", 0xA4400034 },
|
|
{ "AI_DRAM_ADDR_REG", 0xA4500000 },
|
|
{ "AI_LEN_REG", 0xA4500004 },
|
|
{ "AI_CONTROL_REG", 0xA4500008 },
|
|
{ "AI_STATUS_REG", 0xA450000C },
|
|
{ "AI_DACRATE_REG", 0xA4500010 },
|
|
{ "AI_BITRATE_REG", 0xA4500014 },
|
|
{ "PI_DRAM_ADDR_REG", 0xA4600000 },
|
|
{ "PI_CART_ADDR_REG", 0xA4600004 },
|
|
{ "PI_RD_LEN_REG", 0xA4600008 },
|
|
{ "PI_WR_LEN_REG", 0xA460000C },
|
|
{ "PI_STATUS_REG", 0xA4600010 },
|
|
{ "PI_BSD_DOM1_LAT_REG", 0xA4600014 },
|
|
{ "PI_BSD_DOM1_PWD_REG", 0xA4600018 },
|
|
{ "PI_BSD_DOM1_PGS_REG", 0xA460001C },
|
|
{ "PI_BSD_DOM1_RLS_REG", 0xA4600020 },
|
|
{ "PI_BSD_DOM2_LAT_REG", 0xA4600024 },
|
|
{ "PI_BSD_DOM2_PWD_REG", 0xA4600028 },
|
|
{ "PI_BSD_DOM2_PGS_REG", 0xA460002C },
|
|
{ "PI_BSD_DOM2_RLS_REG", 0xA4600030 },
|
|
{ "RI_MODE_REG", 0xA4700000 },
|
|
{ "RI_CONFIG_REG", 0xA4700004 },
|
|
{ "RI_CURRENT_LOAD_REG", 0xA4700008 },
|
|
{ "RI_SELECT_REG", 0xA470000C },
|
|
{ "RI_REFRESH_REG", 0xA4700010 },
|
|
{ "RI_COUNT_REG", 0xA4700010 },
|
|
{ "RI_LATENCY_REG", 0xA4700014 },
|
|
{ "RI_RERROR_REG", 0xA4700018 },
|
|
{ "RI_WERROR_REG", 0xA470001C },
|
|
{ "SI_DRAM_ADDR_REG", 0xA4800000 },
|
|
{ "SI_PIF_ADDR_RD64B_REG", 0xA4800004 },
|
|
{ "SI_PIF_ADDR_WR64B_REG", 0xA4800010 },
|
|
{ "SI_STATUS_REG", 0xA4800018 },
|
|
|
|
{ "PIF_ROM_START", 0xBFC00000 },
|
|
{ "PIF_RAM_START", 0xBFC007C0 },
|
|
|
|
{ "SP_DMEM_START", 0xA4000000 },
|
|
{ "SP_IMEM_START", 0xA4001000 },
|
|
|
|
{ "KUBASE", 0x00000000 },
|
|
{ "K0BASE", 0x80000000 },
|
|
{ "K1BASE", 0xA0000000 },
|
|
{ "K2BASE", 0xC0000000 },
|
|
|
|
{ "UT_VEC", 0x80000000 },
|
|
{ "R_VEC", 0xBFC00000 },
|
|
{ "XUT_VEC", 0x80000080 },
|
|
{ "ECC_VEC", 0x80000100 },
|
|
{ "E_VEC", 0x80000180 },
|
|
|
|
{ "M_GFXTASK", 1 },
|
|
{ "M_AUDTASK", 2 },
|
|
{ "OS_READ", 0 },
|
|
{ "OS_WRITE", 1 },
|
|
|
|
{ "COLOR_BLACK", 0x000000FF },
|
|
{ "COLOR_WHITE", 0xFFFFFFFF },
|
|
{ "COLOR_GRAY", 0x808080FF },
|
|
{ "COLOR_RED", 0xFF0000FF },
|
|
{ "COLOR_GREEN", 0x00FF00FF },
|
|
{ "COLOR_BLUE", 0x0000FFFF },
|
|
{ "COLOR_YELLOW", 0xFFFF00FF },
|
|
{ "COLOR_CYAN", 0x00FFFFFF },
|
|
{ "COLOR_MAGENTA", 0xFF00FFFF },
|
|
|
|
{ "EMU_STARTED", JS_EMU_STARTED },
|
|
{ "EMU_STOPPED", JS_EMU_STOPPED },
|
|
{ "EMU_PAUSED", JS_EMU_PAUSED },
|
|
{ "EMU_RESUMED", JS_EMU_RESUMED },
|
|
{ "EMU_RESETTING", JS_EMU_RESETTING },
|
|
{ "EMU_RESET", JS_EMU_RESET },
|
|
{ "EMU_LOADED_ROM", JS_EMU_LOADED_ROM },
|
|
{ "EMU_LOADED_STATE", JS_EMU_LOADED_STATE },
|
|
{ "EMU_DEBUG_PAUSED", JS_EMU_DEBUG_PAUSED },
|
|
{ "EMU_DEBUG_RESUMED", JS_EMU_DEBUG_RESUMED },
|
|
|
|
{ "IMG_I4", IMG_I4 },
|
|
{ "IMG_I8", IMG_I8 },
|
|
{ "IMG_IA4", IMG_IA4 },
|
|
{ "IMG_IA8", IMG_IA8 },
|
|
{ "IMG_IA16", IMG_IA16 },
|
|
{ "IMG_RGBA16", IMG_RGBA16 },
|
|
{ "IMG_RGBA32", IMG_RGBA32 },
|
|
{ "IMG_CI8_RGBA16", IMG_CI8_RGBA16 },
|
|
{ "IMG_CI4_RGBA16", IMG_CI4_RGBA16 },
|
|
{ "IMG_CI8_IA16", IMG_CI8_IA16 },
|
|
{ "IMG_CI4_IA16", IMG_CI4_IA16 },
|
|
|
|
{ "G_IM_FMT_RGBA", G_IM_FMT_RGBA },
|
|
{ "G_IM_FMT_YUV", G_IM_FMT_YUV },
|
|
{ "G_IM_FMT_CI", G_IM_FMT_CI },
|
|
{ "G_IM_FMT_IA", G_IM_FMT_IA },
|
|
{ "G_IM_FMT_I", G_IM_FMT_I },
|
|
|
|
{ "G_IM_SIZ_4b", G_IM_SIZ_4b },
|
|
{ "G_IM_SIZ_8b", G_IM_SIZ_8b },
|
|
{ "G_IM_SIZ_16b", G_IM_SIZ_16b },
|
|
{ "G_IM_SIZ_32b", G_IM_SIZ_32b },
|
|
|
|
{ "G_TT_NONE", G_TT_NONE },
|
|
{ "G_TT_RGBA16", G_TT_RGBA16 },
|
|
{ "G_TT_IA16", G_TT_IA16 },
|
|
|
|
{ nullptr, 0 },
|
|
};
|
|
|
|
duk_push_global_object(ctx);
|
|
duk_put_number_list(ctx, -1, numbers);
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
CScriptInstance* ScriptAPI::GetInstance(duk_context* ctx)
|
|
{
|
|
duk_get_global_string(ctx, HS_gInstancePtr);
|
|
CScriptInstance* instance = (CScriptInstance*)duk_get_pointer(ctx, -1);
|
|
duk_pop(ctx);
|
|
return instance;
|
|
}
|
|
|
|
JSAppCallbackID ScriptAPI::AddAppCallback(duk_context* ctx, duk_idx_t callbackIdx, JSAppHookID hookId,
|
|
JSDukArgSetupFunc argSetupFunc, JSAppCallbackCondFunc conditionFunc, JSAppCallbackCleanupFunc cleanupFunc)
|
|
{
|
|
void* dukFuncHeapPtr = duk_get_heapptr(ctx, callbackIdx);
|
|
JSAppCallback cb(GetInstance(ctx), dukFuncHeapPtr, conditionFunc, argSetupFunc, cleanupFunc);
|
|
return AddAppCallback(ctx, hookId, cb);
|
|
}
|
|
|
|
JSAppCallbackID ScriptAPI::AddAppCallback(duk_context* ctx, JSAppHookID hookId, JSAppCallback& callback)
|
|
{
|
|
CScriptInstance* inst = GetInstance(ctx);
|
|
JSAppCallbackID callbackId = inst->System()->RawAddAppCallback(hookId, callback);
|
|
|
|
if(callbackId == JS_INVALID_CALLBACK)
|
|
{
|
|
inst->System()->ConsoleLog("[SCRIPTSYS]: error: callback was not added");
|
|
return JS_INVALID_CALLBACK;
|
|
}
|
|
|
|
duk_get_global_string(ctx, HS_gAppCallbacks);
|
|
|
|
duk_push_object(ctx);
|
|
duk_push_number(ctx, hookId);
|
|
duk_put_prop_string(ctx, -2, "hookId");
|
|
duk_push_number(ctx, callbackId);
|
|
duk_put_prop_string(ctx, -2, "callbackId");
|
|
duk_push_heapptr(ctx, callback.m_DukFuncHeapPtr);
|
|
duk_put_prop_string(ctx, -2, "func");
|
|
|
|
duk_push_c_function(ctx, js__AppCallbackFinalizer, 1);
|
|
duk_set_finalizer(ctx, -2);
|
|
|
|
duk_put_prop_index(ctx, -2, callbackId);
|
|
|
|
duk_pop(ctx);
|
|
|
|
return callbackId;
|
|
}
|
|
|
|
bool ScriptAPI::RemoveAppCallback(duk_context* ctx, JSAppCallbackID callbackId)
|
|
{
|
|
duk_get_global_string(ctx, HS_gAppCallbacks);
|
|
duk_bool_t bExists = duk_has_prop_index(ctx, -1, callbackId);
|
|
|
|
if(bExists)
|
|
{
|
|
// will invoke CallbackFinalizer
|
|
duk_del_prop_index(ctx, -1, callbackId);
|
|
}
|
|
|
|
duk_pop(ctx);
|
|
return bExists != 0;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js__AppCallbackFinalizer(duk_context* ctx)
|
|
{
|
|
CScriptInstance* inst = ScriptAPI::GetInstance(ctx);
|
|
|
|
duk_get_prop_string(ctx, 0, "hookId");
|
|
duk_get_prop_string(ctx, 0, "callbackId");
|
|
|
|
JSAppHookID hookId = (JSAppHookID)duk_get_uint(ctx, -2);
|
|
JSAppCallbackID callbackId = (JSAppCallbackID)duk_get_uint(ctx, -1);
|
|
duk_pop_n(ctx, 2);
|
|
|
|
inst->System()->RawRemoveAppCallback(hookId, callbackId);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ScriptAPI::RefObject(duk_context* ctx, duk_idx_t idx)
|
|
{
|
|
idx = duk_normalize_index(ctx, idx);
|
|
CScriptInstance* inst = GetInstance(ctx);
|
|
|
|
if (duk_has_prop_string(ctx, idx, HS_objectRefId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
duk_push_global_object(ctx);
|
|
|
|
duk_get_prop_string(ctx, -1, HS_gNextObjectRefId);
|
|
int curObjectId = duk_get_int(ctx, -1);
|
|
duk_pop(ctx);
|
|
duk_push_int(ctx, curObjectId + 1);
|
|
duk_put_prop_string(ctx, -2, HS_gNextObjectRefId);
|
|
|
|
duk_push_int(ctx, curObjectId);
|
|
duk_put_prop_string(ctx, idx, HS_objectRefId);
|
|
|
|
duk_get_prop_string(ctx, -1, HS_gObjectRefs);
|
|
duk_dup(ctx, idx);
|
|
duk_put_prop_index(ctx, -2, curObjectId);
|
|
|
|
duk_pop_n(ctx, 2);
|
|
|
|
inst->IncRefCount();
|
|
}
|
|
|
|
void ScriptAPI::UnrefObject(duk_context* ctx, duk_idx_t idx)
|
|
{
|
|
idx = duk_normalize_index(ctx, idx);
|
|
CScriptInstance* inst = GetInstance(ctx);
|
|
|
|
if (!duk_has_prop_string(ctx, idx, HS_objectRefId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
duk_get_prop_string(ctx, idx, HS_objectRefId);
|
|
int objectId = duk_get_int(ctx, -1);
|
|
duk_del_prop_string(ctx, idx, HS_objectRefId);
|
|
duk_pop(ctx);
|
|
|
|
duk_push_global_object(ctx);
|
|
duk_get_prop_string(ctx, -1, HS_gObjectRefs);
|
|
duk_del_prop_index(ctx, -1, objectId);
|
|
duk_pop_n(ctx, 2);
|
|
|
|
inst->DecRefCount();
|
|
}
|
|
|
|
// PostCMethodCall variant
|
|
duk_ret_t ScriptAPI::js__UnrefObject(duk_context* ctx)
|
|
{
|
|
duk_push_this(ctx);
|
|
UnrefObject(ctx, -1);
|
|
return 0;
|
|
}
|
|
|
|
void ScriptAPI::InitEmitter(duk_context* ctx, duk_idx_t obj_idx, const std::vector<std::string>& eventNames)
|
|
{
|
|
obj_idx = duk_normalize_index(ctx, obj_idx);
|
|
|
|
duk_push_object(ctx);
|
|
|
|
std::vector<std::string>::const_iterator it;
|
|
for (it = eventNames.begin(); it != eventNames.end(); it++)
|
|
{
|
|
duk_push_object(ctx);
|
|
duk_put_prop_string(ctx, -2, (*it).c_str());
|
|
}
|
|
|
|
duk_put_prop_string(ctx, obj_idx, HS_emitterListeners);
|
|
|
|
duk_push_int(ctx, 0);
|
|
duk_put_prop_string(ctx, obj_idx, HS_emitterNextListenerId);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js__Emitter_emit(duk_context* ctx)
|
|
{
|
|
const char* eventName = duk_get_string(ctx, 0);
|
|
duk_idx_t numListenerArgs = duk_get_top(ctx) - 1;
|
|
|
|
duk_push_this(ctx);
|
|
|
|
duk_get_prop_string(ctx, -1, HS_emitterListeners);
|
|
if (!duk_has_prop_string(ctx, -1, eventName))
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "emit: invalid event name '%s'", eventName);
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_get_prop_string(ctx, -1, eventName);
|
|
duk_enum(ctx, -1, 0);
|
|
|
|
int count = 0;
|
|
|
|
while (duk_next(ctx, -1, (duk_bool_t)true))
|
|
{
|
|
duk_push_this(ctx);
|
|
for (duk_idx_t nArg = 0; nArg < numListenerArgs; nArg++)
|
|
{
|
|
duk_dup(ctx, 1 + nArg);
|
|
}
|
|
|
|
// [ listenerFunc this args... ] -> [ retval ]
|
|
if (duk_pcall_method(ctx, numListenerArgs) != 0)
|
|
{
|
|
duk_throw(ctx);
|
|
}
|
|
|
|
duk_pop_n(ctx, 2);
|
|
count++;
|
|
}
|
|
|
|
// throw if there are no listeners for error event
|
|
if (count == 0 && strcmp("error", eventName) == 0)
|
|
{
|
|
duk_dup(ctx, 1);
|
|
duk_throw(ctx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js__Emitter_on(duk_context* ctx)
|
|
{
|
|
CheckArgs(ctx, { Arg_String, Arg_Function });
|
|
|
|
const char* eventName = duk_get_string(ctx, 0);
|
|
|
|
duk_push_this(ctx);
|
|
duk_get_prop_string(ctx, -1, HS_emitterListeners);
|
|
|
|
if (!duk_has_prop_string(ctx, -1, eventName))
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid event name");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_get_prop_string(ctx, -2, HS_emitterNextListenerId);
|
|
duk_size_t nextIdx = duk_get_int(ctx, -1);
|
|
duk_pop(ctx);
|
|
duk_push_int(ctx, nextIdx + 1);
|
|
duk_put_prop_string(ctx, -3, HS_emitterNextListenerId);
|
|
|
|
duk_get_prop_string(ctx, -1, eventName);
|
|
|
|
duk_pull(ctx, 1);
|
|
duk_put_prop_index(ctx, -2, nextIdx);
|
|
|
|
duk_push_this(ctx);
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js__Emitter_off(duk_context* ctx)
|
|
{
|
|
CheckArgs(ctx, { Arg_String, Arg_Function });
|
|
|
|
const char* eventName = duk_get_string(ctx, 0);
|
|
duk_push_this(ctx);
|
|
duk_get_prop_string(ctx, -1, HS_emitterListeners);
|
|
|
|
if (!duk_has_prop_string(ctx, -1, eventName))
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid event name");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_get_prop_string(ctx, -1, eventName);
|
|
|
|
duk_enum(ctx, -1, 0);
|
|
while (duk_next(ctx, -1, (duk_bool_t)true))
|
|
{
|
|
if (duk_equals(ctx, 1, -1))
|
|
{
|
|
duk_pop(ctx);
|
|
duk_del_prop(ctx, -3);
|
|
}
|
|
else
|
|
{
|
|
duk_pop_n(ctx, 2);
|
|
}
|
|
}
|
|
|
|
duk_push_this(ctx);
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_Duktape_modSearch(duk_context* ctx)
|
|
{
|
|
if (!duk_is_string(ctx, 0))
|
|
{
|
|
return ThrowInvalidArgsError(ctx);
|
|
}
|
|
|
|
const char* id = duk_get_string(ctx, 0);
|
|
|
|
stdstr strPath = GetInstance(ctx)->System()->ModulesDirPath() + id;
|
|
CPath path(strPath);
|
|
|
|
if (path.GetExtension() == "dll")
|
|
{
|
|
HMODULE hModule = LoadLibraryA(strPath.c_str());
|
|
|
|
if (hModule == nullptr)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_ERROR,
|
|
"failed to load native module (\"%s\")", strPath.c_str());
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
stdstr strProcName = stdstr_f("dukopen_%s", path.GetName().c_str());
|
|
duk_c_function fnEntryPoint = (duk_c_function)GetProcAddress(hModule, strProcName.c_str());
|
|
|
|
if (fnEntryPoint == nullptr)
|
|
{
|
|
FreeLibrary(hModule);
|
|
duk_push_error_object(ctx, DUK_ERR_ERROR,
|
|
"failed to locate module entry-point (\"%s\")", strProcName.c_str());
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_push_c_function(ctx, fnEntryPoint, 0);
|
|
|
|
if (duk_pcall(ctx, 0) != 0)
|
|
{
|
|
FreeLibrary(hModule);
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
RegisterNativeModule(ctx, hModule);
|
|
|
|
duk_put_prop_string(ctx, 3, "exports");
|
|
return 0;
|
|
}
|
|
|
|
CFile file(strPath.c_str(), CFile::modeRead);
|
|
|
|
if (!file.IsOpen())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint32_t length = file.GetLength();
|
|
|
|
char* sourceCode = new char[length + 1];
|
|
sourceCode[length] = '\0';
|
|
|
|
if (file.Read(sourceCode, length) != length)
|
|
{
|
|
delete[] sourceCode;
|
|
return 0;
|
|
}
|
|
|
|
duk_push_string(ctx, sourceCode);
|
|
delete[] sourceCode;
|
|
return 1;
|
|
}
|
|
|
|
void ScriptAPI::RegisterNativeModule(duk_context* ctx, HMODULE hModule)
|
|
{
|
|
duk_get_global_string(ctx, HS_gNativeModules);
|
|
duk_size_t index = duk_get_length(ctx, -1);
|
|
duk_push_object(ctx);
|
|
duk_push_pointer(ctx, hModule);
|
|
duk_put_prop_string(ctx, -2, "modPtr");
|
|
duk_push_c_function(ctx, js__NativeModuleFinalizer, 1);
|
|
duk_set_finalizer(ctx, -2);
|
|
duk_put_prop_index(ctx, -2, index);
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js__NativeModuleFinalizer(duk_context* ctx)
|
|
{
|
|
duk_get_prop_string(ctx, 0, "modPtr");
|
|
HMODULE hModule = (HMODULE)duk_get_pointer(ctx, -1);
|
|
FreeLibrary(hModule);
|
|
return 0;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::ThrowInvalidArgsError(duk_context* ctx)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid argument(s)");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::ThrowInvalidArgError(duk_context * ctx, duk_idx_t idx, ArgType wantType)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "argument %d invalid, expected %s", idx, ArgTypeName(wantType));
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::ThrowTooManyArgsError(duk_context * ctx)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "too many arguments");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::ThrowInvalidAssignmentError(duk_context* ctx, ArgType wantType)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid assignment, expected %s", ArgTypeName(wantType));
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::ThrowNotCallableError(duk_context* ctx)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "not callable");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
void ScriptAPI::DebugStack(duk_context* ctx, const char* file, int line)
|
|
{
|
|
duk_push_context_dump(ctx);
|
|
GetInstance(ctx)->System()->ConsoleLog("[SCRIPTSYS] <%s:%d> %s", file, line, duk_to_string(ctx, -1));
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
void ScriptAPI::AllowPrivateCall(duk_context* ctx, bool bAllow)
|
|
{
|
|
duk_push_boolean(ctx, (duk_bool_t)bAllow);
|
|
duk_put_global_string(ctx, HS_gPrivateCallEnabled);
|
|
}
|
|
|
|
bool ScriptAPI::PrivateCallAllowed(duk_context* ctx)
|
|
{
|
|
if (!duk_get_global_string(ctx, HS_gPrivateCallEnabled))
|
|
{
|
|
duk_pop(ctx);
|
|
return false;
|
|
}
|
|
|
|
bool bAllowed = duk_get_boolean(ctx, -1);
|
|
duk_pop(ctx);
|
|
return bAllowed;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_DummyConstructor(duk_context* ctx)
|
|
{
|
|
return ThrowNotCallableError(ctx);
|
|
}
|
|
|
|
void ScriptAPI::PushNewDummyConstructor(duk_context* ctx, bool bFrozen)
|
|
{
|
|
duk_push_c_function(ctx, js_DummyConstructor, 0);
|
|
duk_push_object(ctx);
|
|
duk_put_prop_string(ctx, -2, "prototype");
|
|
|
|
if (bFrozen)
|
|
{
|
|
duk_freeze(ctx, -1);
|
|
}
|
|
}
|
|
|
|
void ScriptAPI::DefineGlobalDummyConstructors(duk_context* ctx, const char* constructorNames[], bool bFreeze)
|
|
{
|
|
duk_push_global_object(ctx);
|
|
|
|
for (size_t i = 0;; i++)
|
|
{
|
|
if (constructorNames[i] == nullptr)
|
|
{
|
|
break;
|
|
}
|
|
duk_push_string(ctx, constructorNames[i]);
|
|
PushNewDummyConstructor(ctx, bFreeze);
|
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
|
}
|
|
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
void ScriptAPI::SetDummyConstructor(duk_context* ctx, duk_idx_t obj_idx, const char* globalConstructorName)
|
|
{
|
|
obj_idx = duk_normalize_index(ctx, obj_idx);
|
|
duk_get_global_string(ctx, globalConstructorName);
|
|
duk_get_prop_string(ctx, -1, "prototype");
|
|
duk_set_prototype(ctx, obj_idx);
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
void ScriptAPI::DukPutPropList(duk_context* ctx, duk_idx_t obj_idx, const DukPropListEntry* props)
|
|
{
|
|
obj_idx = duk_normalize_index(ctx, obj_idx);
|
|
|
|
for (size_t i = 0;; i++)
|
|
{
|
|
const DukPropListEntry& prop = props[i];
|
|
|
|
if (prop.key == nullptr)
|
|
{
|
|
break;
|
|
}
|
|
|
|
duk_uint_t propFlags = 0;
|
|
bool bHiddenSymbol = (prop.key[0] == '\xFF');
|
|
|
|
if (!bHiddenSymbol)
|
|
{
|
|
propFlags |= prop.writable ? DUK_DEFPROP_SET_WRITABLE : 0;
|
|
propFlags |= prop.enumerable ? DUK_DEFPROP_SET_ENUMERABLE : 0;
|
|
}
|
|
else
|
|
{
|
|
if (prop.typeId == Type_DukGetter ||
|
|
prop.typeId == Type_DukGetterSetter)
|
|
{
|
|
// not compatible
|
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
|
}
|
|
}
|
|
|
|
duk_push_string(ctx, prop.key);
|
|
|
|
switch (prop.typeId)
|
|
{
|
|
case Type_DukNumber:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_number(ctx, prop.value.dukNumber.value);
|
|
break;
|
|
case Type_DukInt:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_int(ctx, prop.value.dukInt.value);
|
|
break;
|
|
case Type_DukUInt:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_uint(ctx, prop.value.dukUInt.value);
|
|
break;
|
|
case Type_DukBoolean:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_boolean(ctx, prop.value.dukBoolean.value);
|
|
break;
|
|
case Type_DukString:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_string(ctx, prop.value.dukString.value);
|
|
break;
|
|
case Type_DukPointer:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_pointer(ctx, prop.value.dukPointer.value);
|
|
break;
|
|
case Type_DukCFunction:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_c_function(ctx, prop.value.dukCFunction.func, prop.value.dukCFunction.nargs);
|
|
break;
|
|
case Type_DukDupIndex:
|
|
{
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_idx_t fixedDupIndex = prop.value.dukDupIndex.value;
|
|
if (fixedDupIndex < 0)
|
|
{
|
|
// -1 to account for prop.key push above
|
|
fixedDupIndex = duk_normalize_index(ctx, fixedDupIndex - 1);
|
|
}
|
|
duk_dup(ctx, fixedDupIndex);
|
|
}
|
|
break;
|
|
case Type_DukGetter:
|
|
propFlags |= DUK_DEFPROP_HAVE_GETTER;
|
|
duk_push_c_function(ctx, prop.value.dukGetter.value, 0);
|
|
break;
|
|
case Type_DukGetterSetter:
|
|
propFlags |= DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER;
|
|
duk_push_c_function(ctx, prop.value.dukGetterSetter.getter, 0);
|
|
duk_push_c_function(ctx, prop.value.dukGetterSetter.setter, 1);
|
|
break;
|
|
case Type_DukObject:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_object(ctx);
|
|
if (prop.value.dukObject.props != nullptr)
|
|
{
|
|
DukPutPropList(ctx, -1, prop.value.dukObject.props);
|
|
}
|
|
break;
|
|
case Type_DukProxy:
|
|
propFlags |= DUK_DEFPROP_HAVE_VALUE;
|
|
duk_push_object(ctx); // empty target
|
|
duk_push_object(ctx); // handler
|
|
duk_push_c_function(ctx, prop.value.dukProxy.getter, 2);
|
|
duk_put_prop_string(ctx, -2, "get");
|
|
duk_push_c_function(ctx, prop.value.dukProxy.setter, 3);
|
|
duk_put_prop_string(ctx, -2, "set");
|
|
duk_push_proxy(ctx, 0);
|
|
break;
|
|
default:
|
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
|
break;
|
|
}
|
|
|
|
if (bHiddenSymbol)
|
|
{
|
|
duk_put_prop(ctx, obj_idx);
|
|
}
|
|
else
|
|
{
|
|
duk_def_prop(ctx, obj_idx, propFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
duk_bool_t ScriptAPI::ArgTypeMatches(duk_context* ctx, duk_idx_t idx, ArgType wantType)
|
|
{
|
|
ArgType argType = (ArgType)(wantType & (~ArgAttrs));
|
|
|
|
switch (argType)
|
|
{
|
|
case Arg_Any:
|
|
return true;
|
|
case Arg_Number:
|
|
return duk_is_number(ctx, idx);
|
|
case Arg_BufferData:
|
|
return duk_is_buffer_data(ctx, idx);
|
|
case Arg_String:
|
|
return duk_is_string(ctx, idx);
|
|
case Arg_Function:
|
|
return duk_is_function(ctx, idx);
|
|
case Arg_Object:
|
|
return duk_is_object(ctx, idx);
|
|
case Arg_Array:
|
|
return duk_is_array(ctx, idx);
|
|
case Arg_Boolean:
|
|
return duk_is_boolean(ctx, idx);
|
|
default:
|
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const char* ScriptAPI::ArgTypeName(ArgType argType)
|
|
{
|
|
static const std::map<ArgType, std::string> argTypeNames = {
|
|
{ Arg_Any, "any" },
|
|
{ Arg_Number, "number" },
|
|
{ Arg_BufferData, "bufferdata" },
|
|
{ Arg_String, "string" },
|
|
{ Arg_Function, "function" },
|
|
{ Arg_Object, "object" },
|
|
{ Arg_Array, "array" },
|
|
{ Arg_Boolean, "boolean" }
|
|
};
|
|
|
|
if (argTypeNames.count(argType) == 0)
|
|
{
|
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
|
}
|
|
|
|
return argTypeNames.at(argType).c_str();
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::CheckSetterAssignment(duk_context* ctx, ArgType wantType)
|
|
{
|
|
if (!ArgTypeMatches(ctx, 0, wantType))
|
|
{
|
|
return ThrowInvalidAssignmentError(ctx, wantType);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::CheckArgs(duk_context* ctx, const std::vector<ArgType>& argTypes)
|
|
{
|
|
duk_idx_t nargs = duk_get_top(ctx);
|
|
|
|
if ((size_t)nargs > argTypes.size())
|
|
{
|
|
return ThrowTooManyArgsError(ctx);
|
|
}
|
|
|
|
duk_idx_t idx = 0;
|
|
std::vector<ArgType>::const_iterator it;
|
|
for (it = argTypes.begin(); it != argTypes.end(); it++)
|
|
{
|
|
bool bOptional = (*it & ArgAttr_Optional) != 0;
|
|
ArgType argType = (ArgType)(*it & (~ArgAttrs));
|
|
|
|
if (idx >= nargs)
|
|
{
|
|
if (bOptional)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "argument(s) missing", idx);
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
if (!ArgTypeMatches(ctx, idx, argType))
|
|
{
|
|
return ThrowInvalidArgError(ctx, idx, argType);
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
|
|
return 0;
|
|
}
|