diff --git a/CHANGES b/CHANGES index 57074c8aa..72b6c5eb1 100644 --- a/CHANGES +++ b/CHANGES @@ -46,9 +46,12 @@ Features: Emulation fixes: - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) +Other fixes: + - Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794) Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs + - Qt: Include wayland QPA in AppImage (fixes mgba.io/i/2796) 0.10.1: (2023-01-10) Emulation fixes: diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index a4ec71b65..8c0f11e45 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -34,6 +34,16 @@ CXX_GUARD_START size_t NAME ## Index(const struct NAME* vector, const TYPE* member); \ void NAME ## Copy(struct NAME* dest, const struct NAME* src); +#ifdef NDEBUG +#define VECTOR_BOUNDS_CHECK(NAME, V, L) +#else +#define VECTOR_BOUNDS_CHECK(NAME, V, L) \ + if ((L) >= (V)->size) { \ + fprintf(stderr, "Vector type %s invalid access of index %" PRIuPTR " into vector of size %" PRIuPTR "\n", #NAME, (L), (V)->size); \ + abort(); \ + } +#endif + #define DEFINE_VECTOR(NAME, TYPE) \ void NAME ## Init(struct NAME* vector, size_t capacity) { \ vector->size = 0; \ @@ -50,9 +60,11 @@ CXX_GUARD_START vector->size = 0; \ } \ TYPE* NAME ## GetPointer(struct NAME* vector, size_t location) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ return &vector->vector[location]; \ } \ TYPE const* NAME ## GetConstPointer(const struct NAME* vector, size_t location) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ return &vector->vector[location]; \ } \ TYPE* NAME ## Append(struct NAME* vector) { \ @@ -78,10 +90,12 @@ CXX_GUARD_START vector->vector = realloc(vector->vector, vector->capacity * sizeof(TYPE)); \ } \ void NAME ## Shift(struct NAME* vector, size_t location, size_t difference) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ memmove(&vector->vector[location], &vector->vector[location + difference], (vector->size - location - difference) * sizeof(TYPE)); \ vector->size -= difference; \ } \ void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ NAME ## Resize(vector, difference); \ memmove(&vector->vector[location + difference], &vector->vector[location], (vector->size - location - difference) * sizeof(TYPE)); \ } \ @@ -97,8 +111,16 @@ CXX_GUARD_START dest->size = src->size; \ } \ -DECLARE_VECTOR(StringList, char*); DECLARE_VECTOR(IntList, int); +DECLARE_VECTOR(SInt8List, int8_t); +DECLARE_VECTOR(SInt16List, int16_t); +DECLARE_VECTOR(SInt32List, int32_t); +DECLARE_VECTOR(SIntPtrList, intptr_t); +DECLARE_VECTOR(UInt8List, uint8_t); +DECLARE_VECTOR(UInt16List, uint16_t); +DECLARE_VECTOR(UInt32List, uint32_t); +DECLARE_VECTOR(UIntPtrList, uintptr_t); +DECLARE_VECTOR(StringList, char*); CXX_GUARD_END diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 99eefe069..396084f14 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -89,6 +89,7 @@ uint32_t mScriptContextSetWeakref(struct mScriptContext*, struct mScriptValue* v struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mScriptValue* value); struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value); void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); +void mScriptContextDisownWeakref(struct mScriptContext*, uint32_t weakref); void mScriptContextAttachStdlib(struct mScriptContext* context); void mScriptContextAttachSocket(struct mScriptContext* context); diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 470ed40ce..556fff57b 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -76,6 +76,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_W(TYPE) opaque #define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque +#define mSCRIPT_TYPE_MS_VOID (&mSTVoid) #define mSCRIPT_TYPE_MS_S8 (&mSTSInt8) #define mSCRIPT_TYPE_MS_U8 (&mSTUInt8) #define mSCRIPT_TYPE_MS_S16 (&mSTSInt16) @@ -97,7 +98,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT) #define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME) #define mSCRIPT_TYPE_MS_PS(STRUCT) (&mSTStructPtr_ ## STRUCT) -#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructConstPtr_ ## STRUCT) +#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructPtrConst_ ## STRUCT) #define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper) #define mSCRIPT_TYPE_MS_WLIST (&mSTListWrapper) #define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE) diff --git a/src/core/scripting.c b/src/core/scripting.c index 05433b747..1eca1159b 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -761,14 +761,16 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptConsole, createBuffer) mSCRIPT_DEFINE_DEFAULTS_END; static struct mScriptConsole* _ensureConsole(struct mScriptContext* context) { - struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole)); - struct mScriptConsole* console = value->value.opaque; - if (!console) { - console = calloc(1, sizeof(*console)); - value->value.opaque = console; - value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; - mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole"); + struct mScriptValue* value = mScriptContextGetGlobal(context, "console"); + if (value) { + return value->value.opaque; } + struct mScriptConsole* console = calloc(1, sizeof(*console)); + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptConsole)); + value->value.opaque = console; + value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + mScriptContextSetGlobal(context, "console", value); + mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole"); return console; } diff --git a/src/debugger/parser.c b/src/debugger/parser.c index 0d4924ea9..3c3b037fa 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -11,8 +11,6 @@ DEFINE_VECTOR(LexVector, struct Token); -DEFINE_VECTOR(IntList, int32_t); - enum LexState { LEX_ERROR = -1, LEX_ROOT = 0, diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 690565088..720c5b0ce 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -429,7 +429,7 @@ if(QT_STATIC) list(APPEND QT_LIBRARIES "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security") set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE}") elseif(UNIX) - list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa) + list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa ${QT}::QWaylandIntegrationPlugin) endif() endif() target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES}) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 5e836f07b..c78a8a4d6 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -516,10 +516,10 @@ void PainterGL::create() { #if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders) { - QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); gl2Backend = static_cast(malloc(sizeof(mGLES2Context))); mGLES2ContextCreate(gl2Backend); m_backend = &gl2Backend->d; + QOpenGLFunctions* fn = m_gl->functions(); fn->glGenTextures(m_bridgeTexes.size(), m_bridgeTexes.data()); for (auto tex : m_bridgeTexes) { m_freeTex.enqueue(tex); @@ -546,7 +546,7 @@ void PainterGL::create() { #if defined(BUILD_GLES2) || defined(BUILD_GLES3) mGLES2Context* gl2Backend = reinterpret_cast(painter->m_backend); if (painter->m_widget && painter->supportsShaders()) { - QOpenGLFunctions_Baseline* fn = painter->m_gl->versionFunctions(); + QOpenGLFunctions* fn = painter->m_gl->functions(); fn->glFinish(); painter->m_widget->setTex(painter->m_finalTex[painter->m_finalTexIdx]); painter->m_finalTexIdx ^= 1; @@ -592,7 +592,7 @@ void PainterGL::destroy() { } makeCurrent(); #if defined(BUILD_GLES2) || defined(BUILD_GLES3) - QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); + QOpenGLFunctions* fn = m_gl->functions(); if (m_shader.passes) { mGLES2ShaderFree(&m_shader); } @@ -683,7 +683,7 @@ void PainterGL::start() { if (glContextHasBug(OpenGLBug::GLTHREAD_BLOCKS_SWAP)) { // Suggested on Discord as a way to strongly hint that glthread should be disabled // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/8035 - QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); + QOpenGLFunctions* fn = m_gl->functions(); fn->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); } #endif @@ -975,7 +975,7 @@ QOpenGLContext* PainterGL::shareContext() { } void PainterGL::updateFramebufferHandle() { - QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); + QOpenGLFunctions* fn = m_gl->functions(); // TODO: Figure out why glFlush doesn't work here on Intel/Windows if (glContextHasBug(OpenGLBug::CROSS_THREAD_FLUSH)) { fn->glFinish(); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index f2fc4d457..75b1b39b7 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -199,6 +199,8 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi #ifdef BUILD_SDL m_inputController.addInputDriver(std::make_shared(&m_inputController)); + m_inputController.setGamepadDriver(SDL_BINDING_BUTTON); + m_inputController.setSensorDriver(SDL_BINDING_BUTTON); #endif m_shortcutController->setConfigController(m_config); diff --git a/src/platform/qt/input/GamepadHatEvent.cpp b/src/platform/qt/input/GamepadHatEvent.cpp index 287730ec9..d76098f71 100644 --- a/src/platform/qt/input/GamepadHatEvent.cpp +++ b/src/platform/qt/input/GamepadHatEvent.cpp @@ -16,11 +16,11 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di : QEvent(pressType) , m_hatId(hatId) , m_direction(direction) - , m_key(-1) + , m_keys(0) { ignore(); if (controller && controller->map()) { - m_key = mInputMapHat(controller->map(), type, hatId, direction); + m_keys = mInputMapHat(controller->map(), type, hatId, direction); } } diff --git a/src/platform/qt/input/GamepadHatEvent.h b/src/platform/qt/input/GamepadHatEvent.h index 2ba73088d..c6c49fe3c 100644 --- a/src/platform/qt/input/GamepadHatEvent.h +++ b/src/platform/qt/input/GamepadHatEvent.h @@ -25,7 +25,7 @@ public: int hatId() const { return m_hatId; } Direction direction() const { return m_direction; } - int platformKey() const { return m_key; } + int platformKeys() const { return m_keys; } static Type Down(); static Type Up(); @@ -36,7 +36,7 @@ private: int m_hatId; Direction m_direction; - int m_key; + int m_keys; }; } diff --git a/src/platform/qt/input/InputController.cpp b/src/platform/qt/input/InputController.cpp index a3599bc8b..43614a675 100644 --- a/src/platform/qt/input/InputController.cpp +++ b/src/platform/qt/input/InputController.cpp @@ -236,7 +236,7 @@ int InputController::gamepadIndex(uint32_t type) const { if (!driver) { return -1; } - return driver->activeGamepad(); + return driver->activeGamepadIndex(); } void InputController::setGamepad(uint32_t type, int index) { @@ -357,13 +357,9 @@ Gamepad* InputController::gamepad(uint32_t type) { } if (!driver->supportsGamepads()) { return nullptr; -} - QList driverPads(driver->connectedGamepads()); - int activeGamepad = driver->activeGamepad(); - if (activeGamepad < 0 || activeGamepad >= driverPads.count()) { - return nullptr; } - return driverPads[activeGamepad]; + + return driver->activeGamepad(); } QList InputController::gamepads() { @@ -372,16 +368,15 @@ QList InputController::gamepads() { if (!driver->supportsGamepads()) { continue; } - QList driverPads(driver->connectedGamepads()); - int activeGamepad = driver->activeGamepad(); - if (activeGamepad >= 0 && activeGamepad < driverPads.count()) { - pads.append(driverPads[activeGamepad]); + Gamepad* pad = driver->activeGamepad(); + if (pad) { + pads.append(pad); } } return pads; } -QSet InputController::activeGamepadButtons(int type) { +QSet InputController::activeGamepadButtons(uint32_t type) { QSet activeButtons; Gamepad* pad = gamepad(type); if (!pad) { @@ -396,7 +391,7 @@ QSet InputController::activeGamepadButtons(int type) { return activeButtons; } -QSet> InputController::activeGamepadAxes(int type) { +QSet> InputController::activeGamepadAxes(uint32_t type) { QSet> activeAxes; Gamepad* pad = gamepad(type); if (!pad) { @@ -417,7 +412,7 @@ QSet> InputController::activeGamepadAxes return activeAxes; } -QSet> InputController::activeGamepadHats(int type) { +QSet> InputController::activeGamepadHats(uint32_t type) { QSet> activeHats; Gamepad* pad = gamepad(type); if (!pad) { @@ -432,7 +427,7 @@ QSet> InputController::activeGamepadHats( return activeHats; } -void InputController::testGamepad(int type) { +void InputController::testGamepad(uint32_t type) { QWriteLocker l(&m_eventsLock); auto activeAxes = activeGamepadAxes(type); auto oldAxes = m_activeAxes; @@ -496,15 +491,15 @@ void InputController::testGamepad(int type) { for (auto& hat : activeHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this); - postPendingEvent(event->platformKey()); + postPendingEvents(event->platformKeys()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->platformKey()); + clearPendingEvents(event->platformKeys()); } } for (auto& hat : oldHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this); - clearPendingEvent(event->platformKey()); + clearPendingEvents(event->platformKeys()); sendGamepadEvent(event); } } @@ -530,6 +525,22 @@ void InputController::clearPendingEvent(int key) { m_pendingEvents.remove(key); } +void InputController::postPendingEvents(int keys) { + for (int i = 0; keys; ++i, keys >>= 1) { + if (keys & 1) { + m_pendingEvents.insert(i); + } + } +} + +void InputController::clearPendingEvents(int keys) { + for (int i = 0; keys; ++i, keys >>= 1) { + if (keys & 1) { + m_pendingEvents.remove(i); + } + } +} + bool InputController::hasPendingEvent(int key) const { return m_pendingEvents.contains(key); } diff --git a/src/platform/qt/input/InputController.h b/src/platform/qt/input/InputController.h index 0c2232711..8c1e8104c 100644 --- a/src/platform/qt/input/InputController.h +++ b/src/platform/qt/input/InputController.h @@ -82,8 +82,8 @@ public: static const int32_t AXIS_THRESHOLD = 0x3000; void setGamepadDriver(uint32_t type); - const InputDriver* gamepadDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); } - InputDriver* gamepadDriver() { return m_inputDrivers.value(m_sensorDriver).get(); } + const InputDriver* gamepadDriver() const { return m_inputDrivers.value(m_gamepadDriver).get(); } + InputDriver* gamepadDriver() { return m_inputDrivers.value(m_gamepadDriver).get(); } QStringList connectedGamepads(uint32_t type = 0) const; int gamepadIndex(uint32_t type = 0) const; @@ -115,7 +115,7 @@ signals: void luminanceValueChanged(int value); public slots: - void testGamepad(int type); + void testGamepad(uint32_t type); void update(); void increaseLuminanceLevel(); @@ -136,17 +136,19 @@ private slots: void teardownCam(); private: - void postPendingEvent(int); - void clearPendingEvent(int); - bool hasPendingEvent(int) const; + void postPendingEvent(int key); + void clearPendingEvent(int key); + void postPendingEvents(int keys); + void clearPendingEvents(int keys); + bool hasPendingEvent(int key) const; void sendGamepadEvent(QEvent*); Gamepad* gamepad(uint32_t type); QList gamepads(); - QSet activeGamepadButtons(int type); - QSet> activeGamepadAxes(int type); - QSet> activeGamepadHats(int type); + QSet activeGamepadButtons(uint32_t type); + QSet> activeGamepadAxes(uint32_t type); + QSet> activeGamepadHats(uint32_t type); struct InputControllerLux : GBALuminanceSource { InputController* p; diff --git a/src/platform/qt/input/InputDriver.cpp b/src/platform/qt/input/InputDriver.cpp index 912395e0d..f76649bd8 100644 --- a/src/platform/qt/input/InputDriver.cpp +++ b/src/platform/qt/input/InputDriver.cpp @@ -40,14 +40,32 @@ QList InputDriver::connectedGamepads() const { return {}; } -int InputDriver::activeKeySource() const { +int InputDriver::activeKeySourceIndex() const { return -1; } -int InputDriver::activeGamepad() const { +int InputDriver::activeGamepadIndex() const { return -1; } +KeySource* InputDriver::activeKeySource() { + QList ks(connectedKeySources()); + int activeKeySource = activeKeySourceIndex(); + if (activeKeySource < 0 || activeKeySource >= ks.count()) { + return nullptr; + } + return ks[activeKeySource]; +} + +Gamepad* InputDriver::activeGamepad() { + QList pads(connectedGamepads()); + int activeGamepad = activeGamepadIndex(); + if (activeGamepad < 0 || activeGamepad >= pads.count()) { + return nullptr; + } + return pads[activeGamepad]; +} + void InputDriver::setActiveKeySource(int) { } diff --git a/src/platform/qt/input/InputDriver.h b/src/platform/qt/input/InputDriver.h index f344b271b..cc0a6e0cd 100644 --- a/src/platform/qt/input/InputDriver.h +++ b/src/platform/qt/input/InputDriver.h @@ -44,8 +44,11 @@ public: virtual QList connectedKeySources() const; virtual QList connectedGamepads() const; - virtual int activeKeySource() const; - virtual int activeGamepad() const; + virtual int activeKeySourceIndex() const; + virtual int activeGamepadIndex() const; + + KeySource* activeKeySource(); + Gamepad* activeGamepad(); virtual void setActiveKeySource(int); virtual void setActiveGamepad(int); diff --git a/src/platform/qt/input/InputMapper.cpp b/src/platform/qt/input/InputMapper.cpp index 41a3bc8c3..c84316efd 100644 --- a/src/platform/qt/input/InputMapper.cpp +++ b/src/platform/qt/input/InputMapper.cpp @@ -66,10 +66,7 @@ int InputMapper::mapAxes(QList axes) const { int InputMapper::mapHats(QList hats) const { int platformKeys = 0; for (int i = 0; i < hats.count(); ++i) { - int platformKey = mInputMapHat(m_map, m_type, i, hats[i]); - if (platformKey >= 0) { - platformKeys |= 1 << platformKey; - } + platformKeys |= mInputMapHat(m_map, m_type, i, hats[i]); } return platformKeys; } diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp index 2eda6a4f0..ca9e30b25 100644 --- a/src/platform/qt/input/SDLInputDriver.cpp +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -173,7 +173,7 @@ void SDLInputDriver::updateGamepads() { } #endif -int SDLInputDriver::activeGamepad() const { +int SDLInputDriver::activeGamepadIndex() const { return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; } diff --git a/src/platform/qt/input/SDLInputDriver.h b/src/platform/qt/input/SDLInputDriver.h index 79deac22c..09f4b9555 100644 --- a/src/platform/qt/input/SDLInputDriver.h +++ b/src/platform/qt/input/SDLInputDriver.h @@ -46,7 +46,7 @@ public: QList connectedGamepads() const override; - int activeGamepad() const override; + int activeGamepadIndex() const override; void setActiveGamepad(int) override; void registerTiltAxisX(int axis) override; diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index b3d4bafc9..c53408e8e 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -42,6 +42,7 @@ Q_IMPORT_PLUGIN(AVFServicePlugin); #endif #elif defined(Q_OS_UNIX) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); +Q_IMPORT_PLUGIN(QWaylandIntegrationPlugin); #endif #endif diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 5cfb4ff41..504736f2f 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -12,8 +12,9 @@ set(TEST_FILES if(USE_LUA) list(APPEND SOURCE_FILES engines/lua.c) list(APPEND TEST_FILES - test/stdlib.c - test/lua.c) + test/context.c + test/lua.c + test/stdlib.c) endif() source_group("Scripting" FILES ${SOURCE_FILES}) diff --git a/src/script/context.c b/src/script/context.c index fa1a0b4a2..14775b05c 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -71,8 +71,8 @@ void mScriptContextInit(struct mScriptContext* context) { void mScriptContextDeinit(struct mScriptContext* context) { HashTableDeinit(&context->rootScope); - HashTableDeinit(&context->weakrefs); mScriptContextDrainPool(context); + HashTableDeinit(&context->weakrefs); mScriptListDeinit(&context->refPool); HashTableDeinit(&context->callbacks); TableDeinit(&context->callbackId); @@ -102,9 +102,12 @@ void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* void mScriptContextDrainPool(struct mScriptContext* context) { size_t i; for (i = 0; i < mScriptListSize(&context->refPool); ++i) { - struct mScriptValue* value = mScriptValueUnwrap(mScriptListGetPointer(&context->refPool, i)); - if (value) { + struct mScriptValue* value = mScriptListGetPointer(&context->refPool, i); + if (value->type->base == mSCRIPT_TYPE_WRAPPER) { + value = mScriptValueUnwrap(value); mScriptValueDeref(value); + } else if (value->type == mSCRIPT_TYPE_MS_WEAKREF) { + mScriptContextClearWeakref(context, value->value.u32); } } mScriptListClear(&context->refPool); @@ -201,6 +204,13 @@ void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref TableRemove(&context->weakrefs, weakref); } +void mScriptContextDisownWeakref(struct mScriptContext* context, uint32_t weakref) { + struct mScriptValue* poolEntry = mScriptListAppend(&context->refPool); + poolEntry->type = mSCRIPT_TYPE_MS_WEAKREF; + poolEntry->value.u32 = weakref; + poolEntry->refs = mSCRIPT_VALUE_UNREF; +} + void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback) { struct mScriptValue* list = HashTableLookup(&context->callbacks, callback); if (!list) { diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 37327ee54..3c7266a2a 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -738,7 +738,19 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } break; case mSCRIPT_TYPE_STRING: - lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size); + if (!value->value.string) { + lua_pushnil(luaContext->lua); + break; + } + if (value->type == mSCRIPT_TYPE_MS_STR) { + lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size); + break; + } + if (value->type == mSCRIPT_TYPE_MS_CHARP) { + lua_pushstring(luaContext->lua, value->value.copaque); + break; + } + ok = false; break; case mSCRIPT_TYPE_LIST: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -769,6 +781,10 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v mScriptValueDeref(value); break; case mSCRIPT_TYPE_OBJECT: + if (!value->value.opaque) { + lua_pushnil(luaContext->lua); + break; + } newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); if (needsWeakref) { *newValue = mSCRIPT_MAKE(WEAKREF, weakref); diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 476e24455..95bb32920 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -14,6 +14,8 @@ struct TestA { int32_t i2; int8_t b8; int16_t hUnaligned; + struct mScriptValue table; + struct mScriptList list; int32_t (*ifn0)(struct TestA*); int32_t (*ifn1)(struct TestA*, int); void (*vfn0)(struct TestA*); @@ -103,6 +105,8 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S32, i2) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S8, b8) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S16, hUnaligned) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, TABLE, table) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, LIST, list) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn0) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn1) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, icfn0) @@ -187,6 +191,20 @@ M_TEST_DEFINE(testALayout) { assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); + member = HashTableLookup(&cls->instanceMembers, "table"); + assert_non_null(member); + assert_string_equal(member->name, "table"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_TABLE); + assert_int_equal(member->offset, &((struct TestA*) 0)->table); + + member = HashTableLookup(&cls->instanceMembers, "list"); + assert_non_null(member); + assert_string_equal(member->name, "list"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_LIST); + assert_int_equal(member->offset, &((struct TestA*) 0)->list); + member = HashTableLookup(&cls->instanceMembers, "unknown"); assert_null(member); @@ -280,6 +298,12 @@ M_TEST_DEFINE(testAGet) { .hUnaligned = 4 }; + mScriptListInit(&s.list, 1); + *mScriptListAppend(&s.list) = mSCRIPT_MAKE_S32(5); + + s.table.type = mSCRIPT_TYPE_MS_TABLE; + s.table.type->alloc(&s.table); + struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s); struct mScriptValue val; struct mScriptValue compare; @@ -300,8 +324,34 @@ M_TEST_DEFINE(testAGet) { assert_true(mScriptObjectGet(&sval, "hUnaligned", &val)); assert_true(compare.type->equal(&compare, &val)); + compare = mSCRIPT_MAKE_S32(5); + assert_true(mScriptObjectGet(&sval, "list", &val)); + assert_ptr_equal(val.type, mSCRIPT_TYPE_MS_LIST); + assert_int_equal(mScriptListSize(val.value.list), 1); + assert_true(compare.type->equal(&compare, mScriptListGetPointer(val.value.list, 0))); + + *mScriptListAppend(&s.list) = mSCRIPT_MAKE_S32(6); + compare = mSCRIPT_MAKE_S32(6); + assert_int_equal(mScriptListSize(val.value.list), 2); + assert_true(compare.type->equal(&compare, mScriptListGetPointer(val.value.list, 1))); + + struct mScriptValue* ival = &val; + assert_true(mScriptObjectGet(&sval, "table", &val)); + if (val.type->base == mSCRIPT_TYPE_WRAPPER) { + ival = mScriptValueUnwrap(&val); + } + assert_ptr_equal(ival->type, mSCRIPT_TYPE_MS_TABLE); + assert_int_equal(mScriptTableSize(ival), 0); + compare = mSCRIPT_MAKE_S32(7); + mScriptTableInsert(&s.table, &compare, &compare); + assert_int_equal(mScriptTableSize(&s.table), 1); + assert_int_equal(mScriptTableSize(ival), 1); + assert_false(mScriptObjectGet(&sval, "unknown", &val)); + mScriptListDeinit(&s.list); + mSCRIPT_TYPE_MS_TABLE->free(&s.table); + assert_true(cls->init); mScriptClassDeinit(cls); assert_false(cls->init); diff --git a/src/script/test/context.c b/src/script/test/context.c new file mode 100644 index 000000000..178dc25a7 --- /dev/null +++ b/src/script/test/context.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2013-2023 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/test/suite.h" + +#include +#include +#include + +M_TEST_DEFINE(weakrefBasic) { + struct mScriptContext context; + mScriptContextInit(&context); + + struct mScriptValue weakref = mSCRIPT_VAL(WEAKREF, 1); + struct mScriptValue fakeVal = mSCRIPT_S32(0x7E57CA5E); + struct mScriptValue* val; + + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 1); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + assert_int_equal(mScriptContextSetWeakref(&context, &fakeVal), 1); + assert_int_equal(context.nextWeakref, 2); + assert_int_equal(TableSize(&context.weakrefs), 1); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextClearWeakref(&context, 1); + + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 2); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(drainPool) { + struct mScriptContext context; + mScriptContextInit(&context); + + assert_int_equal(mScriptListSize(&context.refPool), 0); + + struct mScriptValue fakeVal = mSCRIPT_CHARP("foo"); + fakeVal.refs = 2; + + mScriptContextFillPool(&context, &fakeVal); + assert_int_equal(mScriptListSize(&context.refPool), 1); + assert_int_equal(fakeVal.refs, 2); + + mScriptContextDrainPool(&context); + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(fakeVal.refs, 1); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(disownWeakref) { + struct mScriptContext context; + mScriptContextInit(&context); + + struct mScriptValue weakref = mSCRIPT_VAL(WEAKREF, 1); + struct mScriptValue fakeVal = mSCRIPT_S32(0x7E57CA5E); + struct mScriptValue* val; + + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 1); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + assert_int_equal(mScriptContextSetWeakref(&context, &fakeVal), 1); + assert_int_equal(TableSize(&context.weakrefs), 1); + assert_int_equal(context.nextWeakref, 2); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextDisownWeakref(&context, 1); + assert_int_equal(mScriptListSize(&context.refPool), 1); + assert_int_equal(TableSize(&context.weakrefs), 1); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextDrainPool(&context); + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE(mScript, + cmocka_unit_test(weakrefBasic), + cmocka_unit_test(drainPool), + cmocka_unit_test(disownWeakref), +) diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 73954e91e..2c047ec39 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -371,6 +371,33 @@ M_TEST_DEFINE(callCFunc) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(globalNull) { + SETUP_LUA; + + struct Test s = {}; + struct mScriptValue* val; + struct mScriptValue a; + + LOAD_PROGRAM("assert(a)"); + + a = mSCRIPT_MAKE_CHARP("hello"); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_true(lua->run(lua)); + + a = mSCRIPT_MAKE_CHARP(NULL); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_true(lua->run(lua)); + + a = mSCRIPT_MAKE_S(Test, NULL); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + mScriptContextDeinit(&context); +} M_TEST_DEFINE(globalStructFieldGet) { SETUP_LUA; @@ -709,6 +736,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(rootScope), cmocka_unit_test(callLuaFunc), cmocka_unit_test(callCFunc), + cmocka_unit_test(globalNull), cmocka_unit_test(globalStructFieldGet), cmocka_unit_test(globalStructFieldSet), cmocka_unit_test(globalStructMethods), diff --git a/src/script/test/types.c b/src/script/test/types.c index 6ad38563a..ae1df5ba0 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -76,6 +76,14 @@ static int isSequential(struct mScriptList* list) { return true; } +static bool isNullCharp(const char* arg) { + return !arg; +} + +static bool isNullStruct(struct Test* arg) { + return !arg; +} + mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32, ignored); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, in); @@ -86,6 +94,8 @@ mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP, str); mSCRIPT_BIND_FUNCTION(boundIsSequential, S32, isSequential, 1, LIST, list); +mSCRIPT_BIND_FUNCTION(boundIsNullCharp, BOOL, isNullCharp, 1, CHARP, arg); +mSCRIPT_BIND_FUNCTION(boundIsNullStruct, BOOL, isNullStruct, 1, S(Test), arg); M_TEST_DEFINE(voidArgs) { struct mScriptFrame frame; @@ -1261,6 +1271,43 @@ M_TEST_DEFINE(invokeList) { mScriptListDeinit(&list); } +M_TEST_DEFINE(nullString) { + struct mScriptFrame frame; + bool res; + mScriptFrameInit(&frame); + + mSCRIPT_PUSH(&frame.arguments, CHARP, "hi"); + assert_true(mScriptInvoke(&boundIsNullCharp, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_false(res); + + mSCRIPT_PUSH(&frame.arguments, CHARP, NULL); + assert_true(mScriptInvoke(&boundIsNullCharp, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(res); + + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(nullStruct) { + struct mScriptFrame frame; + struct Test v = {}; + bool res; + mScriptFrameInit(&frame); + + mSCRIPT_PUSH(&frame.arguments, S(Test), &v); + assert_true(mScriptInvoke(&boundIsNullStruct, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_false(res); + + mSCRIPT_PUSH(&frame.arguments, S(Test), NULL); + assert_true(mScriptInvoke(&boundIsNullStruct, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(res); + + mScriptFrameDeinit(&frame); +} + M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(voidArgs), cmocka_unit_test(voidFunc), @@ -1295,4 +1342,6 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(stringIsHello), cmocka_unit_test(stringIsNotHello), cmocka_unit_test(invokeList), + cmocka_unit_test(nullString), + cmocka_unit_test(nullStruct), ) diff --git a/src/script/types.c b/src/script/types.c index b63fed4a5..dc9153587 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1178,6 +1178,12 @@ static bool _accessRawMember(struct mScriptClassMember* member, void* raw, bool val->type = mSCRIPT_TYPE_MS_WRAPPER; val->value.table = raw; break; + case mSCRIPT_TYPE_LIST: + val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; + val->type = mSCRIPT_TYPE_MS_LIST; + val->value.list = raw; + break; case mSCRIPT_TYPE_FUNCTION: val->refs = mSCRIPT_VALUE_UNREF; val->flags = 0; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index df0d84c5c..9246b20c4 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -8,6 +8,7 @@ set(BASE_SOURCE_FILES hash.c string.c table.c + vector.c vfs.c) set(SOURCE_FILES diff --git a/src/util/string.c b/src/util/string.c index 9e007dc2d..1a2ffe7a4 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -5,12 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include -#include - #include -DEFINE_VECTOR(StringList, char*); - #ifndef HAVE_STRNDUP char* strndup(const char* start, size_t len) { // This is suboptimal, but anything recent should have strndup diff --git a/src/util/vector.c b/src/util/vector.c new file mode 100644 index 000000000..28750a1b4 --- /dev/null +++ b/src/util/vector.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2013-2023 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +DEFINE_VECTOR(IntList, int); +DEFINE_VECTOR(SInt8List, int8_t); +DEFINE_VECTOR(SInt16List, int16_t); +DEFINE_VECTOR(SInt32List, int32_t); +DEFINE_VECTOR(SIntPtrList, intptr_t); +DEFINE_VECTOR(UInt8List, uint8_t); +DEFINE_VECTOR(UInt16List, uint16_t); +DEFINE_VECTOR(UInt32List, uint32_t); +DEFINE_VECTOR(UIntPtrList, uintptr_t); +DEFINE_VECTOR(StringList, char*);