Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2024-06-21 00:16:40 -07:00
commit 5d0dafbbb1
30 changed files with 406 additions and 68 deletions

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -11,8 +11,6 @@
DEFINE_VECTOR(LexVector, struct Token);
DEFINE_VECTOR(IntList, int32_t);
enum LexState {
LEX_ERROR = -1,
LEX_ROOT = 0,

View File

@ -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})

View File

@ -516,10 +516,10 @@ void PainterGL::create() {
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (m_supportsShaders) {
QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions<QOpenGLFunctions_Baseline>();
gl2Backend = static_cast<mGLES2Context*>(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<mGLES2Context*>(painter->m_backend);
if (painter->m_widget && painter->supportsShaders()) {
QOpenGLFunctions_Baseline* fn = painter->m_gl->versionFunctions<QOpenGLFunctions_Baseline>();
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_Baseline>();
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_Baseline>();
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_Baseline>();
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();

View File

@ -199,6 +199,8 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi
#ifdef BUILD_SDL
m_inputController.addInputDriver(std::make_shared<SDLInputDriver>(&m_inputController));
m_inputController.setGamepadDriver(SDL_BINDING_BUTTON);
m_inputController.setSensorDriver(SDL_BINDING_BUTTON);
#endif
m_shortcutController->setConfigController(m_config);

View File

@ -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);
}
}

View File

@ -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;
};
}

View File

@ -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<Gamepad*> driverPads(driver->connectedGamepads());
int activeGamepad = driver->activeGamepad();
if (activeGamepad < 0 || activeGamepad >= driverPads.count()) {
return nullptr;
}
return driverPads[activeGamepad];
return driver->activeGamepad();
}
QList<Gamepad*> InputController::gamepads() {
@ -372,16 +368,15 @@ QList<Gamepad*> InputController::gamepads() {
if (!driver->supportsGamepads()) {
continue;
}
QList<Gamepad*> 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<int> InputController::activeGamepadButtons(int type) {
QSet<int> InputController::activeGamepadButtons(uint32_t type) {
QSet<int> activeButtons;
Gamepad* pad = gamepad(type);
if (!pad) {
@ -396,7 +391,7 @@ QSet<int> InputController::activeGamepadButtons(int type) {
return activeButtons;
}
QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(uint32_t type) {
QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
Gamepad* pad = gamepad(type);
if (!pad) {
@ -417,7 +412,7 @@ QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes
return activeAxes;
}
QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(uint32_t type) {
QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
Gamepad* pad = gamepad(type);
if (!pad) {
@ -432,7 +427,7 @@ QSet<QPair<int, GamepadHatEvent::Direction>> 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);
}

View File

@ -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<Gamepad*> gamepads();
QSet<int> activeGamepadButtons(int type);
QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes(int type);
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(int type);
QSet<int> activeGamepadButtons(uint32_t type);
QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes(uint32_t type);
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(uint32_t type);
struct InputControllerLux : GBALuminanceSource {
InputController* p;

View File

@ -40,14 +40,32 @@ QList<Gamepad*> 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<KeySource*> ks(connectedKeySources());
int activeKeySource = activeKeySourceIndex();
if (activeKeySource < 0 || activeKeySource >= ks.count()) {
return nullptr;
}
return ks[activeKeySource];
}
Gamepad* InputDriver::activeGamepad() {
QList<Gamepad*> pads(connectedGamepads());
int activeGamepad = activeGamepadIndex();
if (activeGamepad < 0 || activeGamepad >= pads.count()) {
return nullptr;
}
return pads[activeGamepad];
}
void InputDriver::setActiveKeySource(int) {
}

View File

@ -44,8 +44,11 @@ public:
virtual QList<KeySource*> connectedKeySources() const;
virtual QList<Gamepad*> 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);

View File

@ -66,10 +66,7 @@ int InputMapper::mapAxes(QList<int16_t> axes) const {
int InputMapper::mapHats(QList<GamepadHatEvent::Direction> 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;
}

View File

@ -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;
}

View File

@ -46,7 +46,7 @@ public:
QList<Gamepad*> connectedGamepads() const override;
int activeGamepad() const override;
int activeGamepadIndex() const override;
void setActiveGamepad(int) override;
void registerTiltAxisX(int axis) override;

View File

@ -42,6 +42,7 @@ Q_IMPORT_PLUGIN(AVFServicePlugin);
#endif
#elif defined(Q_OS_UNIX)
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
Q_IMPORT_PLUGIN(QWaylandIntegrationPlugin);
#endif
#endif

View File

@ -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})

View File

@ -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) {

View File

@ -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);

View File

@ -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);

103
src/script/test/context.c Normal file
View File

@ -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 <mgba/script/context.h>
#include <mgba/script/macros.h>
#include <mgba/script/types.h>
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),
)

View File

@ -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),

View File

@ -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),
)

View File

@ -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;

View File

@ -8,6 +8,7 @@ set(BASE_SOURCE_FILES
hash.c
string.c
table.c
vector.c
vfs.c)
set(SOURCE_FILES

View File

@ -5,12 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba-util/string.h>
#include <mgba-util/vector.h>
#include <string.h>
DEFINE_VECTOR(StringList, char*);
#ifndef HAVE_STRNDUP
char* strndup(const char* start, size_t len) {
// This is suboptimal, but anything recent should have strndup

17
src/util/vector.c Normal file
View File

@ -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 <mgba-util/vector.h>
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*);