#include "stdafx.h" #include "N64Image.h" #include "ScriptAPI.h" #include #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 & eventNames) { obj_idx = duk_normalize_index(ctx, obj_idx); duk_push_object(ctx); std::vector::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 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 & 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::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; }