From 1a33a71771ded7b2197b898cff89c3b8d2b6b6b0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 13 Jul 2021 20:04:17 -0700 Subject: [PATCH 001/105] Scripting: Initial runtime bringup work --- CMakeLists.txt | 6 + include/mgba/script/types.h | 250 ++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 2 +- src/script/CMakeLists.txt | 12 ++ src/script/test/types.c | 229 +++++++++++++++++++++++++++ src/script/types.c | 299 ++++++++++++++++++++++++++++++++++++ 6 files changed, 797 insertions(+), 1 deletion(-) create mode 100644 include/mgba/script/types.h create mode 100644 src/script/CMakeLists.txt create mode 100644 src/script/test/types.c create mode 100644 src/script/types.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ba9f29c8..825d942ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -733,6 +733,7 @@ if (USE_DISCORD_RPC) endif() if(ENABLE_SCRIPTING) + add_subdirectory(src/script) list(APPEND ENABLES SCRIPTING) if(BUILD_PYTHON) @@ -774,6 +775,11 @@ if(USE_DEBUGGERS) list(APPEND FEATURES DEBUGGERS) endif() +if(ENABLE_SCRIPTING) + list(APPEND FEATURE_SRC ${SCRIPT_SRC}) + list(APPEND TEST_SRC ${SCRIPT_TEST_SRC}) +endif() + foreach(FEATURE IN LISTS FEATURES) list(APPEND FEATURE_DEFINES "USE_${FEATURE}") endforeach() diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h new file mode 100644 index 000000000..678fc4373 --- /dev/null +++ b/include/mgba/script/types.h @@ -0,0 +1,250 @@ +/* Copyright (c) 2013-2021 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/. */ +#ifndef M_SCRIPT_TYPES_H +#define M_SCRIPT_TYPES_H + +#include + +CXX_GUARD_START + +#include + +#define _mAPPLY(...) __VA_ARGS__ + +#define mSCRIPT_VALUE_UNREF -1 +#define mSCRIPT_PARAMS_MAX 8 + +#define mSCRIPT_TYPE_C_S32 int32_t +#define mSCRIPT_TYPE_C_U32 uint32_t +#define mSCRIPT_TYPE_C_F32 float +#define mSCRIPT_TYPE_C_PTR void* +#define mSCRIPT_TYPE_C_TABLE Table* +#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* + +#define mSCRIPT_TYPE_FIELD_S32 s32 +#define mSCRIPT_TYPE_FIELD_U32 u32 +#define mSCRIPT_TYPE_FIELD_F32 f32 +#define mSCRIPT_TYPE_FIELD_PTR opaque +#define mSCRIPT_TYPE_FIELD_TABLE opaque +#define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque + +#define mSCRIPT_TYPE_MS_S32 (&mSTSInt32) +#define mSCRIPT_TYPE_MS_U32 (&mSTUInt32) +#define mSCRIPT_TYPE_MS_F32 (&mSTFloat32) +#define mSCRIPT_TYPE_MS_TABLE (&mSTTable) +#define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) + +#define _mSCRIPT_FIELD_NAME(V) (V)->name + +#define mSCRIPT_TYPE_CMP_GENERIC(TYPE0, TYPE1) (TYPE0 == TYPE1) +#define mSCRIPT_TYPE_CMP_U32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U32, TYPE) +#define mSCRIPT_TYPE_CMP_S32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S32, TYPE) +#define mSCRIPT_TYPE_CMP_F32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F32, TYPE) +#define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE) +#define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME +#define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) _mAPPLY(mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1)) + +#define mSCRIPT_POP(STACK, TYPE, NAME) \ + _mAPPLY(mSCRIPT_TYPE_C_ ## TYPE) NAME; \ + do { \ + struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ + if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ + return false; \ + } \ + NAME = _val->value. _mAPPLY(mSCRIPT_TYPE_FIELD_ ## TYPE); \ + mScriptListResize(STACK, -1); \ + } while (0) + +#define mSCRIPT_POP_0(...) +#define mSCRIPT_POP_1(FRAME, T0) mSCRIPT_POP(FRAME, T0, p0) +#define mSCRIPT_POP_2(FRAME, T0, T1) mSCRIPT_POP(FRAME, T1, p1); mSCRIPT_POP_1(FRAME, T0) +#define mSCRIPT_POP_3(FRAME, T0, T1, T2) mSCRIPT_POP(FRAME, T2, p2); mSCRIPT_POP_2(FRAME, T0, T1) +#define mSCRIPT_POP_4(FRAME, T0, T1, T2, T3) mSCRIPT_POP(FRAME, T3, p3); mSCRIPT_POP_3(FRAME, T0, T1, T2) +#define mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4) mSCRIPT_POP(FRAME, T4, p4); mSCRIPT_POP_4(FRAME, T0, T1, T2, T3) +#define mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) mSCRIPT_POP(FRAME, T5, p5); mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4) +#define mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) mSCRIPT_POP(FRAME, T6, p6); mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) +#define mSCRIPT_POP_8(FRAME, T0, T1, T2, T3, T4, T5, T6, T7) mSCRIPT_POP(FRAME, T7, p7); mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) + +#define mSCRIPT_PUSH(STACK, TYPE, NAME) \ + do { \ + struct mScriptValue* _val = mScriptListAppend(STACK); \ + _val->type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE); \ + _val->refs = mSCRIPT_VALUE_UNREF; \ + _val->value._mAPPLY(mSCRIPT_TYPE_FIELD_ ## TYPE) = NAME; \ + } while (0) + +#define mSCRIPT_ARG_NAMES_0 +#define mSCRIPT_ARG_NAMES_1 p0 +#define mSCRIPT_ARG_NAMES_2 p0, p1 +#define mSCRIPT_ARG_NAMES_3 p0, p1, p2 +#define mSCRIPT_ARG_NAMES_4 p0, p1, p2, p3 +#define mSCRIPT_ARG_NAMES_5 p0, p1, p2, p3, p4 +#define mSCRIPT_ARG_NAMES_6 p0, p1, p2, p3, p4, p5 +#define mSCRIPT_ARG_NAMES_7 p0, p1, p2, p3, p4, p5, p6 +#define mSCRIPT_ARG_NAMES_8 p0, p1, p2, p3, p4, p5, p6, p7 + +#define mSCRIPT_PREFIX_0(PREFIX, ...) +#define mSCRIPT_PREFIX_1(PREFIX, T0) PREFIX ## T0 +#define mSCRIPT_PREFIX_2(PREFIX, T0, T1) PREFIX ## T0, PREFIX ## T1 +#define mSCRIPT_PREFIX_3(PREFIX, T0, T1, T2) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2 +#define mSCRIPT_PREFIX_4(PREFIX, T0, T1, T2, T3) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3 +#define mSCRIPT_PREFIX_5(PREFIX, T0, T1, T2, T3, T4) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4 +#define mSCRIPT_PREFIX_6(PREFIX, T0, T1, T2, T3, T4, T5) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5 +#define mSCRIPT_PREFIX_7(PREFIX, T0, T1, T2, T3, T4, T5, T6) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6 +#define mSCRIPT_PREFIX_8(PREFIX, T0, T1, T2, T3, T4, T5, T6, T7) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6, PREFIX ## T7 + +#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(mSCRIPT_ARG_NAMES_ ## NPARAMS) +#define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ + _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) out = FUNCTION(mSCRIPT_ARG_NAMES_ ## NPARAMS); \ + mSCRIPT_PUSH(&frame->returnValues, RETURN, out) + +#define mSCRIPT_EXPORT_STRUCT(STRUCT) \ + const struct mScriptType mSTStruct_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OBJECT, \ + .size = sizeof(struct STRUCT), \ + .name = "struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + } + +#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \ + static bool _binding_ ## NAME(struct mScriptFrame* frame) { \ + mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } \ + _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ + return true; \ + } \ + const struct mScriptFunction NAME = { \ + .signature = { \ + .parameters = { \ + .count = NPARAMS, \ + .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ + }, \ + .returnType = { \ + .count = 1, \ + .entries = { mSCRIPT_TYPE_MS_ ## RETURN } \ + }, \ + }, \ + .call = _binding_ ## NAME \ + }; + +#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \ + static bool _binding_ ## NAME(struct mScriptFrame* frame) { \ + mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } \ + _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ + return true; \ + } \ + const struct mScriptFunction NAME = { \ + .signature = { \ + .parameters = { \ + .count = NPARAMS, \ + .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ + }, \ + .returnType = { \ + .count = 0, \ + }, \ + }, \ + .call = _binding_ ## NAME \ + }; + +enum { + mSCRIPT_TYPE_VOID = 0, + mSCRIPT_TYPE_SINT, + mSCRIPT_TYPE_UINT, + mSCRIPT_TYPE_FLOAT, + mSCRIPT_TYPE_STRING, + mSCRIPT_TYPE_FUNCTION, + mSCRIPT_TYPE_OPAQUE, + mSCRIPT_TYPE_OBJECT, + mSCRIPT_TYPE_TUPLE, + mSCRIPT_TYPE_LIST, + mSCRIPT_TYPE_TABLE, +}; + +struct Table; +struct mScriptType; +extern const struct mScriptType mSTVoid; +extern const struct mScriptType mSTSInt32; +extern const struct mScriptType mSTUInt32; +extern const struct mScriptType mSTFloat32; +extern const struct mScriptType mSTTable; + +struct mScriptTypeTuple { + size_t count; + const struct mScriptType* entries[mSCRIPT_PARAMS_MAX]; +}; + +struct mScriptTypeFunction { + struct mScriptTypeTuple parameters; + struct mScriptTypeTuple returnType; + // TODO: varargs, kwargs, defaults +}; + +struct mScriptValue; +struct mScriptType { + int base; + size_t size; + const char* name; + void (*alloc)(const struct mScriptType*, struct mScriptValue*); + void (*free)(const struct mScriptType*, struct mScriptValue*); + uint32_t (*hash)(const struct mScriptType*, struct mScriptValue*); + union { + struct mScriptTypeTuple tuple; + struct mScriptTypeFunction function; + void* opaque; + } details; +}; + +struct mScriptValue { + const struct mScriptType* type; + int refs; + union { + int32_t s32; + uint32_t u32; + float f32; + void* opaque; + } value; +}; + +DECLARE_VECTOR(mScriptList, struct mScriptValue) + +struct mScriptFrame { + struct mScriptList arguments; + struct mScriptList returnValues; + // TODO: Exception/failure codes +}; + +struct mScriptFunction { + struct mScriptTypeFunction signature; + bool (*call)(struct mScriptFrame*); +}; + +struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type); +void mScriptValueRef(struct mScriptValue* val); +void mScriptValueDeref(struct mScriptValue* val); + +bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value); +bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key); +struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key); + +void mScriptFrameInit(struct mScriptFrame* frame); +void mScriptFrameDeinit(struct mScriptFrame* frame); + +bool mScriptPopS32(struct mScriptList* list, int32_t* out); +bool mScriptPopU32(struct mScriptList* list, uint32_t* out); +bool mScriptPopF32(struct mScriptList* list, float* out); +bool mScriptPopPointer(struct mScriptList* list, void** out); + +bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output); +bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame); +bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame); + +#endif diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6c639e594..7c1108252 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -28,4 +28,4 @@ source_group("mCore" FILES ${SOURCE_FILES}) source_group("mCore tests" FILES ${TEST_FILES}) export_directory(CORE SOURCE_FILES) -export_directory(CORE_TEST TEST_FILES) \ No newline at end of file +export_directory(CORE_TEST TEST_FILES) diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt new file mode 100644 index 000000000..5d1d1ae6f --- /dev/null +++ b/src/script/CMakeLists.txt @@ -0,0 +1,12 @@ +include(ExportDirectory) +set(SOURCE_FILES + types.c) + +set(TEST_FILES + test/types.c) + +source_group("Scripting" FILES ${SOURCE_FILES}) +source_group("Scripting tests" FILES ${TEST_FILES}) + +export_directory(SCRIPT SOURCE_FILES) +export_directory(SCRIPT_TEST TEST_FILES) diff --git a/src/script/test/types.c b/src/script/test/types.c new file mode 100644 index 000000000..7ccd24091 --- /dev/null +++ b/src/script/test/types.c @@ -0,0 +1,229 @@ +/* Copyright (c) 2013-2021 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 + +struct Test { + int32_t a; +}; + +mSCRIPT_EXPORT_STRUCT(Test); + +static int voidOne(void) { + return 1; +} + +static void discard(int ignored) { + UNUSED(ignored); +} + +static int identityInt(int in) { + return in; +} + +static float identityFloat(float in) { + return in; +} + +static struct Test* identityStruct(struct Test* t) { + return t; +} + +static int addInts(int a, int b) { + return a + b; +} + +static int subInts(int a, int b) { + return a - b; +} + +mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); +mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32); +mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32); +mSCRIPT_BIND_FUNCTION(boundIdentityFloat, F32, identityFloat, 1, F32); +mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test)); +mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32); +mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, S32); + +M_TEST_DEFINE(voidArgs) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + assert_true(mScriptInvoke(&boundVoidOne, &frame)); + int32_t val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(voidFunc) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&boundDiscard, &frame)); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(identityFunctionS32) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&boundIdentityInt, &frame)); + int32_t val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(identityFunctionF32) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, F32, 3.125f); + assert_true(mScriptInvoke(&boundIdentityFloat, &frame)); + float val; + assert_true(mScriptPopF32(&frame.returnValues, &val)); + assert_float_equal(val, 3.125f, 0.f); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(identityFunctionStruct) { + struct mScriptFrame frame; + struct Test v = {}; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(Test), &v); + assert_true(mScriptInvoke(&boundIdentityStruct, &frame)); + struct Test* val; + assert_true(mScriptPopPointer(&frame.returnValues, (void**) &val)); + assert_ptr_equal(val, &v); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(addS32) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.arguments, S32, 2); + assert_true(mScriptInvoke(&boundAddInts, &frame)); + int32_t val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 3); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(subS32) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 2); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&boundSubInts, &frame)); + int32_t val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(wrongArgCountLo) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + assert_false(mScriptInvoke(&boundIdentityInt, &frame)); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(wrongArgCountHi) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_false(mScriptInvoke(&boundIdentityInt, &frame)); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(wrongArgType) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_false(mScriptInvoke(&boundIdentityStruct, &frame)); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(coerceToFloat) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&boundIdentityFloat, &frame)); + float val; + assert_true(mScriptPopF32(&frame.returnValues, &val)); + assert_float_equal(val, 1.f, 0.f); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(coerceFromFloat) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, F32, 1.25f); + assert_true(mScriptInvoke(&boundIdentityInt, &frame)); + int val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(hashTableBasic) { + struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + assert_non_null(table); + + struct mScriptValue* intValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); + assert_ptr_equal(intValue->type, mSCRIPT_TYPE_MS_S32); + assert_int_equal(intValue->value.s32, 0); + assert_int_equal(intValue->refs, 1); + + struct mScriptValue intKey = { + .type = mSCRIPT_TYPE_MS_S32, + .value = { + .s32 = 1234 + }, + .refs = mSCRIPT_VALUE_UNREF + }; + + struct mScriptValue badKey = { + .type = mSCRIPT_TYPE_MS_S32, + .value = { + .s32 = 1235 + }, + .refs = mSCRIPT_VALUE_UNREF + }; + + assert_true(mScriptTableInsert(table, &intKey, intValue)); + assert_int_equal(intValue->refs, 2); + + struct mScriptValue* lookupValue = mScriptTableLookup(table, &intKey); + assert_non_null(lookupValue); + assert_ptr_equal(lookupValue, intValue); + + lookupValue = mScriptTableLookup(table, &badKey); + assert_null(lookupValue); + + assert_true(mScriptTableRemove(table, &intKey)); + assert_int_equal(intValue->refs, 1); + + mScriptValueDeref(intValue); + mScriptValueDeref(table); +} + +M_TEST_SUITE_DEFINE(mScript, + cmocka_unit_test(voidArgs), + cmocka_unit_test(voidFunc), + cmocka_unit_test(identityFunctionS32), + cmocka_unit_test(identityFunctionF32), + cmocka_unit_test(identityFunctionStruct), + cmocka_unit_test(addS32), + cmocka_unit_test(subS32), + cmocka_unit_test(wrongArgCountLo), + cmocka_unit_test(wrongArgCountHi), + cmocka_unit_test(wrongArgType), + cmocka_unit_test(coerceToFloat), + cmocka_unit_test(coerceFromFloat), + cmocka_unit_test(hashTableBasic)) diff --git a/src/script/types.c b/src/script/types.c new file mode 100644 index 000000000..bcdf0f8f6 --- /dev/null +++ b/src/script/types.c @@ -0,0 +1,299 @@ +/* Copyright (c) 2013-2021 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 + +#include + +static void _allocTable(const struct mScriptType*, struct mScriptValue*); +static void _freeTable(const struct mScriptType*, struct mScriptValue*); +static void _deinitTableValue(void*); + +static uint32_t _hashScalar(const struct mScriptType*, struct mScriptValue*); + +const struct mScriptType mSTVoid = { + .base = mSCRIPT_TYPE_VOID, + .size = 0, + .name = "void", + .alloc = NULL, + .free = NULL, + .hash = NULL, +}; + +const struct mScriptType mSTSInt32 = { + .base = mSCRIPT_TYPE_SINT, + .size = 4, + .name = "s32", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, +}; + +const struct mScriptType mSTUInt32 = { + .base = mSCRIPT_TYPE_UINT, + .size = 4, + .name = "u32", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, +}; + +const struct mScriptType mSTFloat32 = { + .base = mSCRIPT_TYPE_FLOAT, + .size = 4, + .name = "f32", + .alloc = NULL, + .free = NULL, + .hash = NULL, +}; + +const struct mScriptType mSTTable = { + .base = mSCRIPT_TYPE_TABLE, + .size = sizeof(struct Table), + .name = "table", + .alloc = _allocTable, + .free = _freeTable, + .hash = NULL, +}; + +DEFINE_VECTOR(mScriptList, struct mScriptValue) + +void _allocTable(const struct mScriptType* type, struct mScriptValue* val) { + UNUSED(type); + val->value.opaque = malloc(sizeof(struct Table)); + TableInit(val->value.opaque, 0, _deinitTableValue); +} + +void _freeTable(const struct mScriptType* type, struct mScriptValue* val) { + UNUSED(type); + TableDeinit(val->value.opaque); + free(val->value.opaque); +} + +void _deinitTableValue(void* val) { + mScriptValueDeref(val); +} + +uint32_t _hashScalar(const struct mScriptType* type, struct mScriptValue* val) { + // From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key + uint32_t x; + switch (type->base) { + case mSCRIPT_TYPE_SINT: + x = val->value.s32; + break; + case mSCRIPT_TYPE_UINT: + x = val->value.u32; + break; + } + x = ((x >> 16) ^ x) * 0x45D9F3B; + x = ((x >> 16) ^ x) * 0x45D9F3B; + x = (x >> 16) ^ x; + return x; +} + +struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) { + // TODO: Use an arena instead of just the generic heap + struct mScriptValue* val = malloc(sizeof(*val)); + val->refs = 1; + if (type->alloc) { + type->alloc(type, val); + } else { + val->value.opaque = NULL; + } + val->type = type; + return val; +} + +void mScriptValueRef(struct mScriptValue* val) { + if (val->refs == INT_MAX) { + abort(); + } else if (val->refs == mSCRIPT_VALUE_UNREF) { + return; + } + ++val->refs; +} + +void mScriptValueDeref(struct mScriptValue* val) { + --val->refs; + if (val->refs > 0) { + return; + } else if (val->refs < 0) { + val->refs = mSCRIPT_VALUE_UNREF; + } + if (val->type->free) { + val->type->free(val->type, val); + } + free(val); +} + +bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value) { + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return false; + } + if (!key->type->hash) { + return false; + } + struct Table* t = table->value.opaque; + uint32_t hash = key->type->hash(key->type, key); + mScriptValueRef(value); + TableInsert(t, hash, value); + return true; +} + +bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key) { + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return false; + } + if (!key->type->hash) { + return false; + } + struct Table* t = table->value.opaque; + uint32_t hash = key->type->hash(key->type, key); + TableRemove(t, hash); + return true; +} + +struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key) { + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return false; + } + if (!key->type->hash) { + return false; + } + struct Table* t = table->value.opaque; + uint32_t hash = key->type->hash(key->type, key); + return TableLookup(t, hash); +} + +void mScriptFrameInit(struct mScriptFrame* frame) { + mScriptListInit(&frame->arguments, 4); + mScriptListInit(&frame->returnValues, 1); +} + +void mScriptFrameDeinit(struct mScriptFrame* frame) { + mScriptListDeinit(&frame->returnValues); + mScriptListDeinit(&frame->arguments); +} + +bool mScriptPopS32(struct mScriptList* list, int32_t* out) { + mSCRIPT_POP(list, S32, val); + *out = val; + return true; +} + +bool mScriptPopU32(struct mScriptList* list, uint32_t* out) { + mSCRIPT_POP(list, U32, val); + *out = val; + return true; +} + +bool mScriptPopF32(struct mScriptList* list, float* out) { + mSCRIPT_POP(list, F32, val); + *out = val; + return true; +} + +bool mScriptPopPointer(struct mScriptList* list, void** out) { + mSCRIPT_POP(list, PTR, val); + *out = val; + return true; +} + +bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output) { + switch (type->base) { + case mSCRIPT_TYPE_VOID: + return false; + case mSCRIPT_TYPE_SINT: + switch (input->type->base) { + case mSCRIPT_TYPE_SINT: + output->value.s32 = input->value.s32; + break; + case mSCRIPT_TYPE_UINT: + output->value.s32 = input->value.u32; + break; + case mSCRIPT_TYPE_FLOAT: + switch (input->type->size) { + case 4: + output->value.s32 = input->value.f32; + break; + default: + return false; + } + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_UINT: + switch (input->type->base) { + case mSCRIPT_TYPE_SINT: + output->value.u32 = input->value.s32; + break; + case mSCRIPT_TYPE_UINT: + output->value.u32 = input->value.u32; + break; + case mSCRIPT_TYPE_FLOAT: + switch (input->type->size) { + case 4: + output->value.u32 = input->value.f32; + break; + default: + return false; + } + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_FLOAT: + switch (input->type->base) { + case mSCRIPT_TYPE_SINT: + output->value.f32 = input->value.s32; + break; + case mSCRIPT_TYPE_UINT: + output->value.f32 = input->value.u32; + break; + case mSCRIPT_TYPE_FLOAT: + switch (input->type->size) { + case 4: + output->value.f32 = input->value.f32; + break; + default: + return false; + } + break; + default: + return false; + } + break; + default: + return false; + } + output->type = type; + return true; +} + +bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame) { + if (types->count != mScriptListSize(frame)) { + return false; + } + size_t i; + for (i = 0; i < types->count; ++i) { + if (types->entries[i] == mScriptListGetPointer(frame, i)->type) { + continue; + } + if (!mScriptCast(types->entries[i], mScriptListGetPointer(frame, i), mScriptListGetPointer(frame, i))) { + return false; + } + } + return true; +} + +bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame) { + if (!mScriptCoerceFrame(&fn->signature.parameters, &frame->arguments)) { + return false; + } + return fn->call(frame); +} From 9d6f424623bcb67ca46a508f5df64fc38373c201 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 19 Jul 2021 23:06:51 -0700 Subject: [PATCH 002/105] Scripting: Allow unsetting bridge debugger --- src/core/scripting.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 9a0ee7698..add6da031 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -91,8 +91,16 @@ void mScriptBridgeInstallEngine(struct mScriptBridge* sb, struct mScriptEngine* #ifdef USE_DEBUGGERS void mScriptBridgeSetDebugger(struct mScriptBridge* sb, struct mDebugger* debugger) { + if (sb->debugger == debugger) { + return; + } + if (sb->debugger) { + sb->debugger->bridge = NULL; + } sb->debugger = debugger; - debugger->bridge = sb; + if (debugger) { + debugger->bridge = sb; + } } struct mDebugger* mScriptBridgeGetDebugger(struct mScriptBridge* sb) { From 34752e95d5a4520c44ad09f1eb4208e2aac3990a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Aug 2021 18:24:27 -0700 Subject: [PATCH 003/105] Scripting: Adapt Table type to use HashTableCustom --- include/mgba/script/types.h | 2 +- src/script/types.c | 67 +++++++++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 678fc4373..39af96ddb 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -195,7 +195,7 @@ struct mScriptType { const char* name; void (*alloc)(const struct mScriptType*, struct mScriptValue*); void (*free)(const struct mScriptType*, struct mScriptValue*); - uint32_t (*hash)(const struct mScriptType*, struct mScriptValue*); + uint32_t (*hash)(const struct mScriptType*, const struct mScriptValue*); union { struct mScriptTypeTuple tuple; struct mScriptTypeFunction function; diff --git a/src/script/types.c b/src/script/types.c index bcdf0f8f6..279bbb601 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -11,7 +11,12 @@ static void _allocTable(const struct mScriptType*, struct mScriptValue*); static void _freeTable(const struct mScriptType*, struct mScriptValue*); static void _deinitTableValue(void*); -static uint32_t _hashScalar(const struct mScriptType*, struct mScriptValue*); +static uint32_t _hashScalar(const struct mScriptType*, const struct mScriptValue*); + +static uint32_t _valHash(const void* val, size_t len, uint32_t seed); +static bool _valEqual(const void* a, const void* b); +static void* _valRef(void*); +static void _valDeref(void*); const struct mScriptType mSTVoid = { .base = mSCRIPT_TYPE_VOID, @@ -63,12 +68,19 @@ DEFINE_VECTOR(mScriptList, struct mScriptValue) void _allocTable(const struct mScriptType* type, struct mScriptValue* val) { UNUSED(type); val->value.opaque = malloc(sizeof(struct Table)); - TableInit(val->value.opaque, 0, _deinitTableValue); + struct TableFunctions funcs = { + .deinitializer = _deinitTableValue, + .hash = _valHash, + .equal = _valEqual, + .ref = _valRef, + .deref = _valDeref + }; + HashTableInitCustom(val->value.opaque, 0, &funcs); } void _freeTable(const struct mScriptType* type, struct mScriptValue* val) { UNUSED(type); - TableDeinit(val->value.opaque); + HashTableDeinit(val->value.opaque); free(val->value.opaque); } @@ -76,7 +88,7 @@ void _deinitTableValue(void* val) { mScriptValueDeref(val); } -uint32_t _hashScalar(const struct mScriptType* type, struct mScriptValue* val) { +uint32_t _hashScalar(const struct mScriptType* type, const struct mScriptValue* val) { // From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key uint32_t x; switch (type->base) { @@ -93,6 +105,43 @@ uint32_t _hashScalar(const struct mScriptType* type, struct mScriptValue* val) { return x; } +uint32_t _valHash(const void* val, size_t len, uint32_t seed) { + UNUSED(len); + const struct mScriptValue* value = val; + uint32_t hash = value->type->hash(value->type, value); + return hash ^ seed; +} + +bool _valEqual(const void* a, const void* b) { + const struct mScriptValue* valueA = a; + const struct mScriptValue* valueB = b; + // TODO: Move equality into type + if (valueA->type != valueB->type) { + return false; + } + switch (valueA->type->base) { + case mSCRIPT_TYPE_VOID: + return true; + case mSCRIPT_TYPE_SINT: + return valueA->value.s32 == valueB->value.s32; + case mSCRIPT_TYPE_UINT: + return valueA->value.u32 == valueB->value.u32; + case mSCRIPT_TYPE_FLOAT: + return valueA->value.f32 == valueB->value.f32; + default: + return valueA->value.opaque == valueB->value.opaque; + } +} + +void* _valRef(void* val) { + mScriptValueRef(val); + return val; +} + +void _valDeref(void* val) { + mScriptValueDeref(val); +} + struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) { // TODO: Use an arena instead of just the generic heap struct mScriptValue* val = malloc(sizeof(*val)); @@ -121,6 +170,7 @@ void mScriptValueDeref(struct mScriptValue* val) { return; } else if (val->refs < 0) { val->refs = mSCRIPT_VALUE_UNREF; + return; } if (val->type->free) { val->type->free(val->type, val); @@ -136,9 +186,8 @@ bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, st return false; } struct Table* t = table->value.opaque; - uint32_t hash = key->type->hash(key->type, key); mScriptValueRef(value); - TableInsert(t, hash, value); + HashTableInsertCustom(t, key, value); return true; } @@ -150,8 +199,7 @@ bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key) { return false; } struct Table* t = table->value.opaque; - uint32_t hash = key->type->hash(key->type, key); - TableRemove(t, hash); + HashTableRemoveCustom(t, key); return true; } @@ -163,8 +211,7 @@ struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScri return false; } struct Table* t = table->value.opaque; - uint32_t hash = key->type->hash(key->type, key); - return TableLookup(t, hash); + return HashTableLookupCustom(t, key); } void mScriptFrameInit(struct mScriptFrame* frame) { From a2b0230c719266806435c54504f83ed7c0534d5c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Aug 2021 18:31:50 -0700 Subject: [PATCH 004/105] Scripting: Add some helper macros --- include/mgba/script/types.h | 12 ++++++++++++ src/script/test/types.c | 17 ++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 39af96ddb..948a50d8f 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -155,6 +155,18 @@ CXX_GUARD_START .call = _binding_ ## NAME \ }; +#define mSCRIPT_MAKE(TYPE, FIELD, VALUE) (struct mScriptValue) { \ + .type = (TYPE), \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = { \ + .FIELD = (VALUE) \ + }, \ + } \ + +#define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S32, s32, VALUE) +#define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U32, u32, VALUE) +#define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F32, f32, VALUE) + enum { mSCRIPT_TYPE_VOID = 0, mSCRIPT_TYPE_SINT, diff --git a/src/script/test/types.c b/src/script/test/types.c index 7ccd24091..efd3701aa 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -180,21 +180,8 @@ M_TEST_DEFINE(hashTableBasic) { assert_int_equal(intValue->value.s32, 0); assert_int_equal(intValue->refs, 1); - struct mScriptValue intKey = { - .type = mSCRIPT_TYPE_MS_S32, - .value = { - .s32 = 1234 - }, - .refs = mSCRIPT_VALUE_UNREF - }; - - struct mScriptValue badKey = { - .type = mSCRIPT_TYPE_MS_S32, - .value = { - .s32 = 1235 - }, - .refs = mSCRIPT_VALUE_UNREF - }; + struct mScriptValue intKey = mSCRIPT_MAKE_S32(1234); + struct mScriptValue badKey = mSCRIPT_MAKE_S32(1235); assert_true(mScriptTableInsert(table, &intKey, intValue)); assert_int_equal(intValue->refs, 2); From 16563fe4d210e5e83cec2bc1c95b27ace7f02952 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Aug 2021 20:56:49 -0700 Subject: [PATCH 005/105] Scripting: Typing cleanup --- include/mgba/script/types.h | 6 +++--- src/script/types.c | 24 +++++++++++------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 948a50d8f..284853d3f 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -205,14 +205,14 @@ struct mScriptType { int base; size_t size; const char* name; - void (*alloc)(const struct mScriptType*, struct mScriptValue*); - void (*free)(const struct mScriptType*, struct mScriptValue*); - uint32_t (*hash)(const struct mScriptType*, const struct mScriptValue*); union { struct mScriptTypeTuple tuple; struct mScriptTypeFunction function; void* opaque; } details; + void (*alloc)(struct mScriptValue*); + void (*free)(struct mScriptValue*); + uint32_t (*hash)(const struct mScriptValue*); }; struct mScriptValue { diff --git a/src/script/types.c b/src/script/types.c index 279bbb601..82288da9d 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -7,11 +7,11 @@ #include -static void _allocTable(const struct mScriptType*, struct mScriptValue*); -static void _freeTable(const struct mScriptType*, struct mScriptValue*); +static void _allocTable(struct mScriptValue*); +static void _freeTable(struct mScriptValue*); static void _deinitTableValue(void*); -static uint32_t _hashScalar(const struct mScriptType*, const struct mScriptValue*); +static uint32_t _hashScalar(const struct mScriptValue*); static uint32_t _valHash(const void* val, size_t len, uint32_t seed); static bool _valEqual(const void* a, const void* b); @@ -65,8 +65,7 @@ const struct mScriptType mSTTable = { DEFINE_VECTOR(mScriptList, struct mScriptValue) -void _allocTable(const struct mScriptType* type, struct mScriptValue* val) { - UNUSED(type); +void _allocTable(struct mScriptValue* val) { val->value.opaque = malloc(sizeof(struct Table)); struct TableFunctions funcs = { .deinitializer = _deinitTableValue, @@ -78,8 +77,7 @@ void _allocTable(const struct mScriptType* type, struct mScriptValue* val) { HashTableInitCustom(val->value.opaque, 0, &funcs); } -void _freeTable(const struct mScriptType* type, struct mScriptValue* val) { - UNUSED(type); +void _freeTable(struct mScriptValue* val) { HashTableDeinit(val->value.opaque); free(val->value.opaque); } @@ -88,10 +86,10 @@ void _deinitTableValue(void* val) { mScriptValueDeref(val); } -uint32_t _hashScalar(const struct mScriptType* type, const struct mScriptValue* val) { +uint32_t _hashScalar(const struct mScriptValue* val) { // From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key uint32_t x; - switch (type->base) { + switch (val->type->base) { case mSCRIPT_TYPE_SINT: x = val->value.s32; break; @@ -108,7 +106,7 @@ uint32_t _hashScalar(const struct mScriptType* type, const struct mScriptValue* uint32_t _valHash(const void* val, size_t len, uint32_t seed) { UNUSED(len); const struct mScriptValue* value = val; - uint32_t hash = value->type->hash(value->type, value); + uint32_t hash = value->type->hash(value); return hash ^ seed; } @@ -146,12 +144,12 @@ struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) { // TODO: Use an arena instead of just the generic heap struct mScriptValue* val = malloc(sizeof(*val)); val->refs = 1; + val->type = type; if (type->alloc) { - type->alloc(type, val); + type->alloc(val); } else { val->value.opaque = NULL; } - val->type = type; return val; } @@ -173,7 +171,7 @@ void mScriptValueDeref(struct mScriptValue* val) { return; } if (val->type->free) { - val->type->free(val->type, val); + val->type->free(val); } free(val); } From 8818bf4048c4e20dff74a0aa2578e469a0e0efe6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Aug 2021 20:57:54 -0700 Subject: [PATCH 006/105] Scripting: Move equality into type implementation --- include/mgba/script/types.h | 1 + src/script/test/types.c | 232 ++++++++++++++++++++++++++++++++++++ src/script/types.c | 97 ++++++++++++--- 3 files changed, 314 insertions(+), 16 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 284853d3f..84c4e4ea1 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -213,6 +213,7 @@ struct mScriptType { void (*alloc)(struct mScriptValue*); void (*free)(struct mScriptValue*); uint32_t (*hash)(const struct mScriptValue*); + bool (*equal)(const struct mScriptValue*, const struct mScriptValue*); }; struct mScriptValue { diff --git a/src/script/test/types.c b/src/script/test/types.c index efd3701aa..03fc2c160 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -171,6 +171,235 @@ M_TEST_DEFINE(coerceFromFloat) { mScriptFrameDeinit(&frame); } +M_TEST_DEFINE(s32Equality) { + struct mScriptValue s32A; + struct mScriptValue s32B; + struct mScriptValue u32; + struct mScriptValue f32; + + s32A = mSCRIPT_MAKE_S32(0); + s32B = mSCRIPT_MAKE_S32(0); + assert_true(s32A.type->equal(&s32A, &s32B)); + + s32B = mSCRIPT_MAKE_S32(1); + assert_false(s32A.type->equal(&s32A, &s32B)); + + s32A = mSCRIPT_MAKE_S32(1); + assert_true(s32A.type->equal(&s32A, &s32B)); + + s32B = mSCRIPT_MAKE_S32(-1); + assert_false(s32A.type->equal(&s32A, &s32B)); + + s32A = mSCRIPT_MAKE_S32(-1); + assert_true(s32A.type->equal(&s32A, &s32B)); + + s32A = mSCRIPT_MAKE_S32(0); + u32 = mSCRIPT_MAKE_U32(0); + assert_true(s32A.type->equal(&s32A, &u32)); + + s32A = mSCRIPT_MAKE_S32(1); + u32 = mSCRIPT_MAKE_U32(1); + assert_true(s32A.type->equal(&s32A, &u32)); + + s32A = mSCRIPT_MAKE_S32(0); + u32 = mSCRIPT_MAKE_U32(1); + assert_false(s32A.type->equal(&s32A, &u32)); + + s32A = mSCRIPT_MAKE_S32(1); + u32 = mSCRIPT_MAKE_U32(0); + assert_false(s32A.type->equal(&s32A, &u32)); + + s32A = mSCRIPT_MAKE_S32(0x7FFFFFFF); + u32 = mSCRIPT_MAKE_U32(0x7FFFFFFF); + assert_true(s32A.type->equal(&s32A, &u32)); + + s32A = mSCRIPT_MAKE_S32(0xFFFFFFFF); + u32 = mSCRIPT_MAKE_U32(0xFFFFFFFF); + assert_false(s32A.type->equal(&s32A, &u32)); + + s32A = mSCRIPT_MAKE_S32(0x80000000); + u32 = mSCRIPT_MAKE_U32(0x80000000); + assert_false(s32A.type->equal(&s32A, &u32)); + + s32A = mSCRIPT_MAKE_S32(0); + f32 = mSCRIPT_MAKE_F32(0); + assert_true(s32A.type->equal(&s32A, &f32)); + + s32A = mSCRIPT_MAKE_S32(1); + f32 = mSCRIPT_MAKE_F32(1); + assert_true(s32A.type->equal(&s32A, &f32)); + + s32A = mSCRIPT_MAKE_S32(0); + f32 = mSCRIPT_MAKE_F32(1); + assert_false(s32A.type->equal(&s32A, &f32)); + + s32A = mSCRIPT_MAKE_S32(1); + f32 = mSCRIPT_MAKE_F32(0); + assert_false(s32A.type->equal(&s32A, &f32)); + + s32A = mSCRIPT_MAKE_S32(-1); + f32 = mSCRIPT_MAKE_F32(-1); + assert_true(s32A.type->equal(&s32A, &f32)); + + s32A = mSCRIPT_MAKE_S32(0); + f32 = mSCRIPT_MAKE_F32(-1); + assert_false(s32A.type->equal(&s32A, &f32)); + + s32A = mSCRIPT_MAKE_S32(-1); + f32 = mSCRIPT_MAKE_F32(0); + assert_false(s32A.type->equal(&s32A, &f32)); +} + +M_TEST_DEFINE(u32Equality) { + struct mScriptValue u32A; + struct mScriptValue u32B; + struct mScriptValue s32; + struct mScriptValue f32; + + u32A = mSCRIPT_MAKE_U32(0); + u32B = mSCRIPT_MAKE_U32(0); + assert_true(u32A.type->equal(&u32A, &u32B)); + + u32B = mSCRIPT_MAKE_U32(1); + assert_false(u32A.type->equal(&u32A, &u32B)); + + u32A = mSCRIPT_MAKE_U32(1); + assert_true(u32A.type->equal(&u32A, &u32B)); + + u32B = mSCRIPT_MAKE_U32(0x80000000U); + assert_false(u32A.type->equal(&u32A, &u32B)); + + u32A = mSCRIPT_MAKE_U32(0x80000000U); + assert_true(u32A.type->equal(&u32A, &u32B)); + + u32A = mSCRIPT_MAKE_U32(0); + s32 = mSCRIPT_MAKE_S32(0); + assert_true(u32A.type->equal(&u32A, &s32)); + + u32A = mSCRIPT_MAKE_U32(1); + s32 = mSCRIPT_MAKE_S32(1); + assert_true(u32A.type->equal(&u32A, &s32)); + + u32A = mSCRIPT_MAKE_U32(0); + s32 = mSCRIPT_MAKE_S32(1); + assert_false(u32A.type->equal(&u32A, &s32)); + + u32A = mSCRIPT_MAKE_U32(1); + s32 = mSCRIPT_MAKE_S32(0); + assert_false(u32A.type->equal(&u32A, &s32)); + + u32A = mSCRIPT_MAKE_U32(0x7FFFFFFF); + s32 = mSCRIPT_MAKE_S32(0x7FFFFFFF); + assert_true(u32A.type->equal(&u32A, &s32)); + + u32A = mSCRIPT_MAKE_U32(0xFFFFFFFF); + s32 = mSCRIPT_MAKE_S32(0xFFFFFFFF); + assert_false(u32A.type->equal(&u32A, &s32)); + + u32A = mSCRIPT_MAKE_U32(0x80000000); + s32 = mSCRIPT_MAKE_S32(0x80000000); + assert_false(u32A.type->equal(&u32A, &s32)); + + u32A = mSCRIPT_MAKE_U32(0); + f32 = mSCRIPT_MAKE_F32(0); + assert_true(u32A.type->equal(&u32A, &f32)); + + u32A = mSCRIPT_MAKE_U32(1); + f32 = mSCRIPT_MAKE_F32(1); + assert_true(u32A.type->equal(&u32A, &f32)); + + u32A = mSCRIPT_MAKE_U32(0); + f32 = mSCRIPT_MAKE_F32(1); + assert_false(u32A.type->equal(&u32A, &f32)); + + u32A = mSCRIPT_MAKE_U32(1); + f32 = mSCRIPT_MAKE_F32(0); + assert_false(u32A.type->equal(&u32A, &f32)); + + u32A = mSCRIPT_MAKE_U32(0x80000000); + f32 = mSCRIPT_MAKE_F32(0x80000000); + assert_true(u32A.type->equal(&u32A, &f32)); + + u32A = mSCRIPT_MAKE_U32(0); + f32 = mSCRIPT_MAKE_F32(0x80000000); + assert_false(u32A.type->equal(&u32A, &f32)); + + u32A = mSCRIPT_MAKE_U32(0x80000000); + f32 = mSCRIPT_MAKE_F32(0); + assert_false(u32A.type->equal(&u32A, &f32)); +} + +M_TEST_DEFINE(f32Equality) { + struct mScriptValue f32A; + struct mScriptValue f32B; + struct mScriptValue s32; + struct mScriptValue u32; + + f32A = mSCRIPT_MAKE_F32(0); + f32B = mSCRIPT_MAKE_F32(0); + assert_true(f32A.type->equal(&f32A, &f32B)); + + f32B = mSCRIPT_MAKE_F32(1); + assert_false(f32A.type->equal(&f32A, &f32B)); + + f32A = mSCRIPT_MAKE_F32(1); + assert_true(f32A.type->equal(&f32A, &f32B)); + + f32B = mSCRIPT_MAKE_F32(1.1); + assert_false(f32A.type->equal(&f32A, &f32B)); + + f32A = mSCRIPT_MAKE_F32(1.1); + assert_true(f32A.type->equal(&f32A, &f32B)); + + f32A = mSCRIPT_MAKE_F32(0); + s32 = mSCRIPT_MAKE_S32(0); + assert_true(f32A.type->equal(&f32A, &s32)); + + f32A = mSCRIPT_MAKE_F32(1); + s32 = mSCRIPT_MAKE_S32(1); + assert_true(f32A.type->equal(&f32A, &s32)); + + f32A = mSCRIPT_MAKE_F32(0); + s32 = mSCRIPT_MAKE_S32(1); + assert_false(f32A.type->equal(&f32A, &s32)); + + f32A = mSCRIPT_MAKE_F32(1); + s32 = mSCRIPT_MAKE_S32(0); + assert_false(f32A.type->equal(&f32A, &s32)); + + f32A = mSCRIPT_MAKE_F32(1.1); + s32 = mSCRIPT_MAKE_S32(1); + assert_false(f32A.type->equal(&f32A, &s32)); + + f32A = mSCRIPT_MAKE_F32(0x40000000); + s32 = mSCRIPT_MAKE_S32(0x40000000); + assert_true(f32A.type->equal(&f32A, &s32)); + + f32A = mSCRIPT_MAKE_F32(0); + u32 = mSCRIPT_MAKE_U32(0); + assert_true(f32A.type->equal(&f32A, &u32)); + + f32A = mSCRIPT_MAKE_F32(1); + u32 = mSCRIPT_MAKE_U32(1); + assert_true(f32A.type->equal(&f32A, &u32)); + + f32A = mSCRIPT_MAKE_F32(0); + u32 = mSCRIPT_MAKE_U32(1); + assert_false(f32A.type->equal(&f32A, &u32)); + + f32A = mSCRIPT_MAKE_F32(1); + u32 = mSCRIPT_MAKE_U32(0); + assert_false(f32A.type->equal(&f32A, &u32)); + + f32A = mSCRIPT_MAKE_F32(1.1); + u32 = mSCRIPT_MAKE_U32(1); + assert_false(f32A.type->equal(&f32A, &u32)); + + f32A = mSCRIPT_MAKE_F32(0x40000000); + u32 = mSCRIPT_MAKE_U32(0x40000000); + assert_true(f32A.type->equal(&f32A, &u32)); +} + M_TEST_DEFINE(hashTableBasic) { struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); assert_non_null(table); @@ -213,4 +442,7 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(wrongArgType), cmocka_unit_test(coerceToFloat), cmocka_unit_test(coerceFromFloat), + cmocka_unit_test(s32Equality), + cmocka_unit_test(u32Equality), + cmocka_unit_test(f32Equality), cmocka_unit_test(hashTableBasic)) diff --git a/src/script/types.c b/src/script/types.c index 82288da9d..f3dd09bf8 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -18,6 +18,11 @@ static bool _valEqual(const void* a, const void* b); static void* _valRef(void*); static void _valDeref(void*); +static bool _typeEqual(const struct mScriptValue*, const struct mScriptValue*); +static bool _s32Equal(const struct mScriptValue*, const struct mScriptValue*); +static bool _u32Equal(const struct mScriptValue*, const struct mScriptValue*); +static bool _f32Equal(const struct mScriptValue*, const struct mScriptValue*); + const struct mScriptType mSTVoid = { .base = mSCRIPT_TYPE_VOID, .size = 0, @@ -25,6 +30,7 @@ const struct mScriptType mSTVoid = { .alloc = NULL, .free = NULL, .hash = NULL, + .equal = _typeEqual, }; const struct mScriptType mSTSInt32 = { @@ -34,6 +40,7 @@ const struct mScriptType mSTSInt32 = { .alloc = NULL, .free = NULL, .hash = _hashScalar, + .equal = _s32Equal, }; const struct mScriptType mSTUInt32 = { @@ -43,6 +50,7 @@ const struct mScriptType mSTUInt32 = { .alloc = NULL, .free = NULL, .hash = _hashScalar, + .equal = _u32Equal, }; const struct mScriptType mSTFloat32 = { @@ -52,6 +60,7 @@ const struct mScriptType mSTFloat32 = { .alloc = NULL, .free = NULL, .hash = NULL, + .equal = _f32Equal, }; const struct mScriptType mSTTable = { @@ -113,22 +122,7 @@ uint32_t _valHash(const void* val, size_t len, uint32_t seed) { bool _valEqual(const void* a, const void* b) { const struct mScriptValue* valueA = a; const struct mScriptValue* valueB = b; - // TODO: Move equality into type - if (valueA->type != valueB->type) { - return false; - } - switch (valueA->type->base) { - case mSCRIPT_TYPE_VOID: - return true; - case mSCRIPT_TYPE_SINT: - return valueA->value.s32 == valueB->value.s32; - case mSCRIPT_TYPE_UINT: - return valueA->value.u32 == valueB->value.u32; - case mSCRIPT_TYPE_FLOAT: - return valueA->value.f32 == valueB->value.f32; - default: - return valueA->value.opaque == valueB->value.opaque; - } + return valueA->type->equal(valueA, valueB); } void* _valRef(void* val) { @@ -140,6 +134,77 @@ void _valDeref(void* val) { mScriptValueDeref(val); } +bool _typeEqual(const struct mScriptValue* a, const struct mScriptValue* b) { + return a->type == b->type; + +} + +bool _s32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { + int32_t val; + switch (b->type->base) { + case mSCRIPT_TYPE_SINT: + val = b->value.s32; + break; + case mSCRIPT_TYPE_UINT: + if (b->value.u32 > (uint32_t) INT_MAX) { + return false; + } + if (a->value.s32 < 0) { + return false; + } + val = b->value.u32; + break; + case mSCRIPT_TYPE_VOID: + return false; + default: + return b->type->equal && b->type->equal(b, a); + } + return a->value.s32 == val; +} + +bool _u32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { + uint32_t val; + switch (b->type->base) { + case mSCRIPT_TYPE_SINT: + if (b->value.s32 < 0) { + return false; + } + if (a->value.u32 > (uint32_t) INT_MAX) { + return false; + } + val = b->value.s32; + break; + case mSCRIPT_TYPE_UINT: + val = b->value.u32; + break; + case mSCRIPT_TYPE_VOID: + return false; + default: + return b->type->equal && b->type->equal(b, a); + } + return a->value.u32 == val; +} + +bool _f32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { + float val; + switch (b->type->base) { + case mSCRIPT_TYPE_SINT: + val = b->value.s32; + break; + case mSCRIPT_TYPE_UINT: + val = b->value.u32; + break; + case mSCRIPT_TYPE_FLOAT: + val = b->value.f32; + break; + case mSCRIPT_TYPE_VOID: + return false; + default: + return b->type->equal && b->type->equal(b, a); + } + return a->value.f32 == val; +} + struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) { // TODO: Use an arena instead of just the generic heap struct mScriptValue* val = malloc(sizeof(*val)); From 9955d0d19b9c0d3fa3e58f7d7a35d8e45ea281e3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 30 Aug 2021 19:48:40 -0700 Subject: [PATCH 007/105] Scripting: Start string bringup --- include/mgba/script/types.h | 20 ++++++ src/script/test/types.c | 89 ++++++++++++++++++++++++++- src/script/types.c | 117 +++++++++++++++++++++++++++++++++++- 3 files changed, 224 insertions(+), 2 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 84c4e4ea1..37679bee7 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -20,6 +20,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_S32 int32_t #define mSCRIPT_TYPE_C_U32 uint32_t #define mSCRIPT_TYPE_C_F32 float +#define mSCRIPT_TYPE_C_STR mScriptString* +#define mSCRIPT_TYPE_C_CHARP const char* #define mSCRIPT_TYPE_C_PTR void* #define mSCRIPT_TYPE_C_TABLE Table* #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* @@ -27,6 +29,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_S32 s32 #define mSCRIPT_TYPE_FIELD_U32 u32 #define mSCRIPT_TYPE_FIELD_F32 f32 +#define mSCRIPT_TYPE_FIELD_STR opaque +#define mSCRIPT_TYPE_FIELD_CHARP opaque #define mSCRIPT_TYPE_FIELD_PTR opaque #define mSCRIPT_TYPE_FIELD_TABLE opaque #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque @@ -34,6 +38,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_S32 (&mSTSInt32) #define mSCRIPT_TYPE_MS_U32 (&mSTUInt32) #define mSCRIPT_TYPE_MS_F32 (&mSTFloat32) +#define mSCRIPT_TYPE_MS_STR (&mSTString) +#define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr) #define mSCRIPT_TYPE_MS_TABLE (&mSTTable) #define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) @@ -43,6 +49,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_U32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U32, TYPE) #define mSCRIPT_TYPE_CMP_S32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S32, TYPE) #define mSCRIPT_TYPE_CMP_F32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F32, TYPE) +#define mSCRIPT_TYPE_CMP_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) +#define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE) #define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE) #define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) _mAPPLY(mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1)) @@ -166,6 +174,7 @@ CXX_GUARD_START #define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S32, s32, VALUE) #define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U32, u32, VALUE) #define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F32, f32, VALUE) +#define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CHARP, opaque, VALUE) enum { mSCRIPT_TYPE_VOID = 0, @@ -187,6 +196,8 @@ extern const struct mScriptType mSTVoid; extern const struct mScriptType mSTSInt32; extern const struct mScriptType mSTUInt32; extern const struct mScriptType mSTFloat32; +extern const struct mScriptType mSTString; +extern const struct mScriptType mSTCharPtr; extern const struct mScriptType mSTTable; struct mScriptTypeTuple { @@ -229,6 +240,12 @@ struct mScriptValue { DECLARE_VECTOR(mScriptList, struct mScriptValue) +struct mScriptString { + size_t length; // Size of the string in code points + size_t size; // Size of the buffer in bytes, excluding NULL byte terminator + char* buffer; // UTF-8 representation of the string +}; + struct mScriptFrame { struct mScriptList arguments; struct mScriptList returnValues; @@ -238,12 +255,15 @@ struct mScriptFrame { struct mScriptFunction { struct mScriptTypeFunction signature; bool (*call)(struct mScriptFrame*); + void* context; }; struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type); void mScriptValueRef(struct mScriptValue* val); void mScriptValueDeref(struct mScriptValue* val); +struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); + bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value); bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key); struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key); diff --git a/src/script/test/types.c b/src/script/test/types.c index 03fc2c160..2d3309482 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -41,6 +41,10 @@ static int subInts(int a, int b) { return a - b; } +static int isHello(const char* str) { + return strcmp(str, "hello") == 0; +} + mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32); @@ -48,6 +52,7 @@ mSCRIPT_BIND_FUNCTION(boundIdentityFloat, F32, identityFloat, 1, F32); mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test)); mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32); mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, S32); +mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP); M_TEST_DEFINE(voidArgs) { struct mScriptFrame frame; @@ -400,6 +405,33 @@ M_TEST_DEFINE(f32Equality) { assert_true(f32A.type->equal(&f32A, &u32)); } +M_TEST_DEFINE(stringEquality) { + struct mScriptValue* stringA = mScriptStringCreateFromUTF8("hello"); + struct mScriptValue* stringB = mScriptStringCreateFromUTF8("world"); + struct mScriptValue* stringC = mScriptStringCreateFromUTF8("hello"); + struct mScriptValue charpA = mSCRIPT_MAKE_CHARP("hello"); + struct mScriptValue charpB = mSCRIPT_MAKE_CHARP("world"); + + assert_true(stringA->type->equal(stringA, stringC)); + assert_false(stringA->type->equal(stringA, stringB)); + + assert_true(stringA->type->equal(stringA, &charpA)); + assert_false(stringA->type->equal(stringA, &charpB)); + + assert_true(charpA.type->equal(&charpA, stringA)); + assert_false(charpA.type->equal(&charpA, stringB)); + + charpB = mSCRIPT_MAKE_CHARP("hello"); + assert_true(charpA.type->equal(&charpA, &charpB)); + + charpB = mSCRIPT_MAKE_CHARP("world"); + assert_false(charpA.type->equal(&charpA, &charpB)); + + mScriptValueDeref(stringA); + mScriptValueDeref(stringB); + mScriptValueDeref(stringC); +} + M_TEST_DEFINE(hashTableBasic) { struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); assert_non_null(table); @@ -429,6 +461,57 @@ M_TEST_DEFINE(hashTableBasic) { mScriptValueDeref(table); } +M_TEST_DEFINE(hashTableString) { + struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + assert_non_null(table); + + struct mScriptValue* intValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); + assert_ptr_equal(intValue->type, mSCRIPT_TYPE_MS_S32); + assert_int_equal(intValue->value.s32, 0); + assert_int_equal(intValue->refs, 1); + + struct mScriptValue key = mSCRIPT_MAKE_CHARP("key"); + struct mScriptValue badKey = mSCRIPT_MAKE_CHARP("bad"); + + assert_true(mScriptTableInsert(table, &key, intValue)); + assert_int_equal(intValue->refs, 2); + + struct mScriptValue* lookupValue = mScriptTableLookup(table, &key); + assert_non_null(lookupValue); + assert_ptr_equal(lookupValue, intValue); + + lookupValue = mScriptTableLookup(table, &badKey); + assert_null(lookupValue); + + assert_true(mScriptTableRemove(table, &key)); + assert_int_equal(intValue->refs, 1); + + mScriptValueDeref(intValue); + mScriptValueDeref(table); +} + +M_TEST_DEFINE(stringIsHello) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CHARP, "hello"); + assert_true(mScriptInvoke(&boundIsHello, &frame)); + int val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(stringIsNotHello) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CHARP, "world"); + assert_true(mScriptInvoke(&boundIsHello, &frame)); + int val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 0); + mScriptFrameDeinit(&frame); +} + M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(voidArgs), cmocka_unit_test(voidFunc), @@ -445,4 +528,8 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(s32Equality), cmocka_unit_test(u32Equality), cmocka_unit_test(f32Equality), - cmocka_unit_test(hashTableBasic)) + cmocka_unit_test(stringEquality), + cmocka_unit_test(hashTableBasic), + cmocka_unit_test(hashTableString), + cmocka_unit_test(stringIsHello), + cmocka_unit_test(stringIsNotHello)) diff --git a/src/script/types.c b/src/script/types.c index f3dd09bf8..57edf070a 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -5,12 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include static void _allocTable(struct mScriptValue*); static void _freeTable(struct mScriptValue*); static void _deinitTableValue(void*); +static void _allocString(struct mScriptValue*); +static void _freeString(struct mScriptValue*); +static uint32_t _hashString(const struct mScriptValue*); + static uint32_t _hashScalar(const struct mScriptValue*); static uint32_t _valHash(const void* val, size_t len, uint32_t seed); @@ -22,6 +27,8 @@ static bool _typeEqual(const struct mScriptValue*, const struct mScriptValue*); static bool _s32Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _u32Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _f32Equal(const struct mScriptValue*, const struct mScriptValue*); +static bool _charpEqual(const struct mScriptValue*, const struct mScriptValue*); +static bool _stringEqual(const struct mScriptValue*, const struct mScriptValue*); const struct mScriptType mSTVoid = { .base = mSCRIPT_TYPE_VOID, @@ -63,6 +70,26 @@ const struct mScriptType mSTFloat32 = { .equal = _f32Equal, }; +const struct mScriptType mSTString = { + .base = mSCRIPT_TYPE_STRING, + .size = sizeof(struct mScriptString), + .name = "string", + .alloc = _allocString, + .free = _freeString, + .hash = _hashString, + .equal = _stringEqual, +}; + +const struct mScriptType mSTCharPtr = { + .base = mSCRIPT_TYPE_STRING, + .size = sizeof(char*), + .name = "charptr", + .alloc = NULL, + .free = NULL, + .hash = _hashString, + .equal = _charpEqual, +}; + const struct mScriptType mSTTable = { .base = mSCRIPT_TYPE_TABLE, .size = sizeof(struct Table), @@ -95,6 +122,35 @@ void _deinitTableValue(void* val) { mScriptValueDeref(val); } +static void _allocString(struct mScriptValue* val) { + struct mScriptString* string = calloc(1, sizeof(*string)); + string->size = 0; + string->buffer = NULL; + val->value.opaque = string; +} + +static void _freeString(struct mScriptValue* val) { + struct mScriptString* string = val->value.opaque; + if (string->size) { + free(string->buffer); + } + free(string); +} + +static uint32_t _hashString(const struct mScriptValue* val) { + const char* buffer = 0; + size_t size = 0; + if (val->type == &mSTString) { + struct mScriptString* string = val->value.opaque; + buffer = string->buffer; + size = string->size; + } else if (val->type == &mSTCharPtr) { + buffer = val->value.opaque; + size = strlen(buffer); + } + return hash32(buffer, size, 0); +} + uint32_t _hashScalar(const struct mScriptValue* val) { // From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key uint32_t x; @@ -136,7 +192,6 @@ void _valDeref(void* val) { bool _typeEqual(const struct mScriptValue* a, const struct mScriptValue* b) { return a->type == b->type; - } bool _s32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { @@ -205,6 +260,58 @@ bool _f32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { return a->value.f32 == val; } +bool _charpEqual(const struct mScriptValue* a, const struct mScriptValue* b) { + const char* valA = a->value.opaque; + const char* valB; + size_t lenA; + size_t lenB; + if (b->type->base != mSCRIPT_TYPE_STRING) { + return false; + } + if (b->type == &mSTCharPtr) { + valB = b->value.opaque; + lenB = strlen(valB); + } else if (b->type == &mSTString) { + struct mScriptString* stringB = b->value.opaque; + valB = stringB->buffer; + lenB = stringB->size; + } else { + // TODO: Allow casting + return false; + } + lenA = strlen(valA); + if (lenA != lenB) { + return false; + } + return strncmp(valA, valB, lenA) == 0; +} + +bool _stringEqual(const struct mScriptValue* a, const struct mScriptValue* b) { + struct mScriptString* stringA = a->value.opaque; + const char* valA = stringA->buffer; + const char* valB; + size_t lenA = stringA->size; + size_t lenB; + if (b->type->base != mSCRIPT_TYPE_STRING) { + return false; + } + if (b->type == &mSTCharPtr) { + valB = b->value.opaque; + lenB = strlen(valB); + } else if (b->type == &mSTString) { + struct mScriptString* stringB = b->value.opaque; + valB = stringB->buffer; + lenB = stringB->size; + } else { + // TODO: Allow casting + return false; + } + if (lenA != lenB) { + return false; + } + return strncmp(valA, valB, lenA) == 0; +} + struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) { // TODO: Use an arena instead of just the generic heap struct mScriptValue* val = malloc(sizeof(*val)); @@ -241,6 +348,14 @@ void mScriptValueDeref(struct mScriptValue* val) { free(val); } +struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); + struct mScriptString* internal = val->value.opaque; + internal->size = strlen(string); + internal->buffer = strdup(string); + return val; +} + bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value) { if (table->type != mSCRIPT_TYPE_MS_TABLE) { return false; From cbae6a61e50e05627a8d3c26a53c517ae28466ef Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 30 Aug 2021 20:03:14 -0700 Subject: [PATCH 008/105] Scripting: Start adding type-aware casting --- include/mgba/script/types.h | 1 + src/script/types.c | 133 +++++++++++++++++------------------- 2 files changed, 65 insertions(+), 69 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 37679bee7..74ebcd09e 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -225,6 +225,7 @@ struct mScriptType { void (*free)(struct mScriptValue*); uint32_t (*hash)(const struct mScriptValue*); bool (*equal)(const struct mScriptValue*, const struct mScriptValue*); + bool (*cast)(const struct mScriptValue*, const struct mScriptType*, struct mScriptValue*); }; struct mScriptValue { diff --git a/src/script/types.c b/src/script/types.c index 57edf070a..833c30ccf 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -16,6 +16,7 @@ static void _allocString(struct mScriptValue*); static void _freeString(struct mScriptValue*); static uint32_t _hashString(const struct mScriptValue*); +static bool _castScalar(const struct mScriptValue*, const struct mScriptType*, struct mScriptValue*); static uint32_t _hashScalar(const struct mScriptValue*); static uint32_t _valHash(const void* val, size_t len, uint32_t seed); @@ -38,6 +39,7 @@ const struct mScriptType mSTVoid = { .free = NULL, .hash = NULL, .equal = _typeEqual, + .cast = NULL, }; const struct mScriptType mSTSInt32 = { @@ -48,6 +50,7 @@ const struct mScriptType mSTSInt32 = { .free = NULL, .hash = _hashScalar, .equal = _s32Equal, + .cast = _castScalar, }; const struct mScriptType mSTUInt32 = { @@ -58,6 +61,7 @@ const struct mScriptType mSTUInt32 = { .free = NULL, .hash = _hashScalar, .equal = _u32Equal, + .cast = _castScalar, }; const struct mScriptType mSTFloat32 = { @@ -68,6 +72,7 @@ const struct mScriptType mSTFloat32 = { .free = NULL, .hash = NULL, .equal = _f32Equal, + .cast = _castScalar, }; const struct mScriptType mSTString = { @@ -168,6 +173,60 @@ uint32_t _hashScalar(const struct mScriptValue* val) { return x; } +bool _castScalar(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { + switch (type->base) { + case mSCRIPT_TYPE_SINT: + switch (input->type->base) { + case mSCRIPT_TYPE_SINT: + output->value.s32 = input->value.s32; + break; + case mSCRIPT_TYPE_UINT: + output->value.s32 = input->value.u32; + break; + case mSCRIPT_TYPE_FLOAT: + output->value.s32 = input->value.f32; + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_UINT: + switch (input->type->base) { + case mSCRIPT_TYPE_SINT: + output->value.u32 = input->value.s32; + break; + case mSCRIPT_TYPE_UINT: + output->value.u32 = input->value.u32; + break; + case mSCRIPT_TYPE_FLOAT: + output->value.u32 = input->value.f32; + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_FLOAT: + switch (input->type->base) { + case mSCRIPT_TYPE_SINT: + output->value.f32 = input->value.s32; + break; + case mSCRIPT_TYPE_UINT: + output->value.f32 = input->value.u32; + break; + case mSCRIPT_TYPE_FLOAT: + output->value.f32 = input->value.f32; + break; + default: + return false; + } + break; + default: + return false; + } + output->type = type; + return true; +} + uint32_t _valHash(const void* val, size_t len, uint32_t seed) { UNUSED(len); const struct mScriptValue* value = val; @@ -427,76 +486,12 @@ bool mScriptPopPointer(struct mScriptList* list, void** out) { } bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output) { - switch (type->base) { - case mSCRIPT_TYPE_VOID: - return false; - case mSCRIPT_TYPE_SINT: - switch (input->type->base) { - case mSCRIPT_TYPE_SINT: - output->value.s32 = input->value.s32; - break; - case mSCRIPT_TYPE_UINT: - output->value.s32 = input->value.u32; - break; - case mSCRIPT_TYPE_FLOAT: - switch (input->type->size) { - case 4: - output->value.s32 = input->value.f32; - break; - default: - return false; - } - break; - default: - return false; - } - break; - case mSCRIPT_TYPE_UINT: - switch (input->type->base) { - case mSCRIPT_TYPE_SINT: - output->value.u32 = input->value.s32; - break; - case mSCRIPT_TYPE_UINT: - output->value.u32 = input->value.u32; - break; - case mSCRIPT_TYPE_FLOAT: - switch (input->type->size) { - case 4: - output->value.u32 = input->value.f32; - break; - default: - return false; - } - break; - default: - return false; - } - break; - case mSCRIPT_TYPE_FLOAT: - switch (input->type->base) { - case mSCRIPT_TYPE_SINT: - output->value.f32 = input->value.s32; - break; - case mSCRIPT_TYPE_UINT: - output->value.f32 = input->value.u32; - break; - case mSCRIPT_TYPE_FLOAT: - switch (input->type->size) { - case 4: - output->value.f32 = input->value.f32; - break; - default: - return false; - } - break; - default: - return false; - } - break; - default: - return false; + if (type->cast && type->cast(input, type, output)) { + return true; + } + if (input->type->cast && input->type->cast(input, type, output)) { + return true; } - output->type = type; return true; } From 2a81e5a1ba1d0272dcbe7da8b2ff1472f6ab0cf3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 16 Feb 2022 00:45:51 -0800 Subject: [PATCH 009/105] Scripting: Add 64-bit types --- include/mgba/script/types.h | 24 ++ src/script/test/types.c | 698 ++++++++++++++++++++++++++---------- src/script/types.c | 313 +++++++++++++--- 3 files changed, 804 insertions(+), 231 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 74ebcd09e..1e9ee73c2 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -20,6 +20,9 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_S32 int32_t #define mSCRIPT_TYPE_C_U32 uint32_t #define mSCRIPT_TYPE_C_F32 float +#define mSCRIPT_TYPE_C_S64 int64_t +#define mSCRIPT_TYPE_C_U64 uint64_t +#define mSCRIPT_TYPE_C_F64 double #define mSCRIPT_TYPE_C_STR mScriptString* #define mSCRIPT_TYPE_C_CHARP const char* #define mSCRIPT_TYPE_C_PTR void* @@ -29,6 +32,9 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_S32 s32 #define mSCRIPT_TYPE_FIELD_U32 u32 #define mSCRIPT_TYPE_FIELD_F32 f32 +#define mSCRIPT_TYPE_FIELD_S64 s64 +#define mSCRIPT_TYPE_FIELD_U64 u64 +#define mSCRIPT_TYPE_FIELD_F64 f64 #define mSCRIPT_TYPE_FIELD_STR opaque #define mSCRIPT_TYPE_FIELD_CHARP opaque #define mSCRIPT_TYPE_FIELD_PTR opaque @@ -38,6 +44,9 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_S32 (&mSTSInt32) #define mSCRIPT_TYPE_MS_U32 (&mSTUInt32) #define mSCRIPT_TYPE_MS_F32 (&mSTFloat32) +#define mSCRIPT_TYPE_MS_S64 (&mSTSInt64) +#define mSCRIPT_TYPE_MS_U64 (&mSTUInt64) +#define mSCRIPT_TYPE_MS_F64 (&mSTFloat64) #define mSCRIPT_TYPE_MS_STR (&mSTString) #define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr) #define mSCRIPT_TYPE_MS_TABLE (&mSTTable) @@ -49,6 +58,9 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_U32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U32, TYPE) #define mSCRIPT_TYPE_CMP_S32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S32, TYPE) #define mSCRIPT_TYPE_CMP_F32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F32, TYPE) +#define mSCRIPT_TYPE_CMP_U64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U64, TYPE) +#define mSCRIPT_TYPE_CMP_S64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S64, TYPE) +#define mSCRIPT_TYPE_CMP_F64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F64, TYPE) #define mSCRIPT_TYPE_CMP_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) #define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE) #define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE) @@ -174,6 +186,9 @@ CXX_GUARD_START #define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S32, s32, VALUE) #define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U32, u32, VALUE) #define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F32, f32, VALUE) +#define mSCRIPT_MAKE_S64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S64, s64, VALUE) +#define mSCRIPT_MAKE_U64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U64, u64, VALUE) +#define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F64, f64, VALUE) #define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CHARP, opaque, VALUE) enum { @@ -196,6 +211,9 @@ extern const struct mScriptType mSTVoid; extern const struct mScriptType mSTSInt32; extern const struct mScriptType mSTUInt32; extern const struct mScriptType mSTFloat32; +extern const struct mScriptType mSTSInt64; +extern const struct mScriptType mSTUInt64; +extern const struct mScriptType mSTFloat64; extern const struct mScriptType mSTString; extern const struct mScriptType mSTCharPtr; extern const struct mScriptType mSTTable; @@ -235,6 +253,9 @@ struct mScriptValue { int32_t s32; uint32_t u32; float f32; + int64_t s64; + uint64_t u64; + double f64; void* opaque; } value; }; @@ -275,6 +296,9 @@ void mScriptFrameDeinit(struct mScriptFrame* frame); bool mScriptPopS32(struct mScriptList* list, int32_t* out); bool mScriptPopU32(struct mScriptList* list, uint32_t* out); bool mScriptPopF32(struct mScriptList* list, float* out); +bool mScriptPopS64(struct mScriptList* list, int64_t* out); +bool mScriptPopU64(struct mScriptList* list, uint64_t* out); +bool mScriptPopF64(struct mScriptList* list, double* out); bool mScriptPopPointer(struct mScriptList* list, void** out); bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output); diff --git a/src/script/test/types.c b/src/script/test/types.c index 2d3309482..fc0e85a33 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -25,6 +25,10 @@ static int identityInt(int in) { return in; } +static int64_t identityInt64(int64_t in) { + return in; +} + static float identityFloat(float in) { return in; } @@ -48,6 +52,7 @@ static int isHello(const char* str) { mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32); +mSCRIPT_BIND_FUNCTION(boundIdentityInt64, S64, identityInt64, 1, S64); mSCRIPT_BIND_FUNCTION(boundIdentityFloat, F32, identityFloat, 1, F32); mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test)); mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32); @@ -83,6 +88,17 @@ M_TEST_DEFINE(identityFunctionS32) { mScriptFrameDeinit(&frame); } +M_TEST_DEFINE(identityFunctionS64) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S64, 1); + assert_true(mScriptInvoke(&boundIdentityInt64, &frame)); + int64_t val; + assert_true(mScriptPopS64(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); +} + M_TEST_DEFINE(identityFunctionF32) { struct mScriptFrame frame; mScriptFrameInit(&frame); @@ -154,6 +170,92 @@ M_TEST_DEFINE(wrongArgType) { mScriptFrameDeinit(&frame); } +M_TEST_DEFINE(wrongPopType) { + struct mScriptFrame frame; + int32_t s32; + int64_t s64; + uint32_t u32; + uint64_t u64; + float f32; + double f64; + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 0); + assert_false(mScriptPopU32(&frame.arguments, &u32)); + assert_false(mScriptPopF32(&frame.arguments, &f32)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S64, 0); + assert_false(mScriptPopU64(&frame.arguments, &u64)); + assert_false(mScriptPopF64(&frame.arguments, &f64)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, U32, 0); + assert_false(mScriptPopS32(&frame.arguments, &s32)); + assert_false(mScriptPopF32(&frame.arguments, &f32)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, U64, 0); + assert_false(mScriptPopS64(&frame.arguments, &s64)); + assert_false(mScriptPopF64(&frame.arguments, &f64)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, F32, 0); + assert_false(mScriptPopS32(&frame.arguments, &s32)); + assert_false(mScriptPopU32(&frame.arguments, &u32)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, F64, 0); + assert_false(mScriptPopS64(&frame.arguments, &s64)); + assert_false(mScriptPopU64(&frame.arguments, &u64)); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(wrongPopSize) { + struct mScriptFrame frame; + int32_t s32; + int64_t s64; + uint32_t u32; + uint64_t u64; + float f32; + double f64; + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 0); + assert_false(mScriptPopS64(&frame.arguments, &s64)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S64, 0); + assert_false(mScriptPopS32(&frame.arguments, &s32)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, U32, 0); + assert_false(mScriptPopU64(&frame.arguments, &u64)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, U64, 0); + assert_false(mScriptPopU32(&frame.arguments, &u32)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, F32, 0); + assert_false(mScriptPopF64(&frame.arguments, &f64)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, F64, 0); + assert_false(mScriptPopF32(&frame.arguments, &f32)); + mScriptFrameDeinit(&frame); +} + M_TEST_DEFINE(coerceToFloat) { struct mScriptFrame frame; mScriptFrameInit(&frame); @@ -176,233 +278,459 @@ M_TEST_DEFINE(coerceFromFloat) { mScriptFrameDeinit(&frame); } +M_TEST_DEFINE(coerceWiden) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, -1); + assert_true(mScriptInvoke(&boundIdentityInt64, &frame)); + int64_t val; + assert_true(mScriptPopS64(&frame.returnValues, &val)); + assert_true(val == -1LL); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(coerceNarrow) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S64, -1); + assert_true(mScriptInvoke(&boundIdentityInt, &frame)); + int32_t val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(val == -1); + mScriptFrameDeinit(&frame); +} + +#define COMPARE_BOOL(EXPECT, T0, V0, T1, V1) \ + a = mSCRIPT_MAKE_ ## T0 (V0); \ + b = mSCRIPT_MAKE_ ## T1 (V1); \ + assert_ ## EXPECT (a.type->equal(&a, &b)); + M_TEST_DEFINE(s32Equality) { - struct mScriptValue s32A; - struct mScriptValue s32B; - struct mScriptValue u32; - struct mScriptValue f32; + struct mScriptValue a; + struct mScriptValue b; - s32A = mSCRIPT_MAKE_S32(0); - s32B = mSCRIPT_MAKE_S32(0); - assert_true(s32A.type->equal(&s32A, &s32B)); + // S32 + COMPARE_BOOL(true, S32, 0, S32, 0); + COMPARE_BOOL(false, S32, 0, S32, 1); + COMPARE_BOOL(true, S32, 1, S32, 1); + COMPARE_BOOL(false, S32, 1, S32, -1); + COMPARE_BOOL(true, S32, -1, S32, -1); - s32B = mSCRIPT_MAKE_S32(1); - assert_false(s32A.type->equal(&s32A, &s32B)); + // S64 + COMPARE_BOOL(true, S32, 0, S64, 0); + COMPARE_BOOL(false, S32, 0, S64, 1); + COMPARE_BOOL(true, S32, 1, S64, 1); + COMPARE_BOOL(false, S32, 1, S64, -1); + COMPARE_BOOL(true, S32, -1, S64, -1); + COMPARE_BOOL(false, S32, 0, S64, 0x100000000LL); + COMPARE_BOOL(false, S32, -1, S64, 0x1FFFFFFFFLL); + COMPARE_BOOL(false, S32, -1, S64, -0x100000001LL); - s32A = mSCRIPT_MAKE_S32(1); - assert_true(s32A.type->equal(&s32A, &s32B)); + // U32 + COMPARE_BOOL(true, S32, 0, U32, 0); + COMPARE_BOOL(false, S32, 0, U32, 1); + COMPARE_BOOL(true, S32, 1, U32, 1); + COMPARE_BOOL(true, S32, 0x7FFFFFFF, U32, 0x7FFFFFFFU); + COMPARE_BOOL(false, S32, 0xFFFFFFFF, U32, 0xFFFFFFFFU); + COMPARE_BOOL(false, S32, 0x80000000, U32, 0x80000000U); - s32B = mSCRIPT_MAKE_S32(-1); - assert_false(s32A.type->equal(&s32A, &s32B)); + // U64 + COMPARE_BOOL(true, S32, 0, U64, 0); + COMPARE_BOOL(false, S32, 0, U64, 1); + COMPARE_BOOL(true, S32, 1, U64, 1); + COMPARE_BOOL(true, S32, 0x7FFFFFFF, U64, 0x7FFFFFFFULL); + COMPARE_BOOL(false, S32, 0xFFFFFFFF, U64, 0xFFFFFFFFULL); + COMPARE_BOOL(false, S32, 0x80000000, U64, 0x80000000ULL); - s32A = mSCRIPT_MAKE_S32(-1); - assert_true(s32A.type->equal(&s32A, &s32B)); + // F32 + COMPARE_BOOL(true, S32, 0, F32, 0); + COMPARE_BOOL(false, S32, 1, F32, 0); + COMPARE_BOOL(false, S32, 0, F32, 1); + COMPARE_BOOL(true, S32, 1, F32, 1); + COMPARE_BOOL(false, S32, 0, F32, -1); + COMPARE_BOOL(false, S32, 1, F32, -1); + COMPARE_BOOL(true, S32, -1, F32, -1); + COMPARE_BOOL(false, S32, 1, F32, 1.1); + COMPARE_BOOL(false, S32, 0, F32, 0.1); + COMPARE_BOOL(true, S32, 0x40000000, F32, 0x40000000); + COMPARE_BOOL(true, S32, -0x40000000, F32, -0x40000000); - s32A = mSCRIPT_MAKE_S32(0); - u32 = mSCRIPT_MAKE_U32(0); - assert_true(s32A.type->equal(&s32A, &u32)); + // F64 + COMPARE_BOOL(true, S32, 0, F64, 0); + COMPARE_BOOL(false, S32, 1, F64, 0); + COMPARE_BOOL(false, S32, 0, F64, 1); + COMPARE_BOOL(true, S32, 1, F64, 1); + COMPARE_BOOL(false, S32, 0, F64, -1); + COMPARE_BOOL(false, S32, 1, F64, -1); + COMPARE_BOOL(true, S32, -1, F64, -1); + COMPARE_BOOL(false, S32, 1, F64, 1.1); + COMPARE_BOOL(false, S32, 0, F64, 0.1); + COMPARE_BOOL(true, S32, 0x40000000, F64, 0x40000000); + COMPARE_BOOL(true, S32, -0x40000000, F64, -0x40000000); +} - s32A = mSCRIPT_MAKE_S32(1); - u32 = mSCRIPT_MAKE_U32(1); - assert_true(s32A.type->equal(&s32A, &u32)); +M_TEST_DEFINE(s64Equality) { + struct mScriptValue a; + struct mScriptValue b; - s32A = mSCRIPT_MAKE_S32(0); - u32 = mSCRIPT_MAKE_U32(1); - assert_false(s32A.type->equal(&s32A, &u32)); + // S32 + COMPARE_BOOL(true, S64, 0, S32, 0); + COMPARE_BOOL(false, S64, 0, S32, 1); + COMPARE_BOOL(true, S64, 1, S32, 1); + COMPARE_BOOL(false, S64, 1, S32, -1); + COMPARE_BOOL(true, S64, -1, S32, -1); + COMPARE_BOOL(false, S64, 0x100000000LL, S32, 0); + COMPARE_BOOL(false, S64, 0x1FFFFFFFFLL, S32, -1); + COMPARE_BOOL(false, S64, -0x100000001LL, S32, -1); - s32A = mSCRIPT_MAKE_S32(1); - u32 = mSCRIPT_MAKE_U32(0); - assert_false(s32A.type->equal(&s32A, &u32)); + // S64 + COMPARE_BOOL(true, S64, 0, S64, 0); + COMPARE_BOOL(false, S64, 0, S64, 1); + COMPARE_BOOL(true, S64, 1, S64, 1); + COMPARE_BOOL(false, S64, 1, S64, -1); + COMPARE_BOOL(true, S64, -1, S64, -1); + COMPARE_BOOL(false, S64, 0, S64, 0x100000000LL); + COMPARE_BOOL(false, S64, -1, S64, 0x1FFFFFFFFLL); + COMPARE_BOOL(false, S64, -1, S64, -0x100000001LL); + COMPARE_BOOL(false, S64, 0x100000000LL, S64, 0); + COMPARE_BOOL(false, S64, 0x1FFFFFFFFLL, S64, -1); + COMPARE_BOOL(false, S64, -0x100000001LL, S64, -1); + COMPARE_BOOL(true, S64, 0x100000000LL, S64, 0x100000000LL); + COMPARE_BOOL(true, S64, 0x1FFFFFFFFLL, S64, 0x1FFFFFFFFLL); + COMPARE_BOOL(true, S64, -0x100000001LL, S64, -0x100000001LL); - s32A = mSCRIPT_MAKE_S32(0x7FFFFFFF); - u32 = mSCRIPT_MAKE_U32(0x7FFFFFFF); - assert_true(s32A.type->equal(&s32A, &u32)); + // U32 + COMPARE_BOOL(true, S64, 0, U32, 0); + COMPARE_BOOL(false, S64, 0, U32, 1); + COMPARE_BOOL(true, S64, 1, U32, 1); + COMPARE_BOOL(true, S64, 0x7FFFFFFFLL, U32, 0x7FFFFFFFU); + COMPARE_BOOL(true, S64, 0xFFFFFFFFLL, U32, 0xFFFFFFFFU); + COMPARE_BOOL(true, S64, 0x80000000LL, U32, 0x80000000U); + COMPARE_BOOL(false, S64, -1, U32, 0xFFFFFFFFU); + COMPARE_BOOL(false, S64, -0x80000000LL, U32, 0x80000000U); - s32A = mSCRIPT_MAKE_S32(0xFFFFFFFF); - u32 = mSCRIPT_MAKE_U32(0xFFFFFFFF); - assert_false(s32A.type->equal(&s32A, &u32)); + // U64 + COMPARE_BOOL(true, S64, 0, U64, 0); + COMPARE_BOOL(false, S64, 0, U64, 1); + COMPARE_BOOL(true, S64, 1, U64, 1); + COMPARE_BOOL(true, S64, 0x07FFFFFFFLL, U64, 0x07FFFFFFFULL); + COMPARE_BOOL(true, S64, 0x0FFFFFFFFLL, U64, 0x0FFFFFFFFULL); + COMPARE_BOOL(true, S64, 0x080000000LL, U64, 0x080000000ULL); + COMPARE_BOOL(false, S64, 0, U64, 0x100000000ULL); + COMPARE_BOOL(false, S64, 0x100000000LL, U64, 0); + COMPARE_BOOL(true, S64, 0x100000000LL, U64, 0x100000000ULL); + COMPARE_BOOL(false, S64, -1, U64, 0x0FFFFFFFFULL); + COMPARE_BOOL(false, S64, -1, U64, 0xFFFFFFFFFFFFFFFFULL); + COMPARE_BOOL(false, S64, -0x080000000LL, U64, 0x080000000ULL); - s32A = mSCRIPT_MAKE_S32(0x80000000); - u32 = mSCRIPT_MAKE_U32(0x80000000); - assert_false(s32A.type->equal(&s32A, &u32)); + // F32 + COMPARE_BOOL(true, S64, 0, F32, 0); + COMPARE_BOOL(false, S64, 1, F32, 0); + COMPARE_BOOL(false, S64, 0, F32, 1); + COMPARE_BOOL(true, S64, 1, F32, 1); + COMPARE_BOOL(false, S64, 0, F32, -1); + COMPARE_BOOL(false, S64, 1, F32, -1); + COMPARE_BOOL(true, S64, -1, F32, -1); + COMPARE_BOOL(false, S64, 1, F32, 1.1); + COMPARE_BOOL(false, S64, 0, F32, 0.1); + COMPARE_BOOL(true, S64, 0x4000000000000000LL, F32, 0x4000000000000000LL); + COMPARE_BOOL(true, S64, -0x4000000000000000LL, F32, -0x4000000000000000LL); - s32A = mSCRIPT_MAKE_S32(0); - f32 = mSCRIPT_MAKE_F32(0); - assert_true(s32A.type->equal(&s32A, &f32)); - - s32A = mSCRIPT_MAKE_S32(1); - f32 = mSCRIPT_MAKE_F32(1); - assert_true(s32A.type->equal(&s32A, &f32)); - - s32A = mSCRIPT_MAKE_S32(0); - f32 = mSCRIPT_MAKE_F32(1); - assert_false(s32A.type->equal(&s32A, &f32)); - - s32A = mSCRIPT_MAKE_S32(1); - f32 = mSCRIPT_MAKE_F32(0); - assert_false(s32A.type->equal(&s32A, &f32)); - - s32A = mSCRIPT_MAKE_S32(-1); - f32 = mSCRIPT_MAKE_F32(-1); - assert_true(s32A.type->equal(&s32A, &f32)); - - s32A = mSCRIPT_MAKE_S32(0); - f32 = mSCRIPT_MAKE_F32(-1); - assert_false(s32A.type->equal(&s32A, &f32)); - - s32A = mSCRIPT_MAKE_S32(-1); - f32 = mSCRIPT_MAKE_F32(0); - assert_false(s32A.type->equal(&s32A, &f32)); + // F64 + COMPARE_BOOL(true, S64, 0, F64, 0); + COMPARE_BOOL(false, S64, 1, F64, 0); + COMPARE_BOOL(false, S64, 0, F64, 1); + COMPARE_BOOL(true, S64, 1, F64, 1); + COMPARE_BOOL(false, S64, 0, F64, -1); + COMPARE_BOOL(false, S64, 1, F64, -1); + COMPARE_BOOL(true, S64, -1, F64, -1); + COMPARE_BOOL(false, S64, 1, F64, 1.1); + COMPARE_BOOL(false, S64, 0, F64, 0.1); + COMPARE_BOOL(true, S64, 0x4000000000000000LL, F64, 0x4000000000000000LL); + COMPARE_BOOL(true, S64, -0x4000000000000000LL, F64, -0x4000000000000000LL); } M_TEST_DEFINE(u32Equality) { - struct mScriptValue u32A; - struct mScriptValue u32B; - struct mScriptValue s32; - struct mScriptValue f32; + struct mScriptValue a; + struct mScriptValue b; - u32A = mSCRIPT_MAKE_U32(0); - u32B = mSCRIPT_MAKE_U32(0); - assert_true(u32A.type->equal(&u32A, &u32B)); + // U32 + COMPARE_BOOL(true, U32, 0, U32, 0); + COMPARE_BOOL(false, U32, 0, U32, 1); + COMPARE_BOOL(true, U32, 1, U32, 1); + COMPARE_BOOL(false, U32, 0x80000000U, U32, 1); + COMPARE_BOOL(true, U32, 0x80000000U, U32, 0x80000000U); + COMPARE_BOOL(false, U32, 0x7FFFFFFFU, U32, 1); + COMPARE_BOOL(true, U32, 0x7FFFFFFFU, U32, 0x7FFFFFFFU); - u32B = mSCRIPT_MAKE_U32(1); - assert_false(u32A.type->equal(&u32A, &u32B)); + // U64 + COMPARE_BOOL(true, U32, 0, U64, 0); + COMPARE_BOOL(false, U32, 0, U64, 1); + COMPARE_BOOL(true, U32, 1, U64, 1); + COMPARE_BOOL(false, U32, 0x80000000U, U64, 1); + COMPARE_BOOL(true, U32, 0x80000000U, U64, 0x080000000ULL); + COMPARE_BOOL(false, U32, 0x7FFFFFFFU, U64, 1); + COMPARE_BOOL(true, U32, 0x7FFFFFFFU, U64, 0x07FFFFFFFULL); + COMPARE_BOOL(false, U32, 0x80000000U, U64, 0x180000000ULL); + COMPARE_BOOL(false, U32, 0, U64, 0x100000000ULL); - u32A = mSCRIPT_MAKE_U32(1); - assert_true(u32A.type->equal(&u32A, &u32B)); + // S32 + COMPARE_BOOL(true, U32, 0, S32, 0); + COMPARE_BOOL(false, U32, 0, S32, 1); + COMPARE_BOOL(true, U32, 1, S32, 1); + COMPARE_BOOL(true, U32, 0x7FFFFFFFU, S32, 0x7FFFFFFF); + COMPARE_BOOL(false, U32, 0xFFFFFFFFU, S32, 0xFFFFFFFF); + COMPARE_BOOL(false, U32, 0x80000000U, S32, 0x80000000); - u32B = mSCRIPT_MAKE_U32(0x80000000U); - assert_false(u32A.type->equal(&u32A, &u32B)); + // S64 + COMPARE_BOOL(true, U32, 0, S64, 0); + COMPARE_BOOL(false, U32, 0, S64, 1); + COMPARE_BOOL(true, U32, 1, S64, 1); + COMPARE_BOOL(true, U32, 0x7FFFFFFFU, S64, 0x07FFFFFFFLL); + COMPARE_BOOL(true, U32, 0xFFFFFFFFU, S64, 0x0FFFFFFFFLL); + COMPARE_BOOL(true, U32, 0x80000000U, S64, 0x080000000LL); + COMPARE_BOOL(false, U32, 0x80000000U, S64, 0x180000000LL); + COMPARE_BOOL(false, U32, 0, S64, 0x100000000LL); - u32A = mSCRIPT_MAKE_U32(0x80000000U); - assert_true(u32A.type->equal(&u32A, &u32B)); + // F32 + COMPARE_BOOL(true, U32, 0, F32, 0); + COMPARE_BOOL(false, U32, 1, F32, 0); + COMPARE_BOOL(false, U32, 0, F32, 1); + COMPARE_BOOL(true, U32, 1, F32, 1); + COMPARE_BOOL(false, U32, 0, F32, -1); + COMPARE_BOOL(false, U32, 1, F32, -1); + COMPARE_BOOL(false, U32, 0xFFFFFFFFU, F32, -1); + COMPARE_BOOL(true, U32, 0x80000000U, F32, 0x80000000); + COMPARE_BOOL(false, U32, 0, F32, 0x80000000); + COMPARE_BOOL(false, U32, 0x80000000U, F32, 0); + COMPARE_BOOL(false, U32, 1, F32, 1.1); + COMPARE_BOOL(false, U32, 0, F32, 0.1); - u32A = mSCRIPT_MAKE_U32(0); - s32 = mSCRIPT_MAKE_S32(0); - assert_true(u32A.type->equal(&u32A, &s32)); + // F64 + COMPARE_BOOL(true, U32, 0, F64, 0); + COMPARE_BOOL(false, U32, 1, F64, 0); + COMPARE_BOOL(false, U32, 0, F64, 1); + COMPARE_BOOL(true, U32, 1, F64, 1); + COMPARE_BOOL(false, U32, 0, F64, -1); + COMPARE_BOOL(false, U32, 1, F64, -1); + COMPARE_BOOL(false, U32, 0xFFFFFFFFU, F64, -1); + COMPARE_BOOL(true, U32, 0x80000000U, F64, 0x80000000); + COMPARE_BOOL(false, U32, 0, F64, 0x80000000); + COMPARE_BOOL(false, U32, 0x80000000U, F64, 0); + COMPARE_BOOL(false, U32, 1, F64, 1.1); + COMPARE_BOOL(false, U32, 0, F64, 0.1); +} - u32A = mSCRIPT_MAKE_U32(1); - s32 = mSCRIPT_MAKE_S32(1); - assert_true(u32A.type->equal(&u32A, &s32)); +M_TEST_DEFINE(u64Equality) { + struct mScriptValue a; + struct mScriptValue b; - u32A = mSCRIPT_MAKE_U32(0); - s32 = mSCRIPT_MAKE_S32(1); - assert_false(u32A.type->equal(&u32A, &s32)); + // U32 + COMPARE_BOOL(true, U64, 0, U32, 0); + COMPARE_BOOL(false, U64, 0, U32, 1); + COMPARE_BOOL(true, U64, 1, U32, 1); + COMPARE_BOOL(false, U64, 0x080000000ULL, U32, 1); + COMPARE_BOOL(true, U64, 0x080000000ULL, U32, 0x80000000U); + COMPARE_BOOL(false, U64, 0x07FFFFFFFULL, U32, 1); + COMPARE_BOOL(true, U64, 0x07FFFFFFFULL, U32, 0x7FFFFFFFU); + COMPARE_BOOL(false, U64, 0x180000000ULL, U32, 0x80000000U); + COMPARE_BOOL(false, U64, 0x100000000ULL, U32, 0); - u32A = mSCRIPT_MAKE_U32(1); - s32 = mSCRIPT_MAKE_S32(0); - assert_false(u32A.type->equal(&u32A, &s32)); + // U64 + COMPARE_BOOL(true, U64, 0, U64, 0); + COMPARE_BOOL(false, U64, 0, U64, 1); + COMPARE_BOOL(true, U64, 1, U64, 1); + COMPARE_BOOL(false, U64, 0x080000000ULL, U64, 1); + COMPARE_BOOL(true, U64, 0x080000000ULL, U64, 0x080000000ULL); + COMPARE_BOOL(false, U64, 0x07FFFFFFFULL, U64, 1); + COMPARE_BOOL(true, U64, 0x07FFFFFFFULL, U64, 0x07FFFFFFFULL); + COMPARE_BOOL(true, U64, 0x180000000ULL, U64, 0x180000000ULL); + COMPARE_BOOL(true, U64, 0x100000000ULL, U64, 0x100000000ULL); - u32A = mSCRIPT_MAKE_U32(0x7FFFFFFF); - s32 = mSCRIPT_MAKE_S32(0x7FFFFFFF); - assert_true(u32A.type->equal(&u32A, &s32)); + // S32 + COMPARE_BOOL(true, U64, 0, S32, 0); + COMPARE_BOOL(false, U64, 0, S32, 1); + COMPARE_BOOL(true, U64, 1, S32, 1); + COMPARE_BOOL(true, U64, 0x07FFFFFFFULL, S32, 0x7FFFFFFF); + COMPARE_BOOL(false, U64, 0x0FFFFFFFFULL, S32, 0xFFFFFFFF); + COMPARE_BOOL(false, U64, 0x080000000ULL, S32, 0x80000000); + COMPARE_BOOL(false, U64, 0x100000000ULL, S32, 0); - u32A = mSCRIPT_MAKE_U32(0xFFFFFFFF); - s32 = mSCRIPT_MAKE_S32(0xFFFFFFFF); - assert_false(u32A.type->equal(&u32A, &s32)); + // S64 + COMPARE_BOOL(true, U64, 0, S64, 0); + COMPARE_BOOL(false, U64, 0, S64, 1); + COMPARE_BOOL(true, U64, 1, S64, 1); + COMPARE_BOOL(true, U64, 0x07FFFFFFFULL, S64, 0x07FFFFFFFLL); + COMPARE_BOOL(true, U64, 0x0FFFFFFFFULL, S64, 0x0FFFFFFFFLL); + COMPARE_BOOL(true, U64, 0x080000000ULL, S64, 0x080000000LL); + COMPARE_BOOL(false, U64, 0, S64, 0x100000000LL); + COMPARE_BOOL(false, U64, 0x100000000ULL, S64, 0); + COMPARE_BOOL(true, U64, 0x100000000ULL, S64, 0x100000000LL); + COMPARE_BOOL(false, U64, 0x0FFFFFFFFULL, S64, -1); + COMPARE_BOOL(false, U64, 0xFFFFFFFFFFFFFFFFULL, S64, -1); + COMPARE_BOOL(true, U64, 0x080000000ULL, S64, 0x080000000LL); - u32A = mSCRIPT_MAKE_U32(0x80000000); - s32 = mSCRIPT_MAKE_S32(0x80000000); - assert_false(u32A.type->equal(&u32A, &s32)); + // F32 + COMPARE_BOOL(true, U64, 0, F32, 0); + COMPARE_BOOL(false, U64, 1, F32, 0); + COMPARE_BOOL(false, U64, 0, F32, 1); + COMPARE_BOOL(true, U64, 1, F32, 1); + COMPARE_BOOL(false, U64, 0, F32, -1); + COMPARE_BOOL(false, U64, 1, F32, -1); + COMPARE_BOOL(false, U64, 0xFFFFFFFFFFFFFFFFULL, F32, -1); + COMPARE_BOOL(true, U64, 0x8000000000000000ULL, F32, 0x8000000000000000ULL); + COMPARE_BOOL(false, U64, 0, F32, 0x8000000000000000ULL); + COMPARE_BOOL(false, U64, 0x8000000000000000ULL, F32, 0); + COMPARE_BOOL(false, U64, 1, F32, 1.1); + COMPARE_BOOL(false, U64, 0, F32, 0.1); - u32A = mSCRIPT_MAKE_U32(0); - f32 = mSCRIPT_MAKE_F32(0); - assert_true(u32A.type->equal(&u32A, &f32)); - - u32A = mSCRIPT_MAKE_U32(1); - f32 = mSCRIPT_MAKE_F32(1); - assert_true(u32A.type->equal(&u32A, &f32)); - - u32A = mSCRIPT_MAKE_U32(0); - f32 = mSCRIPT_MAKE_F32(1); - assert_false(u32A.type->equal(&u32A, &f32)); - - u32A = mSCRIPT_MAKE_U32(1); - f32 = mSCRIPT_MAKE_F32(0); - assert_false(u32A.type->equal(&u32A, &f32)); - - u32A = mSCRIPT_MAKE_U32(0x80000000); - f32 = mSCRIPT_MAKE_F32(0x80000000); - assert_true(u32A.type->equal(&u32A, &f32)); - - u32A = mSCRIPT_MAKE_U32(0); - f32 = mSCRIPT_MAKE_F32(0x80000000); - assert_false(u32A.type->equal(&u32A, &f32)); - - u32A = mSCRIPT_MAKE_U32(0x80000000); - f32 = mSCRIPT_MAKE_F32(0); - assert_false(u32A.type->equal(&u32A, &f32)); + // F64 + COMPARE_BOOL(true, U64, 0, F64, 0); + COMPARE_BOOL(false, U64, 1, F64, 0); + COMPARE_BOOL(false, U64, 0, F64, 1); + COMPARE_BOOL(true, U64, 1, F64, 1); + COMPARE_BOOL(false, U64, 0, F64, -1); + COMPARE_BOOL(false, U64, 1, F64, -1); + COMPARE_BOOL(false, U64, 0xFFFFFFFFFFFFFFFFULL, F64, -1); + COMPARE_BOOL(true, U64, 0x8000000000000000ULL, F64, 0x8000000000000000ULL); + COMPARE_BOOL(false, U64, 0, F64, 0x8000000000000000ULL); + COMPARE_BOOL(false, U64, 0x8000000000000000ULL, F64, 0); + COMPARE_BOOL(false, U64, 1, F64, 1.1); + COMPARE_BOOL(false, U64, 0, F64, 0.1); } M_TEST_DEFINE(f32Equality) { - struct mScriptValue f32A; - struct mScriptValue f32B; - struct mScriptValue s32; - struct mScriptValue u32; + struct mScriptValue a; + struct mScriptValue b; - f32A = mSCRIPT_MAKE_F32(0); - f32B = mSCRIPT_MAKE_F32(0); - assert_true(f32A.type->equal(&f32A, &f32B)); + // F32 + COMPARE_BOOL(true, F32, 0, F32, 0); + COMPARE_BOOL(false, F32, 0, F32, 1); + COMPARE_BOOL(true, F32, 1, F32, 1); + COMPARE_BOOL(true, F32, -1, F32, -1); + COMPARE_BOOL(false, F32, 1.1, F32, 1); + COMPARE_BOOL(false, F32, 1, F32, 1.1); + COMPARE_BOOL(true, F32, 1.1, F32, 1.1); - f32B = mSCRIPT_MAKE_F32(1); - assert_false(f32A.type->equal(&f32A, &f32B)); + // F64 + COMPARE_BOOL(true, F32, 0, F64, 0); + COMPARE_BOOL(false, F32, 0, F64, 1); + COMPARE_BOOL(true, F32, 1, F64, 1); + COMPARE_BOOL(true, F32, -1, F64, -1); + COMPARE_BOOL(false, F32, 1.1, F64, 1); + COMPARE_BOOL(false, F32, 1, F64, 1.1); + COMPARE_BOOL(true, F32, 1.25, F64, 1.25); - f32A = mSCRIPT_MAKE_F32(1); - assert_true(f32A.type->equal(&f32A, &f32B)); + // S32 + COMPARE_BOOL(true, F32, 0, S32, 0); + COMPARE_BOOL(false, F32, 0, S32, 1); + COMPARE_BOOL(false, F32, 1, S32, 0); + COMPARE_BOOL(true, F32, 1, S32, 1); + COMPARE_BOOL(false, F32, 1.1, S32, 1); + COMPARE_BOOL(true, F32, -1, S32, -1); + COMPARE_BOOL(false, F32, -1.1, S32, -1); + COMPARE_BOOL(true, F32, 0x40000000, S32, 0x40000000); + COMPARE_BOOL(true, F32, -0x40000000, S32, -0x40000000); - f32B = mSCRIPT_MAKE_F32(1.1); - assert_false(f32A.type->equal(&f32A, &f32B)); + // S64 + COMPARE_BOOL(true, F32, 0, S64, 0); + COMPARE_BOOL(false, F32, 0, S64, 1); + COMPARE_BOOL(false, F32, 1, S64, 0); + COMPARE_BOOL(true, F32, 1, S64, 1); + COMPARE_BOOL(false, F32, 1.1, S64, 1); + COMPARE_BOOL(true, F32, -1, S64, -1); + COMPARE_BOOL(false, F32, -1.1, S64, -1); + COMPARE_BOOL(true, F32, 0x040000000LL, S64, 0x040000000LL); + COMPARE_BOOL(true, F32, 0x100000000LL, S64, 0x100000000LL); + COMPARE_BOOL(false, F32, 0x100000000LL, S64, 0); + COMPARE_BOOL(false, F32, 0, S64, 0x100000000LL); + COMPARE_BOOL(true, F32, -0x040000000LL, S64, -0x040000000LL); - f32A = mSCRIPT_MAKE_F32(1.1); - assert_true(f32A.type->equal(&f32A, &f32B)); + // U32 + COMPARE_BOOL(true, F32, 0, U32, 0); + COMPARE_BOOL(false, F32, 0, U32, 1); + COMPARE_BOOL(false, F32, 1, U32, 0); + COMPARE_BOOL(true, F32, 1, U32, 1); + COMPARE_BOOL(false, F32, 1.1, U32, 1); + COMPARE_BOOL(true, F32, 0x40000000, U32, 0x40000000); - f32A = mSCRIPT_MAKE_F32(0); - s32 = mSCRIPT_MAKE_S32(0); - assert_true(f32A.type->equal(&f32A, &s32)); + // U64 + COMPARE_BOOL(true, F32, 0, U64, 0); + COMPARE_BOOL(false, F32, 0, U64, 1); + COMPARE_BOOL(false, F32, 1, U64, 0); + COMPARE_BOOL(true, F32, 1, U64, 1); + COMPARE_BOOL(false, F32, 1.1, U64, 1); + COMPARE_BOOL(true, F32, 0x040000000ULL, U64, 0x040000000ULL); + COMPARE_BOOL(true, F32, 0x100000000ULL, U64, 0x100000000ULL); + COMPARE_BOOL(false, F32, 0x100000000ULL, U64, 0); + COMPARE_BOOL(false, F32, 0, U64, 0x100000000ULL); +} - f32A = mSCRIPT_MAKE_F32(1); - s32 = mSCRIPT_MAKE_S32(1); - assert_true(f32A.type->equal(&f32A, &s32)); +M_TEST_DEFINE(f64Equality) { + struct mScriptValue a; + struct mScriptValue b; - f32A = mSCRIPT_MAKE_F32(0); - s32 = mSCRIPT_MAKE_S32(1); - assert_false(f32A.type->equal(&f32A, &s32)); + // F32 + COMPARE_BOOL(true, F64, 0, F32, 0); + COMPARE_BOOL(false, F64, 0, F32, 1); + COMPARE_BOOL(true, F64, 1, F32, 1); + COMPARE_BOOL(true, F64, -1, F32, -1); + COMPARE_BOOL(false, F64, 1.1, F32, 1); + COMPARE_BOOL(false, F64, 1, F32, 1.1); + COMPARE_BOOL(true, F64, 1.25, F32, 1.25); - f32A = mSCRIPT_MAKE_F32(1); - s32 = mSCRIPT_MAKE_S32(0); - assert_false(f32A.type->equal(&f32A, &s32)); + // F64 + COMPARE_BOOL(true, F64, 0, F64, 0); + COMPARE_BOOL(false, F64, 0, F64, 1); + COMPARE_BOOL(true, F64, 1, F64, 1); + COMPARE_BOOL(true, F64, -1, F64, -1); + COMPARE_BOOL(false, F64, 1.1, F64, 1); + COMPARE_BOOL(false, F64, 1, F64, 1.1); + COMPARE_BOOL(true, F64, 1.1, F64, 1.1); - f32A = mSCRIPT_MAKE_F32(1.1); - s32 = mSCRIPT_MAKE_S32(1); - assert_false(f32A.type->equal(&f32A, &s32)); + // S32 + COMPARE_BOOL(true, F64, 0, S32, 0); + COMPARE_BOOL(false, F64, 0, S32, 1); + COMPARE_BOOL(false, F64, 1, S32, 0); + COMPARE_BOOL(true, F64, 1, S32, 1); + COMPARE_BOOL(false, F64, 1.1, S32, 1); + COMPARE_BOOL(true, F64, -1, S32, -1); + COMPARE_BOOL(false, F64, -1.1, S32, -1); + COMPARE_BOOL(true, F64, 0x40000000, S32, 0x40000000); + COMPARE_BOOL(true, F64, -0x40000000, S32, -0x40000000); - f32A = mSCRIPT_MAKE_F32(0x40000000); - s32 = mSCRIPT_MAKE_S32(0x40000000); - assert_true(f32A.type->equal(&f32A, &s32)); + // S64 + COMPARE_BOOL(true, F64, 0, S64, 0); + COMPARE_BOOL(false, F64, 0, S64, 1); + COMPARE_BOOL(false, F64, 1, S64, 0); + COMPARE_BOOL(true, F64, 1, S64, 1); + COMPARE_BOOL(false, F64, 1.1, S64, 1); + COMPARE_BOOL(true, F64, -1, S64, -1); + COMPARE_BOOL(false, F64, -1.1, S64, -1); + COMPARE_BOOL(true, F64, 0x040000000LL, S64, 0x040000000LL); + COMPARE_BOOL(true, F64, 0x100000000LL, S64, 0x100000000LL); + COMPARE_BOOL(false, F64, 0x100000000LL, S64, 0); + COMPARE_BOOL(false, F64, 0, S64, 0x100000000LL); + COMPARE_BOOL(true, F64, -0x040000000LL, S64, -0x040000000LL); - f32A = mSCRIPT_MAKE_F32(0); - u32 = mSCRIPT_MAKE_U32(0); - assert_true(f32A.type->equal(&f32A, &u32)); + // U32 + COMPARE_BOOL(true, F64, 0, U32, 0); + COMPARE_BOOL(false, F64, 0, U32, 1); + COMPARE_BOOL(false, F64, 1, U32, 0); + COMPARE_BOOL(true, F64, 1, U32, 1); + COMPARE_BOOL(false, F64, 1.1, U32, 1); + COMPARE_BOOL(true, F64, 0x40000000, U32, 0x40000000); - f32A = mSCRIPT_MAKE_F32(1); - u32 = mSCRIPT_MAKE_U32(1); - assert_true(f32A.type->equal(&f32A, &u32)); - - f32A = mSCRIPT_MAKE_F32(0); - u32 = mSCRIPT_MAKE_U32(1); - assert_false(f32A.type->equal(&f32A, &u32)); - - f32A = mSCRIPT_MAKE_F32(1); - u32 = mSCRIPT_MAKE_U32(0); - assert_false(f32A.type->equal(&f32A, &u32)); - - f32A = mSCRIPT_MAKE_F32(1.1); - u32 = mSCRIPT_MAKE_U32(1); - assert_false(f32A.type->equal(&f32A, &u32)); - - f32A = mSCRIPT_MAKE_F32(0x40000000); - u32 = mSCRIPT_MAKE_U32(0x40000000); - assert_true(f32A.type->equal(&f32A, &u32)); + // U64 + COMPARE_BOOL(true, F64, 0, U64, 0); + COMPARE_BOOL(false, F64, 0, U64, 1); + COMPARE_BOOL(false, F64, 1, U64, 0); + COMPARE_BOOL(true, F64, 1, U64, 1); + COMPARE_BOOL(false, F64, 1.1, U64, 1); + COMPARE_BOOL(true, F64, 0x040000000ULL, U64, 0x040000000ULL); + COMPARE_BOOL(true, F64, 0x100000000ULL, U64, 0x100000000ULL); + COMPARE_BOOL(false, F64, 0x100000000ULL, U64, 0); + COMPARE_BOOL(false, F64, 0, U64, 0x100000000ULL); } M_TEST_DEFINE(stringEquality) { @@ -516,6 +844,7 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(voidArgs), cmocka_unit_test(voidFunc), cmocka_unit_test(identityFunctionS32), + cmocka_unit_test(identityFunctionS64), cmocka_unit_test(identityFunctionF32), cmocka_unit_test(identityFunctionStruct), cmocka_unit_test(addS32), @@ -523,11 +852,18 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(wrongArgCountLo), cmocka_unit_test(wrongArgCountHi), cmocka_unit_test(wrongArgType), + cmocka_unit_test(wrongPopType), + cmocka_unit_test(wrongPopSize), cmocka_unit_test(coerceToFloat), cmocka_unit_test(coerceFromFloat), + cmocka_unit_test(coerceNarrow), + cmocka_unit_test(coerceWiden), cmocka_unit_test(s32Equality), + cmocka_unit_test(s64Equality), cmocka_unit_test(u32Equality), + cmocka_unit_test(u64Equality), cmocka_unit_test(f32Equality), + cmocka_unit_test(f64Equality), cmocka_unit_test(stringEquality), cmocka_unit_test(hashTableBasic), cmocka_unit_test(hashTableString), diff --git a/src/script/types.c b/src/script/types.c index 833c30ccf..c750b1810 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -28,6 +28,9 @@ static bool _typeEqual(const struct mScriptValue*, const struct mScriptValue*); static bool _s32Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _u32Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _f32Equal(const struct mScriptValue*, const struct mScriptValue*); +static bool _s64Equal(const struct mScriptValue*, const struct mScriptValue*); +static bool _u64Equal(const struct mScriptValue*, const struct mScriptValue*); +static bool _f64Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _charpEqual(const struct mScriptValue*, const struct mScriptValue*); static bool _stringEqual(const struct mScriptValue*, const struct mScriptValue*); @@ -75,6 +78,39 @@ const struct mScriptType mSTFloat32 = { .cast = _castScalar, }; +const struct mScriptType mSTSInt64 = { + .base = mSCRIPT_TYPE_SINT, + .size = 8, + .name = "s64", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, + .equal = _s64Equal, + .cast = _castScalar, +}; + +const struct mScriptType mSTUInt64 = { + .base = mSCRIPT_TYPE_UINT, + .size = 8, + .name = "u64", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, + .equal = _u64Equal, + .cast = _castScalar, +}; + +const struct mScriptType mSTFloat64 = { + .base = mSCRIPT_TYPE_FLOAT, + .size = 8, + .name = "f64", + .alloc = NULL, + .free = NULL, + .hash = NULL, + .equal = _f64Equal, + .cast = _castScalar, +}; + const struct mScriptType mSTString = { .base = mSCRIPT_TYPE_STRING, .size = sizeof(struct mScriptString), @@ -158,7 +194,7 @@ static uint32_t _hashString(const struct mScriptValue* val) { uint32_t _hashScalar(const struct mScriptValue* val) { // From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key - uint32_t x; + uint32_t x = 0; switch (val->type->base) { case mSCRIPT_TYPE_SINT: x = val->value.s32; @@ -173,50 +209,81 @@ uint32_t _hashScalar(const struct mScriptValue* val) { return x; } +#define AS(NAME, TYPE) \ + bool _as ## NAME(const struct mScriptValue* input, mSCRIPT_TYPE_C_ ## TYPE * T) { \ + switch (input->type->base) { \ + case mSCRIPT_TYPE_SINT: \ + if (input->type->size == 4) { \ + *T = input->value.s32; \ + } else if (input->type->size == 8) { \ + *T = input->value.s64; \ + } \ + break; \ + case mSCRIPT_TYPE_UINT: \ + if (input->type->size == 4) { \ + *T = input->value.u32; \ + } else if (input->type->size == 8) { \ + *T = input->value.u64; \ + } \ + break; \ + case mSCRIPT_TYPE_FLOAT: \ + if (input->type->size == 4) { \ + *T = input->value.f32; \ + } else if (input->type->size == 8) { \ + *T = input->value.f64; \ + } \ + break; \ + default: \ + return false; \ + } \ + return true; \ + } + +_mAPPLY(AS(SInt32, S32)); +_mAPPLY(AS(UInt32, U32)); +_mAPPLY(AS(Float32, F32)); +_mAPPLY(AS(SInt64, S64)); +_mAPPLY(AS(UInt64, U64)); +_mAPPLY(AS(Float64, F64)); + bool _castScalar(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { switch (type->base) { case mSCRIPT_TYPE_SINT: - switch (input->type->base) { - case mSCRIPT_TYPE_SINT: - output->value.s32 = input->value.s32; - break; - case mSCRIPT_TYPE_UINT: - output->value.s32 = input->value.u32; - break; - case mSCRIPT_TYPE_FLOAT: - output->value.s32 = input->value.f32; - break; - default: + if (type->size == 4) { + if (!_asSInt32(input, &output->value.s32)) { + return false; + } + } else if (type->size == 8) { + if (!_asSInt64(input, &output->value.s64)) { + return false; + } + } else { return false; } break; case mSCRIPT_TYPE_UINT: - switch (input->type->base) { - case mSCRIPT_TYPE_SINT: - output->value.u32 = input->value.s32; - break; - case mSCRIPT_TYPE_UINT: - output->value.u32 = input->value.u32; - break; - case mSCRIPT_TYPE_FLOAT: - output->value.u32 = input->value.f32; - break; - default: + if (type->size == 4) { + if (!_asUInt32(input, &output->value.u32)) { + return false; + } + } else if (type->size == 8) { + if (!_asUInt64(input, &output->value.u64)) { + return false; + } + } else { return false; } break; case mSCRIPT_TYPE_FLOAT: - switch (input->type->base) { - case mSCRIPT_TYPE_SINT: - output->value.f32 = input->value.s32; - break; - case mSCRIPT_TYPE_UINT: - output->value.f32 = input->value.u32; - break; - case mSCRIPT_TYPE_FLOAT: - output->value.f32 = input->value.f32; - break; - default: + if (type->size == 4) { + if (!_asFloat32(input, &output->value.f32)) { + return false; + } + } else if (type->size == 8) { + if (!_asFloat64(input, &output->value.f64)) { + return false; + } + } else { return false; } break; @@ -257,16 +324,34 @@ bool _s32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { int32_t val; switch (b->type->base) { case mSCRIPT_TYPE_SINT: - val = b->value.s32; - break; - case mSCRIPT_TYPE_UINT: - if (b->value.u32 > (uint32_t) INT_MAX) { + if (b->type->size == 4) { + val = b->value.s32; + } else if (b->type->size == 8) { + if (b->value.s64 > INT_MAX || b->value.s64 < INT_MIN) { + return false; + } + val = b->value.s64; + } else { return false; } + break; + case mSCRIPT_TYPE_UINT: if (a->value.s32 < 0) { return false; } - val = b->value.u32; + if (b->type->size == 4) { + if (b->value.u32 > (uint32_t) INT_MAX) { + return false; + } + val = b->value.u32; + } else if (b->type->size == 8) { + if (b->value.u64 > (uint64_t) INT_MAX) { + return false; + } + val = b->value.u64; + } else { + return false; + } break; case mSCRIPT_TYPE_VOID: return false; @@ -280,16 +365,34 @@ bool _u32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { uint32_t val; switch (b->type->base) { case mSCRIPT_TYPE_SINT: - if (b->value.s32 < 0) { + if (b->type->size == 4) { + if (a->value.u32 > (uint32_t) INT_MAX) { + return false; + } + if (b->value.s32 < 0) { + return false; + } + val = b->value.s32; + } else if (b->type->size == 8) { + if (b->value.s64 > (int64_t) UINT_MAX || b->value.s64 < 0) { + return false; + } + val = b->value.s64; + } else { return false; } - if (a->value.u32 > (uint32_t) INT_MAX) { - return false; - } - val = b->value.s32; break; case mSCRIPT_TYPE_UINT: - val = b->value.u32; + if (b->type->size == 4) { + val = b->value.u32; + } else if (b->type->size == 8) { + if (b->value.u64 > UINT_MAX) { + return false; + } + val = b->value.u64; + } else { + return false; + } break; case mSCRIPT_TYPE_VOID: return false; @@ -303,13 +406,11 @@ bool _f32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { float val; switch (b->type->base) { case mSCRIPT_TYPE_SINT: - val = b->value.s32; - break; case mSCRIPT_TYPE_UINT: - val = b->value.u32; - break; case mSCRIPT_TYPE_FLOAT: - val = b->value.f32; + if (!_asFloat32(b, &val)) { + return false; + } break; case mSCRIPT_TYPE_VOID: return false; @@ -319,6 +420,100 @@ bool _f32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { return a->value.f32 == val; } +bool _s64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { + int64_t val; + switch (b->type->base) { + case mSCRIPT_TYPE_SINT: + if (b->type->size == 4) { + val = b->value.s32; + } else if (b->type->size == 8) { + val = b->value.s64; + } else { + return false; + } + break; + case mSCRIPT_TYPE_UINT: + if (a->value.s64 < 0) { + return false; + } + if (b->type->size == 4) { + val = b->value.u32; + } else if (b->type->size == 8) { + if (b->value.u64 > (uint64_t) INT64_MAX) { + return false; + } + val = b->value.u64; + } else { + return false; + } + break; + case mSCRIPT_TYPE_VOID: + return false; + default: + return b->type->equal && b->type->equal(b, a); + } + return a->value.s64 == val; +} + +bool _u64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { + uint64_t val; + switch (b->type->base) { + case mSCRIPT_TYPE_SINT: + if (b->type->size == 4) { + if (a->value.u64 > (uint64_t) INT_MAX) { + return false; + } + if (b->value.s32 < 0) { + return false; + } + val = b->value.s32; + } else if (b->type->size == 8) { + if (a->value.u64 > (uint64_t) INT64_MAX) { + return false; + } + if (b->value.s64 < 0) { + return false; + } + val = b->value.s64; + } else { + return false; + } + break; + case mSCRIPT_TYPE_UINT: + if (b->type->size == 4) { + val = b->value.u32; + } else if (b->type->size == 8) { + val = b->value.u64; + } else { + return false; + } + break; + case mSCRIPT_TYPE_VOID: + return false; + default: + return b->type->equal && b->type->equal(b, a); + } + return a->value.u64 == val; +} + +bool _f64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { + double val; + switch (b->type->base) { + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + if (!_asFloat64(b, &val)) { + return false; + } + break; + case mSCRIPT_TYPE_VOID: + return false; + default: + return b->type->equal && b->type->equal(b, a); + } + return a->value.f64 == val; +} + bool _charpEqual(const struct mScriptValue* a, const struct mScriptValue* b) { const char* valA = a->value.opaque; const char* valB; @@ -467,18 +662,36 @@ bool mScriptPopS32(struct mScriptList* list, int32_t* out) { return true; } +bool mScriptPopS64(struct mScriptList* list, int64_t* out) { + mSCRIPT_POP(list, S64, val); + *out = val; + return true; +} + bool mScriptPopU32(struct mScriptList* list, uint32_t* out) { mSCRIPT_POP(list, U32, val); *out = val; return true; } +bool mScriptPopU64(struct mScriptList* list, uint64_t* out) { + mSCRIPT_POP(list, U64, val); + *out = val; + return true; +} + bool mScriptPopF32(struct mScriptList* list, float* out) { mSCRIPT_POP(list, F32, val); *out = val; return true; } +bool mScriptPopF64(struct mScriptList* list, double* out) { + mSCRIPT_POP(list, F64, val); + *out = val; + return true; +} + bool mScriptPopPointer(struct mScriptList* list, void** out) { mSCRIPT_POP(list, PTR, val); *out = val; From c507157ab8d5266eb5272bec92781a8d0759555b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 15 Feb 2022 17:44:07 -0800 Subject: [PATCH 010/105] Scripting: Start bringing up execution contexts --- include/mgba/script/context.h | 56 +++++++++++++++++++++++++++++++++++ include/mgba/script/types.h | 9 +++--- src/script/CMakeLists.txt | 1 + src/script/context.c | 22 ++++++++++++++ src/script/test/types.c | 1 + src/script/types.c | 7 ----- 6 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 include/mgba/script/context.h create mode 100644 src/script/context.c diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h new file mode 100644 index 000000000..9c2310a41 --- /dev/null +++ b/include/mgba/script/context.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2013-2022 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/. */ +#ifndef M_SCRIPT_CONTEXT_H +#define M_SCRIPT_CONTEXT_H + +#include + +CXX_GUARD_START + +#include +#include +#include + +struct mScriptFrame; +struct mScriptFunction; +struct mScriptEngineContext; + +struct mScriptContext { + struct mScriptValue rootScope; + struct Table engines; +}; + +struct mScriptEngine2 { + const char* name; + + void (*init)(struct mScriptEngine2*); + void (*deinit)(struct mScriptEngine2*); + + struct mScriptEngineContext* (*create)(struct mScriptEngine2*, struct mScriptContext*); +}; + +struct mScriptEngineContext { + struct mScriptContext* context; + void (*destroy)(struct mScriptEngineContext*); + + bool (*setGlobal)(struct mScriptEngineContext*, const char* name, struct mScriptValue*); + struct mScriptValue* (*getGlobal)(struct mScriptEngineContext*, const char* name); + + bool (*load)(struct mScriptEngineContext*, struct VFile*, const char** error); + bool (*run)(struct mScriptEngineContext*); +}; + +void mScriptContextInit(struct mScriptContext*); +void mScriptContextDeinit(struct mScriptContext*); + +void mScriptContextRegisterEngine(struct mScriptContext*, struct mScriptEngine2*); + +void mScriptContextAddGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value); +void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key); + +bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame); + +#endif diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 1e9ee73c2..0af2c11db 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -131,7 +131,8 @@ CXX_GUARD_START } #define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \ - static bool _binding_ ## NAME(struct mScriptFrame* frame) { \ + static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \ if (mScriptListSize(&frame->arguments)) { \ return false; \ @@ -154,7 +155,8 @@ CXX_GUARD_START }; #define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \ - static bool _binding_ ## NAME(struct mScriptFrame* frame) { \ + static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \ if (mScriptListSize(&frame->arguments)) { \ return false; \ @@ -276,7 +278,7 @@ struct mScriptFrame { struct mScriptFunction { struct mScriptTypeFunction signature; - bool (*call)(struct mScriptFrame*); + bool (*call)(struct mScriptFrame*, void* context); void* context; }; @@ -303,6 +305,5 @@ bool mScriptPopPointer(struct mScriptList* list, void** out); bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output); bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame); -bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame); #endif diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 5d1d1ae6f..c7b39d104 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -1,5 +1,6 @@ include(ExportDirectory) set(SOURCE_FILES + context.c types.c) set(TEST_FILES diff --git a/src/script/context.c b/src/script/context.c new file mode 100644 index 000000000..f8ae22e19 --- /dev/null +++ b/src/script/context.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2013-2022 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 + +void mScriptContextInit(struct mScriptContext* context) { + // TODO: rootScope + HashTableInit(&context->engines, 0, NULL); +} + +void mScriptContextDeinit(struct mScriptContext* context) { + HashTableDeinit(&context->engines); +} + +bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame) { + if (!mScriptCoerceFrame(&fn->signature.parameters, &frame->arguments)) { + return false; + } + return fn->call(frame, fn->context); +} diff --git a/src/script/test/types.c b/src/script/test/types.c index fc0e85a33..caeb8cd05 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "util/test/suite.h" +#include #include struct Test { diff --git a/src/script/types.c b/src/script/types.c index c750b1810..ff7cc1e24 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -723,10 +723,3 @@ bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList } return true; } - -bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame) { - if (!mScriptCoerceFrame(&fn->signature.parameters, &frame->arguments)) { - return false; - } - return fn->call(frame); -} From 512572769e3aceecd33ec6bc6f733b1a169cbabb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 16 Feb 2022 17:09:11 -0800 Subject: [PATCH 011/105] Scripting: Rework functions; add wrapper type for stack references --- include/mgba/script/context.h | 2 +- include/mgba/script/types.h | 84 ++++++++++++++++++++++++++--------- src/script/context.c | 9 +++- src/script/types.c | 41 ++++++++++++++++- 4 files changed, 110 insertions(+), 26 deletions(-) diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 9c2310a41..0b83a02fe 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -51,6 +51,6 @@ void mScriptContextRegisterEngine(struct mScriptContext*, struct mScriptEngine2* void mScriptContextAddGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value); void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key); -bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame); +bool mScriptInvoke(const struct mScriptValue* fn, struct mScriptFrame* frame); #endif diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 0af2c11db..b5f52d2dd 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -27,6 +27,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_CHARP const char* #define mSCRIPT_TYPE_C_PTR void* #define mSCRIPT_TYPE_C_TABLE Table* +#define mSCRIPT_TYPE_C_WRAPPER mScriptValue* #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* #define mSCRIPT_TYPE_FIELD_S32 s32 @@ -39,6 +40,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_CHARP opaque #define mSCRIPT_TYPE_FIELD_PTR opaque #define mSCRIPT_TYPE_FIELD_TABLE opaque +#define mSCRIPT_TYPE_FIELD_WRAPPER opaque #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque #define mSCRIPT_TYPE_MS_S32 (&mSTSInt32) @@ -50,6 +52,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_STR (&mSTString) #define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr) #define mSCRIPT_TYPE_MS_TABLE (&mSTTable) +#define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper) #define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) #define _mSCRIPT_FIELD_NAME(V) (V)->name @@ -72,9 +75,17 @@ CXX_GUARD_START do { \ struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ - return false; \ + if (_val->type == mSCRIPT_TYPE_MS_WRAPPER) { \ + _val = mScriptValueUnwrap(_val); \ + if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ + return false; \ + } \ + } else { \ + return false; \ + } \ } \ NAME = _val->value. _mAPPLY(mSCRIPT_TYPE_FIELD_ ## TYPE); \ + mScriptValueDeref(_val); \ mScriptListResize(STACK, -1); \ } while (0) @@ -140,18 +151,30 @@ CXX_GUARD_START _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ return true; \ } \ - const struct mScriptFunction NAME = { \ - .signature = { \ - .parameters = { \ - .count = NPARAMS, \ - .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ + static const struct mScriptType _type_ ## NAME = { \ + .base = mSCRIPT_TYPE_FUNCTION, \ + .details = { \ + .function = { \ + .parameters = { \ + .count = NPARAMS, \ + .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ + }, \ + .returnType = { \ + .count = 1, \ + .entries = { mSCRIPT_TYPE_MS_ ## RETURN } \ + }, \ }, \ - .returnType = { \ - .count = 1, \ - .entries = { mSCRIPT_TYPE_MS_ ## RETURN } \ - }, \ - }, \ + } \ + }; \ + static struct mScriptFunction _function_ ## NAME = { \ .call = _binding_ ## NAME \ + }; \ + const struct mScriptValue NAME = { \ + .type = &_type_ ## NAME, \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = { \ + .opaque = &_function_ ## NAME \ + } \ }; #define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \ @@ -164,17 +187,29 @@ CXX_GUARD_START _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ return true; \ } \ - const struct mScriptFunction NAME = { \ - .signature = { \ - .parameters = { \ - .count = NPARAMS, \ - .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ + static const struct mScriptType _type_ ## NAME = { \ + .base = mSCRIPT_TYPE_FUNCTION, \ + .details = { \ + .function = { \ + .parameters = { \ + .count = NPARAMS, \ + .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ + }, \ + .returnType = { \ + .count = 0, \ + }, \ }, \ - .returnType = { \ - .count = 0, \ - }, \ - }, \ + } \ + }; \ + static struct mScriptFunction _function_ ## NAME = { \ .call = _binding_ ## NAME \ + }; \ + const struct mScriptValue NAME = { \ + .type = &_type_ ## NAME, \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = { \ + .opaque = &_function_ ## NAME \ + } \ }; #define mSCRIPT_MAKE(TYPE, FIELD, VALUE) (struct mScriptValue) { \ @@ -205,6 +240,7 @@ enum { mSCRIPT_TYPE_TUPLE, mSCRIPT_TYPE_LIST, mSCRIPT_TYPE_TABLE, + mSCRIPT_TYPE_WRAPPER }; struct Table; @@ -219,16 +255,18 @@ extern const struct mScriptType mSTFloat64; extern const struct mScriptType mSTString; extern const struct mScriptType mSTCharPtr; extern const struct mScriptType mSTTable; +extern const struct mScriptType mSTWrapper; struct mScriptTypeTuple { size_t count; const struct mScriptType* entries[mSCRIPT_PARAMS_MAX]; + bool variable; }; struct mScriptTypeFunction { struct mScriptTypeTuple parameters; struct mScriptTypeTuple returnType; - // TODO: varargs, kwargs, defaults + // TODO: kwargs, defaults }; struct mScriptValue; @@ -277,7 +315,6 @@ struct mScriptFrame { }; struct mScriptFunction { - struct mScriptTypeFunction signature; bool (*call)(struct mScriptFrame*, void* context); void* context; }; @@ -286,6 +323,9 @@ struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type); void mScriptValueRef(struct mScriptValue* val); void mScriptValueDeref(struct mScriptValue* val); +void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out); +struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val); + struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value); diff --git a/src/script/context.c b/src/script/context.c index f8ae22e19..321d2f222 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -14,9 +14,14 @@ void mScriptContextDeinit(struct mScriptContext* context) { HashTableDeinit(&context->engines); } -bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame) { - if (!mScriptCoerceFrame(&fn->signature.parameters, &frame->arguments)) { +bool mScriptInvoke(const struct mScriptValue* val, struct mScriptFrame* frame) { + if (val->type->base != mSCRIPT_TYPE_FUNCTION) { return false; } + const struct mScriptTypeFunction* signature = &val->type->details.function; + if (!mScriptCoerceFrame(&signature->parameters, &frame->arguments)) { + return false; + } + const struct mScriptFunction* fn = val->value.opaque; return fn->call(frame, fn->context); } diff --git a/src/script/types.c b/src/script/types.c index ff7cc1e24..e8f1a73a2 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -140,6 +140,15 @@ const struct mScriptType mSTTable = { .hash = NULL, }; +const struct mScriptType mSTWrapper = { + .base = mSCRIPT_TYPE_WRAPPER, + .size = sizeof(struct mScriptValue), + .name = "wrapper", + .alloc = NULL, + .free = NULL, + .hash = NULL, +}; + DEFINE_VECTOR(mScriptList, struct mScriptValue) void _allocTable(struct mScriptValue* val) { @@ -602,6 +611,36 @@ void mScriptValueDeref(struct mScriptValue* val) { free(val); } +void mScriptValueWrap(struct mScriptValue* value, struct mScriptValue* out) { + if (value->refs == mSCRIPT_VALUE_UNREF) { + memcpy(out, value, sizeof(*out)); + return; + } + out->refs = mSCRIPT_VALUE_UNREF; + switch (value->type->base) { + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + case mSCRIPT_TYPE_WRAPPER: + out->type = value->type; + memcpy(&out->value, &value->value, sizeof(out->value)); + return; + default: + break; + } + + out->type = mSCRIPT_TYPE_MS_WRAPPER; + out->value.opaque = value; + mScriptValueRef(value); +} + +struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* value) { + if (value->type == mSCRIPT_TYPE_MS_WRAPPER) { + return value->value.opaque; + } + return NULL; +} + struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); struct mScriptString* internal = val->value.opaque; @@ -709,7 +748,7 @@ bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* inpu } bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame) { - if (types->count != mScriptListSize(frame)) { + if (types->count != mScriptListSize(frame) && (!types->variable || mScriptListSize(frame) < types->count)) { return false; } size_t i; From ce97d869064408ec0b5c588dc90e67cad08d469e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 15 Feb 2022 17:44:21 -0800 Subject: [PATCH 012/105] Scripting: Start bringing up Lua bindings --- CMakeLists.txt | 20 +++++++ include/mgba/internal/script/lua.h | 12 ++++ src/script/CMakeLists.txt | 5 ++ src/script/engines/lua.c | 91 ++++++++++++++++++++++++++++++ src/script/test/lua.c | 65 +++++++++++++++++++++ 5 files changed, 193 insertions(+) create mode 100644 include/mgba/internal/script/lua.h create mode 100644 src/script/engines/lua.c create mode 100644 src/script/test/lua.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 825d942ea..42aaa896c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ if(NOT LIBMGBA_ONLY) set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support") set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support") set(USE_ELF ON CACHE BOOL "Whether or not to enable ELF support") + set(USE_LUA ON CACHE BOOL "Whether or not to enable Lua scripting support") set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core") set(M_CORE_GB ON CACHE BOOL "Build Game Boy core") set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support") @@ -735,6 +736,17 @@ endif() if(ENABLE_SCRIPTING) add_subdirectory(src/script) list(APPEND ENABLES SCRIPTING) + if(NOT USE_LUA VERSION_LESS 5.1) + find_feature(USE_LUA "Lua" ${USE_LUA}) + else() + find_feature(USE_LUA "Lua") + endif() + if(USE_LUA) + list(APPEND FEATURE_DEFINES USE_LUA) + include_directories(AFTER ${LUA_INCLUDE_DIR}) + list(APPEND FEATURE_DEFINES LUA_VERSION_ONLY=\"${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}\") + list(APPEND DEPENDENCY_LIB ${LUA_LIBRARY}) + endif() if(BUILD_PYTHON) find_package(PythonLibs ${USE_PYTHON_VERSION}) @@ -1226,6 +1238,14 @@ if(NOT QUIET AND NOT LIBMGBA_ONLY) message(STATUS " ELF loading support: ${USE_ELF}") message(STATUS " Discord Rich Presence support: ${USE_DISCORD_RPC}") message(STATUS " OpenGL support: ${SUMMARY_GL}") + message(STATUS "Scripting support: ${ENABLE_SCRIPTING}") + if(ENABLE_SCRIPTING) + if(LUA_VERSION_STRING) + message(STATUS " Lua: ${LUA_VERSION_STRING}") + else() + message(STATUS " Lua: ${USE_LUA}") + endif() + endif() message(STATUS "Frontends:") message(STATUS " Qt: ${BUILD_QT}") message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}") diff --git a/include/mgba/internal/script/lua.h b/include/mgba/internal/script/lua.h new file mode 100644 index 000000000..709106450 --- /dev/null +++ b/include/mgba/internal/script/lua.h @@ -0,0 +1,12 @@ +/* Copyright (c) 2013-2022 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/. */ +#ifndef M_SCRIPT_LUA_H +#define M_SCRIPT_LUA_H +#include + +extern struct mScriptEngine2* const mSCRIPT_ENGINE_LUA; + +#endif diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index c7b39d104..ff4e4146f 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -6,6 +6,11 @@ set(SOURCE_FILES set(TEST_FILES test/types.c) +if(USE_LUA) + list(APPEND SOURCE_FILES engines/lua.c) + list(APPEND TEST_FILES test/lua.c) +endif() + source_group("Scripting" FILES ${SOURCE_FILES}) source_group("Scripting tests" FILES ${TEST_FILES}) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c new file mode 100644 index 000000000..1782c43ad --- /dev/null +++ b/src/script/engines/lua.c @@ -0,0 +1,91 @@ +/* Copyright (c) 2013-2022 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 +#include + +static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*); + +static void _luaDestroy(struct mScriptEngineContext*); +static bool _luaLoad(struct mScriptEngineContext*, struct VFile*, const char** error); + +struct mScriptEngineContextLua { + struct mScriptEngineContext d; + lua_State* lua; +}; + +static struct mScriptEngineLua { + struct mScriptEngine2 d; +} _engineLua = { + .d = { + .name = "lua-" LUA_VERSION_ONLY, + .init = NULL, + .deinit = NULL, + .create = _luaCreate + } +}; + +struct mScriptEngine2* const mSCRIPT_ENGINE_LUA = &_engineLua.d; + +struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mScriptContext* context) { + UNUSED(engine); + struct mScriptEngineContextLua* luaContext = calloc(1, sizeof(*luaContext)); + luaContext->d = (struct mScriptEngineContext) { + .context = context, + .destroy = _luaDestroy, + .load = _luaLoad, + }; + luaContext->lua = luaL_newstate(); + return &luaContext->d; +} + +void _luaDestroy(struct mScriptEngineContext* ctx) { + struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; + lua_close(luaContext->lua); + free(luaContext); +} + +#define LUA_BLOCKSIZE 0x1000 +struct mScriptEngineLuaReader { + struct VFile* vf; + char block[LUA_BLOCKSIZE]; +}; + +static const char* _reader(lua_State* lua, void* context, size_t* size) { + UNUSED(lua); + struct mScriptEngineLuaReader* reader = context; + ssize_t s = reader->vf->read(reader->vf, reader->block, sizeof(reader->block)); + if (s < 0) { + return NULL; + } + *size = s; + return reader->block; +} + +bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf, const char** error) { + struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; + struct mScriptEngineLuaReader data = { + .vf = vf + }; + int ret = lua_load(luaContext->lua, _reader, &data, NULL, "t"); + switch (ret) { + case LUA_OK: + if (error) { + *error = NULL; + } + return true; + case LUA_ERRSYNTAX: + if (error) { + *error = "Syntax error"; + } + break; + default: + if (error) { + *error = "Unknown error"; + } + break; + } + return false; +} diff --git a/src/script/test/lua.c b/src/script/test/lua.c new file mode 100644 index 000000000..695e0b115 --- /dev/null +++ b/src/script/test/lua.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2013-2022 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 + +M_TEST_SUITE_SETUP(mScriptLua) { + if (mSCRIPT_ENGINE_LUA->init) { + mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_SUITE_TEARDOWN(mScriptLua) { + if (mSCRIPT_ENGINE_LUA->deinit) { + mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_DEFINE(create) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + lua->destroy(lua); + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(loadGood) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + const char* program = "-- test\n"; + struct VFile* vf = VFileFromConstMemory(program, strlen(program)); + const char* error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(loadSyntax) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + const char* program = "Invalid syntax! )\n"; + struct VFile* vf = VFileFromConstMemory(program, strlen(program)); + const char* error = NULL; + assert_false(lua->load(lua, vf, &error)); + assert_non_null(error); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, + cmocka_unit_test(create), + cmocka_unit_test(loadGood), + cmocka_unit_test(loadSyntax)) From 2c11c4806abfc9796bcadcc1dc9fba737e84c93d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 15 Feb 2022 22:09:35 -0800 Subject: [PATCH 013/105] Scripting: Add calling Lua functions --- src/script/engines/lua.c | 228 +++++++++++++++++++++++++++++++++++++++ src/script/test/lua.c | 129 +++++++++++++++++++++- 2 files changed, 355 insertions(+), 2 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 1782c43ad..cc22b4e14 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -9,13 +9,57 @@ static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*); static void _luaDestroy(struct mScriptEngineContext*); +static struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext*, const char* name); static bool _luaLoad(struct mScriptEngineContext*, struct VFile*, const char** error); +static bool _luaRun(struct mScriptEngineContext*); + +static bool _luaCall(struct mScriptFrame*, void* context); + +struct mScriptEngineContextLua; +static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptFrame*); +static bool _luaPopFrame(struct mScriptEngineContextLua*, struct mScriptFrame*); +static bool _luaInvoke(struct mScriptEngineContextLua*, struct mScriptFrame*); + +static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext); +static bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue*); + +static void _luaDeref(struct mScriptValue*); + +#if LUA_VERSION_NUM < 503 +#define lua_pushinteger lua_pushnumber +#endif + +const struct mScriptType mSTLuaFunc = { + .base = mSCRIPT_TYPE_FUNCTION, + .size = 0, + .name = "lua-" LUA_VERSION_ONLY "::function", + .details = { + .function = { + .parameters = { + .variable = true + }, + .returnType = { + .variable = true + } + } + }, + .alloc = NULL, + .free = _luaDeref, + .hash = NULL, + .equal = NULL, + .cast = NULL, +}; struct mScriptEngineContextLua { struct mScriptEngineContext d; lua_State* lua; }; +struct mScriptEngineContextLuaRef { + struct mScriptEngineContextLua* context; + int ref; +}; + static struct mScriptEngineLua { struct mScriptEngine2 d; } _engineLua = { @@ -35,7 +79,9 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS luaContext->d = (struct mScriptEngineContext) { .context = context, .destroy = _luaDestroy, + .getGlobal = _luaGetGlobal, .load = _luaLoad, + .run = _luaRun }; luaContext->lua = luaL_newstate(); return &luaContext->d; @@ -47,6 +93,96 @@ void _luaDestroy(struct mScriptEngineContext* ctx) { free(luaContext); } +struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext* ctx, const char* name) { + struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; + lua_getglobal(luaContext->lua, name); + return _luaCoerce(luaContext); +} + +struct mScriptValue* _luaWrapFunction(struct mScriptEngineContextLua* luaContext) { + struct mScriptValue* value = mScriptValueAlloc(&mSTLuaFunc); + struct mScriptFunction* fn = calloc(1, sizeof(*fn)); + struct mScriptEngineContextLuaRef* ref = calloc(1, sizeof(*ref)); + fn->call = _luaCall; + fn->context = ref; + ref->context = luaContext; + ref->ref = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); + value->value.opaque = fn; + return value; +} + +struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { + if (lua_isnone(luaContext->lua, -1)) { + lua_pop(luaContext->lua, 1); + return NULL; + } + + struct mScriptValue* value = NULL; + switch (lua_type(luaContext->lua, -1)) { + case LUA_TNUMBER: +#if LUA_VERSION_NUM >= 503 + if (lua_isinteger(luaContext->lua, -1)) { + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S64); + value->value.s64 = lua_tointeger(luaContext->lua, -1); + break; + } +#endif + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_F64); + value->value.f64 = lua_tonumber(luaContext->lua, -1); + break; + case LUA_TBOOLEAN: + break; + case LUA_TSTRING: + break; + case LUA_TFUNCTION: + return _luaWrapFunction(luaContext); + } + lua_pop(luaContext->lua, 1); + return value; +} + +bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* value) { + if (value->type == mSCRIPT_TYPE_MS_WRAPPER) { + value = mScriptValueUnwrap(value); + } + bool ok = true; + switch (value->type->base) { + case mSCRIPT_TYPE_SINT: + if (value->type->size == 4) { + lua_pushinteger(luaContext->lua, value->value.s32); + } else if (value->type->size == 8) { + lua_pushinteger(luaContext->lua, value->value.s64); + } else { + ok = false; + } + break; + case mSCRIPT_TYPE_UINT: + if (value->type->size == 4) { + lua_pushinteger(luaContext->lua, value->value.u32); + } else if (value->type->size == 8) { + lua_pushinteger(luaContext->lua, value->value.u64); + } else { + ok = false; + } + break; + case mSCRIPT_TYPE_FLOAT: + if (value->type->size == 4) { + lua_pushnumber(luaContext->lua, value->value.f32); + } else if (value->type->size == 8) { + lua_pushnumber(luaContext->lua, value->value.f64); + } else { + ok = false; + } + break; + default: + ok = false; + break; + } + + mScriptValueDeref(value); + return ok; +} + #define LUA_BLOCKSIZE 0x1000 struct mScriptEngineLuaReader { struct VFile* vf; @@ -89,3 +225,95 @@ bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf, const char** e } return false; } + +bool _luaRun(struct mScriptEngineContext* context) { + struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) context; + return _luaInvoke(luaContext, NULL); +} + +bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* frame) { + bool ok = true; + if (frame) { + size_t i; + for (i = 0; i < mScriptListSize(&frame->arguments); ++i) { + if (!_luaWrap(luaContext, mScriptListGetPointer(&frame->arguments, i))) { + ok = false; + break; + } + } + } + if (!ok) { + lua_pop(luaContext->lua, lua_gettop(luaContext->lua)); + } + return ok; +} + +bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* frame) { + int count = lua_gettop(luaContext->lua); + bool ok = true; + if (frame) { + int i; + for (i = 0; i < count; ++i) { + struct mScriptValue* value = _luaCoerce(luaContext); + if (!value) { + ok = false; + break; + } + lua_pop(luaContext->lua, 1); + mScriptValueWrap(value, mScriptListAppend(&frame->returnValues)); + mScriptValueDeref(value); + } + if (count > i) { + lua_pop(luaContext->lua, count - i); + } + } + return ok; +} + +bool _luaCall(struct mScriptFrame* frame, void* context) { + struct mScriptEngineContextLuaRef* ref = context; + lua_rawgeti(ref->context->lua, LUA_REGISTRYINDEX, ref->ref); + if (!_luaInvoke(ref->context, frame)) { + return false; + } + return true; +} + +bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* frame) { + int nargs = 0; + if (frame) { + nargs = mScriptListSize(&frame->arguments); + } + + if (frame && !_luaPushFrame(luaContext, frame)) { + return false; + } + + int ret = lua_pcall(luaContext->lua, nargs, LUA_MULTRET, 0); + + if (ret == LUA_ERRRUN) { + lua_pop(luaContext->lua, 1); + } + if (ret) { + return false; + } + + if (!_luaPopFrame(luaContext, frame)) { + return false; + } + + return true; +} + +void _luaDeref(struct mScriptValue* value) { + struct mScriptEngineContextLuaRef* ref; + if (value->type->base == mSCRIPT_TYPE_FUNCTION) { + struct mScriptFunction* function = value->value.opaque; + ref = function->context; + free(function); + } else { + return; + } + luaL_unref(ref->context->lua, LUA_REGISTRYINDEX, ref->ref); + free(ref); +} diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 695e0b115..b160877b1 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -44,7 +44,7 @@ M_TEST_DEFINE(loadGood) { mScriptContextDeinit(&context); } -M_TEST_DEFINE(loadSyntax) { +M_TEST_DEFINE(loadBadSyntax) { struct mScriptContext context; mScriptContextInit(&context); struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); @@ -59,7 +59,132 @@ M_TEST_DEFINE(loadSyntax) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(runNop) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + const char* program = "return"; + struct VFile* vf = VFileFromConstMemory(program, strlen(program)); + const char* error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + assert_true(lua->run(lua)); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(getGlobal) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct mScriptValue a = mSCRIPT_MAKE_S32(1); + struct mScriptValue* val; + const char* program; + struct VFile* vf; + const char* error; + + program = "a = 1"; + vf = VFileFromConstMemory(program, strlen(program)); + error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + assert_true(lua->run(lua)); + + val = lua->getGlobal(lua, "a"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + program = "b = 1"; + vf = VFileFromConstMemory(program, strlen(program)); + error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + assert_true(lua->run(lua)); + + val = lua->getGlobal(lua, "a"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_S32(2); + program = "a = 2"; + vf = VFileFromConstMemory(program, strlen(program)); + error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + assert_true(lua->run(lua)); + + val = lua->getGlobal(lua, "a"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_S32(3); + program = "b = a + b"; + vf = VFileFromConstMemory(program, strlen(program)); + error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + assert_true(lua->run(lua)); + + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(callLuaFunc) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct mScriptValue* fn; + const char* program; + struct VFile* vf; + const char* error; + + program = "function a(b) return b + 1 end"; + vf = VFileFromConstMemory(program, strlen(program)); + error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + assert_true(lua->run(lua)); + + fn = lua->getGlobal(lua, "a"); + assert_non_null(fn); + assert_int_equal(fn->type->base, mSCRIPT_TYPE_FUNCTION); + + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(fn, &frame)); + int64_t val; + assert_true(mScriptPopS64(&frame.returnValues, &val)); + assert_int_equal(val, 2); + + mScriptFrameDeinit(&frame); + mScriptValueDeref(fn); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(create), cmocka_unit_test(loadGood), - cmocka_unit_test(loadSyntax)) + cmocka_unit_test(loadBadSyntax), + cmocka_unit_test(runNop), + cmocka_unit_test(getGlobal), + cmocka_unit_test(callLuaFunc)) From 36efaf6330212fe6fc1d96602e934b3fdccd7b03 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 11 Mar 2022 15:36:42 -0800 Subject: [PATCH 014/105] Scripting: Add Lua setGlobal, make sure calling run twice works --- src/script/engines/lua.c | 18 ++++++++++++ src/script/test/lua.c | 60 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index cc22b4e14..80728540f 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -10,6 +10,7 @@ static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mS static void _luaDestroy(struct mScriptEngineContext*); static struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext*, const char* name); +static bool _luaSetGlobal(struct mScriptEngineContext*, const char* name, struct mScriptValue*); static bool _luaLoad(struct mScriptEngineContext*, struct VFile*, const char** error); static bool _luaRun(struct mScriptEngineContext*); @@ -53,6 +54,7 @@ const struct mScriptType mSTLuaFunc = { struct mScriptEngineContextLua { struct mScriptEngineContext d; lua_State* lua; + int func; }; struct mScriptEngineContextLuaRef { @@ -80,15 +82,20 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS .context = context, .destroy = _luaDestroy, .getGlobal = _luaGetGlobal, + .setGlobal = _luaSetGlobal, .load = _luaLoad, .run = _luaRun }; luaContext->lua = luaL_newstate(); + luaContext->func = -1; return &luaContext->d; } void _luaDestroy(struct mScriptEngineContext* ctx) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; + if (luaContext->func > 0) { + luaL_unref(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func); + } lua_close(luaContext->lua); free(luaContext); } @@ -99,6 +106,15 @@ struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext* ctx, const char* return _luaCoerce(luaContext); } +bool _luaSetGlobal(struct mScriptEngineContext* ctx, const char* name, struct mScriptValue* value) { + struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; + if (!_luaWrap(luaContext, value)) { + return false; + } + lua_setglobal(luaContext->lua, name); + return true; +} + struct mScriptValue* _luaWrapFunction(struct mScriptEngineContextLua* luaContext) { struct mScriptValue* value = mScriptValueAlloc(&mSTLuaFunc); struct mScriptFunction* fn = calloc(1, sizeof(*fn)); @@ -211,6 +227,7 @@ bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf, const char** e if (error) { *error = NULL; } + luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); return true; case LUA_ERRSYNTAX: if (error) { @@ -228,6 +245,7 @@ bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf, const char** e bool _luaRun(struct mScriptEngineContext* context) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) context; + lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func); return _luaInvoke(luaContext, NULL); } diff --git a/src/script/test/lua.c b/src/script/test/lua.c index b160877b1..cfe9071e5 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -71,6 +71,9 @@ M_TEST_DEFINE(runNop) { assert_null(error); assert_true(lua->run(lua)); + // Make sure we can run it twice + assert_true(lua->run(lua)); + lua->destroy(lua); mScriptContextDeinit(&context); } @@ -145,6 +148,62 @@ M_TEST_DEFINE(getGlobal) { mScriptContextDeinit(&context); } + +M_TEST_DEFINE(setGlobal) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct mScriptValue a = mSCRIPT_MAKE_S32(1); + struct mScriptValue* val; + const char* program; + struct VFile* vf; + const char* error; + + program = "a = b"; + vf = VFileFromConstMemory(program, strlen(program)); + error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + assert_true(lua->setGlobal(lua, "b", &a)); + + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + assert_true(lua->run(lua)); + + val = lua->getGlobal(lua, "a"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + a = mSCRIPT_MAKE_S32(2); + assert_false(a.type->equal(&a, val)); + mScriptValueDeref(val); + + val = lua->getGlobal(lua, "a"); + assert_non_null(val); + assert_false(a.type->equal(&a, val)); + mScriptValueDeref(val); + + assert_true(lua->setGlobal(lua, "b", &a)); + + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + assert_true(lua->run(lua)); + + val = lua->getGlobal(lua, "a"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + M_TEST_DEFINE(callLuaFunc) { struct mScriptContext context; mScriptContextInit(&context); @@ -187,4 +246,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(loadBadSyntax), cmocka_unit_test(runNop), cmocka_unit_test(getGlobal), + cmocka_unit_test(setGlobal), cmocka_unit_test(callLuaFunc)) From 9d92c185c602d3bd1becce7f1bd4305d0ea41677 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 11 Mar 2022 17:49:13 -0800 Subject: [PATCH 015/105] Scripting: Minor cleanup --- include/mgba/script/types.h | 4 ++-- src/script/types.c | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index b5f52d2dd..9e2fad9fd 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -23,11 +23,11 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_S64 int64_t #define mSCRIPT_TYPE_C_U64 uint64_t #define mSCRIPT_TYPE_C_F64 double -#define mSCRIPT_TYPE_C_STR mScriptString* +#define mSCRIPT_TYPE_C_STR struct mScriptString* #define mSCRIPT_TYPE_C_CHARP const char* #define mSCRIPT_TYPE_C_PTR void* #define mSCRIPT_TYPE_C_TABLE Table* -#define mSCRIPT_TYPE_C_WRAPPER mScriptValue* +#define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue* #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* #define mSCRIPT_TYPE_FIELD_S32 s32 diff --git a/src/script/types.c b/src/script/types.c index e8f1a73a2..b5fef71e1 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -598,11 +598,10 @@ void mScriptValueRef(struct mScriptValue* val) { } void mScriptValueDeref(struct mScriptValue* val) { - --val->refs; - if (val->refs > 0) { + if (val->refs > 1) { + --val->refs; return; - } else if (val->refs < 0) { - val->refs = mSCRIPT_VALUE_UNREF; + } else if (val->refs <= 0) { return; } if (val->type->free) { From 7fb7d53c5d40eb23c2ba77884223ae74d921bd12 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 11 Mar 2022 17:51:45 -0800 Subject: [PATCH 016/105] Scripting: Add Lua function call thunk --- src/script/engines/lua.c | 76 ++++++++++++++++++++++++++++++++++------ src/script/test/lua.c | 66 ++++++++++++++++++++++++++++++++-- 2 files changed, 129 insertions(+), 13 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 80728540f..0681ced98 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -17,8 +17,8 @@ static bool _luaRun(struct mScriptEngineContext*); static bool _luaCall(struct mScriptFrame*, void* context); struct mScriptEngineContextLua; -static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptFrame*); -static bool _luaPopFrame(struct mScriptEngineContextLua*, struct mScriptFrame*); +static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptList*); +static bool _luaPopFrame(struct mScriptEngineContextLua*, struct mScriptList*); static bool _luaInvoke(struct mScriptEngineContextLua*, struct mScriptFrame*); static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext); @@ -26,6 +26,8 @@ static bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptV static void _luaDeref(struct mScriptValue*); +static int _luaThunk(lua_State* lua); + #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber #endif @@ -190,12 +192,18 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v ok = false; } break; + case mSCRIPT_TYPE_FUNCTION: + lua_pushlightuserdata(luaContext->lua, value); + lua_pushcclosure(luaContext->lua, _luaThunk, 1); + break; default: ok = false; break; } - mScriptValueDeref(value); + if (ok) { + mScriptValueDeref(value); + } return ok; } @@ -249,12 +257,12 @@ bool _luaRun(struct mScriptEngineContext* context) { return _luaInvoke(luaContext, NULL); } -bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* frame) { +bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame) { bool ok = true; if (frame) { size_t i; - for (i = 0; i < mScriptListSize(&frame->arguments); ++i) { - if (!_luaWrap(luaContext, mScriptListGetPointer(&frame->arguments, i))) { + for (i = 0; i < mScriptListSize(frame); ++i) { + if (!_luaWrap(luaContext, mScriptListGetPointer(frame, i))) { ok = false; break; } @@ -266,7 +274,7 @@ bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptFra return ok; } -bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* frame) { +bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame) { int count = lua_gettop(luaContext->lua); bool ok = true; if (frame) { @@ -277,8 +285,7 @@ bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptFram ok = false; break; } - lua_pop(luaContext->lua, 1); - mScriptValueWrap(value, mScriptListAppend(&frame->returnValues)); + mScriptValueWrap(value, mScriptListAppend(frame)); mScriptValueDeref(value); } if (count > i) { @@ -303,11 +310,17 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* nargs = mScriptListSize(&frame->arguments); } - if (frame && !_luaPushFrame(luaContext, frame)) { + if (frame && !_luaPushFrame(luaContext, &frame->arguments)) { return false; } + lua_pushliteral(luaContext->lua, "mCtx"); + lua_pushlightuserdata(luaContext->lua, luaContext); + lua_rawset(luaContext->lua, LUA_REGISTRYINDEX); int ret = lua_pcall(luaContext->lua, nargs, LUA_MULTRET, 0); + lua_pushliteral(luaContext->lua, "mCtx"); + lua_pushnil(luaContext->lua); + lua_rawset(luaContext->lua, LUA_REGISTRYINDEX); if (ret == LUA_ERRRUN) { lua_pop(luaContext->lua, 1); @@ -316,7 +329,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* return false; } - if (!_luaPopFrame(luaContext, frame)) { + if (!_luaPopFrame(luaContext, &frame->returnValues)) { return false; } @@ -335,3 +348,44 @@ void _luaDeref(struct mScriptValue* value) { luaL_unref(ref->context->lua, LUA_REGISTRYINDEX, ref->ref); free(ref); } + +int _luaThunk(lua_State* lua) { + lua_pushliteral(lua, "mCtx"); + int type = lua_rawget(lua, LUA_REGISTRYINDEX); + if (type != LUA_TLIGHTUSERDATA) { + lua_pop(lua, 1); + lua_pushliteral(lua, "Function called from invalid context"); + lua_error(lua); + } + + struct mScriptEngineContextLua* luaContext = lua_touserdata(lua, -1); + lua_pop(lua, 1); + if (luaContext->lua != lua) { + lua_pushliteral(lua, "Function called from invalid context"); + lua_error(lua); + } + + struct mScriptFrame frame; + mScriptFrameInit(&frame); + if (!_luaPopFrame(luaContext, &frame.arguments)) { + mScriptFrameDeinit(&frame); + lua_pushliteral(lua, "Error calling function (setting arguments)"); + lua_error(lua); + } + + struct mScriptValue* fn = lua_touserdata(lua, lua_upvalueindex(1)); + if (!fn || !mScriptInvoke(fn, &frame)) { + mScriptFrameDeinit(&frame); + lua_pushliteral(lua, "Error calling function (invoking)"); + lua_error(lua); + } + + if (!_luaPushFrame(luaContext, &frame.returnValues)) { + mScriptFrameDeinit(&frame); + lua_pushliteral(lua, "Error calling function (getting return values)"); + lua_error(lua); + } + mScriptFrameDeinit(&frame); + + return lua_gettop(luaContext->lua); +} diff --git a/src/script/test/lua.c b/src/script/test/lua.c index cfe9071e5..2d78cf691 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -7,6 +7,17 @@ #include +static int identityInt(int in) { + return in; +} + +static int addInts(int a, int b) { + return a + b; +} + +mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32); +mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32); + M_TEST_SUITE_SETUP(mScriptLua) { if (mSCRIPT_ENGINE_LUA->init) { mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); @@ -214,7 +225,7 @@ M_TEST_DEFINE(callLuaFunc) { struct VFile* vf; const char* error; - program = "function a(b) return b + 1 end"; + program = "function a(b) return b + 1 end; function c(d, e) return d + e end"; vf = VFileFromConstMemory(program, strlen(program)); error = NULL; assert_true(lua->load(lua, vf, &error)); @@ -236,6 +247,56 @@ M_TEST_DEFINE(callLuaFunc) { mScriptFrameDeinit(&frame); mScriptValueDeref(fn); + fn = lua->getGlobal(lua, "c"); + assert_non_null(fn); + assert_int_equal(fn->type->base, mSCRIPT_TYPE_FUNCTION); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.arguments, S32, 2); + assert_true(mScriptInvoke(fn, &frame)); + assert_true(mScriptPopS64(&frame.returnValues, &val)); + assert_int_equal(val, 3); + + mScriptFrameDeinit(&frame); + mScriptValueDeref(fn); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(callCFunc) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct mScriptValue a = mSCRIPT_MAKE_S32(1); + struct mScriptValue* val; + const char* program; + struct VFile* vf; + const char* error; + + program = "a = b(1); c = d(1, 2)"; + vf = VFileFromConstMemory(program, strlen(program)); + error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + + assert_true(lua->setGlobal(lua, "b", &boundIdentityInt)); + assert_true(lua->setGlobal(lua, "d", &boundAddInts)); + assert_true(lua->run(lua)); + + val = lua->getGlobal(lua, "a"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_S32(3); + val = lua->getGlobal(lua, "c"); + assert_non_null(val); + assert_true(a.type->equal(&a, val)); + mScriptValueDeref(val); + lua->destroy(lua); mScriptContextDeinit(&context); } @@ -247,4 +308,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(runNop), cmocka_unit_test(getGlobal), cmocka_unit_test(setGlobal), - cmocka_unit_test(callLuaFunc)) + cmocka_unit_test(callLuaFunc), + cmocka_unit_test(callCFunc)) From 2e00104d1aa14034bcf6885586a89f70a45a0bb1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 28 Apr 2022 15:48:33 -0700 Subject: [PATCH 017/105] Scripting: Clean up function binding CPP --- include/mgba/script/types.h | 54 ++++++++++++------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 9e2fad9fd..92207bac6 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -141,16 +141,7 @@ CXX_GUARD_START .free = NULL, \ } -#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \ - static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \ - if (mScriptListSize(&frame->arguments)) { \ - return false; \ - } \ - _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ - return true; \ - } \ +#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ static const struct mScriptType _type_ ## NAME = { \ .base = mSCRIPT_TYPE_FUNCTION, \ .details = { \ @@ -160,8 +151,8 @@ CXX_GUARD_START .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ }, \ .returnType = { \ - .count = 1, \ - .entries = { mSCRIPT_TYPE_MS_ ## RETURN } \ + .count = NRET, \ + .entries = { RETURN } \ }, \ }, \ } \ @@ -175,7 +166,19 @@ CXX_GUARD_START .value = { \ .opaque = &_function_ ## NAME \ } \ - }; + } + +#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \ + static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } \ + _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ + return true; \ + } \ + _mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) #define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \ static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ @@ -187,30 +190,7 @@ CXX_GUARD_START _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ return true; \ } \ - static const struct mScriptType _type_ ## NAME = { \ - .base = mSCRIPT_TYPE_FUNCTION, \ - .details = { \ - .function = { \ - .parameters = { \ - .count = NPARAMS, \ - .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ - }, \ - .returnType = { \ - .count = 0, \ - }, \ - }, \ - } \ - }; \ - static struct mScriptFunction _function_ ## NAME = { \ - .call = _binding_ ## NAME \ - }; \ - const struct mScriptValue NAME = { \ - .type = &_type_ ## NAME, \ - .refs = mSCRIPT_VALUE_UNREF, \ - .value = { \ - .opaque = &_function_ ## NAME \ - } \ - }; + _mSCRIPT_BIND_FUNCTION(NAME, 0, , NPARAMS, __VA_ARGS__) #define mSCRIPT_MAKE(TYPE, FIELD, VALUE) (struct mScriptValue) { \ .type = (TYPE), \ From 4c1e7369153d44d4b999bd405e69e404bb89e132 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 28 Apr 2022 19:47:31 -0700 Subject: [PATCH 018/105] Scripting: Fix cast failure --- src/script/types.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/types.c b/src/script/types.c index b5fef71e1..09a9e74cc 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -743,7 +743,7 @@ bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* inpu if (input->type->cast && input->type->cast(input, type, output)) { return true; } - return true; + return false; } bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame) { From c8848876fa80344cc91ae795beb1620fd6092ada Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 27 Apr 2022 00:29:53 -0700 Subject: [PATCH 019/105] Scripting: Add 8/16-bit integer types for struct interop --- include/mgba/script/types.h | 24 +++++++++++++ src/script/engines/lua.c | 4 +-- src/script/types.c | 68 ++++++++++++++++++++++++++++++------- 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 92207bac6..1ddaffb55 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -17,6 +17,10 @@ CXX_GUARD_START #define mSCRIPT_VALUE_UNREF -1 #define mSCRIPT_PARAMS_MAX 8 +#define mSCRIPT_TYPE_C_S8 int8_t +#define mSCRIPT_TYPE_C_U8 uint8_t +#define mSCRIPT_TYPE_C_S16 int16_t +#define mSCRIPT_TYPE_C_U16 uint16_t #define mSCRIPT_TYPE_C_S32 int32_t #define mSCRIPT_TYPE_C_U32 uint32_t #define mSCRIPT_TYPE_C_F32 float @@ -30,6 +34,10 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue* #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* +#define mSCRIPT_TYPE_FIELD_S8 s32 +#define mSCRIPT_TYPE_FIELD_U8 s32 +#define mSCRIPT_TYPE_FIELD_S16 s32 +#define mSCRIPT_TYPE_FIELD_U16 s32 #define mSCRIPT_TYPE_FIELD_S32 s32 #define mSCRIPT_TYPE_FIELD_U32 u32 #define mSCRIPT_TYPE_FIELD_F32 f32 @@ -43,6 +51,10 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_WRAPPER opaque #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque +#define mSCRIPT_TYPE_MS_S8 (&mSTSInt8) +#define mSCRIPT_TYPE_MS_U8 (&mSTUInt8) +#define mSCRIPT_TYPE_MS_S16 (&mSTSInt16) +#define mSCRIPT_TYPE_MS_U16 (&mSTUInt16) #define mSCRIPT_TYPE_MS_S32 (&mSTSInt32) #define mSCRIPT_TYPE_MS_U32 (&mSTUInt32) #define mSCRIPT_TYPE_MS_F32 (&mSTFloat32) @@ -58,6 +70,10 @@ CXX_GUARD_START #define _mSCRIPT_FIELD_NAME(V) (V)->name #define mSCRIPT_TYPE_CMP_GENERIC(TYPE0, TYPE1) (TYPE0 == TYPE1) +#define mSCRIPT_TYPE_CMP_U8(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U8, TYPE) +#define mSCRIPT_TYPE_CMP_S8(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S8, TYPE) +#define mSCRIPT_TYPE_CMP_U16(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U16, TYPE) +#define mSCRIPT_TYPE_CMP_S16(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S16, TYPE) #define mSCRIPT_TYPE_CMP_U32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U32, TYPE) #define mSCRIPT_TYPE_CMP_S32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S32, TYPE) #define mSCRIPT_TYPE_CMP_F32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F32, TYPE) @@ -200,6 +216,10 @@ CXX_GUARD_START }, \ } \ +#define mSCRIPT_MAKE_S8(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S8, s32, VALUE) +#define mSCRIPT_MAKE_U8(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U8, u32, VALUE) +#define mSCRIPT_MAKE_S16(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S16, s32, VALUE) +#define mSCRIPT_MAKE_U16(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U16, u32, VALUE) #define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S32, s32, VALUE) #define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U32, u32, VALUE) #define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F32, f32, VALUE) @@ -226,6 +246,10 @@ enum { struct Table; struct mScriptType; extern const struct mScriptType mSTVoid; +extern const struct mScriptType mSTSInt8; +extern const struct mScriptType mSTUInt8; +extern const struct mScriptType mSTSInt16; +extern const struct mScriptType mSTUInt16; extern const struct mScriptType mSTSInt32; extern const struct mScriptType mSTUInt32; extern const struct mScriptType mSTFloat32; diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 0681ced98..e7da8c8bf 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -166,7 +166,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v bool ok = true; switch (value->type->base) { case mSCRIPT_TYPE_SINT: - if (value->type->size == 4) { + if (value->type->size <= 4) { lua_pushinteger(luaContext->lua, value->value.s32); } else if (value->type->size == 8) { lua_pushinteger(luaContext->lua, value->value.s64); @@ -175,7 +175,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } break; case mSCRIPT_TYPE_UINT: - if (value->type->size == 4) { + if (value->type->size <= 4) { lua_pushinteger(luaContext->lua, value->value.u32); } else if (value->type->size == 8) { lua_pushinteger(luaContext->lua, value->value.u64); diff --git a/src/script/types.c b/src/script/types.c index 09a9e74cc..cba832563 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -45,6 +45,50 @@ const struct mScriptType mSTVoid = { .cast = NULL, }; +const struct mScriptType mSTSInt8 = { + .base = mSCRIPT_TYPE_SINT, + .size = 1, + .name = "s8", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, + .equal = _s32Equal, + .cast = _castScalar, +}; + +const struct mScriptType mSTUInt8 = { + .base = mSCRIPT_TYPE_UINT, + .size = 1, + .name = "u8", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, + .equal = _u32Equal, + .cast = _castScalar, +}; + +const struct mScriptType mSTSInt16 = { + .base = mSCRIPT_TYPE_SINT, + .size = 2, + .name = "s16", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, + .equal = _s32Equal, + .cast = _castScalar, +}; + +const struct mScriptType mSTUInt16 = { + .base = mSCRIPT_TYPE_UINT, + .size = 2, + .name = "u16", + .alloc = NULL, + .free = NULL, + .hash = _hashScalar, + .equal = _u32Equal, + .cast = _castScalar, +}; + const struct mScriptType mSTSInt32 = { .base = mSCRIPT_TYPE_SINT, .size = 4, @@ -222,14 +266,14 @@ uint32_t _hashScalar(const struct mScriptValue* val) { bool _as ## NAME(const struct mScriptValue* input, mSCRIPT_TYPE_C_ ## TYPE * T) { \ switch (input->type->base) { \ case mSCRIPT_TYPE_SINT: \ - if (input->type->size == 4) { \ + if (input->type->size <= 4) { \ *T = input->value.s32; \ } else if (input->type->size == 8) { \ *T = input->value.s64; \ } \ break; \ case mSCRIPT_TYPE_UINT: \ - if (input->type->size == 4) { \ + if (input->type->size <= 4) { \ *T = input->value.u32; \ } else if (input->type->size == 8) { \ *T = input->value.u64; \ @@ -258,7 +302,7 @@ _mAPPLY(AS(Float64, F64)); bool _castScalar(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { switch (type->base) { case mSCRIPT_TYPE_SINT: - if (type->size == 4) { + if (type->size <= 4) { if (!_asSInt32(input, &output->value.s32)) { return false; } @@ -271,7 +315,7 @@ bool _castScalar(const struct mScriptValue* input, const struct mScriptType* typ } break; case mSCRIPT_TYPE_UINT: - if (type->size == 4) { + if (type->size <= 4) { if (!_asUInt32(input, &output->value.u32)) { return false; } @@ -333,7 +377,7 @@ bool _s32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { int32_t val; switch (b->type->base) { case mSCRIPT_TYPE_SINT: - if (b->type->size == 4) { + if (b->type->size <= 4) { val = b->value.s32; } else if (b->type->size == 8) { if (b->value.s64 > INT_MAX || b->value.s64 < INT_MIN) { @@ -348,7 +392,7 @@ bool _s32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { if (a->value.s32 < 0) { return false; } - if (b->type->size == 4) { + if (b->type->size <= 4) { if (b->value.u32 > (uint32_t) INT_MAX) { return false; } @@ -374,7 +418,7 @@ bool _u32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { uint32_t val; switch (b->type->base) { case mSCRIPT_TYPE_SINT: - if (b->type->size == 4) { + if (b->type->size <= 4) { if (a->value.u32 > (uint32_t) INT_MAX) { return false; } @@ -392,7 +436,7 @@ bool _u32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { } break; case mSCRIPT_TYPE_UINT: - if (b->type->size == 4) { + if (b->type->size <= 4) { val = b->value.u32; } else if (b->type->size == 8) { if (b->value.u64 > UINT_MAX) { @@ -433,7 +477,7 @@ bool _s64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { int64_t val; switch (b->type->base) { case mSCRIPT_TYPE_SINT: - if (b->type->size == 4) { + if (b->type->size <= 4) { val = b->value.s32; } else if (b->type->size == 8) { val = b->value.s64; @@ -445,7 +489,7 @@ bool _s64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { if (a->value.s64 < 0) { return false; } - if (b->type->size == 4) { + if (b->type->size <= 4) { val = b->value.u32; } else if (b->type->size == 8) { if (b->value.u64 > (uint64_t) INT64_MAX) { @@ -468,7 +512,7 @@ bool _u64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { uint64_t val; switch (b->type->base) { case mSCRIPT_TYPE_SINT: - if (b->type->size == 4) { + if (b->type->size <= 4) { if (a->value.u64 > (uint64_t) INT_MAX) { return false; } @@ -489,7 +533,7 @@ bool _u64Equal(const struct mScriptValue* a, const struct mScriptValue* b) { } break; case mSCRIPT_TYPE_UINT: - if (b->type->size == 4) { + if (b->type->size <= 4) { val = b->value.u32; } else if (b->type->size == 8) { val = b->value.u64; From af2e226cc4bac1a5db2c31fb3318508312ffa197 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 27 Apr 2022 00:30:49 -0700 Subject: [PATCH 020/105] Scripting: First pass on structs/classes --- include/mgba/script/types.h | 75 ++++++++++++++++++++++++ src/script/CMakeLists.txt | 1 + src/script/test/classes.c | 113 ++++++++++++++++++++++++++++++++++++ src/script/test/types.c | 3 + src/script/types.c | 69 ++++++++++++++++++++++ 5 files changed, 261 insertions(+) create mode 100644 src/script/test/classes.c diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 1ddaffb55..58ba0b0fe 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -10,6 +10,7 @@ CXX_GUARD_START +#include #include #define _mAPPLY(...) __VA_ARGS__ @@ -151,12 +152,51 @@ CXX_GUARD_START #define mSCRIPT_EXPORT_STRUCT(STRUCT) \ const struct mScriptType mSTStruct_ ## STRUCT = { \ .base = mSCRIPT_TYPE_OBJECT, \ + .details = { \ + .cls = &_mSTStructDetails_ ## STRUCT \ + }, \ .size = sizeof(struct STRUCT), \ .name = "struct::" #STRUCT, \ .alloc = NULL, \ .free = NULL, \ } +#define mSCRIPT_DECLARE_STRUCT(STRUCT) extern const struct mScriptType mSTStruct_ ## STRUCT; +#define mSCRIPT_DEFINE_STRUCT(STRUCT) \ + static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ + .init = false, \ + .details = (const struct mScriptClassInitDetails[]) { + +#define mSCRIPT_DEFINE_DOCSTRING(DOCSTRING) { \ + .type = mSCRIPT_CLASS_INIT_DOCSTRING, \ + .info = { \ + .comment = DOCSTRING \ + } \ +}, + +#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) { \ + .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ + .info = { \ + .member = { \ + .name = #NAME, \ + .type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE), \ + .offset = offsetof(STRUCT, NAME) \ + } \ + } \ +}, + +#define mSCRIPT_DEFINE_STATIC_MEMBER(TYPE, NAME) { \ + .type = mSCRIPT_CLASS_INIT_STATIC_MEMBER, \ + .info = { \ + .member = { \ + .name = #NAME, \ + .type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE) \ + } \ + }, \ +}, + +#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } }; + #define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ static const struct mScriptType _type_ ## NAME = { \ .base = mSCRIPT_TYPE_FUNCTION, \ @@ -243,6 +283,14 @@ enum { mSCRIPT_TYPE_WRAPPER }; +enum mScriptClassInitType { + mSCRIPT_CLASS_INIT_END = 0, + mSCRIPT_CLASS_INIT_DOCSTRING, + mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, + mSCRIPT_CLASS_INIT_STATIC_MEMBER, + mSCRIPT_CLASS_INIT_INHERIT, +}; + struct Table; struct mScriptType; extern const struct mScriptType mSTVoid; @@ -273,6 +321,29 @@ struct mScriptTypeFunction { // TODO: kwargs, defaults }; +struct mScriptClassMember { + const char* name; + const char* docstring; + const struct mScriptType* type; + size_t offset; +}; + +struct mScriptClassInitDetails { + enum mScriptClassInitType type; + union { + const char* comment; + const struct mScriptTypeClass* parent; + struct mScriptClassMember member; + } info; +}; + +struct mScriptTypeClass { + bool init; + const struct mScriptClassInitDetails* details; + struct Table staticMembers; + struct Table instanceMembers; +}; + struct mScriptValue; struct mScriptType { int base; @@ -281,6 +352,7 @@ struct mScriptType { union { struct mScriptTypeTuple tuple; struct mScriptTypeFunction function; + struct mScriptTypeClass* cls; void* opaque; } details; void (*alloc)(struct mScriptValue*); @@ -339,6 +411,9 @@ struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScri void mScriptFrameInit(struct mScriptFrame* frame); void mScriptFrameDeinit(struct mScriptFrame* frame); +void mScriptClassInit(struct mScriptTypeClass* cls); +void mScriptClassDeinit(struct mScriptTypeClass* cls); + bool mScriptPopS32(struct mScriptList* list, int32_t* out); bool mScriptPopU32(struct mScriptList* list, uint32_t* out); bool mScriptPopF32(struct mScriptList* list, float* out); diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index ff4e4146f..aa7cc1562 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCE_FILES types.c) set(TEST_FILES + test/classes.c test/types.c) if(USE_LUA) diff --git a/src/script/test/classes.c b/src/script/test/classes.c new file mode 100644 index 000000000..1135342cb --- /dev/null +++ b/src/script/test/classes.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2013-2022 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 + +struct TestA { + int32_t i; + int32_t i2; + int8_t b8; + int16_t hUnaligned; +}; + +#define MEMBER_A_DOCSTRING "Member a" + +mSCRIPT_DEFINE_STRUCT(TestA) + mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) + mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S32, i) + mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S32, i2) + mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S8, b8) + mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S16, hUnaligned) + + mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) + mSCRIPT_DEFINE_STATIC_MEMBER(S32, i) + mSCRIPT_DEFINE_STATIC_MEMBER(S32, i2) + mSCRIPT_DEFINE_STATIC_MEMBER(S8, b8) + mSCRIPT_DEFINE_STATIC_MEMBER(S16, hUnaligned) +mSCRIPT_DEFINE_END + +mSCRIPT_EXPORT_STRUCT(TestA); + +M_TEST_DEFINE(testALayout) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; + assert_false(cls->init); + mScriptClassInit(cls); + assert_true(cls->init); + + struct mScriptClassMember* member; + + // Instance members + member = HashTableLookup(&cls->instanceMembers, "i"); + assert_non_null(member); + assert_string_equal(member->name, "i"); + assert_string_equal(member->docstring, MEMBER_A_DOCSTRING); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); + assert_int_equal(member->offset, 0); + + member = HashTableLookup(&cls->instanceMembers, "i2"); + assert_non_null(member); + assert_string_equal(member->name, "i2"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); + assert_int_equal(member->offset, sizeof(int32_t)); + + member = HashTableLookup(&cls->instanceMembers, "b8"); + assert_non_null(member); + assert_string_equal(member->name, "b8"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S8); + assert_int_equal(member->offset, sizeof(int32_t) * 2); + + member = HashTableLookup(&cls->instanceMembers, "hUnaligned"); + assert_non_null(member); + assert_string_equal(member->name, "hUnaligned"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); + assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); + + member = HashTableLookup(&cls->instanceMembers, "unknown"); + assert_null(member); + + // Static members + member = HashTableLookup(&cls->staticMembers, "i"); + assert_non_null(member); + assert_string_equal(member->name, "i"); + assert_string_equal(member->docstring, MEMBER_A_DOCSTRING); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); + assert_int_equal(member->offset, 0); + + member = HashTableLookup(&cls->staticMembers, "i2"); + assert_non_null(member); + assert_string_equal(member->name, "i2"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); + assert_int_equal(member->offset, sizeof(int32_t)); + + member = HashTableLookup(&cls->staticMembers, "b8"); + assert_non_null(member); + assert_string_equal(member->name, "b8"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S8); + assert_int_equal(member->offset, sizeof(int32_t) * 2); + + member = HashTableLookup(&cls->staticMembers, "hUnaligned"); + assert_non_null(member); + assert_string_equal(member->name, "hUnaligned"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); + assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); + + member = HashTableLookup(&cls->staticMembers, "unknown"); + assert_null(member); + + mScriptClassDeinit(cls); + assert_false(cls->init); +} + +M_TEST_SUITE_DEFINE(mScriptClasses, + cmocka_unit_test(testALayout)) diff --git a/src/script/test/types.c b/src/script/test/types.c index caeb8cd05..b3806bfa6 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -12,6 +12,9 @@ struct Test { int32_t a; }; +mSCRIPT_DEFINE_STRUCT(Test) +mSCRIPT_DEFINE_END + mSCRIPT_EXPORT_STRUCT(Test); static int voidOne(void) { diff --git a/src/script/types.c b/src/script/types.c index cba832563..bc2ffcae0 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -8,6 +8,8 @@ #include #include +#define MAX_ALIGNMENT 8 + static void _allocTable(struct mScriptValue*); static void _freeTable(struct mScriptValue*); static void _deinitTableValue(void*); @@ -738,6 +740,73 @@ void mScriptFrameDeinit(struct mScriptFrame* frame) { mScriptListDeinit(&frame->arguments); } +void mScriptClassInit(struct mScriptTypeClass* cls) { + if (cls->init) { + return; + } + HashTableInit(&cls->staticMembers, 0, free); + HashTableInit(&cls->instanceMembers, 0, free); + size_t staticOffset = 0; + const char* docstring = NULL; + + size_t i; + for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { + const struct mScriptClassInitDetails* details = &cls->details[i]; + struct mScriptClassMember* member; + + switch (details->type) { + case mSCRIPT_CLASS_INIT_END: + break; + case mSCRIPT_CLASS_INIT_DOCSTRING: + docstring = details->info.comment; + break; + case mSCRIPT_CLASS_INIT_INHERIT: + // TODO + abort(); + break; + case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER: + member = calloc(1, sizeof(*member)); + memcpy(member, &details->info.member, sizeof(*member)); + if (docstring) { + member->docstring = docstring; + docstring = NULL; + } + HashTableInsert(&cls->instanceMembers, member->name, member); + break; + case mSCRIPT_CLASS_INIT_STATIC_MEMBER: + member = calloc(1, sizeof(*member)); + memcpy(member, &details->info.member, sizeof(*member)); + if (docstring) { + member->docstring = docstring; + docstring = NULL; + } + + // Alignment check + if (staticOffset & (details->info.member.type->size - 1)) { + size_t size = details->info.member.type->size; + if (size > MAX_ALIGNMENT) { + size = MAX_ALIGNMENT; + } + staticOffset = (staticOffset & ~(size - 1)) + size; + } + member->offset = staticOffset; + staticOffset += details->info.member.type->size; + HashTableInsert(&cls->staticMembers, member->name, member); + break; + } + } + cls->init = true; +} + +void mScriptClassDeinit(struct mScriptTypeClass* cls) { + if (!cls->init) { + return; + } + HashTableDeinit(&cls->instanceMembers); + HashTableDeinit(&cls->staticMembers); + cls->init = false; +} + bool mScriptPopS32(struct mScriptList* list, int32_t* out) { mSCRIPT_POP(list, S32, val); *out = val; From 1a6fa25a3395c0024f3ada8d2b6d937dcfe9b058 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 28 Apr 2022 15:44:50 -0700 Subject: [PATCH 021/105] Scripting: Add struct dynamic dispatch binding --- include/mgba/script/types.h | 120 ++++++++++++++++++++++-- src/script/test/classes.c | 181 ++++++++++++++++++++++++++++++++---- src/script/test/types.c | 2 +- src/script/types.c | 78 ++++++++++++++++ 4 files changed, 356 insertions(+), 25 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 58ba0b0fe..faf6972b6 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -13,7 +13,13 @@ CXX_GUARD_START #include #include +#define _mCPP_EMPTY() +#define _mCPP_CAT(A, B) A ## B + +#define _mDEFER(X) X _mCPP_EMPTY() +#define _mBLOCK(...) __VA_ARGS__ _mDEFER(_mCPP_EMPTY)() #define _mAPPLY(...) __VA_ARGS__ +#define _mCAT(A, B) _mCPP_CAT(A, B) #define mSCRIPT_VALUE_UNREF -1 #define mSCRIPT_PARAMS_MAX 8 @@ -34,6 +40,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_TABLE Table* #define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue* #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* +#define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME #define mSCRIPT_TYPE_FIELD_S8 s32 #define mSCRIPT_TYPE_FIELD_U8 s32 @@ -51,6 +58,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_TABLE opaque #define mSCRIPT_TYPE_FIELD_WRAPPER opaque #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque +#define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) copaque #define mSCRIPT_TYPE_MS_S8 (&mSTSInt8) #define mSCRIPT_TYPE_MS_U8 (&mSTUInt8) @@ -67,6 +75,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_TABLE (&mSTTable) #define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper) #define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) +#define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME) #define _mSCRIPT_FIELD_NAME(V) (V)->name @@ -85,6 +94,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE) #define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE) #define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME +#define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) _mAPPLY(mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1)) #define mSCRIPT_POP(STACK, TYPE, NAME) \ @@ -116,6 +126,15 @@ CXX_GUARD_START #define mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) mSCRIPT_POP(FRAME, T6, p6); mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) #define mSCRIPT_POP_8(FRAME, T0, T1, T2, T3, T4, T5, T6, T7) mSCRIPT_POP(FRAME, T7, p7); mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) +#define _mCOMMA_0(N, ...) N +#define _mCOMMA_1(N, ...) N, __VA_ARGS__ +#define _mCOMMA_2(N, ...) N, __VA_ARGS__ +#define _mCOMMA_3(N, ...) N, __VA_ARGS__ +#define _mCOMMA_4(N, ...) N, __VA_ARGS__ +#define _mCOMMA_5(N, ...) N, __VA_ARGS__ +#define _mCOMMA_6(N, ...) N, __VA_ARGS__ +#define _mCOMMA_7(N, ...) N, __VA_ARGS__ + #define mSCRIPT_PUSH(STACK, TYPE, NAME) \ do { \ struct mScriptValue* _val = mScriptListAppend(STACK); \ @@ -143,10 +162,29 @@ CXX_GUARD_START #define mSCRIPT_PREFIX_6(PREFIX, T0, T1, T2, T3, T4, T5) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5 #define mSCRIPT_PREFIX_7(PREFIX, T0, T1, T2, T3, T4, T5, T6) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6 #define mSCRIPT_PREFIX_8(PREFIX, T0, T1, T2, T3, T4, T5, T6, T7) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6, PREFIX ## T7 +#define mSCRIPT_PREFIX_N(N) _mAPPLY(mSCRIPT_PREFIX_ ## N) -#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(mSCRIPT_ARG_NAMES_ ## NPARAMS) +#define _mSUCC0 1 +#define _mSUCC1 2 +#define _mSUCC2 3 +#define _mSUCC3 4 +#define _mSUCC4 5 +#define _mSUCC5 6 +#define _mSUCC6 7 +#define _mSUCC7 8 + +#define _mPREC1 0 +#define _mPREC2 1 +#define _mPREC3 2 +#define _mPREC4 3 +#define _mPREC5 4 +#define _mPREC6 5 +#define _mPREC7 6 +#define _mPREC8 7 + +#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) #define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ - _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) out = FUNCTION(mSCRIPT_ARG_NAMES_ ## NPARAMS); \ + _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ mSCRIPT_PUSH(&frame->returnValues, RETURN, out) #define mSCRIPT_EXPORT_STRUCT(STRUCT) \ @@ -161,8 +199,9 @@ CXX_GUARD_START .free = NULL, \ } -#define mSCRIPT_DECLARE_STRUCT(STRUCT) extern const struct mScriptType mSTStruct_ ## STRUCT; +#define mSCRIPT_DECLARE_STRUCT(STRUCT) extern const struct mScriptType mSTStruct_ ## STRUCT #define mSCRIPT_DEFINE_STRUCT(STRUCT) \ + const struct mScriptType mSTStruct_ ## STRUCT; \ static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ .init = false, \ .details = (const struct mScriptClassInitDetails[]) { @@ -180,7 +219,7 @@ CXX_GUARD_START .member = { \ .name = #NAME, \ .type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE), \ - .offset = offsetof(STRUCT, NAME) \ + .offset = offsetof(struct STRUCT, NAME) \ } \ } \ }, @@ -195,7 +234,72 @@ CXX_GUARD_START }, \ }, -#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } }; +#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, NPARAMS, ...) \ + _mDEFER(_mDEFER(_mCAT(mSCRIPT_POP_, _mSUCC ## NPARAMS)) (&frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__))); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } + +#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, NRET, RETURN, NPARAMS, ...) \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \ + static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \ + .call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \ + }; \ + \ + static void _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME(struct mScriptValue* val) { \ + val->value.copaque = &_mSTStructBindingFunction_ ## TYPE ## _ ## NAME; \ + }\ + static const struct mScriptType _mSTStructBindingType_ ## TYPE ## _ ## NAME = { \ + .base = mSCRIPT_TYPE_FUNCTION, \ + .name = "struct::" #TYPE "." #NAME, \ + .alloc = _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME, \ + .details = { \ + .function = { \ + .parameters = { \ + .count = _mSUCC ## NPARAMS, \ + .entries = { _mAPPLY(mSCRIPT_TYPE_MS_S(TYPE)), mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__) } \ + }, \ + .returnType = { \ + .count = NRET, \ + .entries = { RETURN } \ + }, \ + }, \ + } \ + }; + +#define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ + typedef _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ + \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, NPARAMS, __VA_ARGS__); \ + _mSCRIPT_CALL(RETURN, p0->NAME, _mSUCC ## NPARAMS); \ + return true; \ + } \ + +#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, NPARAMS, ...) \ + typedef void (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, 0, , NPARAMS, __VA_ARGS__) \ + \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, NPARAMS, __VA_ARGS__); \ + _mSCRIPT_CALL_VOID(p0->NAME, _mSUCC ## NPARAMS); \ + return true; \ + } \ + +#define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) { \ + .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ + .info = { \ + .member = { \ + .name = #NAME, \ + .type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME \ + } \ + }, \ +}, + +#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } #define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ static const struct mScriptType _type_ ## NAME = { \ @@ -220,7 +324,7 @@ CXX_GUARD_START .type = &_type_ ## NAME, \ .refs = mSCRIPT_VALUE_UNREF, \ .value = { \ - .opaque = &_function_ ## NAME \ + .copaque = &_function_ ## NAME \ } \ } @@ -267,6 +371,7 @@ CXX_GUARD_START #define mSCRIPT_MAKE_U64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U64, u64, VALUE) #define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F64, f64, VALUE) #define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CHARP, opaque, VALUE) +#define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S(STRUCT), opaque, VALUE) enum { mSCRIPT_TYPE_VOID = 0, @@ -373,6 +478,7 @@ struct mScriptValue { uint64_t u64; double f64; void* opaque; + const void* copaque; } value; }; @@ -414,6 +520,8 @@ void mScriptFrameDeinit(struct mScriptFrame* frame); void mScriptClassInit(struct mScriptTypeClass* cls); void mScriptClassDeinit(struct mScriptTypeClass* cls); +bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScriptValue*); + bool mScriptPopS32(struct mScriptList* list, int32_t* out); bool mScriptPopU32(struct mScriptList* list, uint32_t* out); bool mScriptPopF32(struct mScriptList* list, float* out); diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 1135342cb..4760f100c 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -13,23 +13,53 @@ struct TestA { int32_t i2; int8_t b8; int16_t hUnaligned; + int32_t (*ifn0)(struct TestA*); + int32_t (*ifn1)(struct TestA*, int); + void (*vfn0)(struct TestA*); + void (*vfn1)(struct TestA*, int); }; +static int32_t testAi0(struct TestA* a) { + return a->i; +} + +static int32_t testAi1(struct TestA* a, int b) { + return a->i + b; +} + +static void testAv0(struct TestA* a) { + ++a->i; +} + +static void testAv1(struct TestA* a, int b) { + a->i += b; +} + #define MEMBER_A_DOCSTRING "Member a" +mSCRIPT_DECLARE_STRUCT(TestA); +mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, ifn0, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, ifn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, vfn0, 0); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, vfn1, 1, S32); + mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) - mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S32, i) - mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S32, i2) - mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S8, b8) - mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S16, hUnaligned) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S32, i) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S32, i2) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S8, b8) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S16, hUnaligned) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn0) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn1) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, vfn0) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, vfn1) mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) - mSCRIPT_DEFINE_STATIC_MEMBER(S32, i) - mSCRIPT_DEFINE_STATIC_MEMBER(S32, i2) - mSCRIPT_DEFINE_STATIC_MEMBER(S8, b8) - mSCRIPT_DEFINE_STATIC_MEMBER(S16, hUnaligned) -mSCRIPT_DEFINE_END + mSCRIPT_DEFINE_STATIC_MEMBER(S32, s_i) + mSCRIPT_DEFINE_STATIC_MEMBER(S32, s_i2) + mSCRIPT_DEFINE_STATIC_MEMBER(S8, s_b8) + mSCRIPT_DEFINE_STATIC_MEMBER(S16, s_hUnaligned) +mSCRIPT_DEFINE_END; mSCRIPT_EXPORT_STRUCT(TestA); @@ -74,30 +104,30 @@ M_TEST_DEFINE(testALayout) { assert_null(member); // Static members - member = HashTableLookup(&cls->staticMembers, "i"); + member = HashTableLookup(&cls->staticMembers, "s_i"); assert_non_null(member); - assert_string_equal(member->name, "i"); + assert_string_equal(member->name, "s_i"); assert_string_equal(member->docstring, MEMBER_A_DOCSTRING); assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); assert_int_equal(member->offset, 0); - member = HashTableLookup(&cls->staticMembers, "i2"); + member = HashTableLookup(&cls->staticMembers, "s_i2"); assert_non_null(member); - assert_string_equal(member->name, "i2"); + assert_string_equal(member->name, "s_i2"); assert_null(member->docstring); assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); assert_int_equal(member->offset, sizeof(int32_t)); - member = HashTableLookup(&cls->staticMembers, "b8"); + member = HashTableLookup(&cls->staticMembers, "s_b8"); assert_non_null(member); - assert_string_equal(member->name, "b8"); + assert_string_equal(member->name, "s_b8"); assert_null(member->docstring); assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S8); assert_int_equal(member->offset, sizeof(int32_t) * 2); - member = HashTableLookup(&cls->staticMembers, "hUnaligned"); + member = HashTableLookup(&cls->staticMembers, "s_hUnaligned"); assert_non_null(member); - assert_string_equal(member->name, "hUnaligned"); + assert_string_equal(member->name, "s_hUnaligned"); assert_null(member->docstring); assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); @@ -109,5 +139,120 @@ M_TEST_DEFINE(testALayout) { assert_false(cls->init); } +M_TEST_DEFINE(testATranslation) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; + + struct TestA s = { + .i = 1, + .i2 = 2, + .b8 = 3, + .hUnaligned = 4 + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s); + struct mScriptValue val; + struct mScriptValue compare; + + compare = mSCRIPT_MAKE_S32(1); + assert_true(mScriptObjectGet(&sval, "i", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(2); + assert_true(mScriptObjectGet(&sval, "i2", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(3); + assert_true(mScriptObjectGet(&sval, "b8", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(4); + assert_true(mScriptObjectGet(&sval, "hUnaligned", &val)); + assert_true(compare.type->equal(&compare, &val)); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + +M_TEST_DEFINE(testAFunctions) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; + assert_false(cls->init); + mScriptClassInit(cls); + assert_true(cls->init); + + struct mScriptClassMember* member; + + // Instance methods + member = HashTableLookup(&cls->instanceMembers, "ifn0"); + assert_non_null(member); + assert_string_equal(member->name, "ifn0"); + assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION); + assert_int_equal(member->type->details.function.parameters.count, 1); + assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA)); + + struct TestA s = { + .i = 1, + .ifn0 = testAi0, + .ifn1 = testAi1, + .vfn0 = testAv0, + .vfn1 = testAv1, + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s); + struct mScriptValue val; + struct mScriptFrame frame; + int32_t rval; + + assert_true(mScriptObjectGet(&sval, "ifn0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 1); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "ifn1", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "vfn0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "ifn0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "vfn1", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 2); + assert_true(mScriptInvoke(&val, &frame)); + mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "ifn0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 4); + mScriptFrameDeinit(&frame); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_SUITE_DEFINE(mScriptClasses, - cmocka_unit_test(testALayout)) + cmocka_unit_test(testALayout), + cmocka_unit_test(testATranslation), + cmocka_unit_test(testAFunctions)) diff --git a/src/script/test/types.c b/src/script/test/types.c index b3806bfa6..f7910a623 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -13,7 +13,7 @@ struct Test { }; mSCRIPT_DEFINE_STRUCT(Test) -mSCRIPT_DEFINE_END +mSCRIPT_DEFINE_END; mSCRIPT_EXPORT_STRUCT(Test); diff --git a/src/script/types.c b/src/script/types.c index bc2ffcae0..9b2aa9f0b 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -807,6 +807,84 @@ void mScriptClassDeinit(struct mScriptTypeClass* cls) { cls->init = false; } +bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScriptValue* val) { + if (obj->type->base != mSCRIPT_TYPE_OBJECT) { + return false; + } + + struct mScriptTypeClass* cls = obj->type->details.cls; + if (!cls) { + return false; + } + + mScriptClassInit(cls); + + struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member); + if (!m) { + return false; + } + + void* rawMember = (void *)((uintptr_t) obj->value.opaque + m->offset); + switch (m->type->base) { + case mSCRIPT_TYPE_SINT: + switch (m->type->size) { + case 1: + *val = mSCRIPT_MAKE_S32(*(int8_t *) rawMember); + break; + case 2: + *val = mSCRIPT_MAKE_S32(*(int16_t *) rawMember); + break; + case 4: + *val = mSCRIPT_MAKE_S32(*(int32_t *) rawMember); + break; + case 8: + *val = mSCRIPT_MAKE_S64(*(int64_t *) rawMember); + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_UINT: + switch (m->type->size) { + case 1: + *val = mSCRIPT_MAKE_U32(*(uint8_t *) rawMember); + break; + case 2: + *val = mSCRIPT_MAKE_U32(*(uint16_t *) rawMember); + break; + case 4: + *val = mSCRIPT_MAKE_U32(*(uint32_t *) rawMember); + break; + case 8: + *val = mSCRIPT_MAKE_U64(*(uint64_t *) rawMember); + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_FLOAT: + switch (m->type->size) { + case 4: + *val = mSCRIPT_MAKE_F32(*(mSCRIPT_TYPE_C_F32 *) rawMember); + break; + case 8: + *val = mSCRIPT_MAKE_F64(*(mSCRIPT_TYPE_C_F64 *) rawMember); + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_FUNCTION: + val->refs = mSCRIPT_VALUE_UNREF; + val->type = m->type; + m->type->alloc(val); + break; + default: + return false; + } + return true; +} + bool mScriptPopS32(struct mScriptList* list, int32_t* out) { mSCRIPT_POP(list, S32, val); *out = val; From e912450e85fc65b744e1f248ae2d2ff90136cf5b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 28 Apr 2022 19:37:09 -0700 Subject: [PATCH 022/105] Scripting: Attempt to add const structs --- include/mgba/script/types.h | 71 ++++++++++++++++++++++++++++++----- src/script/test/classes.c | 72 ++++++++++++++++++++++++++++++++++-- src/script/test/types.c | 74 +++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 14 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index faf6972b6..c8d3ee378 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -40,6 +40,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_TABLE Table* #define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue* #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* +#define mSCRIPT_TYPE_C_CS(STRUCT) const struct STRUCT* #define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME #define mSCRIPT_TYPE_FIELD_S8 s32 @@ -58,6 +59,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_TABLE opaque #define mSCRIPT_TYPE_FIELD_WRAPPER opaque #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque +#define mSCRIPT_TYPE_FIELD_CS(STRUCT) copaque #define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) copaque #define mSCRIPT_TYPE_MS_S8 (&mSTSInt8) @@ -75,6 +77,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_TABLE (&mSTTable) #define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper) #define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) +#define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT) #define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME) #define _mSCRIPT_FIELD_NAME(V) (V)->name @@ -94,6 +97,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE) #define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE) #define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME +#define mSCRIPT_TYPE_CMP_CS(STRUCT) mSCRIPT_TYPE_MS_CS(STRUCT)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) _mAPPLY(mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1)) @@ -188,6 +192,15 @@ CXX_GUARD_START mSCRIPT_PUSH(&frame->returnValues, RETURN, out) #define mSCRIPT_EXPORT_STRUCT(STRUCT) \ + mSCRIPT_DECLARE_STRUCT(STRUCT) \ + static bool _mSTStructCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \ + if (input->type == type || (input->type == &mSTStruct_ ## STRUCT && type == &mSTStructConst_ ## STRUCT)) { \ + output->type = type; \ + output->value.opaque = input->value.opaque; \ + return true; \ + } \ + return false; \ + } \ const struct mScriptType mSTStruct_ ## STRUCT = { \ .base = mSCRIPT_TYPE_OBJECT, \ .details = { \ @@ -197,11 +210,27 @@ CXX_GUARD_START .name = "struct::" #STRUCT, \ .alloc = NULL, \ .free = NULL, \ + .cast = _mSTStructCast_ ## STRUCT, \ + }; \ + const struct mScriptType mSTStructConst_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OBJECT, \ + .details = { \ + .cls = &_mSTStructDetails_ ## STRUCT \ + }, \ + .size = sizeof(struct STRUCT), \ + .name = "const struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = _mSTStructCast_ ## STRUCT, \ } -#define mSCRIPT_DECLARE_STRUCT(STRUCT) extern const struct mScriptType mSTStruct_ ## STRUCT +#define mSCRIPT_DECLARE_STRUCT(STRUCT) \ + extern const struct mScriptType mSTStruct_ ## STRUCT; \ + extern const struct mScriptType mSTStructConst_ ## STRUCT; + #define mSCRIPT_DEFINE_STRUCT(STRUCT) \ const struct mScriptType mSTStruct_ ## STRUCT; \ + const struct mScriptType mSTStructConst_ ## STRUCT; \ static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ .init = false, \ .details = (const struct mScriptClassInitDetails[]) { @@ -234,13 +263,13 @@ CXX_GUARD_START }, \ }, -#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, NPARAMS, ...) \ +#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \ _mDEFER(_mDEFER(_mCAT(mSCRIPT_POP_, _mSUCC ## NPARAMS)) (&frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__))); \ if (mScriptListSize(&frame->arguments)) { \ return false; \ } -#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, NRET, RETURN, NPARAMS, ...) \ +#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, ...) \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \ static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \ .call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \ @@ -257,7 +286,7 @@ CXX_GUARD_START .function = { \ .parameters = { \ .count = _mSUCC ## NPARAMS, \ - .entries = { _mAPPLY(mSCRIPT_TYPE_MS_S(TYPE)), mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__) } \ + .entries = { _mAPPLY(mSCRIPT_TYPE_MS_ ## S(TYPE)), mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__) } \ }, \ .returnType = { \ .count = NRET, \ @@ -267,24 +296,46 @@ CXX_GUARD_START } \ }; -#define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ +#define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ typedef _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, NPARAMS, __VA_ARGS__); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, __VA_ARGS__); \ _mSCRIPT_CALL(RETURN, p0->NAME, _mSUCC ## NPARAMS); \ return true; \ } \ -#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, NPARAMS, ...) \ +#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TYPE, NAME, NPARAMS, ...) \ typedef void (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, 0, , NPARAMS, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, NPARAMS, __VA_ARGS__); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, __VA_ARGS__); \ + _mSCRIPT_CALL_VOID(p0->NAME, _mSUCC ## NPARAMS); \ + return true; \ + } \ + +#define mSCRIPT_DECLARE_STRUCT_CD_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ + typedef _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ + \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, __VA_ARGS__); \ + _mSCRIPT_CALL(RETURN, p0->NAME, _mSUCC ## NPARAMS); \ + return true; \ + } \ + +#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ + typedef void (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, __VA_ARGS__) \ + \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, __VA_ARGS__); \ _mSCRIPT_CALL_VOID(p0->NAME, _mSUCC ## NPARAMS); \ return true; \ } \ diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 4760f100c..4ee15fbf0 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -17,6 +17,8 @@ struct TestA { int32_t (*ifn1)(struct TestA*, int); void (*vfn0)(struct TestA*); void (*vfn1)(struct TestA*, int); + int32_t (*icfn0)(const struct TestA*); + int32_t (*icfn1)(const struct TestA*, int); }; static int32_t testAi0(struct TestA* a) { @@ -27,6 +29,14 @@ static int32_t testAi1(struct TestA* a, int b) { return a->i + b; } +static int32_t testAic0(const struct TestA* a) { + return a->i; +} + +static int32_t testAic1(const struct TestA* a, int b) { + return a->i + b; +} + static void testAv0(struct TestA* a) { ++a->i; } @@ -38,10 +48,12 @@ static void testAv1(struct TestA* a, int b) { #define MEMBER_A_DOCSTRING "Member a" mSCRIPT_DECLARE_STRUCT(TestA); -mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, ifn0, 0); -mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, ifn1, 1, S32); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, vfn0, 0); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, vfn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_D_METHOD(TestA, S32, ifn0, 0); +mSCRIPT_DECLARE_STRUCT_D_METHOD(TestA, S32, ifn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn0, 0); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn0, 0); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn1, 1, S32); mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) @@ -51,6 +63,8 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S16, hUnaligned) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn0) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn1) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, icfn0) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, icfn1) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, vfn0) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, vfn1) @@ -194,6 +208,8 @@ M_TEST_DEFINE(testAFunctions) { .i = 1, .ifn0 = testAi0, .ifn1 = testAi1, + .icfn0 = testAic0, + .icfn1 = testAic1, .vfn0 = testAv0, .vfn1 = testAv1, }; @@ -220,6 +236,40 @@ M_TEST_DEFINE(testAFunctions) { assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "icfn0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 1); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "icfn0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 1); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "icfn1", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "icfn1", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "vfn0", &val)); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); @@ -232,6 +282,13 @@ M_TEST_DEFINE(testAFunctions) { assert_true(mScriptPopS32(&frame.returnValues, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "icfn0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "vfn1", &val)); mScriptFrameInit(&frame); @@ -246,6 +303,13 @@ M_TEST_DEFINE(testAFunctions) { assert_true(mScriptPopS32(&frame.returnValues, &rval)); assert_int_equal(rval, 4); mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "icfn0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 4); + mScriptFrameDeinit(&frame); assert_true(cls->init); mScriptClassDeinit(cls); diff --git a/src/script/test/types.c b/src/script/test/types.c index f7910a623..c0cca0273 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -260,6 +260,79 @@ M_TEST_DEFINE(wrongPopSize) { mScriptFrameDeinit(&frame); } +bool mScriptPopCSTest(struct mScriptList* list, const struct Test** out) { + mSCRIPT_POP(list, CS(Test), val); + *out = val; + return true; +} + +bool mScriptPopSTest(struct mScriptList* list, struct Test** out) { + mSCRIPT_POP(list, S(Test), val); + *out = val; + return true; +} + +M_TEST_DEFINE(wrongConst) { + struct mScriptFrame frame; + struct Test a; + struct Test* b; + const struct Test* cb; + struct mScriptTypeTuple signature = { + .count = 1, + .variable = false + }; + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(Test), &a); + signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); + assert_true(mScriptCoerceFrame(&signature, &frame.arguments)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); + signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); + assert_true(mScriptCoerceFrame(&signature, &frame.arguments)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(Test), &a); + signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); + assert_true(mScriptCoerceFrame(&signature, &frame.arguments)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); + signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); + assert_false(mScriptCoerceFrame(&signature, &frame.arguments)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(Test), &a); + assert_true(mScriptPopSTest(&frame.arguments, &b)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(Test), &a); + assert_false(mScriptPopCSTest(&frame.arguments, &cb)); + signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); + assert_true(mScriptCoerceFrame(&signature, &frame.arguments)); + assert_true(mScriptPopCSTest(&frame.arguments, &cb)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); + assert_false(mScriptPopSTest(&frame.arguments, &b)); + signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); + assert_false(mScriptCoerceFrame(&signature, &frame.arguments)); + assert_false(mScriptPopSTest(&frame.arguments, &b)); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); + assert_true(mScriptPopCSTest(&frame.arguments, &cb)); + mScriptFrameDeinit(&frame); +} + M_TEST_DEFINE(coerceToFloat) { struct mScriptFrame frame; mScriptFrameInit(&frame); @@ -858,6 +931,7 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(wrongArgType), cmocka_unit_test(wrongPopType), cmocka_unit_test(wrongPopSize), + cmocka_unit_test(wrongConst), cmocka_unit_test(coerceToFloat), cmocka_unit_test(coerceFromFloat), cmocka_unit_test(coerceNarrow), From 3179419f5255dea8e3111124ef6d198debf3848c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 28 Apr 2022 21:49:39 -0700 Subject: [PATCH 023/105] Scripting: Add static dispatch to classes --- include/mgba/script/types.h | 29 +++++--- src/script/test/classes.c | 129 +++++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 10 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index c8d3ee378..b41c45bd9 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -296,50 +296,63 @@ CXX_GUARD_START } \ }; -#define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ +#define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ typedef _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, __VA_ARGS__); \ - _mSCRIPT_CALL(RETURN, p0->NAME, _mSUCC ## NPARAMS); \ + _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC ## NPARAMS); \ return true; \ } \ -#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TYPE, NAME, NPARAMS, ...) \ +#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ typedef void (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, __VA_ARGS__); \ - _mSCRIPT_CALL_VOID(p0->NAME, _mSUCC ## NPARAMS); \ + _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC ## NPARAMS); \ return true; \ } \ -#define mSCRIPT_DECLARE_STRUCT_CD_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ +#define mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ typedef _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, __VA_ARGS__); \ - _mSCRIPT_CALL(RETURN, p0->NAME, _mSUCC ## NPARAMS); \ + _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC ## NPARAMS); \ return true; \ } \ -#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ +#define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ typedef void (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, __VA_ARGS__); \ - _mSCRIPT_CALL_VOID(p0->NAME, _mSUCC ## NPARAMS); \ + _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC ## NPARAMS); \ return true; \ } \ + +#define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TYPE, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_CD_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + #define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) { \ .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ .info = { \ diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 4ee15fbf0..97f95d0d1 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -54,6 +54,12 @@ mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn0, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn1, 1, S32); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn0, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, i0, testAi0, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, i1, testAi1, 1, S32); +mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic0, testAic0, 0); +mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic1, testAic1, 1, S32); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v0, testAv0, 0); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v1, testAv1, 1, S32); mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) @@ -67,6 +73,12 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, icfn1) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, vfn0) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, vfn1) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, i0) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, i1) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ic0) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ic1) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v0) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v1) mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) mSCRIPT_DEFINE_STATIC_MEMBER(S32, s_i) @@ -188,7 +200,119 @@ M_TEST_DEFINE(testATranslation) { assert_false(cls->init); } -M_TEST_DEFINE(testAFunctions) { +M_TEST_DEFINE(testAStatic) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; + assert_false(cls->init); + mScriptClassInit(cls); + assert_true(cls->init); + + struct TestA s = { + .i = 1, + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s); + struct mScriptValue val; + struct mScriptFrame frame; + int32_t rval; + + assert_true(mScriptObjectGet(&sval, "i0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 1); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "i1", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "ic0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 1); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "ic0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 1); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "ic1", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "ic1", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "v0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "i0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "ic0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 2); + mScriptFrameDeinit(&frame); + + assert_true(mScriptObjectGet(&sval, "v1", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 2); + assert_true(mScriptInvoke(&val, &frame)); + mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "i0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 4); + mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "ic0", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + assert_true(mScriptInvoke(&val, &frame)); + assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_int_equal(rval, 4); + mScriptFrameDeinit(&frame); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + +M_TEST_DEFINE(testADynamic) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); mScriptClassInit(cls); @@ -319,4 +443,5 @@ M_TEST_DEFINE(testAFunctions) { M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), cmocka_unit_test(testATranslation), - cmocka_unit_test(testAFunctions)) + cmocka_unit_test(testAStatic), + cmocka_unit_test(testADynamic)) From eadc0c677bb87e314be5b2eca949625fb8d0e11d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 28 Apr 2022 22:12:12 -0700 Subject: [PATCH 024/105] Scripting: Minor code cleanup --- include/mgba/script/types.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index b41c45bd9..73d85bf48 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -177,15 +177,6 @@ CXX_GUARD_START #define _mSUCC6 7 #define _mSUCC7 8 -#define _mPREC1 0 -#define _mPREC2 1 -#define _mPREC3 2 -#define _mPREC4 3 -#define _mPREC5 4 -#define _mPREC6 5 -#define _mPREC7 6 -#define _mPREC8 7 - #define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) #define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ @@ -460,7 +451,6 @@ enum mScriptClassInitType { mSCRIPT_CLASS_INIT_INHERIT, }; -struct Table; struct mScriptType; extern const struct mScriptType mSTVoid; extern const struct mScriptType mSTSInt8; From b0567832f8d01b2ee84ce9d4821f1eb5bcbb248d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 29 Apr 2022 00:32:06 -0700 Subject: [PATCH 025/105] Scripting: More const bringup --- include/mgba/script/types.h | 8 ++++++-- src/script/types.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 73d85bf48..f52ace715 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -205,6 +205,7 @@ CXX_GUARD_START }; \ const struct mScriptType mSTStructConst_ ## STRUCT = { \ .base = mSCRIPT_TYPE_OBJECT, \ + .isConst = true, \ .details = { \ .cls = &_mSTStructDetails_ ## STRUCT \ }, \ @@ -427,8 +428,9 @@ CXX_GUARD_START #define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F64, f64, VALUE) #define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CHARP, opaque, VALUE) #define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S(STRUCT), opaque, VALUE) +#define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CS(STRUCT), copaque, VALUE) -enum { +enum mScriptTypeBase { mSCRIPT_TYPE_VOID = 0, mSCRIPT_TYPE_SINT, mSCRIPT_TYPE_UINT, @@ -505,7 +507,9 @@ struct mScriptTypeClass { struct mScriptValue; struct mScriptType { - int base; + enum mScriptTypeBase base : 8; + bool isConst; + size_t size; const char* name; union { diff --git a/src/script/types.c b/src/script/types.c index 9b2aa9f0b..aa14c3e4a 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -255,6 +255,7 @@ uint32_t _hashScalar(const struct mScriptValue* val) { x = val->value.s32; break; case mSCRIPT_TYPE_UINT: + default: x = val->value.u32; break; } From 9ddada00f2a682f0bd2688d46bab5b5e422cb2c9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 29 Apr 2022 00:32:25 -0700 Subject: [PATCH 026/105] Scripting: Add struct setter --- include/mgba/script/types.h | 1 + src/script/test/classes.c | 57 +++++++++++++++++++++++++- src/script/types.c | 79 +++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 2 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index f52ace715..37777abe4 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -579,6 +579,7 @@ void mScriptClassInit(struct mScriptTypeClass* cls); void mScriptClassDeinit(struct mScriptTypeClass* cls); bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScriptValue*); +bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue*); bool mScriptPopS32(struct mScriptList* list, int32_t* out); bool mScriptPopU32(struct mScriptList* list, uint32_t* out); diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 97f95d0d1..a08da753b 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -165,7 +165,7 @@ M_TEST_DEFINE(testALayout) { assert_false(cls->init); } -M_TEST_DEFINE(testATranslation) { +M_TEST_DEFINE(testAGet) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; struct TestA s = { @@ -200,6 +200,58 @@ M_TEST_DEFINE(testATranslation) { assert_false(cls->init); } +M_TEST_DEFINE(testASet) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; + + struct TestA s = { + .i = 1, + .i2 = 2, + .b8 = 3, + .hUnaligned = 4 + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s); + struct mScriptValue val; + + val = mSCRIPT_MAKE_S32(2); + assert_true(mScriptObjectSet(&sval, "i", &val)); + assert_int_equal(s.i, 2); + + val = mSCRIPT_MAKE_S32(3); + assert_true(mScriptObjectSet(&sval, "i2", &val)); + assert_int_equal(s.i2, 3); + + val = mSCRIPT_MAKE_S32(4); + assert_true(mScriptObjectSet(&sval, "b8", &val)); + assert_int_equal(s.b8, 4); + + val = mSCRIPT_MAKE_S32(5); + assert_true(mScriptObjectSet(&sval, "hUnaligned", &val)); + assert_int_equal(s.hUnaligned, 5); + + sval = mSCRIPT_MAKE_CS(TestA, &s); + + val = mSCRIPT_MAKE_S32(3); + assert_false(mScriptObjectSet(&sval, "i", &val)); + assert_int_equal(s.i, 2); + + val = mSCRIPT_MAKE_S32(4); + assert_false(mScriptObjectSet(&sval, "i2", &val)); + assert_int_equal(s.i2, 3); + + val = mSCRIPT_MAKE_S32(5); + assert_false(mScriptObjectSet(&sval, "b8", &val)); + assert_int_equal(s.b8, 4); + + val = mSCRIPT_MAKE_S32(6); + assert_false(mScriptObjectSet(&sval, "hUnaligned", &val)); + assert_int_equal(s.hUnaligned, 5); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_DEFINE(testAStatic) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); @@ -442,6 +494,7 @@ M_TEST_DEFINE(testADynamic) { M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), - cmocka_unit_test(testATranslation), + cmocka_unit_test(testAGet), + cmocka_unit_test(testASet), cmocka_unit_test(testAStatic), cmocka_unit_test(testADynamic)) diff --git a/src/script/types.c b/src/script/types.c index aa14c3e4a..fd179c91f 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -886,6 +886,85 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri return true; } +bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue* val) { + if (obj->type->base != mSCRIPT_TYPE_OBJECT || obj->type->isConst) { + return false; + } + + struct mScriptTypeClass* cls = obj->type->details.cls; + if (!cls) { + return false; + } + + mScriptClassInit(cls); + + struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member); + if (!m) { + return false; + } + + void* rawMember = (void *)((uintptr_t) obj->value.opaque + m->offset); + if (m->type != val->type) { + if (!mScriptCast(m->type, val, val)) { + return false; + } + } + + switch (m->type->base) { + case mSCRIPT_TYPE_SINT: + switch (m->type->size) { + case 1: + *(int8_t *) rawMember = val->value.s32; + break; + case 2: + *(int16_t *) rawMember = val->value.s32; + break; + case 4: + *(int32_t *) rawMember = val->value.s32; + break; + case 8: + *(int64_t *) rawMember = val->value.s64; + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_UINT: + switch (m->type->size) { + case 1: + *(uint8_t *) rawMember = val->value.u32; + break; + case 2: + *(uint16_t *) rawMember = val->value.u32; + break; + case 4: + *(uint32_t *) rawMember = val->value.u32; + break; + case 8: + *(uint64_t *) rawMember = val->value.u64; + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_FLOAT: + switch (m->type->size) { + case 4: + *(mSCRIPT_TYPE_C_F32 *) rawMember = val->value.f32; + break; + case 8: + *(mSCRIPT_TYPE_C_F64 *) rawMember = val->value.f64; + break; + default: + return false; + } + break; + default: + return false; + } + return true; +} + bool mScriptPopS32(struct mScriptList* list, int32_t* out) { mSCRIPT_POP(list, S32, val); *out = val; From 7c9ea1ec9b3b4cfe85ce9d055be11814357404c9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 29 Apr 2022 16:01:45 -0700 Subject: [PATCH 027/105] Scripting: Add basic inheritance + struct struct member access + const casting --- include/mgba/script/types.h | 12 +- src/script/test/classes.c | 355 +++++++++++++++++++++++++++++++++++- src/script/types.c | 83 ++++++--- 3 files changed, 418 insertions(+), 32 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 37777abe4..f2e9a37a9 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -202,6 +202,7 @@ CXX_GUARD_START .alloc = NULL, \ .free = NULL, \ .cast = _mSTStructCast_ ## STRUCT, \ + .constType = &mSTStructConst_ ## STRUCT, \ }; \ const struct mScriptType mSTStructConst_ ## STRUCT = { \ .base = mSCRIPT_TYPE_OBJECT, \ @@ -255,6 +256,13 @@ CXX_GUARD_START }, \ }, +#define mSCRIPT_DEFINE_INHERIT(PARENT) { \ + .type = mSCRIPT_CLASS_INIT_INHERIT, \ + .info = { \ + .parent = mSCRIPT_TYPE_MS_S(PARENT) \ + } \ +}, + #define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \ _mDEFER(_mDEFER(_mCAT(mSCRIPT_POP_, _mSUCC ## NPARAMS)) (&frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__))); \ if (mScriptListSize(&frame->arguments)) { \ @@ -493,7 +501,7 @@ struct mScriptClassInitDetails { enum mScriptClassInitType type; union { const char* comment; - const struct mScriptTypeClass* parent; + const struct mScriptType* parent; struct mScriptClassMember member; } info; }; @@ -501,6 +509,7 @@ struct mScriptClassInitDetails { struct mScriptTypeClass { bool init; const struct mScriptClassInitDetails* details; + const struct mScriptType* parent; struct Table staticMembers; struct Table instanceMembers; }; @@ -518,6 +527,7 @@ struct mScriptType { struct mScriptTypeClass* cls; void* opaque; } details; + const struct mScriptType* constType; void (*alloc)(struct mScriptValue*); void (*free)(struct mScriptValue*); uint32_t (*hash)(const struct mScriptValue*); diff --git a/src/script/test/classes.c b/src/script/test/classes.c index a08da753b..272bc5fc0 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -21,6 +21,20 @@ struct TestA { int32_t (*icfn1)(const struct TestA*, int); }; +struct TestB { + struct TestA d; + int32_t i3; +}; + +struct TestC { + int32_t i; +}; + +struct TestD { + struct TestC a; + struct TestC b; +}; + static int32_t testAi0(struct TestA* a) { return a->i; } @@ -87,7 +101,24 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STATIC_MEMBER(S16, s_hUnaligned) mSCRIPT_DEFINE_END; +mSCRIPT_DEFINE_STRUCT(TestB) + mSCRIPT_DEFINE_INHERIT(TestA) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestB, S32, i3) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(TestC) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestC, S32, i) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(TestD) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), a) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), b) +mSCRIPT_DEFINE_END; + mSCRIPT_EXPORT_STRUCT(TestA); +mSCRIPT_EXPORT_STRUCT(TestB); +mSCRIPT_EXPORT_STRUCT(TestC); +mSCRIPT_EXPORT_STRUCT(TestD); M_TEST_DEFINE(testALayout) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; @@ -492,9 +523,331 @@ M_TEST_DEFINE(testADynamic) { assert_false(cls->init); } +M_TEST_DEFINE(testBLayout) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls; + assert_false(cls->init); + mScriptClassInit(cls); + assert_true(cls->init); + + struct mScriptClassMember* member; + + // Instance members + member = HashTableLookup(&cls->instanceMembers, "i"); + assert_non_null(member); + assert_string_equal(member->name, "i"); + assert_string_equal(member->docstring, MEMBER_A_DOCSTRING); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); + assert_int_equal(member->offset, 0); + + member = HashTableLookup(&cls->instanceMembers, "i2"); + assert_non_null(member); + assert_string_equal(member->name, "i2"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); + assert_int_equal(member->offset, sizeof(int32_t)); + + member = HashTableLookup(&cls->instanceMembers, "b8"); + assert_non_null(member); + assert_string_equal(member->name, "b8"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S8); + assert_int_equal(member->offset, sizeof(int32_t) * 2); + + member = HashTableLookup(&cls->instanceMembers, "hUnaligned"); + assert_non_null(member); + assert_string_equal(member->name, "hUnaligned"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); + assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); + size_t hOffset = member->offset; + + member = HashTableLookup(&cls->instanceMembers, "i3"); + assert_non_null(member); + assert_string_equal(member->name, "i3"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); + assert_true(member->offset >= hOffset + sizeof(int16_t)); + + member = HashTableLookup(&cls->instanceMembers, "_super"); + assert_non_null(member); + assert_string_equal(member->name, "_super"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestA)); + assert_int_equal(member->offset, 0); + + member = HashTableLookup(&cls->instanceMembers, "unknown"); + assert_null(member); + + mScriptClassDeinit(cls); + assert_false(cls->init); +} + +M_TEST_DEFINE(testBGet) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls; + + struct TestB s = { + .d = { + .i = 1, + .i2 = 2, + .b8 = 3, + .hUnaligned = 4 + }, + .i3 = 5 + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestB, &s); + struct mScriptValue super; + struct mScriptValue val; + struct mScriptValue compare; + + compare = mSCRIPT_MAKE_S32(1); + assert_true(mScriptObjectGet(&sval, "i", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(2); + assert_true(mScriptObjectGet(&sval, "i2", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(3); + assert_true(mScriptObjectGet(&sval, "b8", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(4); + assert_true(mScriptObjectGet(&sval, "hUnaligned", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(5); + assert_true(mScriptObjectGet(&sval, "i3", &val)); + assert_true(compare.type->equal(&compare, &val)); + + // Superclass explicit access + assert_true(mScriptObjectGet(&sval, "_super", &super)); + assert_true(super.type == mSCRIPT_TYPE_MS_S(TestA)); + + compare = mSCRIPT_MAKE_S32(1); + assert_true(mScriptObjectGet(&super, "i", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(2); + assert_true(mScriptObjectGet(&super, "i2", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(3); + assert_true(mScriptObjectGet(&super, "b8", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(4); + assert_true(mScriptObjectGet(&super, "hUnaligned", &val)); + assert_true(compare.type->equal(&compare, &val)); + + assert_false(mScriptObjectGet(&super, "i3", &val)); + + // Test const-correctness + sval = mSCRIPT_MAKE_CS(TestB, &s); + assert_true(mScriptObjectGet(&sval, "_super", &super)); + assert_true(super.type == mSCRIPT_TYPE_MS_CS(TestA)); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + +M_TEST_DEFINE(testBSet) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls; + + struct TestB s = { + .d = { + .i = 1, + .i2 = 2, + .b8 = 3, + .hUnaligned = 4 + }, + .i3 = 5 + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestB, &s); + struct mScriptValue super; + struct mScriptValue val; + + val = mSCRIPT_MAKE_S32(2); + assert_true(mScriptObjectSet(&sval, "i", &val)); + assert_int_equal(s.d.i, 2); + + val = mSCRIPT_MAKE_S32(3); + assert_true(mScriptObjectSet(&sval, "i2", &val)); + assert_int_equal(s.d.i2, 3); + + val = mSCRIPT_MAKE_S32(4); + assert_true(mScriptObjectSet(&sval, "b8", &val)); + assert_int_equal(s.d.b8, 4); + + val = mSCRIPT_MAKE_S32(5); + assert_true(mScriptObjectSet(&sval, "hUnaligned", &val)); + assert_int_equal(s.d.hUnaligned, 5); + + val = mSCRIPT_MAKE_S32(6); + assert_true(mScriptObjectSet(&sval, "i3", &val)); + assert_int_equal(s.i3, 6); + + // Superclass explicit access + assert_true(mScriptObjectGet(&sval, "_super", &super)); + assert_true(super.type == mSCRIPT_TYPE_MS_S(TestA)); + + val = mSCRIPT_MAKE_S32(3); + assert_true(mScriptObjectSet(&super, "i", &val)); + assert_int_equal(s.d.i, 3); + + val = mSCRIPT_MAKE_S32(4); + assert_true(mScriptObjectSet(&super, "i2", &val)); + assert_int_equal(s.d.i2, 4); + + val = mSCRIPT_MAKE_S32(5); + assert_true(mScriptObjectSet(&super, "b8", &val)); + assert_int_equal(s.d.b8, 5); + + val = mSCRIPT_MAKE_S32(6); + assert_true(mScriptObjectSet(&super, "hUnaligned", &val)); + assert_int_equal(s.d.hUnaligned, 6); + + val = mSCRIPT_MAKE_S32(7); + assert_false(mScriptObjectSet(&super, "i3", &val)); + assert_int_equal(s.i3, 6); + + // Const access + sval = mSCRIPT_MAKE_CS(TestB, &s); + + val = mSCRIPT_MAKE_S32(4); + assert_false(mScriptObjectSet(&sval, "i", &val)); + assert_int_equal(s.d.i, 3); + + val = mSCRIPT_MAKE_S32(5); + assert_false(mScriptObjectSet(&sval, "i2", &val)); + assert_int_equal(s.d.i2, 4); + + val = mSCRIPT_MAKE_S32(6); + assert_false(mScriptObjectSet(&sval, "b8", &val)); + assert_int_equal(s.d.b8, 5); + + val = mSCRIPT_MAKE_S32(7); + assert_false(mScriptObjectSet(&sval, "hUnaligned", &val)); + assert_int_equal(s.d.hUnaligned, 6); + + val = mSCRIPT_MAKE_S32(8); + assert_false(mScriptObjectSet(&sval, "i3", &val)); + assert_int_equal(s.i3, 6); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + +M_TEST_DEFINE(testDLayout) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls; + assert_false(cls->init); + mScriptClassInit(cls); + assert_true(cls->init); + + struct mScriptClassMember* member; + + // Instance members + member = HashTableLookup(&cls->instanceMembers, "a"); + assert_non_null(member); + assert_string_equal(member->name, "a"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestC)); + assert_int_equal(member->offset, 0); + + member = HashTableLookup(&cls->instanceMembers, "b"); + assert_non_null(member); + assert_string_equal(member->name, "b"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestC)); + assert_int_equal(member->offset, sizeof(struct TestC)); + + mScriptClassDeinit(cls); + assert_false(cls->init); +} + +M_TEST_DEFINE(testDGet) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls; + + struct TestD s = { + .a = { 1 }, + .b = { 2 }, + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestD, &s); + struct mScriptValue val; + struct mScriptValue member; + struct mScriptValue compare; + + compare = mSCRIPT_MAKE_S32(1); + assert_true(mScriptObjectGet(&sval, "a", &member)); + assert_true(mScriptObjectGet(&member, "i", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32(2); + assert_true(mScriptObjectGet(&sval, "b", &member)); + assert_true(mScriptObjectGet(&member, "i", &val)); + assert_true(compare.type->equal(&compare, &val)); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + +M_TEST_DEFINE(testDSet) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls; + + struct TestD s = { + .a = { 1 }, + .b = { 2 }, + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestD, &s); + struct mScriptValue member; + struct mScriptValue val; + + val = mSCRIPT_MAKE_S32(2); + assert_true(mScriptObjectGet(&sval, "a", &member)); + assert_true(mScriptObjectSet(&member, "i", &val)); + assert_int_equal(s.a.i, 2); + assert_int_equal(s.b.i, 2); + + val = mSCRIPT_MAKE_S32(3); + assert_true(mScriptObjectGet(&sval, "b", &member)); + assert_true(mScriptObjectSet(&member, "i", &val)); + assert_int_equal(s.a.i, 2); + assert_int_equal(s.b.i, 3); + + sval = mSCRIPT_MAKE_CS(TestD, &s); + + val = mSCRIPT_MAKE_S32(4); + assert_true(mScriptObjectGet(&sval, "a", &member)); + assert_false(mScriptObjectSet(&member, "i", &val)); + assert_int_equal(s.a.i, 2); + assert_int_equal(s.b.i, 3); + + val = mSCRIPT_MAKE_S32(5); + assert_true(mScriptObjectGet(&sval, "b", &member)); + assert_false(mScriptObjectSet(&member, "i", &val)); + assert_int_equal(s.a.i, 2); + assert_int_equal(s.b.i, 3); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), cmocka_unit_test(testAGet), cmocka_unit_test(testASet), cmocka_unit_test(testAStatic), - cmocka_unit_test(testADynamic)) + cmocka_unit_test(testADynamic), + cmocka_unit_test(testBLayout), + cmocka_unit_test(testBGet), + cmocka_unit_test(testBSet), + cmocka_unit_test(testDLayout), + cmocka_unit_test(testDGet), + cmocka_unit_test(testDSet)) diff --git a/src/script/types.c b/src/script/types.c index fd179c91f..66423761a 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -741,33 +741,34 @@ void mScriptFrameDeinit(struct mScriptFrame* frame) { mScriptListDeinit(&frame->arguments); } -void mScriptClassInit(struct mScriptTypeClass* cls) { - if (cls->init) { - return; - } - HashTableInit(&cls->staticMembers, 0, free); - HashTableInit(&cls->instanceMembers, 0, free); - size_t staticOffset = 0; +static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScriptClassInitDetails* details, bool child) { const char* docstring = NULL; + size_t staticOffset = 0; size_t i; - for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { - const struct mScriptClassInitDetails* details = &cls->details[i]; + for (i = 0; details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { + const struct mScriptClassInitDetails* detail = &details[i]; struct mScriptClassMember* member; - switch (details->type) { + switch (detail->type) { case mSCRIPT_CLASS_INIT_END: break; case mSCRIPT_CLASS_INIT_DOCSTRING: - docstring = details->info.comment; + docstring = detail->info.comment; break; case mSCRIPT_CLASS_INIT_INHERIT: - // TODO - abort(); + member = calloc(1, sizeof(*member)); + member->name = "_super"; + member->type = detail->info.parent; + if (!child) { + cls->parent = detail->info.parent; + } + HashTableInsert(&cls->instanceMembers, member->name, member); + _mScriptClassInit(cls, detail->info.parent->details.cls->details, true); break; case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER: member = calloc(1, sizeof(*member)); - memcpy(member, &details->info.member, sizeof(*member)); + memcpy(member, &detail->info.member, sizeof(*member)); if (docstring) { member->docstring = docstring; docstring = NULL; @@ -775,27 +776,40 @@ void mScriptClassInit(struct mScriptTypeClass* cls) { HashTableInsert(&cls->instanceMembers, member->name, member); break; case mSCRIPT_CLASS_INIT_STATIC_MEMBER: - member = calloc(1, sizeof(*member)); - memcpy(member, &details->info.member, sizeof(*member)); - if (docstring) { - member->docstring = docstring; - docstring = NULL; - } - - // Alignment check - if (staticOffset & (details->info.member.type->size - 1)) { - size_t size = details->info.member.type->size; - if (size > MAX_ALIGNMENT) { - size = MAX_ALIGNMENT; + if (!child) { + member = calloc(1, sizeof(*member)); + memcpy(member, &detail->info.member, sizeof(*member)); + if (docstring) { + member->docstring = docstring; + docstring = NULL; } - staticOffset = (staticOffset & ~(size - 1)) + size; + + // Alignment check + if (staticOffset & (detail->info.member.type->size - 1)) { + size_t size = detail->info.member.type->size; + if (size > MAX_ALIGNMENT) { + size = MAX_ALIGNMENT; + } + staticOffset = (staticOffset & ~(size - 1)) + size; + } + member->offset = staticOffset; + staticOffset += detail->info.member.type->size; + HashTableInsert(&cls->staticMembers, member->name, member); } - member->offset = staticOffset; - staticOffset += details->info.member.type->size; - HashTableInsert(&cls->staticMembers, member->name, member); break; } } +} + +void mScriptClassInit(struct mScriptTypeClass* cls) { + if (cls->init) { + return; + } + HashTableInit(&cls->staticMembers, 0, free); + HashTableInit(&cls->instanceMembers, 0, free); + + _mScriptClassInit(cls, cls->details, false); + cls->init = true; } @@ -880,6 +894,15 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri val->type = m->type; m->type->alloc(val); break; + case mSCRIPT_TYPE_OBJECT: + val->refs = mSCRIPT_VALUE_UNREF; + val->value.opaque = rawMember; + if (obj->type->isConst && !m->type->isConst) { + val->type = m->type->constType; + } else { + val->type = m->type; + } + break; default: return false; } From edb07e23c839973558bc3b5c0ec244ae7e7bcf74 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 1 May 2022 23:17:45 -0700 Subject: [PATCH 028/105] Scripting: Fix leaks in Lua test --- src/script/test/lua.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 2d78cf691..c8656476e 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -53,6 +53,7 @@ M_TEST_DEFINE(loadGood) { lua->destroy(lua); mScriptContextDeinit(&context); + vf->close(vf); } M_TEST_DEFINE(loadBadSyntax) { @@ -68,6 +69,7 @@ M_TEST_DEFINE(loadBadSyntax) { lua->destroy(lua); mScriptContextDeinit(&context); + vf->close(vf); } M_TEST_DEFINE(runNop) { @@ -87,6 +89,7 @@ M_TEST_DEFINE(runNop) { lua->destroy(lua); mScriptContextDeinit(&context); + vf->close(vf); } M_TEST_DEFINE(getGlobal) { @@ -111,6 +114,7 @@ M_TEST_DEFINE(getGlobal) { assert_non_null(val); assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); + vf->close(vf); program = "b = 1"; vf = VFileFromConstMemory(program, strlen(program)); @@ -128,6 +132,7 @@ M_TEST_DEFINE(getGlobal) { assert_non_null(val); assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); + vf->close(vf); a = mSCRIPT_MAKE_S32(2); program = "a = 2"; @@ -141,6 +146,7 @@ M_TEST_DEFINE(getGlobal) { assert_non_null(val); assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); + vf->close(vf); a = mSCRIPT_MAKE_S32(3); program = "b = a + b"; @@ -157,9 +163,9 @@ M_TEST_DEFINE(getGlobal) { lua->destroy(lua); mScriptContextDeinit(&context); + vf->close(vf); } - M_TEST_DEFINE(setGlobal) { struct mScriptContext context; mScriptContextInit(&context); @@ -213,6 +219,7 @@ M_TEST_DEFINE(setGlobal) { lua->destroy(lua); mScriptContextDeinit(&context); + vf->close(vf); } M_TEST_DEFINE(callLuaFunc) { @@ -263,6 +270,7 @@ M_TEST_DEFINE(callLuaFunc) { lua->destroy(lua); mScriptContextDeinit(&context); + vf->close(vf); } M_TEST_DEFINE(callCFunc) { @@ -299,6 +307,7 @@ M_TEST_DEFINE(callCFunc) { lua->destroy(lua); mScriptContextDeinit(&context); + vf->close(vf); } M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, From 4dae4d8f7f0e2fe867afa1c2408af53fdb60328c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 1 May 2022 23:53:23 -0700 Subject: [PATCH 029/105] Scripting: Start hooking up objects in Lua --- src/script/engines/lua.c | 71 ++++++++++++++++++++-- src/script/test/lua.c | 126 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 192 insertions(+), 5 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index e7da8c8bf..eb1db84a8 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -27,6 +27,8 @@ static bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptV static void _luaDeref(struct mScriptValue*); static int _luaThunk(lua_State* lua); +static int _luaGetObject(lua_State* lua); +static int _luaSetObject(lua_State* lua); #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber @@ -90,6 +92,16 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS }; luaContext->lua = luaL_newstate(); luaContext->func = -1; + + luaL_newmetatable(luaContext->lua, "mSTStruct"); + lua_pushliteral(luaContext->lua, "__index"); + lua_pushcfunction(luaContext->lua, _luaGetObject); + lua_rawset(luaContext->lua, -3); + lua_pushliteral(luaContext->lua, "__newindex"); + lua_pushcfunction(luaContext->lua, _luaSetObject); + lua_rawset(luaContext->lua, -3); + lua_pop(luaContext->lua, 1); + return &luaContext->d; } @@ -149,6 +161,8 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { value->value.f64 = lua_tonumber(luaContext->lua, -1); break; case LUA_TBOOLEAN: + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); + value->value.s32 = lua_toboolean(luaContext->lua, -1); break; case LUA_TSTRING: break; @@ -195,15 +209,18 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v case mSCRIPT_TYPE_FUNCTION: lua_pushlightuserdata(luaContext->lua, value); lua_pushcclosure(luaContext->lua, _luaThunk, 1); + mScriptValueRef(value); + break; + case mSCRIPT_TYPE_OBJECT: + lua_pushlightuserdata(luaContext->lua, value); + luaL_setmetatable(luaContext->lua, "mSTStruct"); + mScriptValueRef(value); break; default: ok = false; break; } - if (ok) { - mScriptValueDeref(value); - } return ok; } @@ -349,7 +366,7 @@ void _luaDeref(struct mScriptValue* value) { free(ref); } -int _luaThunk(lua_State* lua) { +static struct mScriptEngineContextLua* _luaGetContext(lua_State* lua) { lua_pushliteral(lua, "mCtx"); int type = lua_rawget(lua, LUA_REGISTRYINDEX); if (type != LUA_TLIGHTUSERDATA) { @@ -364,7 +381,11 @@ int _luaThunk(lua_State* lua) { lua_pushliteral(lua, "Function called from invalid context"); lua_error(lua); } + return luaContext; +} +int _luaThunk(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); struct mScriptFrame frame; mScriptFrameInit(&frame); if (!_luaPopFrame(luaContext, &frame.arguments)) { @@ -389,3 +410,45 @@ int _luaThunk(lua_State* lua) { return lua_gettop(luaContext->lua); } + +int _luaGetObject(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + const char* key = lua_tostring(lua, -1); + struct mScriptValue* obj = lua_touserdata(lua, -2); + struct mScriptValue val; + + if (!mScriptObjectGet(obj, key, &val)) { + lua_pop(lua, 2); + lua_pushliteral(lua, "Invalid key"); + lua_error(lua); + } + + lua_pop(lua, 2); + if (!_luaWrap(luaContext, &val)) { + lua_pushliteral(lua, "Invalid value"); + lua_error(lua); + } + return 1; +} + + +int _luaSetObject(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + const char* key = lua_tostring(lua, -2); + struct mScriptValue* obj = lua_touserdata(lua, -3); + struct mScriptValue* val = _luaCoerce(luaContext); + + lua_pop(lua, 2); + if (!val) { + lua_pushliteral(lua, "Invalid value"); + lua_error(lua); + } + + if (!mScriptObjectSet(obj, key, val)) { + mScriptValueDeref(val); + lua_pushliteral(lua, "Invalid key"); + lua_error(lua); + } + mScriptValueDeref(val); + return 0; +} diff --git a/src/script/test/lua.c b/src/script/test/lua.c index c8656476e..a48d6b5be 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -7,6 +7,15 @@ #include +struct Test { + int32_t i; + int32_t (*ifn0)(struct Test*); + int32_t (*ifn1)(struct Test*, int); + void (*vfn0)(struct Test*); + void (*vfn1)(struct Test*, int); + int32_t (*icfn0)(const struct Test*); +}; + static int identityInt(int in) { return in; } @@ -15,9 +24,57 @@ static int addInts(int a, int b) { return a + b; } +static int32_t testI0(struct Test* a) { + return a->i; +} + +static int32_t testI1(struct Test* a, int b) { + return a->i + b; +} + +static int32_t testIC0(const struct Test* a) { + return a->i; +} + +static void testV0(struct Test* a) { + ++a->i; +} + +static void testV1(struct Test* a, int b) { + a->i += b; +} + mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32); mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32); +mSCRIPT_DECLARE_STRUCT(Test); +mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn0, 0); +mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(Test, S32, icfn0, 0); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(Test, vfn0, 0); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(Test, vfn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_METHOD(Test, S32, i0, testI0, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(Test, S32, i1, testI1, 1, S32); +mSCRIPT_DECLARE_STRUCT_C_METHOD(Test, S32, ic0, testIC0, 0); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v0, testV0, 0); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v1, testV1, 1, S32); + +mSCRIPT_DEFINE_STRUCT(Test) + mSCRIPT_DEFINE_STRUCT_MEMBER(Test, S32, i) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, ifn0) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, ifn1) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, icfn0) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, vfn0) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, vfn1) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, i0) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, i1) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, ic0) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, v0) + mSCRIPT_DEFINE_STRUCT_METHOD(Test, v1) +mSCRIPT_DEFINE_END; + +mSCRIPT_EXPORT_STRUCT(Test); + M_TEST_SUITE_SETUP(mScriptLua) { if (mSCRIPT_ENGINE_LUA->init) { mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); @@ -310,6 +367,72 @@ M_TEST_DEFINE(callCFunc) { vf->close(vf); } +M_TEST_DEFINE(setGlobalStruct) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct Test s = { + .i = 1 + }; + + struct mScriptValue a = mSCRIPT_MAKE_S(Test, &s); + struct mScriptValue b; + struct mScriptValue* val; + const char* program; + struct VFile* vf; + const char* error; + + program = "b = a.i"; + vf = VFileFromConstMemory(program, strlen(program)); + error = NULL; + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + assert_true(lua->setGlobal(lua, "a", &a)); + + assert_true(lua->run(lua)); + + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + s.i = 2; + assert_true(lua->run(lua)); + + b = mSCRIPT_MAKE_S32(2); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + vf->close(vf); + + program = "a.i = b"; + vf = VFileFromConstMemory(program, strlen(program)); + assert_true(lua->load(lua, vf, &error)); + assert_null(error); + b = mSCRIPT_MAKE_S32(3); + assert_true(lua->setGlobal(lua, "b", &b)); + + assert_true(lua->run(lua)); + + assert_int_equal(s.i, 3); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + b = mSCRIPT_MAKE_S32(4); + assert_true(lua->setGlobal(lua, "b", &b)); + + assert_false(lua->run(lua)); + + assert_int_equal(s.i, 3); + + lua->destroy(lua); + mScriptContextDeinit(&context); + vf->close(vf); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(create), cmocka_unit_test(loadGood), @@ -318,4 +441,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(getGlobal), cmocka_unit_test(setGlobal), cmocka_unit_test(callLuaFunc), - cmocka_unit_test(callCFunc)) + cmocka_unit_test(callCFunc), + cmocka_unit_test(setGlobalStruct)) From 16bad9f14144ae02aa84585165256cff0b4c54ff Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 3 May 2022 20:28:40 -0700 Subject: [PATCH 030/105] Scripting: Testing sugar --- src/script/test/lua.c | 93 +++++++++---------------------------------- 1 file changed, 19 insertions(+), 74 deletions(-) diff --git a/src/script/test/lua.c b/src/script/test/lua.c index a48d6b5be..a112f604c 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -7,6 +7,15 @@ #include +#define LOAD_PROGRAM(PROG) \ + do { \ + struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ + const char* error = NULL; \ + assert_true(lua->load(lua, vf, &error)); \ + assert_null(error); \ + vf->close(vf); \ + } while(0) + struct Test { int32_t i; int32_t (*ifn0)(struct Test*); @@ -134,11 +143,7 @@ M_TEST_DEFINE(runNop) { mScriptContextInit(&context); struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); - const char* program = "return"; - struct VFile* vf = VFileFromConstMemory(program, strlen(program)); - const char* error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("return"); assert_true(lua->run(lua)); // Make sure we can run it twice @@ -146,7 +151,6 @@ M_TEST_DEFINE(runNop) { lua->destroy(lua); mScriptContextDeinit(&context); - vf->close(vf); } M_TEST_DEFINE(getGlobal) { @@ -156,28 +160,16 @@ M_TEST_DEFINE(getGlobal) { struct mScriptValue a = mSCRIPT_MAKE_S32(1); struct mScriptValue* val; - const char* program; - struct VFile* vf; - const char* error; - program = "a = 1"; - vf = VFileFromConstMemory(program, strlen(program)); - error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("a = 1"); assert_true(lua->run(lua)); val = lua->getGlobal(lua, "a"); assert_non_null(val); assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); - vf->close(vf); - program = "b = 1"; - vf = VFileFromConstMemory(program, strlen(program)); - error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("b = 1"); assert_true(lua->run(lua)); val = lua->getGlobal(lua, "a"); @@ -189,28 +181,18 @@ M_TEST_DEFINE(getGlobal) { assert_non_null(val); assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); - vf->close(vf); a = mSCRIPT_MAKE_S32(2); - program = "a = 2"; - vf = VFileFromConstMemory(program, strlen(program)); - error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("a = 2"); assert_true(lua->run(lua)); val = lua->getGlobal(lua, "a"); assert_non_null(val); assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); - vf->close(vf); a = mSCRIPT_MAKE_S32(3); - program = "b = a + b"; - vf = VFileFromConstMemory(program, strlen(program)); - error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("b = a + b"); assert_true(lua->run(lua)); val = lua->getGlobal(lua, "b"); @@ -220,7 +202,6 @@ M_TEST_DEFINE(getGlobal) { lua->destroy(lua); mScriptContextDeinit(&context); - vf->close(vf); } M_TEST_DEFINE(setGlobal) { @@ -230,15 +211,8 @@ M_TEST_DEFINE(setGlobal) { struct mScriptValue a = mSCRIPT_MAKE_S32(1); struct mScriptValue* val; - const char* program; - struct VFile* vf; - const char* error; - program = "a = b"; - vf = VFileFromConstMemory(program, strlen(program)); - error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("a = b"); assert_true(lua->setGlobal(lua, "b", &a)); val = lua->getGlobal(lua, "b"); @@ -276,7 +250,6 @@ M_TEST_DEFINE(setGlobal) { lua->destroy(lua); mScriptContextDeinit(&context); - vf->close(vf); } M_TEST_DEFINE(callLuaFunc) { @@ -285,15 +258,8 @@ M_TEST_DEFINE(callLuaFunc) { struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); struct mScriptValue* fn; - const char* program; - struct VFile* vf; - const char* error; - program = "function a(b) return b + 1 end; function c(d, e) return d + e end"; - vf = VFileFromConstMemory(program, strlen(program)); - error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("function a(b) return b + 1 end; function c(d, e) return d + e end"); assert_true(lua->run(lua)); fn = lua->getGlobal(lua, "a"); @@ -327,7 +293,6 @@ M_TEST_DEFINE(callLuaFunc) { lua->destroy(lua); mScriptContextDeinit(&context); - vf->close(vf); } M_TEST_DEFINE(callCFunc) { @@ -337,15 +302,8 @@ M_TEST_DEFINE(callCFunc) { struct mScriptValue a = mSCRIPT_MAKE_S32(1); struct mScriptValue* val; - const char* program; - struct VFile* vf; - const char* error; - program = "a = b(1); c = d(1, 2)"; - vf = VFileFromConstMemory(program, strlen(program)); - error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("a = b(1); c = d(1, 2)"); assert_true(lua->setGlobal(lua, "b", &boundIdentityInt)); assert_true(lua->setGlobal(lua, "d", &boundAddInts)); @@ -364,7 +322,6 @@ M_TEST_DEFINE(callCFunc) { lua->destroy(lua); mScriptContextDeinit(&context); - vf->close(vf); } M_TEST_DEFINE(setGlobalStruct) { @@ -379,15 +336,8 @@ M_TEST_DEFINE(setGlobalStruct) { struct mScriptValue a = mSCRIPT_MAKE_S(Test, &s); struct mScriptValue b; struct mScriptValue* val; - const char* program; - struct VFile* vf; - const char* error; - program = "b = a.i"; - vf = VFileFromConstMemory(program, strlen(program)); - error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("b = a.i"); assert_true(lua->setGlobal(lua, "a", &a)); assert_true(lua->run(lua)); @@ -406,12 +356,8 @@ M_TEST_DEFINE(setGlobalStruct) { assert_non_null(val); assert_true(b.type->equal(&b, val)); mScriptValueDeref(val); - vf->close(vf); - program = "a.i = b"; - vf = VFileFromConstMemory(program, strlen(program)); - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + LOAD_PROGRAM("a.i = b"); b = mSCRIPT_MAKE_S32(3); assert_true(lua->setGlobal(lua, "b", &b)); @@ -430,7 +376,6 @@ M_TEST_DEFINE(setGlobalStruct) { lua->destroy(lua); mScriptContextDeinit(&context); - vf->close(vf); } M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, From 5c67c3b6000288bd57a587b46d959ee99576d071 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 3 May 2022 20:44:56 -0700 Subject: [PATCH 031/105] Scripting: Lua method calling cleanup and testing --- include/mgba/script/types.h | 11 ++- src/script/engines/lua.c | 57 ++++++++--- src/script/test/lua.c | 186 +++++++++++++++++++++++++++++++++--- 3 files changed, 226 insertions(+), 28 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index f2e9a37a9..446157a83 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -366,8 +366,16 @@ CXX_GUARD_START #define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } #define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ + static struct mScriptFunction _function_ ## NAME = { \ + .call = _binding_ ## NAME \ + }; \ + static void _alloc_ ## NAME(struct mScriptValue* val) { \ + val->value.copaque = &_function_ ## NAME; \ + } \ static const struct mScriptType _type_ ## NAME = { \ .base = mSCRIPT_TYPE_FUNCTION, \ + .name = "function::" #NAME, \ + .alloc = _alloc_ ## NAME, \ .details = { \ .function = { \ .parameters = { \ @@ -381,9 +389,6 @@ CXX_GUARD_START }, \ } \ }; \ - static struct mScriptFunction _function_ ## NAME = { \ - .call = _binding_ ## NAME \ - }; \ const struct mScriptValue NAME = { \ .type = &_type_ ## NAME, \ .refs = mSCRIPT_VALUE_UNREF, \ diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index eb1db84a8..2d9eb5294 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -4,6 +4,7 @@ * 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 +#include #include static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*); @@ -79,6 +80,12 @@ static struct mScriptEngineLua { struct mScriptEngine2* const mSCRIPT_ENGINE_LUA = &_engineLua.d; +static const luaL_Reg _mSTStruct[] = { + { "__index", _luaGetObject }, + { "__newindex", _luaSetObject }, + { NULL, NULL } +}; + struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mScriptContext* context) { UNUSED(engine); struct mScriptEngineContextLua* luaContext = calloc(1, sizeof(*luaContext)); @@ -93,13 +100,14 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS luaContext->lua = luaL_newstate(); luaContext->func = -1; + luaL_openlibs(luaContext->lua); + luaL_newmetatable(luaContext->lua, "mSTStruct"); - lua_pushliteral(luaContext->lua, "__index"); - lua_pushcfunction(luaContext->lua, _luaGetObject); - lua_rawset(luaContext->lua, -3); - lua_pushliteral(luaContext->lua, "__newindex"); - lua_pushcfunction(luaContext->lua, _luaSetObject); - lua_rawset(luaContext->lua, -3); +#if LUA_VERSION_NUM < 502 + luaL_register(luaContext->lua, NULL, _mSTStruct); +#else + luaL_setfuncs(luaContext->lua, _mSTStruct, 0); +#endif lua_pop(luaContext->lua, 1); return &luaContext->d; @@ -129,7 +137,7 @@ bool _luaSetGlobal(struct mScriptEngineContext* ctx, const char* name, struct mS return true; } -struct mScriptValue* _luaWrapFunction(struct mScriptEngineContextLua* luaContext) { +struct mScriptValue* _luaCoerceFunction(struct mScriptEngineContextLua* luaContext) { struct mScriptValue* value = mScriptValueAlloc(&mSTLuaFunc); struct mScriptFunction* fn = calloc(1, sizeof(*fn)); struct mScriptEngineContextLuaRef* ref = calloc(1, sizeof(*ref)); @@ -167,7 +175,18 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { case LUA_TSTRING: break; case LUA_TFUNCTION: - return _luaWrapFunction(luaContext); + return _luaCoerceFunction(luaContext); + case LUA_TUSERDATA: + if (!lua_getmetatable(luaContext->lua, -1)) { + break; + } + luaL_getmetatable(luaContext->lua, "mSTStruct"); + if (!lua_rawequal(luaContext->lua, -1, -2)) { + lua_pop(luaContext->lua, 2); + break; + } + lua_pop(luaContext->lua, 2); + value = lua_touserdata(luaContext->lua, -1); } lua_pop(luaContext->lua, 1); return value; @@ -178,6 +197,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v value = mScriptValueUnwrap(value); } bool ok = true; + struct mScriptValue* newValue; switch (value->type->base) { case mSCRIPT_TYPE_SINT: if (value->type->size <= 4) { @@ -207,14 +227,16 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } break; case mSCRIPT_TYPE_FUNCTION: - lua_pushlightuserdata(luaContext->lua, value); + newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + newValue->type = value->type; + newValue->refs = mSCRIPT_VALUE_UNREF; + newValue->type->alloc(newValue); lua_pushcclosure(luaContext->lua, _luaThunk, 1); - mScriptValueRef(value); break; case mSCRIPT_TYPE_OBJECT: - lua_pushlightuserdata(luaContext->lua, value); + newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + mScriptValueWrap(value, newValue); luaL_setmetatable(luaContext->lua, "mSTStruct"); - mScriptValueRef(value); break; default: ok = false; @@ -308,6 +330,15 @@ bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList if (count > i) { lua_pop(luaContext->lua, count - i); } + + if (ok) { + for (i = 0; i < (ssize_t) (mScriptListSize(frame) / 2); ++i) { + struct mScriptValue buffer; + memcpy(&buffer, mScriptListGetPointer(frame, i), sizeof(buffer)); + memcpy(mScriptListGetPointer(frame, i), mScriptListGetPointer(frame, mScriptListSize(frame) - i - 1), sizeof(buffer)); + memcpy(mScriptListGetPointer(frame, mScriptListSize(frame) - i - 1), &buffer, sizeof(buffer)); + } + } } return ok; } @@ -346,7 +377,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* return false; } - if (!_luaPopFrame(luaContext, &frame->returnValues)) { + if (frame && !_luaPopFrame(luaContext, &frame->returnValues)) { return false; } diff --git a/src/script/test/lua.c b/src/script/test/lua.c index a112f604c..e6afdda0f 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -324,24 +324,26 @@ M_TEST_DEFINE(callCFunc) { mScriptContextDeinit(&context); } -M_TEST_DEFINE(setGlobalStruct) { +M_TEST_DEFINE(globalStructFieldGet) { struct mScriptContext context; mScriptContextInit(&context); struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); struct Test s = { - .i = 1 + .i = 1, }; - struct mScriptValue a = mSCRIPT_MAKE_S(Test, &s); + struct mScriptValue a; struct mScriptValue b; struct mScriptValue* val; LOAD_PROGRAM("b = a.i"); + + a = mSCRIPT_MAKE_S(Test, &s); assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; assert_true(lua->run(lua)); - b = mSCRIPT_MAKE_S32(1); val = lua->getGlobal(lua, "b"); assert_non_null(val); @@ -350,30 +352,188 @@ M_TEST_DEFINE(setGlobalStruct) { s.i = 2; assert_true(lua->run(lua)); - b = mSCRIPT_MAKE_S32(2); val = lua->getGlobal(lua, "b"); assert_non_null(val); assert_true(b.type->equal(&b, val)); mScriptValueDeref(val); - LOAD_PROGRAM("a.i = b"); - b = mSCRIPT_MAKE_S32(3); - assert_true(lua->setGlobal(lua, "b", &b)); + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + lua->destroy(lua); + mScriptContextDeinit(&context); +} + + +M_TEST_DEFINE(globalStructFieldSet) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct Test s = { + .i = 1, + }; + + struct mScriptValue a; + struct mScriptValue b; + + LOAD_PROGRAM("a.i = b"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(2); + assert_true(lua->setGlobal(lua, "b", &b)); + assert_true(lua->run(lua)); + assert_int_equal(s.i, 2); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(2); + assert_true(lua->setGlobal(lua, "b", &b)); + assert_false(lua->run(lua)); + assert_int_equal(s.i, 1); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + + +M_TEST_DEFINE(globalStructMethods) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct Test s = { + .i = 1, + .ifn0 = testI0, + .ifn1 = testI1, + .icfn0 = testIC0, + .vfn0 = testV0, + .vfn1 = testV1, + }; + + struct mScriptValue a; + struct mScriptValue b; + struct mScriptValue* val; + + // ifn0 + LOAD_PROGRAM("b = a:ifn0()"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + // ifn1 + LOAD_PROGRAM("b = a:ifn1(c)"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(1); + assert_true(lua->setGlobal(lua, "c", &b)); + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(2); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + b = mSCRIPT_MAKE_S32(2); + assert_true(lua->setGlobal(lua, "c", &b)); + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(3); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + // vfn0 + LOAD_PROGRAM("a:vfn0()"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + assert_true(lua->run(lua)); + assert_int_equal(s.i, 2); + assert_true(lua->run(lua)); assert_int_equal(s.i, 3); a = mSCRIPT_MAKE_CS(Test, &s); assert_true(lua->setGlobal(lua, "a", &a)); - b = mSCRIPT_MAKE_S32(4); - assert_true(lua->setGlobal(lua, "b", &b)); - assert_false(lua->run(lua)); + // vfn1 + LOAD_PROGRAM("a:vfn1(c)"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(1); + assert_true(lua->setGlobal(lua, "c", &b)); + assert_true(lua->run(lua)); + assert_int_equal(s.i, 2); + b = mSCRIPT_MAKE_S32(2); + assert_true(lua->setGlobal(lua, "c", &b)); + s.i = 1; + assert_true(lua->run(lua)); assert_int_equal(s.i, 3); + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(1); + assert_true(lua->setGlobal(lua, "c", &b)); + assert_false(lua->run(lua)); + assert_int_equal(s.i, 1); + + // icfn0 + LOAD_PROGRAM("b = a:icfn0()"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + lua->destroy(lua); mScriptContextDeinit(&context); } @@ -387,4 +547,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(setGlobal), cmocka_unit_test(callLuaFunc), cmocka_unit_test(callCFunc), - cmocka_unit_test(setGlobalStruct)) + cmocka_unit_test(globalStructFieldGet), + cmocka_unit_test(globalStructFieldSet), + cmocka_unit_test(globalStructMethods)) From 7226b7ee31c3e37a61ec4c57d8e74df2e1c9865a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 4 May 2022 18:26:52 -0700 Subject: [PATCH 032/105] Scripting: Interface cleanup --- include/mgba/script/types.h | 58 ++++++++++++++++++------------------- src/script/test/classes.c | 5 ---- src/script/test/lua.c | 2 -- src/script/test/types.c | 2 -- 4 files changed, 29 insertions(+), 38 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 446157a83..ad7c679d0 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -182,8 +182,14 @@ CXX_GUARD_START _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ mSCRIPT_PUSH(&frame->returnValues, RETURN, out) -#define mSCRIPT_EXPORT_STRUCT(STRUCT) \ - mSCRIPT_DECLARE_STRUCT(STRUCT) \ +#define mSCRIPT_DECLARE_STRUCT(STRUCT) \ + extern const struct mScriptType mSTStruct_ ## STRUCT; \ + extern const struct mScriptType mSTStructConst_ ## STRUCT; + +#define mSCRIPT_DEFINE_STRUCT(STRUCT) \ + const struct mScriptType mSTStruct_ ## STRUCT; \ + const struct mScriptType mSTStructConst_ ## STRUCT; \ + static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT; \ static bool _mSTStructCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \ if (input->type == type || (input->type == &mSTStruct_ ## STRUCT && type == &mSTStructConst_ ## STRUCT)) { \ output->type = type; \ @@ -215,15 +221,7 @@ CXX_GUARD_START .alloc = NULL, \ .free = NULL, \ .cast = _mSTStructCast_ ## STRUCT, \ - } - -#define mSCRIPT_DECLARE_STRUCT(STRUCT) \ - extern const struct mScriptType mSTStruct_ ## STRUCT; \ - extern const struct mScriptType mSTStructConst_ ## STRUCT; - -#define mSCRIPT_DEFINE_STRUCT(STRUCT) \ - const struct mScriptType mSTStruct_ ## STRUCT; \ - const struct mScriptType mSTStructConst_ ## STRUCT; \ + }; \ static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ .init = false, \ .details = (const struct mScriptClassInitDetails[]) { @@ -353,16 +351,18 @@ CXX_GUARD_START #define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) -#define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) { \ +#define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) { \ .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ .info = { \ .member = { \ - .name = #NAME, \ + .name = #EXPORTED_NAME, \ .type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME \ } \ }, \ }, +#define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) + #define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } #define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ @@ -421,27 +421,27 @@ CXX_GUARD_START } \ _mSCRIPT_BIND_FUNCTION(NAME, 0, , NPARAMS, __VA_ARGS__) -#define mSCRIPT_MAKE(TYPE, FIELD, VALUE) (struct mScriptValue) { \ - .type = (TYPE), \ +#define mSCRIPT_MAKE(TYPE, VALUE) (struct mScriptValue) { \ + .type = (mSCRIPT_TYPE_MS_ ## TYPE), \ .refs = mSCRIPT_VALUE_UNREF, \ .value = { \ - .FIELD = (VALUE) \ + .mSCRIPT_TYPE_FIELD_ ## TYPE = (VALUE) \ }, \ } \ -#define mSCRIPT_MAKE_S8(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S8, s32, VALUE) -#define mSCRIPT_MAKE_U8(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U8, u32, VALUE) -#define mSCRIPT_MAKE_S16(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S16, s32, VALUE) -#define mSCRIPT_MAKE_U16(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U16, u32, VALUE) -#define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S32, s32, VALUE) -#define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U32, u32, VALUE) -#define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F32, f32, VALUE) -#define mSCRIPT_MAKE_S64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S64, s64, VALUE) -#define mSCRIPT_MAKE_U64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U64, u64, VALUE) -#define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F64, f64, VALUE) -#define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CHARP, opaque, VALUE) -#define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S(STRUCT), opaque, VALUE) -#define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CS(STRUCT), copaque, VALUE) +#define mSCRIPT_MAKE_S8(VALUE) mSCRIPT_MAKE(S8, VALUE) +#define mSCRIPT_MAKE_U8(VALUE) mSCRIPT_MAKE(U8, VALUE) +#define mSCRIPT_MAKE_S16(VALUE) mSCRIPT_MAKE(S16, VALUE) +#define mSCRIPT_MAKE_U16(VALUE) mSCRIPT_MAKE(U16, VALUE) +#define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(S32, VALUE) +#define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(U32, VALUE) +#define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(F32, VALUE) +#define mSCRIPT_MAKE_S64(VALUE) mSCRIPT_MAKE(S64, VALUE) +#define mSCRIPT_MAKE_U64(VALUE) mSCRIPT_MAKE(U64, VALUE) +#define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(F64, VALUE) +#define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(CHARP, VALUE) +#define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(S(STRUCT), VALUE) +#define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(CS(STRUCT), VALUE) enum mScriptTypeBase { mSCRIPT_TYPE_VOID = 0, diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 272bc5fc0..5a0681d5e 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -115,11 +115,6 @@ mSCRIPT_DEFINE_STRUCT(TestD) mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), b) mSCRIPT_DEFINE_END; -mSCRIPT_EXPORT_STRUCT(TestA); -mSCRIPT_EXPORT_STRUCT(TestB); -mSCRIPT_EXPORT_STRUCT(TestC); -mSCRIPT_EXPORT_STRUCT(TestD); - M_TEST_DEFINE(testALayout) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); diff --git a/src/script/test/lua.c b/src/script/test/lua.c index e6afdda0f..2dab24824 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -82,8 +82,6 @@ mSCRIPT_DEFINE_STRUCT(Test) mSCRIPT_DEFINE_STRUCT_METHOD(Test, v1) mSCRIPT_DEFINE_END; -mSCRIPT_EXPORT_STRUCT(Test); - M_TEST_SUITE_SETUP(mScriptLua) { if (mSCRIPT_ENGINE_LUA->init) { mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); diff --git a/src/script/test/types.c b/src/script/test/types.c index c0cca0273..b08a705ed 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -15,8 +15,6 @@ struct Test { mSCRIPT_DEFINE_STRUCT(Test) mSCRIPT_DEFINE_END; -mSCRIPT_EXPORT_STRUCT(Test); - static int voidOne(void) { return 1; } From ce3710323bb3277370531b65d7fff9240b299056 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 3 May 2022 21:50:53 -0700 Subject: [PATCH 033/105] Scripting: Start working on mCore bridge --- include/mgba/core/scripting.h | 11 ++- include/mgba/script/context.h | 6 +- include/mgba/script/types.h | 1 + src/core/CMakeLists.txt | 11 ++- src/core/scripting.c | 34 +++++++- src/core/test/scripting.c | 143 ++++++++++++++++++++++++++++++++++ src/script/context.c | 55 ++++++++++++- src/script/engines/lua.c | 12 +++ src/script/types.c | 13 ++++ 9 files changed, 278 insertions(+), 8 deletions(-) create mode 100644 src/core/test/scripting.c diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h index ebf8f88cf..c65897e17 100644 --- a/include/mgba/core/scripting.h +++ b/include/mgba/core/scripting.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* Copyright (c) 2013-2022 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 @@ -13,6 +13,10 @@ CXX_GUARD_START #ifdef USE_DEBUGGERS #include #endif +#include + +struct mCore; +mSCRIPT_DECLARE_STRUCT(mCore); struct mScriptBridge; struct VFile; @@ -47,6 +51,11 @@ bool mScriptBridgeLoadScript(struct mScriptBridge*, const char* name); bool mScriptBridgeLookupSymbol(struct mScriptBridge*, const char* name, int32_t* out); +struct mScriptContext; +struct mCore; +void mScriptContextAttachCore(struct mScriptContext*, struct mCore*); +void mScriptContextDetachCore(struct mScriptContext*); + CXX_GUARD_END #endif diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 0b83a02fe..d37caa293 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -19,7 +19,7 @@ struct mScriptFunction; struct mScriptEngineContext; struct mScriptContext { - struct mScriptValue rootScope; + struct Table rootScope; struct Table engines; }; @@ -46,9 +46,9 @@ struct mScriptEngineContext { void mScriptContextInit(struct mScriptContext*); void mScriptContextDeinit(struct mScriptContext*); -void mScriptContextRegisterEngine(struct mScriptContext*, struct mScriptEngine2*); +struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext*, struct mScriptEngine2*); -void mScriptContextAddGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value); +void mScriptContextSetGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value); void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key); bool mScriptInvoke(const struct mScriptValue* fn, struct mScriptFrame* frame); diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index ad7c679d0..b3dfda5ec 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -580,6 +580,7 @@ void mScriptValueDeref(struct mScriptValue* val); void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out); struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val); +const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val); struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7c1108252..bd1f3ecf8 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -14,7 +14,6 @@ set(SOURCE_FILES map-cache.c mem-search.c rewind.c - scripting.c serialize.c sync.c thread.c @@ -24,6 +23,16 @@ set(SOURCE_FILES set(TEST_FILES test/core.c) +if(ENABLE_SCRIPTING) + list(APPEND SOURCE_FILES + scripting.c) + + if(USE_LUA) + list(APPEND TEST_FILES + test/scripting.c) + endif() +endif() + source_group("mCore" FILES ${SOURCE_FILES}) source_group("mCore tests" FILES ${TEST_FILES}) diff --git a/src/core/scripting.c b/src/core/scripting.c index add6da031..e4a472d79 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -1,10 +1,12 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* Copyright (c) 2013-2022 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 +#include +#include #include #include @@ -144,3 +146,33 @@ bool mScriptBridgeLookupSymbol(struct mScriptBridge* sb, const char* name, int32 HashTableEnumerate(&sb->engines, _seLookupSymbol, &info); return info.success; } + +mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frameCycles, 0); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, step, 0); + +mSCRIPT_DEFINE_STRUCT(mCore) +mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") +mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, currentFrame, frameCounter) +mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per frame") +mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frameCycles) +mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per second") +mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frequency) +mSCRIPT_DEFINE_DOCSTRING("Run until the next frame") +mSCRIPT_DEFINE_STRUCT_METHOD(mCore, runFrame) +mSCRIPT_DEFINE_DOCSTRING("Run a single instruction") +mSCRIPT_DEFINE_STRUCT_METHOD(mCore, step) +mSCRIPT_DEFINE_END; + +void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core) { + struct mScriptValue* coreValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mCore)); + coreValue->value.opaque = core; + mScriptContextSetGlobal(context, "emu", coreValue); + mScriptValueDeref(coreValue); +} + +void mScriptContextDetachCore(struct mScriptContext* context) { + mScriptContextRemoveGlobal(context, "emu"); +} diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c new file mode 100644 index 000000000..cfe009601 --- /dev/null +++ b/src/core/test/scripting.c @@ -0,0 +1,143 @@ +/* Copyright (c) 2013-2022 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 +#include +#include + +#ifdef M_CORE_GBA +#define TEST_PLATFORM mPLATFORM_GBA +#elif defined(M_CORE_GB) +#define TEST_PLATFORM mPLATFORM_GB +#else +#error "Need a valid platform for testing" +#endif + +static const uint8_t _fakeGBROM[0x4000] = { + [0x100] = 0x18, // Loop forever + [0x101] = 0xFE, // jr, $-2 + [0x102] = 0xCE, // Enough of the header to fool the core + [0x103] = 0xED, + [0x104] = 0x66, + [0x105] = 0x66, +}; + +#define SETUP_LUA \ + struct mScriptContext context; \ + mScriptContextInit(&context); \ + struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA) + +#define CREATE_CORE \ + struct mCore* core = mCoreCreate(TEST_PLATFORM); \ + assert_non_null(core); \ + assert_true(core->init(core)); \ + switch (core->platform(core)) { \ + case mPLATFORM_GBA: \ + core->busWrite32(core, 0x020000C0, 0xEAFFFFFE); \ + break; \ + case mPLATFORM_GB: \ + assert_true(core->loadROM(core, VFileFromConstMemory(_fakeGBROM, sizeof(_fakeGBROM)))); \ + break; \ + case mPLATFORM_NONE: \ + break; \ + } \ + mCoreInitConfig(core, NULL); \ + mScriptContextAttachCore(&context, core) + +#define TEARDOWN_CORE \ + mCoreConfigDeinit(&core->config); \ + core->deinit(core) + +#define LOAD_PROGRAM(PROG) \ + do { \ + struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ + const char* error = NULL; \ + assert_true(lua->load(lua, vf, &error)); \ + assert_null(error); \ + vf->close(vf); \ + } while(0) + +#define TEST_VALUE(TYPE, NAME, VALUE) \ + do { \ + struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ + struct mScriptValue* global = lua->getGlobal(lua, NAME); \ + assert_non_null(global); \ + assert_true(global->type->equal(global, &val)); \ + mScriptValueDeref(global); \ + } while(0) + +M_TEST_SUITE_SETUP(mScriptCore) { + if (mSCRIPT_ENGINE_LUA->init) { + mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_SUITE_TEARDOWN(mScriptCore) { + if (mSCRIPT_ENGINE_LUA->deinit) { + mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_DEFINE(globals) { + SETUP_LUA; + CREATE_CORE; + core->reset(core); + + LOAD_PROGRAM("assert(emu)"); + assert_true(lua->run(lua)); + + TEARDOWN_CORE; + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(infoFuncs) { + SETUP_LUA; + CREATE_CORE; + core->reset(core); + + LOAD_PROGRAM( + "frequency = emu:frequency()\n" + "frameCycles = emu:frameCycles()\n" + ); + assert_true(lua->run(lua)); + + TEST_VALUE(S32, "frequency", core->frequency(core)); + TEST_VALUE(S32, "frameCycles", core->frameCycles(core)); + + TEARDOWN_CORE; + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(runFrame) { + SETUP_LUA; + CREATE_CORE; + core->reset(core); + + LOAD_PROGRAM( + "frame = emu:currentFrame()\n" + "emu:runFrame()\n" + ); + + int i; + for (i = 0; i < 5; ++i) { + assert_true(lua->run(lua)); + TEST_VALUE(S32, "frame", i); + } + + TEARDOWN_CORE; + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore, + cmocka_unit_test(globals), + cmocka_unit_test(infoFuncs), + cmocka_unit_test(runFrame), +) diff --git a/src/script/context.c b/src/script/context.c index 321d2f222..9cc8b09b0 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -5,13 +5,64 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +struct mScriptKVPair { + const char* key; + struct mScriptValue* value; +}; + +static void _engineContextDestroy(void* ctx) { + struct mScriptEngineContext* context = ctx; + context->destroy(context); +} + +static void _contextAddGlobal(const char* key, void* value, void* user) { + UNUSED(key); + struct mScriptEngineContext* context = value; + struct mScriptKVPair* pair = user; + context->setGlobal(context, pair->key, pair->value); +} + +static void _contextRemoveGlobal(const char* key, void* value, void* user) { + UNUSED(key); + struct mScriptEngineContext* context = value; + context->setGlobal(context, user, NULL); +} + void mScriptContextInit(struct mScriptContext* context) { - // TODO: rootScope - HashTableInit(&context->engines, 0, NULL); + HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref); + HashTableInit(&context->engines, 0, _engineContextDestroy); } void mScriptContextDeinit(struct mScriptContext* context) { HashTableDeinit(&context->engines); + HashTableDeinit(&context->rootScope); +} + +struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* context, struct mScriptEngine2* engine) { + struct mScriptEngineContext* ectx = engine->create(engine, context); + if (ectx) { + HashTableInsert(&context->engines, engine->name, ectx); + } + return ectx; +} + +void mScriptContextSetGlobal(struct mScriptContext* context, const char* key, struct mScriptValue* value) { + mScriptValueRef(value); + HashTableInsert(&context->rootScope, key, value); + struct mScriptKVPair pair = { + .key = key, + .value = value + }; + HashTableEnumerate(&context->engines, _contextAddGlobal, &pair); +} + +void mScriptContextRemoveGlobal(struct mScriptContext* context, const char* key) { + if (!HashTableLookup(&context->rootScope, key)) { + return; + } + // Since _contextRemoveGlobal doesn't mutate |key|, this cast should be safe + HashTableEnumerate(&context->engines, _contextRemoveGlobal, (char*) key); + HashTableRemove(&context->rootScope, key); } bool mScriptInvoke(const struct mScriptValue* val, struct mScriptFrame* frame) { diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 2d9eb5294..0df0142d0 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -30,6 +30,7 @@ static void _luaDeref(struct mScriptValue*); static int _luaThunk(lua_State* lua); static int _luaGetObject(lua_State* lua); static int _luaSetObject(lua_State* lua); +static int _luaGcObject(lua_State* lua); #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber @@ -83,6 +84,7 @@ struct mScriptEngine2* const mSCRIPT_ENGINE_LUA = &_engineLua.d; static const luaL_Reg _mSTStruct[] = { { "__index", _luaGetObject }, { "__newindex", _luaSetObject }, + { "__gc", _luaGcObject }, { NULL, NULL } }; @@ -483,3 +485,13 @@ int _luaSetObject(lua_State* lua) { mScriptValueDeref(val); return 0; } + +static int _luaGcObject(lua_State* lua) { + struct mScriptValue* val = lua_touserdata(lua, -1); + val = mScriptValueUnwrap(val); + if (!val) { + return 0; + } + mScriptValueDeref(val); + return 0; +} diff --git a/src/script/types.c b/src/script/types.c index 66423761a..0582de186 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -687,6 +687,13 @@ struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* value) { return NULL; } +const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* value) { + if (value->type == mSCRIPT_TYPE_MS_WRAPPER) { + return value->value.copaque; + } + return NULL; +} + struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); struct mScriptString* internal = val->value.opaque; @@ -823,6 +830,9 @@ void mScriptClassDeinit(struct mScriptTypeClass* cls) { } bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScriptValue* val) { + if (obj->type->base == mSCRIPT_TYPE_WRAPPER) { + obj = mScriptValueUnwrap(obj); + } if (obj->type->base != mSCRIPT_TYPE_OBJECT) { return false; } @@ -1031,6 +1041,9 @@ bool mScriptPopPointer(struct mScriptList* list, void** out) { } bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output) { + if (input->type->base == mSCRIPT_TYPE_WRAPPER) { + input = mScriptValueUnwrapConst(input); + } if (type->cast && type->cast(input, type, output)) { return true; } From 421c645e14f54682a1d5a8154f2358e5635201a6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 4 May 2022 21:35:51 -0700 Subject: [PATCH 034/105] Scripting: More testing sugar --- src/script/test/lua.c | 46 ++++++++++++------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 2dab24824..42efae6a0 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -7,6 +7,11 @@ #include +#define SETUP_LUA \ + struct mScriptContext context; \ + mScriptContextInit(&context); \ + struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA) + #define LOAD_PROGRAM(PROG) \ do { \ struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ @@ -137,9 +142,7 @@ M_TEST_DEFINE(loadBadSyntax) { } M_TEST_DEFINE(runNop) { - struct mScriptContext context; - mScriptContextInit(&context); - struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + SETUP_LUA; LOAD_PROGRAM("return"); assert_true(lua->run(lua)); @@ -147,14 +150,11 @@ M_TEST_DEFINE(runNop) { // Make sure we can run it twice assert_true(lua->run(lua)); - lua->destroy(lua); mScriptContextDeinit(&context); } M_TEST_DEFINE(getGlobal) { - struct mScriptContext context; - mScriptContextInit(&context); - struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + SETUP_LUA; struct mScriptValue a = mSCRIPT_MAKE_S32(1); struct mScriptValue* val; @@ -198,14 +198,11 @@ M_TEST_DEFINE(getGlobal) { assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); - lua->destroy(lua); mScriptContextDeinit(&context); } M_TEST_DEFINE(setGlobal) { - struct mScriptContext context; - mScriptContextInit(&context); - struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + SETUP_LUA; struct mScriptValue a = mSCRIPT_MAKE_S32(1); struct mScriptValue* val; @@ -246,14 +243,11 @@ M_TEST_DEFINE(setGlobal) { assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); - lua->destroy(lua); mScriptContextDeinit(&context); } M_TEST_DEFINE(callLuaFunc) { - struct mScriptContext context; - mScriptContextInit(&context); - struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + SETUP_LUA; struct mScriptValue* fn; @@ -289,14 +283,11 @@ M_TEST_DEFINE(callLuaFunc) { mScriptFrameDeinit(&frame); mScriptValueDeref(fn); - lua->destroy(lua); mScriptContextDeinit(&context); } M_TEST_DEFINE(callCFunc) { - struct mScriptContext context; - mScriptContextInit(&context); - struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + SETUP_LUA; struct mScriptValue a = mSCRIPT_MAKE_S32(1); struct mScriptValue* val; @@ -318,14 +309,11 @@ M_TEST_DEFINE(callCFunc) { assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); - lua->destroy(lua); mScriptContextDeinit(&context); } M_TEST_DEFINE(globalStructFieldGet) { - struct mScriptContext context; - mScriptContextInit(&context); - struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + SETUP_LUA; struct Test s = { .i = 1, @@ -367,15 +355,11 @@ M_TEST_DEFINE(globalStructFieldGet) { assert_true(b.type->equal(&b, val)); mScriptValueDeref(val); - lua->destroy(lua); mScriptContextDeinit(&context); } - M_TEST_DEFINE(globalStructFieldSet) { - struct mScriptContext context; - mScriptContextInit(&context); - struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + SETUP_LUA; struct Test s = { .i = 1, @@ -402,15 +386,12 @@ M_TEST_DEFINE(globalStructFieldSet) { assert_false(lua->run(lua)); assert_int_equal(s.i, 1); - lua->destroy(lua); mScriptContextDeinit(&context); } M_TEST_DEFINE(globalStructMethods) { - struct mScriptContext context; - mScriptContextInit(&context); - struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + SETUP_LUA; struct Test s = { .i = 1, @@ -532,7 +513,6 @@ M_TEST_DEFINE(globalStructMethods) { assert_true(b.type->equal(&b, val)); mScriptValueDeref(val); - lua->destroy(lua); mScriptContextDeinit(&context); } From 8c45d51b8eaf7822c743c3c6fd11b4caeeb0c64b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 6 May 2022 01:07:07 -0700 Subject: [PATCH 035/105] Scripting: Code cleanup, add parameter names --- include/mgba-util/macros.h | 79 ++++++++++++++++++++++++++++++++ include/mgba/script/types.h | 87 ++++++++++++++---------------------- src/script/test/classes.c | 89 ++++++++++++++++++++++++++++++++++--- src/script/test/lua.c | 12 ++--- src/script/test/types.c | 16 +++---- src/script/types.c | 12 ++--- 6 files changed, 216 insertions(+), 79 deletions(-) create mode 100644 include/mgba-util/macros.h diff --git a/include/mgba-util/macros.h b/include/mgba-util/macros.h new file mode 100644 index 000000000..d9f208df9 --- /dev/null +++ b/include/mgba-util/macros.h @@ -0,0 +1,79 @@ +/* Copyright (c) 2013-2022 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/. */ +#ifndef M_MACROS_H +#define M_MACROS_H + +#define _mCPP_CAT(A, B) A ## B + +#define _mCALL(FN, ...) FN(__VA_ARGS__) +#define _mCAT(A, B) _mCPP_CAT(A, B) +#define _mSTRINGIFY(X, ...) #X + +#define _mCALL_0(FN, ...) +#define _mCALL_1(FN, A) FN(A) +#define _mCALL_2(FN, A, B) FN(A), FN(B) +#define _mCALL_3(FN, A, ...) FN(A), _mCALL_2(FN, __VA_ARGS__) +#define _mCALL_4(FN, A, ...) FN(A), _mCALL_3(FN, __VA_ARGS__) +#define _mCALL_5(FN, A, ...) FN(A), _mCALL_4(FN, __VA_ARGS__) +#define _mCALL_6(FN, A, ...) FN(A), _mCALL_5(FN, __VA_ARGS__) +#define _mCALL_7(FN, A, ...) FN(A), _mCALL_6(FN, __VA_ARGS__) +#define _mCALL_8(FN, A, ...) FN(A), _mCALL_7(FN, __VA_ARGS__) +#define _mCALL_9(FN, A, ...) FN(A), _mCALL_8(FN, __VA_ARGS__) + +#define _mCOMMA_0(N, ...) N +#define _mCOMMA_1(N, ...) N, __VA_ARGS__ +#define _mCOMMA_2(N, ...) N, __VA_ARGS__ +#define _mCOMMA_3(N, ...) N, __VA_ARGS__ +#define _mCOMMA_4(N, ...) N, __VA_ARGS__ +#define _mCOMMA_5(N, ...) N, __VA_ARGS__ +#define _mCOMMA_6(N, ...) N, __VA_ARGS__ +#define _mCOMMA_7(N, ...) N, __VA_ARGS__ +#define _mCOMMA_8(N, ...) N, __VA_ARGS__ +#define _mCOMMA_9(N, ...) N, __VA_ARGS__ + +#define _mEVEN_0(...) +#define _mEVEN_1(A, B, ...) A +#define _mEVEN_2(A, B, ...) A, _mEVEN_1(__VA_ARGS__) +#define _mEVEN_3(A, B, ...) A, _mEVEN_2(__VA_ARGS__) +#define _mEVEN_4(A, B, ...) A, _mEVEN_3(__VA_ARGS__) +#define _mEVEN_5(A, B, ...) A, _mEVEN_4(__VA_ARGS__) +#define _mEVEN_6(A, B, ...) A, _mEVEN_5(__VA_ARGS__) +#define _mEVEN_7(A, B, ...) A, _mEVEN_6(__VA_ARGS__) +#define _mEVEN_8(A, B, ...) A, _mEVEN_7(__VA_ARGS__) +#define _mEVEN_9(A, B, ...) A, _mEVEN_7(__VA_ARGS__) + +#define _mODD_0(...) +#define _mODD_1(A, B, ...) B +#define _mODD_2(A, B, ...) B, _mODD_1(__VA_ARGS__) +#define _mODD_3(A, B, ...) B, _mODD_2(__VA_ARGS__) +#define _mODD_4(A, B, ...) B, _mODD_3(__VA_ARGS__) +#define _mODD_5(A, B, ...) B, _mODD_4(__VA_ARGS__) +#define _mODD_6(A, B, ...) B, _mODD_5(__VA_ARGS__) +#define _mODD_7(A, B, ...) B, _mODD_6(__VA_ARGS__) +#define _mODD_8(A, B, ...) B, _mODD_7(__VA_ARGS__) +#define _mODD_9(A, B, ...) B, _mODD_7(__VA_ARGS__) + +#define _mSUCC_0 1 +#define _mSUCC_1 2 +#define _mSUCC_2 3 +#define _mSUCC_3 4 +#define _mSUCC_4 5 +#define _mSUCC_5 6 +#define _mSUCC_6 7 +#define _mSUCC_7 8 +#define _mSUCC_8 9 + +#define _mPRED_1 0 +#define _mPRED_2 1 +#define _mPRED_3 2 +#define _mPRED_4 3 +#define _mPRED_5 4 +#define _mPRED_6 5 +#define _mPRED_7 6 +#define _mPRED_8 7 +#define _mPRED_9 8 + +#endif diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index b3dfda5ec..d4d552e4a 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -10,17 +10,10 @@ CXX_GUARD_START +#include #include #include -#define _mCPP_EMPTY() -#define _mCPP_CAT(A, B) A ## B - -#define _mDEFER(X) X _mCPP_EMPTY() -#define _mBLOCK(...) __VA_ARGS__ _mDEFER(_mCPP_EMPTY)() -#define _mAPPLY(...) __VA_ARGS__ -#define _mCAT(A, B) _mCPP_CAT(A, B) - #define mSCRIPT_VALUE_UNREF -1 #define mSCRIPT_PARAMS_MAX 8 @@ -99,10 +92,10 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP_CS(STRUCT) mSCRIPT_TYPE_MS_CS(STRUCT)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME -#define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) _mAPPLY(mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1)) +#define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1) #define mSCRIPT_POP(STACK, TYPE, NAME) \ - _mAPPLY(mSCRIPT_TYPE_C_ ## TYPE) NAME; \ + mSCRIPT_TYPE_C_ ## TYPE NAME; \ do { \ struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ @@ -115,7 +108,7 @@ CXX_GUARD_START return false; \ } \ } \ - NAME = _val->value. _mAPPLY(mSCRIPT_TYPE_FIELD_ ## TYPE); \ + NAME = _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE; \ mScriptValueDeref(_val); \ mScriptListResize(STACK, -1); \ } while (0) @@ -130,21 +123,12 @@ CXX_GUARD_START #define mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) mSCRIPT_POP(FRAME, T6, p6); mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) #define mSCRIPT_POP_8(FRAME, T0, T1, T2, T3, T4, T5, T6, T7) mSCRIPT_POP(FRAME, T7, p7); mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) -#define _mCOMMA_0(N, ...) N -#define _mCOMMA_1(N, ...) N, __VA_ARGS__ -#define _mCOMMA_2(N, ...) N, __VA_ARGS__ -#define _mCOMMA_3(N, ...) N, __VA_ARGS__ -#define _mCOMMA_4(N, ...) N, __VA_ARGS__ -#define _mCOMMA_5(N, ...) N, __VA_ARGS__ -#define _mCOMMA_6(N, ...) N, __VA_ARGS__ -#define _mCOMMA_7(N, ...) N, __VA_ARGS__ - #define mSCRIPT_PUSH(STACK, TYPE, NAME) \ do { \ struct mScriptValue* _val = mScriptListAppend(STACK); \ - _val->type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE); \ + _val->type = mSCRIPT_TYPE_MS_ ## TYPE; \ _val->refs = mSCRIPT_VALUE_UNREF; \ - _val->value._mAPPLY(mSCRIPT_TYPE_FIELD_ ## TYPE) = NAME; \ + _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE = NAME; \ } while (0) #define mSCRIPT_ARG_NAMES_0 @@ -166,20 +150,11 @@ CXX_GUARD_START #define mSCRIPT_PREFIX_6(PREFIX, T0, T1, T2, T3, T4, T5) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5 #define mSCRIPT_PREFIX_7(PREFIX, T0, T1, T2, T3, T4, T5, T6) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6 #define mSCRIPT_PREFIX_8(PREFIX, T0, T1, T2, T3, T4, T5, T6, T7) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6, PREFIX ## T7 -#define mSCRIPT_PREFIX_N(N) _mAPPLY(mSCRIPT_PREFIX_ ## N) - -#define _mSUCC0 1 -#define _mSUCC1 2 -#define _mSUCC2 3 -#define _mSUCC3 4 -#define _mSUCC4 5 -#define _mSUCC5 6 -#define _mSUCC6 7 -#define _mSUCC7 8 +#define mSCRIPT_PREFIX_N(N) mSCRIPT_PREFIX_ ## N #define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) #define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ - _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ + mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ mSCRIPT_PUSH(&frame->returnValues, RETURN, out) #define mSCRIPT_DECLARE_STRUCT(STRUCT) \ @@ -238,7 +213,7 @@ CXX_GUARD_START .info = { \ .member = { \ .name = #NAME, \ - .type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE), \ + .type = mSCRIPT_TYPE_MS_ ## TYPE, \ .offset = offsetof(struct STRUCT, NAME) \ } \ } \ @@ -249,7 +224,7 @@ CXX_GUARD_START .info = { \ .member = { \ .name = #NAME, \ - .type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE) \ + .type = mSCRIPT_TYPE_MS_ ## TYPE \ } \ }, \ }, @@ -262,7 +237,7 @@ CXX_GUARD_START }, #define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \ - _mDEFER(_mDEFER(_mCAT(mSCRIPT_POP_, _mSUCC ## NPARAMS)) (&frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__))); \ + _mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \ if (mScriptListSize(&frame->arguments)) { \ return false; \ } @@ -283,8 +258,9 @@ CXX_GUARD_START .details = { \ .function = { \ .parameters = { \ - .count = _mSUCC ## NPARAMS, \ - .entries = { _mAPPLY(mSCRIPT_TYPE_MS_ ## S(TYPE)), mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__) } \ + .count = _mSUCC_ ## NPARAMS, \ + .entries = { mSCRIPT_TYPE_MS_ ## S(TYPE), _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ + .names = { "this", _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ }, \ .returnType = { \ .count = NRET, \ @@ -294,47 +270,50 @@ CXX_GUARD_START } \ }; +#define _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, RETURN, NAME, CONST, NPARAMS, ...) \ + typedef RETURN (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mCOMMA_ ## NPARAMS(CONST struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__))) + #define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - typedef _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, __VA_ARGS__); \ - _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC ## NPARAMS); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \ return true; \ } \ #define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - typedef void (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, __VA_ARGS__); \ - _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC ## NPARAMS); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ return true; \ } \ #define mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - typedef _mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, __VA_ARGS__); \ - _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC ## NPARAMS); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \ return true; \ } \ #define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - typedef void (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mAPPLY(_mCOMMA_ ## NPARAMS(struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))); \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, __VA_ARGS__) \ \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, __VA_ARGS__); \ - _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC ## NPARAMS); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ return true; \ } \ @@ -380,7 +359,8 @@ CXX_GUARD_START .function = { \ .parameters = { \ .count = NPARAMS, \ - .entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \ + .entries = { _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ + .names = { _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ }, \ .returnType = { \ .count = NRET, \ @@ -400,7 +380,7 @@ CXX_GUARD_START #define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \ static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \ + _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ if (mScriptListSize(&frame->arguments)) { \ return false; \ } \ @@ -412,7 +392,7 @@ CXX_GUARD_START #define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \ static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \ + _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ if (mScriptListSize(&frame->arguments)) { \ return false; \ } \ @@ -486,6 +466,7 @@ extern const struct mScriptType mSTWrapper; struct mScriptTypeTuple { size_t count; const struct mScriptType* entries[mSCRIPT_PARAMS_MAX]; + const char* names[mSCRIPT_PARAMS_MAX]; bool variable; }; diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 5a0681d5e..4896d8357 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -63,17 +63,17 @@ static void testAv1(struct TestA* a, int b) { mSCRIPT_DECLARE_STRUCT(TestA); mSCRIPT_DECLARE_STRUCT_D_METHOD(TestA, S32, ifn0, 0); -mSCRIPT_DECLARE_STRUCT_D_METHOD(TestA, S32, ifn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_D_METHOD(TestA, S32, ifn1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn0, 0); -mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn0, 0); -mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, i0, testAi0, 0); -mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, i1, testAi1, 1, S32); +mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, i1, testAi1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic0, testAic0, 0); -mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic1, testAic1, 1, S32); +mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic1, testAic1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v0, testAv0, 0); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v1, testAv1, 1, S32); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v1, testAv1, 1, S32, b); mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) @@ -191,6 +191,82 @@ M_TEST_DEFINE(testALayout) { assert_false(cls->init); } +M_TEST_DEFINE(testASignatures) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; + assert_false(cls->init); + mScriptClassInit(cls); + assert_true(cls->init); + + struct mScriptClassMember* member; + + member = HashTableLookup(&cls->instanceMembers, "ifn0"); + assert_non_null(member); + assert_string_equal(member->name, "ifn0"); + assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION); + assert_int_equal(member->type->details.function.parameters.count, 1); + assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA)); + assert_string_equal(member->type->details.function.parameters.names[0], "this"); + assert_int_equal(member->type->details.function.returnType.count, 1); + assert_ptr_equal(member->type->details.function.returnType.entries[0], mSCRIPT_TYPE_MS_S32); + + member = HashTableLookup(&cls->instanceMembers, "ifn1"); + assert_non_null(member); + assert_string_equal(member->name, "ifn1"); + assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION); + assert_int_equal(member->type->details.function.parameters.count, 2); + assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA)); + assert_ptr_equal(member->type->details.function.parameters.entries[1], mSCRIPT_TYPE_MS_S32); + assert_string_equal(member->type->details.function.parameters.names[0], "this"); + assert_string_equal(member->type->details.function.parameters.names[1], "b"); + assert_int_equal(member->type->details.function.returnType.count, 1); + assert_ptr_equal(member->type->details.function.returnType.entries[0], mSCRIPT_TYPE_MS_S32); + + member = HashTableLookup(&cls->instanceMembers, "vfn0"); + assert_non_null(member); + assert_string_equal(member->name, "vfn0"); + assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION); + assert_int_equal(member->type->details.function.parameters.count, 1); + assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA)); + assert_string_equal(member->type->details.function.parameters.names[0], "this"); + assert_int_equal(member->type->details.function.returnType.count, 0); + + member = HashTableLookup(&cls->instanceMembers, "vfn1"); + assert_non_null(member); + assert_string_equal(member->name, "vfn1"); + assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION); + assert_int_equal(member->type->details.function.parameters.count, 2); + assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA)); + assert_ptr_equal(member->type->details.function.parameters.entries[1], mSCRIPT_TYPE_MS_S32); + assert_string_equal(member->type->details.function.parameters.names[0], "this"); + assert_string_equal(member->type->details.function.parameters.names[1], "b"); + assert_int_equal(member->type->details.function.returnType.count, 0); + + member = HashTableLookup(&cls->instanceMembers, "icfn0"); + assert_non_null(member); + assert_string_equal(member->name, "icfn0"); + assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION); + assert_int_equal(member->type->details.function.parameters.count, 1); + assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_CS(TestA)); + assert_string_equal(member->type->details.function.parameters.names[0], "this"); + assert_int_equal(member->type->details.function.returnType.count, 1); + assert_ptr_equal(member->type->details.function.returnType.entries[0], mSCRIPT_TYPE_MS_S32); + + member = HashTableLookup(&cls->instanceMembers, "icfn1"); + assert_non_null(member); + assert_string_equal(member->name, "icfn1"); + assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION); + assert_int_equal(member->type->details.function.parameters.count, 2); + assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_CS(TestA)); + assert_ptr_equal(member->type->details.function.parameters.entries[1], mSCRIPT_TYPE_MS_S32); + assert_string_equal(member->type->details.function.parameters.names[0], "this"); + assert_string_equal(member->type->details.function.parameters.names[1], "b"); + assert_int_equal(member->type->details.function.returnType.count, 1); + assert_ptr_equal(member->type->details.function.returnType.entries[0], mSCRIPT_TYPE_MS_S32); + + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_DEFINE(testAGet) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; @@ -836,6 +912,7 @@ M_TEST_DEFINE(testDSet) { M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), + cmocka_unit_test(testASignatures), cmocka_unit_test(testAGet), cmocka_unit_test(testASet), cmocka_unit_test(testAStatic), diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 42efae6a0..43ffae081 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -58,20 +58,20 @@ static void testV1(struct Test* a, int b) { a->i += b; } -mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32); -mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32); +mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, a); +mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b); mSCRIPT_DECLARE_STRUCT(Test); mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn0, 0); -mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_CD_METHOD(Test, S32, icfn0, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(Test, vfn0, 0); -mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(Test, vfn1, 1, S32); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(Test, vfn1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_METHOD(Test, S32, i0, testI0, 0); -mSCRIPT_DECLARE_STRUCT_METHOD(Test, S32, i1, testI1, 1, S32); +mSCRIPT_DECLARE_STRUCT_METHOD(Test, S32, i1, testI1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_C_METHOD(Test, S32, ic0, testIC0, 0); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v0, testV0, 0); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v1, testV1, 1, S32); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v1, testV1, 1, S32, b); mSCRIPT_DEFINE_STRUCT(Test) mSCRIPT_DEFINE_STRUCT_MEMBER(Test, S32, i) diff --git a/src/script/test/types.c b/src/script/test/types.c index b08a705ed..ed1f26a79 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -52,14 +52,14 @@ static int isHello(const char* str) { } mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); -mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32); -mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32); -mSCRIPT_BIND_FUNCTION(boundIdentityInt64, S64, identityInt64, 1, S64); -mSCRIPT_BIND_FUNCTION(boundIdentityFloat, F32, identityFloat, 1, F32); -mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test)); -mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32); -mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, S32); -mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP); +mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32, ignored); +mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, in); +mSCRIPT_BIND_FUNCTION(boundIdentityInt64, S64, identityInt64, 1, S64, in); +mSCRIPT_BIND_FUNCTION(boundIdentityFloat, F32, identityFloat, 1, F32, in); +mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test), t); +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); M_TEST_DEFINE(voidArgs) { struct mScriptFrame frame; diff --git a/src/script/types.c b/src/script/types.c index 0582de186..bed27625e 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -295,12 +295,12 @@ uint32_t _hashScalar(const struct mScriptValue* val) { return true; \ } -_mAPPLY(AS(SInt32, S32)); -_mAPPLY(AS(UInt32, U32)); -_mAPPLY(AS(Float32, F32)); -_mAPPLY(AS(SInt64, S64)); -_mAPPLY(AS(UInt64, U64)); -_mAPPLY(AS(Float64, F64)); +AS(SInt32, S32); +AS(UInt32, U32); +AS(Float32, F32); +AS(SInt64, S64); +AS(UInt64, U64); +AS(Float64, F64); bool _castScalar(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { switch (type->base) { From 66142ab4dc18d7a3e4b0eedeabe858f46fbb8048 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 6 May 2022 01:44:55 -0700 Subject: [PATCH 036/105] Scripting: Hook up memory access --- src/core/scripting.c | 20 ++++++++++++ src/core/test/scripting.c | 69 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/core/scripting.c b/src/core/scripting.c index e4a472d79..8adc5f55d 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -153,6 +153,13 @@ mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, step, 0); +mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U8, busRead8, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U16, busRead16, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead32, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite8, 2, U32, address, U8, value); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, value); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value); + mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, currentFrame, frameCounter) @@ -164,6 +171,19 @@ mSCRIPT_DEFINE_DOCSTRING("Run until the next frame") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, runFrame) mSCRIPT_DEFINE_DOCSTRING("Run a single instruction") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, step) + +mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given bus address") +mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read8, busRead8) +mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given bus address") +mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read16, busRead16) +mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given bus address") +mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read32, busRead32) +mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given bus address") +mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write8, busWrite8) +mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given bus address") +mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write16, busWrite16) +mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given bus address") +mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write32, busWrite32) mSCRIPT_DEFINE_END; void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core) { diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index cfe009601..b3af97742 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -12,9 +12,13 @@ #include #ifdef M_CORE_GBA +#include #define TEST_PLATFORM mPLATFORM_GBA +#define RAM_BASE BASE_WORKING_IRAM #elif defined(M_CORE_GB) +#include #define TEST_PLATFORM mPLATFORM_GB +#define RAM_BASE GB_BASE_WORKING_RAM_BANK0 #else #error "Need a valid platform for testing" #endif @@ -136,8 +140,73 @@ M_TEST_DEFINE(runFrame) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(memoryRead) { + SETUP_LUA; + CREATE_CORE; + core->reset(core); + + LOAD_PROGRAM( + "a8 = emu:read8(base + 0)\n" + "b8 = emu:read8(base + 1)\n" + "c8 = emu:read8(base + 2)\n" + "d8 = emu:read8(base + 3)\n" + "a16 = emu:read16(base + 4)\n" + "b16 = emu:read16(base + 6)\n" + "a32 = emu:read32(base + 8)\n" + ); + + int i; + for (i = 0; i < 12; ++i) { + core->busWrite8(core, RAM_BASE + i, i + 1); + } + struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE); + lua->setGlobal(lua, "base", &base); + assert_true(lua->run(lua)); + + TEST_VALUE(S32, "a8", 1); + TEST_VALUE(S32, "b8", 2); + TEST_VALUE(S32, "c8", 3); + TEST_VALUE(S32, "d8", 4); + TEST_VALUE(S32, "a16", 0x0605); + TEST_VALUE(S32, "b16", 0x0807); + TEST_VALUE(S32, "a32", 0x0C0B0A09); + + TEARDOWN_CORE; + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(memoryWrite) { + SETUP_LUA; + CREATE_CORE; + core->reset(core); + + LOAD_PROGRAM( + "emu:write8(base + 0, 1)\n" + "emu:write8(base + 1, 2)\n" + "emu:write8(base + 2, 3)\n" + "emu:write8(base + 3, 4)\n" + "emu:write16(base + 4, 0x0605)\n" + "emu:write16(base + 6, 0x0807)\n" + "emu:write32(base + 8, 0x0C0B0A09)\n" + ); + + struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE); + lua->setGlobal(lua, "base", &base); + assert_true(lua->run(lua)); + + int i; + for (i = 0; i < 12; ++i) { + assert_int_equal(core->busRead8(core, RAM_BASE + i), i + 1); + } + + TEARDOWN_CORE; + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore, cmocka_unit_test(globals), cmocka_unit_test(infoFuncs), cmocka_unit_test(runFrame), + cmocka_unit_test(memoryRead), + cmocka_unit_test(memoryWrite), ) From b9f88061caffe99708b6bad20865867a1be45155 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 6 May 2022 15:32:33 -0700 Subject: [PATCH 037/105] Scripting: Add error reporting --- include/mgba/script/context.h | 1 + src/script/engines/lua.c | 23 ++++++++++++++++++++++- src/script/test/lua.c | 24 +++++++++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index d37caa293..7f08b8aa2 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -41,6 +41,7 @@ struct mScriptEngineContext { bool (*load)(struct mScriptEngineContext*, struct VFile*, const char** error); bool (*run)(struct mScriptEngineContext*); + const char* (*getError)(struct mScriptEngineContext*); }; void mScriptContextInit(struct mScriptContext*); diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 0df0142d0..fca966d66 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -4,6 +4,9 @@ * 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 + +#include + #include #include @@ -14,6 +17,7 @@ static struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext*, const ch static bool _luaSetGlobal(struct mScriptEngineContext*, const char* name, struct mScriptValue*); static bool _luaLoad(struct mScriptEngineContext*, struct VFile*, const char** error); static bool _luaRun(struct mScriptEngineContext*); +static const char* _luaGetError(struct mScriptEngineContext*); static bool _luaCall(struct mScriptFrame*, void* context); @@ -61,6 +65,7 @@ struct mScriptEngineContextLua { struct mScriptEngineContext d; lua_State* lua; int func; + char* lastError; }; struct mScriptEngineContextLuaRef { @@ -97,7 +102,8 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS .getGlobal = _luaGetGlobal, .setGlobal = _luaSetGlobal, .load = _luaLoad, - .run = _luaRun + .run = _luaRun, + .getError = _luaGetError }; luaContext->lua = luaL_newstate(); luaContext->func = -1; @@ -117,6 +123,10 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS void _luaDestroy(struct mScriptEngineContext* ctx) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; + if (luaContext->lastError) { + free(luaContext->lastError); + luaContext->lastError = NULL; + } if (luaContext->func > 0) { luaL_unref(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func); } @@ -298,6 +308,11 @@ bool _luaRun(struct mScriptEngineContext* context) { return _luaInvoke(luaContext, NULL); } +const char* _luaGetError(struct mScriptEngineContext* context) { + struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) context; + return luaContext->lastError; +} + bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame) { bool ok = true; if (frame) { @@ -360,6 +375,11 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* nargs = mScriptListSize(&frame->arguments); } + if (luaContext->lastError) { + free(luaContext->lastError); + luaContext->lastError = NULL; + } + if (frame && !_luaPushFrame(luaContext, &frame->arguments)) { return false; } @@ -373,6 +393,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* lua_rawset(luaContext->lua, LUA_REGISTRYINDEX); if (ret == LUA_ERRRUN) { + luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1)); lua_pop(luaContext->lua, 1); } if (ret) { diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 43ffae081..9ec08f945 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -516,6 +516,26 @@ M_TEST_DEFINE(globalStructMethods) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(errorReporting) { + SETUP_LUA; + + assert_null(lua->getError(lua)); + + LOAD_PROGRAM("assert(false)"); + + assert_false(lua->run(lua)); + const char* errorBuffer = lua->getError(lua); + assert_non_null(errorBuffer); + assert_non_null(strstr(errorBuffer, "assertion failed")); + + LOAD_PROGRAM("assert(true)"); + + assert_true(lua->run(lua)); + assert_null(lua->getError(lua)); + + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(create), cmocka_unit_test(loadGood), @@ -527,4 +547,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(callCFunc), cmocka_unit_test(globalStructFieldGet), cmocka_unit_test(globalStructFieldSet), - cmocka_unit_test(globalStructMethods)) + cmocka_unit_test(globalStructMethods), + cmocka_unit_test(errorReporting), +) From 8326ecddce83c96f7af8aad3ad950db2a653e1cd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 6 May 2022 20:27:52 -0700 Subject: [PATCH 038/105] Scripting: Add missing C++ guard ends --- include/mgba/script/context.h | 2 ++ include/mgba/script/types.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 7f08b8aa2..0241cbd8a 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -54,4 +54,6 @@ void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key); bool mScriptInvoke(const struct mScriptValue* fn, struct mScriptFrame* frame); +CXX_GUARD_END + #endif diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index d4d552e4a..e5ca34b82 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -589,4 +589,6 @@ bool mScriptPopPointer(struct mScriptList* list, void** out); bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output); bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame); +CXX_GUARD_END + #endif From cdfa6ac54ba6685616979dcbee8d64d2fed5ef26 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 May 2022 20:03:13 -0700 Subject: [PATCH 039/105] Scripting: Fix unsetting globals in Lua --- src/script/engines/lua.c | 4 +++- src/script/test/lua.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index fca966d66..ae34066e7 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -142,7 +142,9 @@ struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext* ctx, const char* bool _luaSetGlobal(struct mScriptEngineContext* ctx, const char* name, struct mScriptValue* value) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; - if (!_luaWrap(luaContext, value)) { + if (!value) { + lua_pushnil(luaContext->lua); + } else if (!_luaWrap(luaContext, value)) { return false; } lua_setglobal(luaContext->lua, name); diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 9ec08f945..437fe4131 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -243,6 +243,10 @@ M_TEST_DEFINE(setGlobal) { assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); + assert_true(lua->setGlobal(lua, "b", NULL)); + val = lua->getGlobal(lua, "b"); + assert_null(val); + mScriptContextDeinit(&context); } From 7bb051b01da624c4e0449c2fe833db761cc35a2d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 May 2022 20:51:17 -0700 Subject: [PATCH 040/105] Scripting: Start hooking things together --- include/mgba/core/thread.h | 8 ++++++ include/mgba/script/context.h | 7 +++++ src/core/thread.c | 11 ++++++++ src/script/context.c | 51 +++++++++++++++++++++++++++++++++++ src/script/engines/lua.c | 11 ++++++++ 5 files changed, 88 insertions(+) diff --git a/include/mgba/core/thread.h b/include/mgba/core/thread.h index deadc36c6..72e9de7b5 100644 --- a/include/mgba/core/thread.h +++ b/include/mgba/core/thread.h @@ -11,6 +11,10 @@ CXX_GUARD_START #include +#ifdef ENABLE_SCRIPTING +#include +#include +#endif struct mCoreThread; struct mCore; @@ -39,6 +43,10 @@ struct mCoreThread { void* userData; void (*run)(struct mCoreThread*); +#ifdef ENABLE_SCRIPTING + struct mScriptContext* scriptContext; +#endif + struct mCoreThreadInternal* impl; }; diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 0241cbd8a..bb3b98b78 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -36,6 +36,8 @@ struct mScriptEngineContext { struct mScriptContext* context; void (*destroy)(struct mScriptEngineContext*); + bool (*isScript)(struct mScriptEngineContext*, const char* name, struct VFile* vf); + bool (*setGlobal)(struct mScriptEngineContext*, const char* name, struct mScriptValue*); struct mScriptValue* (*getGlobal)(struct mScriptEngineContext*, const char* name); @@ -48,10 +50,15 @@ void mScriptContextInit(struct mScriptContext*); void mScriptContextDeinit(struct mScriptContext*); struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext*, struct mScriptEngine2*); +void mScriptContextRegisterEngines(struct mScriptContext*); void mScriptContextSetGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value); void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key); +struct VFile; +bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf); +bool mScriptContextLoadFile(struct mScriptContext*, const char* path); + bool mScriptInvoke(const struct mScriptValue* fn, struct mScriptFrame* frame); CXX_GUARD_END diff --git a/src/core/thread.c b/src/core/thread.c index 801cf5bb6..2375aea4c 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -219,6 +219,12 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { mLogFilterLoad(threadContext->logger.d.filter, &core->config); } +#ifdef ENABLE_SCRIPTING + if (threadContext->scriptContext) { + mScriptContextAttachCore(threadContext->scriptContext, core); + } +#endif + mCoreThreadRewindParamsChanged(threadContext); if (threadContext->startCallback) { threadContext->startCallback(threadContext); @@ -337,6 +343,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { if (threadContext->cleanCallback) { threadContext->cleanCallback(threadContext); } +#ifdef ENABLE_SCRIPTING + if (threadContext->scriptContext) { + mScriptContextDetachCore(threadContext->scriptContext); + } +#endif core->clearCoreCallbacks(core); if (threadContext->logger.d.filter == &filter) { diff --git a/src/script/context.c b/src/script/context.c index 9cc8b09b0..44d0fdca6 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -4,12 +4,21 @@ * 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 +#ifdef USE_LUA +#include +#endif struct mScriptKVPair { const char* key; struct mScriptValue* value; }; +struct mScriptFileInfo { + const char* name; + struct VFile* vf; + struct mScriptEngineContext* context; +}; + static void _engineContextDestroy(void* ctx) { struct mScriptEngineContext* context = ctx; context->destroy(context); @@ -28,6 +37,18 @@ static void _contextRemoveGlobal(const char* key, void* value, void* user) { context->setGlobal(context, user, NULL); } +static void _contextFindForFile(const char* key, void* value, void* user) { + UNUSED(key); + struct mScriptFileInfo* info = user; + struct mScriptEngineContext* context = value; + if (info->context) { + return; + } + if (context->isScript(context, info->name, info->vf)) { + info->context = context; + } +} + void mScriptContextInit(struct mScriptContext* context) { HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref); HashTableInit(&context->engines, 0, _engineContextDestroy); @@ -46,6 +67,13 @@ struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* return ectx; } +void mScriptContextRegisterEngines(struct mScriptContext* context) { + UNUSED(context); +#ifdef USE_LUA + mScriptContextRegisterEngine(context, mSCRIPT_ENGINE_LUA); +#endif +} + void mScriptContextSetGlobal(struct mScriptContext* context, const char* key, struct mScriptValue* value) { mScriptValueRef(value); HashTableInsert(&context->rootScope, key, value); @@ -65,6 +93,29 @@ void mScriptContextRemoveGlobal(struct mScriptContext* context, const char* key) HashTableRemove(&context->rootScope, key); } +bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) { + struct mScriptFileInfo info = { + .name = name, + .vf = vf, + .context = NULL + }; + HashTableEnumerate(&context->engines, _contextFindForFile, &info); + if (!info.context) { + return false; + } + return info.context->load(info.context, vf, NULL); +} + +bool mScriptContextLoadFile(struct mScriptContext* context, const char* path) { + struct VFile* vf = VFileOpen(path, O_RDONLY); + if (!vf) { + return false; + } + bool ret = mScriptContextLoadVF(context, path, vf); + vf->close(vf); + return ret; +} + bool mScriptInvoke(const struct mScriptValue* val, struct mScriptFrame* frame) { if (val->type->base != mSCRIPT_TYPE_FUNCTION) { return false; diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index ae34066e7..c002dafc6 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -13,6 +13,7 @@ static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*); static void _luaDestroy(struct mScriptEngineContext*); +static bool _luaIsScript(struct mScriptEngineContext*, const char*, struct VFile*); static struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext*, const char* name); static bool _luaSetGlobal(struct mScriptEngineContext*, const char* name, struct mScriptValue*); static bool _luaLoad(struct mScriptEngineContext*, struct VFile*, const char** error); @@ -99,6 +100,7 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS luaContext->d = (struct mScriptEngineContext) { .context = context, .destroy = _luaDestroy, + .isScript = _luaIsScript, .getGlobal = _luaGetGlobal, .setGlobal = _luaSetGlobal, .load = _luaLoad, @@ -134,6 +136,15 @@ void _luaDestroy(struct mScriptEngineContext* ctx) { free(luaContext); } +bool _luaIsScript(struct mScriptEngineContext* ctx, const char* name, struct VFile* vf) { + UNUSED(ctx); + UNUSED(vf); + if (!name) { + return false; + } + return endswith(name, ".lua"); +} + struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext* ctx, const char* name) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; lua_getglobal(luaContext->lua, name); From af44a65c3dc08cd8e71bb5eec9dd4f496236a9ff Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 May 2022 22:26:00 -0700 Subject: [PATCH 041/105] Scripting: Add weak references for opaque runtime access --- include/mgba/script/context.h | 7 +++++ include/mgba/script/types.h | 7 ++++- src/core/test/scripting.c | 28 ++++++++++++++++++++ src/script/context.c | 48 +++++++++++++++++++++++++++++++++-- src/script/engines/lua.c | 27 +++++++++++++++++++- src/script/types.c | 9 +++++++ 6 files changed, 122 insertions(+), 4 deletions(-) diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index bb3b98b78..b808872e1 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -21,6 +21,8 @@ struct mScriptEngineContext; struct mScriptContext { struct Table rootScope; struct Table engines; + struct Table weakrefs; + uint32_t nextWeakref; }; struct mScriptEngine2 { @@ -55,6 +57,11 @@ void mScriptContextRegisterEngines(struct mScriptContext*); void mScriptContextSetGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value); void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key); +uint32_t mScriptContextSetWeakref(struct mScriptContext*, struct mScriptValue* value); +struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mScriptValue* value); +struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value); +void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); + struct VFile; bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf); bool mScriptContextLoadFile(struct mScriptContext*, const char* path); diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index e5ca34b82..e55ce4057 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -32,6 +32,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_PTR void* #define mSCRIPT_TYPE_C_TABLE Table* #define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue* +#define mSCRIPT_TYPE_C_WEAKREF uint32_t #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* #define mSCRIPT_TYPE_C_CS(STRUCT) const struct STRUCT* #define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME @@ -51,6 +52,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_PTR opaque #define mSCRIPT_TYPE_FIELD_TABLE opaque #define mSCRIPT_TYPE_FIELD_WRAPPER opaque +#define mSCRIPT_TYPE_FIELD_WEAKREF u32 #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque #define mSCRIPT_TYPE_FIELD_CS(STRUCT) copaque #define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) copaque @@ -69,6 +71,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr) #define mSCRIPT_TYPE_MS_TABLE (&mSTTable) #define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper) +#define mSCRIPT_TYPE_MS_WEAKREF (&mSTWeakref) #define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) #define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT) #define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME) @@ -435,7 +438,8 @@ enum mScriptTypeBase { mSCRIPT_TYPE_TUPLE, mSCRIPT_TYPE_LIST, mSCRIPT_TYPE_TABLE, - mSCRIPT_TYPE_WRAPPER + mSCRIPT_TYPE_WRAPPER, + mSCRIPT_TYPE_WEAKREF, }; enum mScriptClassInitType { @@ -462,6 +466,7 @@ extern const struct mScriptType mSTString; extern const struct mScriptType mSTCharPtr; extern const struct mScriptType mSTTable; extern const struct mScriptType mSTWrapper; +extern const struct mScriptType mSTWeakref; struct mScriptTypeTuple { size_t count; diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index b3af97742..63d2ef61e 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -120,6 +120,33 @@ M_TEST_DEFINE(infoFuncs) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(detach) { + SETUP_LUA; + CREATE_CORE; + core->reset(core); + + LOAD_PROGRAM( + "assert(emu)\n" + "a = emu\n" + ); + assert_true(lua->run(lua)); + + mScriptContextDetachCore(&context); + + LOAD_PROGRAM( + "assert(not emu)\n" + ); + assert_true(lua->run(lua)); + + LOAD_PROGRAM( + "a:frequency()\n" + ); + assert_false(lua->run(lua)); + + TEARDOWN_CORE; + mScriptContextDeinit(&context); +} + M_TEST_DEFINE(runFrame) { SETUP_LUA; CREATE_CORE; @@ -206,6 +233,7 @@ M_TEST_DEFINE(memoryWrite) { M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore, cmocka_unit_test(globals), cmocka_unit_test(infoFuncs), + cmocka_unit_test(detach), cmocka_unit_test(runFrame), cmocka_unit_test(memoryRead), cmocka_unit_test(memoryWrite), diff --git a/src/script/context.c b/src/script/context.c index 44d0fdca6..13fd262fb 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -52,11 +52,14 @@ static void _contextFindForFile(const char* key, void* value, void* user) { void mScriptContextInit(struct mScriptContext* context) { HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref); HashTableInit(&context->engines, 0, _engineContextDestroy); + TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref); + context->nextWeakref = 0; } void mScriptContextDeinit(struct mScriptContext* context) { HashTableDeinit(&context->engines); HashTableDeinit(&context->rootScope); + HashTableDeinit(&context->weakrefs); } struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* context, struct mScriptEngine2* engine) { @@ -75,7 +78,13 @@ void mScriptContextRegisterEngines(struct mScriptContext* context) { } void mScriptContextSetGlobal(struct mScriptContext* context, const char* key, struct mScriptValue* value) { - mScriptValueRef(value); + struct mScriptValue* oldValue = HashTableLookup(&context->rootScope, key); + if (oldValue) { + mScriptContextClearWeakref(context, oldValue->value.u32); + } + uint32_t weakref = mScriptContextSetWeakref(context, value); + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_WEAKREF); + value->value.u32 = weakref; HashTableInsert(&context->rootScope, key, value); struct mScriptKVPair pair = { .key = key, @@ -90,7 +99,42 @@ void mScriptContextRemoveGlobal(struct mScriptContext* context, const char* key) } // Since _contextRemoveGlobal doesn't mutate |key|, this cast should be safe HashTableEnumerate(&context->engines, _contextRemoveGlobal, (char*) key); - HashTableRemove(&context->rootScope, key); + struct mScriptValue* oldValue = HashTableLookup(&context->rootScope, key); + if (oldValue) { + mScriptContextClearWeakref(context, oldValue->value.u32); + HashTableRemove(&context->rootScope, key); + } +} + +uint32_t mScriptContextSetWeakref(struct mScriptContext* context, struct mScriptValue* value) { + mScriptValueRef(value); + TableInsert(&context->weakrefs, context->nextWeakref, value); + + uint32_t nextWeakref = context->nextWeakref; + ++context->nextWeakref; + while (TableLookup(&context->weakrefs, context->nextWeakref)) { + ++context->nextWeakref; + } + return nextWeakref; +} + +struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext* context, struct mScriptValue* value) { + uint32_t weakref = mScriptContextSetWeakref(context, value); + mScriptValueDeref(value); + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_WEAKREF); + value->value.u32 = weakref; + return value; +} + +struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext* context, struct mScriptValue* value) { + if (value->type != mSCRIPT_TYPE_MS_WEAKREF) { + return value; + } + return TableLookup(&context->weakrefs, value->value.u32); +} + +void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref) { + TableRemove(&context->weakrefs, weakref); } bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) { diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index c002dafc6..35d7f7c45 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -212,15 +212,23 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { } lua_pop(luaContext->lua, 2); value = lua_touserdata(luaContext->lua, -1); + value = mScriptContextAccessWeakref(luaContext->d.context, value); } lua_pop(luaContext->lua, 1); return value; } bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* value) { + uint32_t weakref; + bool needsWeakref = false; if (value->type == mSCRIPT_TYPE_MS_WRAPPER) { value = mScriptValueUnwrap(value); } + if (value->type == mSCRIPT_TYPE_MS_WEAKREF) { + weakref = value->value.u32; + value = mScriptContextAccessWeakref(luaContext->d.context, value); + needsWeakref = true; + } bool ok = true; struct mScriptValue* newValue; switch (value->type->base) { @@ -260,7 +268,11 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v break; case mSCRIPT_TYPE_OBJECT: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); - mScriptValueWrap(value, newValue); + if (needsWeakref) { + *newValue = mSCRIPT_MAKE(WEAKREF, weakref); + } else { + mScriptValueWrap(value, newValue); + } luaL_setmetatable(luaContext->lua, "mSTStruct"); break; default: @@ -484,6 +496,13 @@ int _luaGetObject(lua_State* lua) { struct mScriptValue* obj = lua_touserdata(lua, -2); struct mScriptValue val; + obj = mScriptContextAccessWeakref(luaContext->d.context, obj); + if (!obj) { + lua_pop(lua, 2); + lua_pushliteral(lua, "Invalid object"); + lua_error(lua); + } + if (!mScriptObjectGet(obj, key, &val)) { lua_pop(lua, 2); lua_pushliteral(lua, "Invalid key"); @@ -505,6 +524,12 @@ int _luaSetObject(lua_State* lua) { struct mScriptValue* obj = lua_touserdata(lua, -3); struct mScriptValue* val = _luaCoerce(luaContext); + obj = mScriptContextAccessWeakref(luaContext->d.context, obj); + if (!obj) { + lua_pushliteral(lua, "Invalid object"); + lua_error(lua); + } + lua_pop(lua, 2); if (!val) { lua_pushliteral(lua, "Invalid value"); diff --git a/src/script/types.c b/src/script/types.c index bed27625e..5a12ef2a9 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -195,6 +195,15 @@ const struct mScriptType mSTWrapper = { .hash = NULL, }; +const struct mScriptType mSTWeakref = { + .base = mSCRIPT_TYPE_WEAKREF, + .size = sizeof(uint32_t), + .name = "weakref", + .alloc = NULL, + .free = NULL, + .hash = NULL, +}; + DEFINE_VECTOR(mScriptList, struct mScriptValue) void _allocTable(struct mScriptValue* val) { From e3758597f8acbf7b4f319927d4e6ae1eb0c4cffc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 9 May 2022 00:41:18 -0700 Subject: [PATCH 042/105] Scripting: Hook up Lua strings and add context autodrain pool --- include/mgba/script/context.h | 4 ++++ include/mgba/script/types.h | 6 +++++- src/script/context.c | 33 +++++++++++++++++++++++++++++++++ src/script/engines/lua.c | 11 +++++++++++ src/script/types.c | 7 +++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index b808872e1..a6b478b17 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -21,6 +21,7 @@ struct mScriptEngineContext; struct mScriptContext { struct Table rootScope; struct Table engines; + struct mScriptList refPool; struct Table weakrefs; uint32_t nextWeakref; }; @@ -51,6 +52,9 @@ struct mScriptEngineContext { void mScriptContextInit(struct mScriptContext*); void mScriptContextDeinit(struct mScriptContext*); +void mScriptContextFillPool(struct mScriptContext*, struct mScriptValue*); +void mScriptContextDrainPool(struct mScriptContext*); + struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext*, struct mScriptEngine2*); void mScriptContextRegisterEngines(struct mScriptContext*); diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index e55ce4057..2990e4891 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -101,9 +101,11 @@ CXX_GUARD_START mSCRIPT_TYPE_C_ ## TYPE NAME; \ do { \ struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ + bool deref = true; \ if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ if (_val->type == mSCRIPT_TYPE_MS_WRAPPER) { \ _val = mScriptValueUnwrap(_val); \ + deref = false; \ if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ return false; \ } \ @@ -112,7 +114,9 @@ CXX_GUARD_START } \ } \ NAME = _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE; \ - mScriptValueDeref(_val); \ + if (deref) { \ + mScriptValueDeref(_val); \ + } \ mScriptListResize(STACK, -1); \ } while (0) diff --git a/src/script/context.c b/src/script/context.c index 13fd262fb..93d414360 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -52,6 +52,7 @@ static void _contextFindForFile(const char* key, void* value, void* user) { void mScriptContextInit(struct mScriptContext* context) { HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref); HashTableInit(&context->engines, 0, _engineContextDestroy); + mScriptListInit(&context->refPool, 0); TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref); context->nextWeakref = 0; } @@ -60,6 +61,38 @@ void mScriptContextDeinit(struct mScriptContext* context) { HashTableDeinit(&context->engines); HashTableDeinit(&context->rootScope); HashTableDeinit(&context->weakrefs); + mScriptContextDrainPool(context); + mScriptListDeinit(&context->refPool); +} + +void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* value) { + if (value->refs == mSCRIPT_VALUE_UNREF) { + return; + } + switch (value->type->base) { + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + return; + default: + break; + } + + struct mScriptValue* poolEntry = mScriptListAppend(&context->refPool); + poolEntry->type = mSCRIPT_TYPE_MS_WRAPPER; + poolEntry->value.opaque = value; + poolEntry->refs = mSCRIPT_VALUE_UNREF; +} + +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) { + mScriptValueDeref(value); + } + } + mScriptListClear(&context->refPool); } struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* context, struct mScriptEngine2* engine) { diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 35d7f7c45..a33eca20b 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -198,6 +198,9 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { value->value.s32 = lua_toboolean(luaContext->lua, -1); break; case LUA_TSTRING: + value = mScriptStringCreateFromUTF8(lua_tostring(luaContext->lua, -1)); + mScriptValueWrap(value, mScriptListAppend(&luaContext->d.context->refPool)); + mScriptValueDeref(value); break; case LUA_TFUNCTION: return _luaCoerceFunction(luaContext); @@ -259,6 +262,9 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v ok = false; } break; + case mSCRIPT_TYPE_STRING: + lua_pushstring(luaContext->lua, ((struct mScriptString*) value->value.opaque)->buffer); + break; case mSCRIPT_TYPE_FUNCTION: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); newValue->type = value->type; @@ -426,8 +432,10 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* } if (frame && !_luaPopFrame(luaContext, &frame->returnValues)) { + mScriptContextDrainPool(luaContext->d.context); return false; } + mScriptContextDrainPool(luaContext->d.context); return true; } @@ -468,6 +476,7 @@ int _luaThunk(lua_State* lua) { struct mScriptFrame frame; mScriptFrameInit(&frame); if (!_luaPopFrame(luaContext, &frame.arguments)) { + mScriptContextDrainPool(luaContext->d.context); mScriptFrameDeinit(&frame); lua_pushliteral(lua, "Error calling function (setting arguments)"); lua_error(lua); @@ -479,6 +488,7 @@ int _luaThunk(lua_State* lua) { lua_pushliteral(lua, "Error calling function (invoking)"); lua_error(lua); } + mScriptContextDrainPool(luaContext->d.context); if (!_luaPushFrame(luaContext, &frame.returnValues)) { mScriptFrameDeinit(&frame); @@ -542,6 +552,7 @@ int _luaSetObject(lua_State* lua) { lua_error(lua); } mScriptValueDeref(val); + mScriptContextDrainPool(luaContext->d.context); return 0; } diff --git a/src/script/types.c b/src/script/types.c index 5a12ef2a9..974bf01fa 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1071,6 +1071,13 @@ bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList if (types->entries[i] == mScriptListGetPointer(frame, i)->type) { continue; } + struct mScriptValue* unwrapped = NULL; + if (mScriptListGetPointer(frame, i)->type == mSCRIPT_TYPE_MS_WRAPPER) { + unwrapped = mScriptValueUnwrap(mScriptListGetPointer(frame, i)); + if (types->entries[i] == unwrapped->type) { + continue; + } + } if (!mScriptCast(types->entries[i], mScriptListGetPointer(frame, i), mScriptListGetPointer(frame, i))) { return false; } From 17d23739750b86fac2d53d4639c892538f9eed1d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 9 May 2022 00:42:13 -0700 Subject: [PATCH 043/105] Scripting: Add "console" logger bridge --- include/mgba/core/log.h | 3 + include/mgba/core/scripting.h | 9 ++- src/core/log.c | 8 +++ src/core/scripting.c | 35 ++++++++++++ src/core/test/scripting.c | 101 ++++++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 1 deletion(-) diff --git a/include/mgba/core/log.h b/include/mgba/core/log.h index e2ac55e4b..d6217aca1 100644 --- a/include/mgba/core/log.h +++ b/include/mgba/core/log.h @@ -56,6 +56,9 @@ int mLogFilterLevels(const struct mLogFilter*, int category); ATTRIBUTE_FORMAT(printf, 3, 4) void mLog(int category, enum mLogLevel level, const char* format, ...); +ATTRIBUTE_FORMAT(printf, 4, 5) +void mLogExplicit(struct mLogger*, int category, enum mLogLevel level, const char* format, ...); + #define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY, mLOG_ ## LEVEL, __VA_ARGS__) #define mLOG_DECLARE_CATEGORY(CATEGORY) extern int _mLOG_CAT_ ## CATEGORY; diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h index c65897e17..fbb560237 100644 --- a/include/mgba/core/scripting.h +++ b/include/mgba/core/scripting.h @@ -10,13 +10,18 @@ CXX_GUARD_START +#include #ifdef USE_DEBUGGERS #include #endif #include +mLOG_DECLARE_CATEGORY(SCRIPT); + struct mCore; +struct mLogger; mSCRIPT_DECLARE_STRUCT(mCore); +mSCRIPT_DECLARE_STRUCT(mLogger); struct mScriptBridge; struct VFile; @@ -52,10 +57,12 @@ bool mScriptBridgeLoadScript(struct mScriptBridge*, const char* name); bool mScriptBridgeLookupSymbol(struct mScriptBridge*, const char* name, int32_t* out); struct mScriptContext; -struct mCore; void mScriptContextAttachCore(struct mScriptContext*, struct mCore*); void mScriptContextDetachCore(struct mScriptContext*); +void mScriptContextAttachLogger(struct mScriptContext*, struct mLogger*); +void mScriptContextDetachLogger(struct mScriptContext*); + CXX_GUARD_END #endif diff --git a/src/core/log.c b/src/core/log.c index e0f5a002a..4612c02b9 100644 --- a/src/core/log.c +++ b/src/core/log.c @@ -80,6 +80,14 @@ void mLog(int category, enum mLogLevel level, const char* format, ...) { va_end(args); } +void mLogExplicit(struct mLogger* context, int category, enum mLogLevel level, const char* format, ...) { + va_list args; + va_start(args, format); + if (!context->filter || mLogFilterTest(context->filter, category, level)) { + context->log(context, category, level, format, args); + } +} + void mLogFilterInit(struct mLogFilter* filter) { HashTableInit(&filter->categories, 8, NULL); TableInit(&filter->levels, 8, NULL); diff --git a/src/core/scripting.c b/src/core/scripting.c index 8adc5f55d..9b4f4534e 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -10,6 +10,8 @@ #include #include +mLOG_DEFINE_CATEGORY(SCRIPT, "Scripting", "script"); + struct mScriptBridge { struct Table engines; struct mDebugger* debugger; @@ -196,3 +198,36 @@ void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core void mScriptContextDetachCore(struct mScriptContext* context) { mScriptContextRemoveGlobal(context, "emu"); } + +void mScriptLog(struct mLogger* logger, struct mScriptString* msg) { + mLogExplicit(logger, _mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg->buffer); +} + +void mScriptWarn(struct mLogger* logger, struct mScriptString* msg) { + mLogExplicit(logger, _mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer); +} + +void mScriptError(struct mLogger* logger, struct mScriptString* msg) { + mLogExplicit(logger, _mLOG_CAT_SCRIPT, mLOG_ERROR, "%s", msg->buffer); +} + +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mLogger, log, mScriptLog, 1, STR, msg); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mLogger, warn, mScriptWarn, 1, STR, msg); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mLogger, error, mScriptError, 1, STR, msg); + +mSCRIPT_DEFINE_STRUCT(mLogger) +mSCRIPT_DEFINE_STRUCT_METHOD(mLogger, log) +mSCRIPT_DEFINE_STRUCT_METHOD(mLogger, warn) +mSCRIPT_DEFINE_STRUCT_METHOD(mLogger, error) +mSCRIPT_DEFINE_END; + +void mScriptContextAttachLogger(struct mScriptContext* context, struct mLogger* logger) { + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mLogger)); + value->value.opaque = logger; + mScriptContextSetGlobal(context, "console", value); + mScriptValueDeref(value); +} + +void mScriptContextDetachLogger(struct mScriptContext* context) { + mScriptContextRemoveGlobal(context, "console"); +} diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index 63d2ef61e..0f5e55f7f 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -6,6 +6,7 @@ #include "util/test/suite.h" #include +#include #include #include #include @@ -23,6 +24,13 @@ #error "Need a valid platform for testing" #endif +struct mScriptTestLogger { + struct mLogger d; + char* log; + char* warn; + char* error; +}; + static const uint8_t _fakeGBROM[0x4000] = { [0x100] = 0x18, // Loop forever [0x101] = 0xFE, // jr, $-2 @@ -76,6 +84,59 @@ static const uint8_t _fakeGBROM[0x4000] = { mScriptValueDeref(global); \ } while(0) +static void _mScriptTestLog(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) { + UNUSED(category); + struct mScriptTestLogger* logger = (struct mScriptTestLogger*) log; + + char* message; +#ifdef HAVE_VASPRINTF + vasprintf(&message, format, args); +#else + char messageBuf[64]; + vsnprintf(messageBuf, format, args); + message = strdup(messageBuf); +#endif + switch (level) { + case mLOG_INFO: + if (logger->log) { + free(logger->log); + } + logger->log = message; + break; + case mLOG_WARN: + if (logger->warn) { + free(logger->warn); + } + logger->warn = message; + break; + case mLOG_ERROR: + if (logger->error) { + free(logger->error); + } + logger->error = message; + break; + default: + free(message); + } +} + +static void mScriptTestLoggerInit(struct mScriptTestLogger* logger) { + memset(logger, 0, sizeof(*logger)); + logger->d.log = _mScriptTestLog; +} + +static void mScriptTestLoggerDeinit(struct mScriptTestLogger* logger) { + if (logger->log) { + free(logger->log); + } + if (logger->warn) { + free(logger->warn); + } + if (logger->error) { + free(logger->error); + } +} + M_TEST_SUITE_SETUP(mScriptCore) { if (mSCRIPT_ENGINE_LUA->init) { mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); @@ -230,6 +291,45 @@ M_TEST_DEFINE(memoryWrite) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(logging) { + SETUP_LUA; + struct mScriptTestLogger logger; + mScriptTestLoggerInit(&logger); + + mScriptContextAttachLogger(&context, &logger.d); + + LOAD_PROGRAM( + "assert(console)\n" + "console:log(\"log\")\n" + "console:warn(\"warn\")\n" + "console:error(\"error\")\n" + "a = console\n" + ); + + assert_true(lua->run(lua)); + assert_non_null(logger.log); + assert_non_null(logger.warn); + assert_non_null(logger.error); + assert_string_equal(logger.log, "log"); + assert_string_equal(logger.warn, "warn"); + assert_string_equal(logger.error, "error"); + + mScriptContextDetachLogger(&context); + + LOAD_PROGRAM( + "assert(not console)\n" + ); + assert_true(lua->run(lua)); + + LOAD_PROGRAM( + "a:log(\"l2\")\n" + ); + assert_false(lua->run(lua)); + + mScriptTestLoggerDeinit(&logger); + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore, cmocka_unit_test(globals), cmocka_unit_test(infoFuncs), @@ -237,4 +337,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore, cmocka_unit_test(runFrame), cmocka_unit_test(memoryRead), cmocka_unit_test(memoryWrite), + cmocka_unit_test(logging), ) From 0c28e34a7e0165b696e758eb11e5c8b93129e2c3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 May 2022 17:22:15 -0700 Subject: [PATCH 044/105] Scripting: Remove static members --- include/mgba/script/types.h | 11 ---------- src/script/test/classes.c | 42 ------------------------------------- src/script/types.c | 25 ---------------------- 3 files changed, 78 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 2990e4891..60acdd529 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -226,15 +226,6 @@ CXX_GUARD_START } \ }, -#define mSCRIPT_DEFINE_STATIC_MEMBER(TYPE, NAME) { \ - .type = mSCRIPT_CLASS_INIT_STATIC_MEMBER, \ - .info = { \ - .member = { \ - .name = #NAME, \ - .type = mSCRIPT_TYPE_MS_ ## TYPE \ - } \ - }, \ -}, #define mSCRIPT_DEFINE_INHERIT(PARENT) { \ .type = mSCRIPT_CLASS_INIT_INHERIT, \ @@ -450,7 +441,6 @@ enum mScriptClassInitType { mSCRIPT_CLASS_INIT_END = 0, mSCRIPT_CLASS_INIT_DOCSTRING, mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, - mSCRIPT_CLASS_INIT_STATIC_MEMBER, mSCRIPT_CLASS_INIT_INHERIT, }; @@ -505,7 +495,6 @@ struct mScriptTypeClass { bool init; const struct mScriptClassInitDetails* details; const struct mScriptType* parent; - struct Table staticMembers; struct Table instanceMembers; }; diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 4896d8357..aa1060e93 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -93,12 +93,6 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ic1) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v0) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v1) - - mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) - mSCRIPT_DEFINE_STATIC_MEMBER(S32, s_i) - mSCRIPT_DEFINE_STATIC_MEMBER(S32, s_i2) - mSCRIPT_DEFINE_STATIC_MEMBER(S8, s_b8) - mSCRIPT_DEFINE_STATIC_MEMBER(S16, s_hUnaligned) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(TestB) @@ -123,7 +117,6 @@ M_TEST_DEFINE(testALayout) { struct mScriptClassMember* member; - // Instance members member = HashTableLookup(&cls->instanceMembers, "i"); assert_non_null(member); assert_string_equal(member->name, "i"); @@ -155,38 +148,6 @@ M_TEST_DEFINE(testALayout) { member = HashTableLookup(&cls->instanceMembers, "unknown"); assert_null(member); - // Static members - member = HashTableLookup(&cls->staticMembers, "s_i"); - assert_non_null(member); - assert_string_equal(member->name, "s_i"); - assert_string_equal(member->docstring, MEMBER_A_DOCSTRING); - assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); - assert_int_equal(member->offset, 0); - - member = HashTableLookup(&cls->staticMembers, "s_i2"); - assert_non_null(member); - assert_string_equal(member->name, "s_i2"); - assert_null(member->docstring); - assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32); - assert_int_equal(member->offset, sizeof(int32_t)); - - member = HashTableLookup(&cls->staticMembers, "s_b8"); - assert_non_null(member); - assert_string_equal(member->name, "s_b8"); - assert_null(member->docstring); - assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S8); - assert_int_equal(member->offset, sizeof(int32_t) * 2); - - member = HashTableLookup(&cls->staticMembers, "s_hUnaligned"); - assert_non_null(member); - assert_string_equal(member->name, "s_hUnaligned"); - assert_null(member->docstring); - assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); - assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); - - member = HashTableLookup(&cls->staticMembers, "unknown"); - assert_null(member); - mScriptClassDeinit(cls); assert_false(cls->init); } @@ -474,7 +435,6 @@ M_TEST_DEFINE(testADynamic) { struct mScriptClassMember* member; - // Instance methods member = HashTableLookup(&cls->instanceMembers, "ifn0"); assert_non_null(member); assert_string_equal(member->name, "ifn0"); @@ -602,7 +562,6 @@ M_TEST_DEFINE(testBLayout) { struct mScriptClassMember* member; - // Instance members member = HashTableLookup(&cls->instanceMembers, "i"); assert_non_null(member); assert_string_equal(member->name, "i"); @@ -820,7 +779,6 @@ M_TEST_DEFINE(testDLayout) { struct mScriptClassMember* member; - // Instance members member = HashTableLookup(&cls->instanceMembers, "a"); assert_non_null(member); assert_string_equal(member->name, "a"); diff --git a/src/script/types.c b/src/script/types.c index 974bf01fa..a29ab4862 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -759,7 +759,6 @@ void mScriptFrameDeinit(struct mScriptFrame* frame) { static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScriptClassInitDetails* details, bool child) { const char* docstring = NULL; - size_t staticOffset = 0; size_t i; for (i = 0; details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { @@ -791,28 +790,6 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript } HashTableInsert(&cls->instanceMembers, member->name, member); break; - case mSCRIPT_CLASS_INIT_STATIC_MEMBER: - if (!child) { - member = calloc(1, sizeof(*member)); - memcpy(member, &detail->info.member, sizeof(*member)); - if (docstring) { - member->docstring = docstring; - docstring = NULL; - } - - // Alignment check - if (staticOffset & (detail->info.member.type->size - 1)) { - size_t size = detail->info.member.type->size; - if (size > MAX_ALIGNMENT) { - size = MAX_ALIGNMENT; - } - staticOffset = (staticOffset & ~(size - 1)) + size; - } - member->offset = staticOffset; - staticOffset += detail->info.member.type->size; - HashTableInsert(&cls->staticMembers, member->name, member); - } - break; } } } @@ -821,7 +798,6 @@ void mScriptClassInit(struct mScriptTypeClass* cls) { if (cls->init) { return; } - HashTableInit(&cls->staticMembers, 0, free); HashTableInit(&cls->instanceMembers, 0, free); _mScriptClassInit(cls, cls->details, false); @@ -834,7 +810,6 @@ void mScriptClassDeinit(struct mScriptTypeClass* cls) { return; } HashTableDeinit(&cls->instanceMembers); - HashTableDeinit(&cls->staticMembers); cls->init = false; } From c296ea79ffd3b43fcac33d0d75a59b62ffe427e3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 May 2022 20:33:39 -0700 Subject: [PATCH 045/105] Scripting: Add flag for freeing the value buffer --- include/mgba/script/types.h | 6 ++++++ src/script/types.c | 3 +++ 2 files changed, 9 insertions(+) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 60acdd529..646ee7704 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -135,6 +135,7 @@ CXX_GUARD_START struct mScriptValue* _val = mScriptListAppend(STACK); \ _val->type = mSCRIPT_TYPE_MS_ ## TYPE; \ _val->refs = mSCRIPT_VALUE_UNREF; \ + _val->flags = 0; \ _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE = NAME; \ } while (0) @@ -444,6 +445,10 @@ enum mScriptClassInitType { mSCRIPT_CLASS_INIT_INHERIT, }; +enum { + mSCRIPT_VALUE_FLAG_FREE_BUFFER = 1 +}; + struct mScriptType; extern const struct mScriptType mSTVoid; extern const struct mScriptType mSTSInt8; @@ -522,6 +527,7 @@ struct mScriptType { struct mScriptValue { const struct mScriptType* type; int refs; + uint32_t flags; union { int32_t s32; uint32_t u32; diff --git a/src/script/types.c b/src/script/types.c index a29ab4862..cf57bda5e 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -636,6 +636,7 @@ struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) { struct mScriptValue* val = malloc(sizeof(*val)); val->refs = 1; val->type = type; + val->flags = 0; if (type->alloc) { type->alloc(val); } else { @@ -662,6 +663,8 @@ void mScriptValueDeref(struct mScriptValue* val) { } if (val->type->free) { val->type->free(val); + } else if (val->flags & mSCRIPT_VALUE_FLAG_FREE_BUFFER) { + free(val->value.opaque); } free(val); } From aa91ffabfd2464d53b762dc4a75a985c4c6a98c1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 May 2022 20:41:33 -0700 Subject: [PATCH 046/105] Scripting: Revamp pointer handling, struct casting --- include/mgba/script/types.h | 51 +++++++++-- src/script/types.c | 177 ++++++++++++++++++++++-------------- 2 files changed, 156 insertions(+), 72 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 646ee7704..421a7bfb4 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -30,12 +30,15 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_STR struct mScriptString* #define mSCRIPT_TYPE_C_CHARP const char* #define mSCRIPT_TYPE_C_PTR void* +#define mSCRIPT_TYPE_C_CPTR const void* #define mSCRIPT_TYPE_C_TABLE Table* #define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue* #define mSCRIPT_TYPE_C_WEAKREF uint32_t #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* #define mSCRIPT_TYPE_C_CS(STRUCT) const struct STRUCT* #define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME +#define mSCRIPT_TYPE_C_PS(X) void +#define mSCRIPT_TYPE_C_PCS(X) void #define mSCRIPT_TYPE_FIELD_S8 s32 #define mSCRIPT_TYPE_FIELD_U8 s32 @@ -48,7 +51,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_U64 u64 #define mSCRIPT_TYPE_FIELD_F64 f64 #define mSCRIPT_TYPE_FIELD_STR opaque -#define mSCRIPT_TYPE_FIELD_CHARP opaque +#define mSCRIPT_TYPE_FIELD_CHARP copaque #define mSCRIPT_TYPE_FIELD_PTR opaque #define mSCRIPT_TYPE_FIELD_TABLE opaque #define mSCRIPT_TYPE_FIELD_WRAPPER opaque @@ -56,6 +59,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque #define mSCRIPT_TYPE_FIELD_CS(STRUCT) copaque #define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) copaque +#define mSCRIPT_TYPE_FIELD_PS(STRUCT) opaque +#define mSCRIPT_TYPE_FIELD_PCS(STRUCT) copaque #define mSCRIPT_TYPE_MS_S8 (&mSTSInt8) #define mSCRIPT_TYPE_MS_U8 (&mSTUInt8) @@ -75,6 +80,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) #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_FIELD_NAME(V) (V)->name @@ -167,18 +174,30 @@ CXX_GUARD_START #define mSCRIPT_DECLARE_STRUCT(STRUCT) \ extern const struct mScriptType mSTStruct_ ## STRUCT; \ - extern const struct mScriptType mSTStructConst_ ## STRUCT; + extern const struct mScriptType mSTStructConst_ ## STRUCT; \ + extern const struct mScriptType mSTStructPtr_ ## STRUCT; \ + extern const struct mScriptType mSTStructPtrConst_ ## STRUCT; #define mSCRIPT_DEFINE_STRUCT(STRUCT) \ const struct mScriptType mSTStruct_ ## STRUCT; \ const struct mScriptType mSTStructConst_ ## STRUCT; \ + const struct mScriptType mSTStructPtr_ ## STRUCT; \ + const struct mScriptType mSTStructPtrConst_ ## STRUCT; \ static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT; \ - static bool _mSTStructCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \ - if (input->type == type || (input->type == &mSTStruct_ ## STRUCT && type == &mSTStructConst_ ## STRUCT)) { \ + static bool _mSTStructPtrCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \ + if (input->type == type || (input->type->constType == type)) { \ output->type = type; \ output->value.opaque = input->value.opaque; \ return true; \ } \ + if (input->type != &mSTStructPtr_ ## STRUCT && input->type != &mSTStructPtrConst_ ## STRUCT) { \ + return false; \ + } \ + if (type == &mSTStructConst_ ## STRUCT || (!input->type->isConst && type == &mSTStruct_ ## STRUCT)) { \ + output->type = type; \ + output->value.opaque = *(void**) input->value.opaque; \ + return true; \ + } \ return false; \ } \ const struct mScriptType mSTStruct_ ## STRUCT = { \ @@ -190,7 +209,7 @@ CXX_GUARD_START .name = "struct::" #STRUCT, \ .alloc = NULL, \ .free = NULL, \ - .cast = _mSTStructCast_ ## STRUCT, \ + .cast = mScriptObjectCast, \ .constType = &mSTStructConst_ ## STRUCT, \ }; \ const struct mScriptType mSTStructConst_ ## STRUCT = { \ @@ -203,7 +222,25 @@ CXX_GUARD_START .name = "const struct::" #STRUCT, \ .alloc = NULL, \ .free = NULL, \ - .cast = _mSTStructCast_ ## STRUCT, \ + .cast = mScriptObjectCast, \ + }; \ + const struct mScriptType mSTStructPtr_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OPAQUE, \ + .size = sizeof(void*), \ + .name = "ptr struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = _mSTStructPtrCast_ ## STRUCT, \ + .constType = &mSTStructPtrConst_ ## STRUCT, \ + }; \ + const struct mScriptType mSTStructPtrConst_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OPAQUE, \ + .isConst = true, \ + .size = sizeof(void*), \ + .name = "ptr const struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = _mSTStructPtrCast_ ## STRUCT, \ }; \ static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ .init = false, \ @@ -580,7 +617,9 @@ void mScriptClassInit(struct mScriptTypeClass* cls); void mScriptClassDeinit(struct mScriptTypeClass* cls); bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScriptValue*); +bool mScriptObjectGetConst(const struct mScriptValue* obj, const char* member, struct mScriptValue*); bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue*); +bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) ; bool mScriptPopS32(struct mScriptList* list, int32_t* out); bool mScriptPopU32(struct mScriptList* list, uint32_t* out); diff --git a/src/script/types.c b/src/script/types.c index cf57bda5e..4ff7038e8 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -816,6 +816,86 @@ void mScriptClassDeinit(struct mScriptTypeClass* cls) { cls->init = false; } + +static bool _accessRawMember(struct mScriptClassMember* member, void* raw, bool isConst, struct mScriptValue* val) { + raw = (void*) ((uintptr_t) raw + member->offset); + switch (member->type->base) { + case mSCRIPT_TYPE_SINT: + switch (member->type->size) { + case 1: + *val = mSCRIPT_MAKE_S32(*(int8_t *) raw); + break; + case 2: + *val = mSCRIPT_MAKE_S32(*(int16_t *) raw); + break; + case 4: + *val = mSCRIPT_MAKE_S32(*(int32_t *) raw); + break; + case 8: + *val = mSCRIPT_MAKE_S64(*(int64_t *) raw); + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_UINT: + switch (member->type->size) { + case 1: + *val = mSCRIPT_MAKE_U32(*(uint8_t *) raw); + break; + case 2: + *val = mSCRIPT_MAKE_U32(*(uint16_t *) raw); + break; + case 4: + *val = mSCRIPT_MAKE_U32(*(uint32_t *) raw); + break; + case 8: + *val = mSCRIPT_MAKE_U64(*(uint64_t *) raw); + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_FLOAT: + switch (member->type->size) { + case 4: + *val = mSCRIPT_MAKE_F32(*(mSCRIPT_TYPE_C_F32 *) raw); + break; + case 8: + *val = mSCRIPT_MAKE_F64(*(mSCRIPT_TYPE_C_F64 *) raw); + break; + default: + return false; + } + break; + case mSCRIPT_TYPE_FUNCTION: + val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; + val->type = member->type; + member->type->alloc(val); + break; + case mSCRIPT_TYPE_OBJECT: + val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; + val->value.opaque = raw; + if (isConst && !member->type->isConst) { + val->type = member->type->constType; + } else { + val->type = member->type; + } + break; + case mSCRIPT_TYPE_OPAQUE: + val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; + val->value.opaque = raw; + val->type = member->type; + break; + default: + return false; + } + return true; +} + bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScriptValue* val) { if (obj->type->base == mSCRIPT_TYPE_WRAPPER) { obj = mScriptValueUnwrap(obj); @@ -836,74 +916,28 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri return false; } - void* rawMember = (void *)((uintptr_t) obj->value.opaque + m->offset); - switch (m->type->base) { - case mSCRIPT_TYPE_SINT: - switch (m->type->size) { - case 1: - *val = mSCRIPT_MAKE_S32(*(int8_t *) rawMember); - break; - case 2: - *val = mSCRIPT_MAKE_S32(*(int16_t *) rawMember); - break; - case 4: - *val = mSCRIPT_MAKE_S32(*(int32_t *) rawMember); - break; - case 8: - *val = mSCRIPT_MAKE_S64(*(int64_t *) rawMember); - break; - default: - return false; - } - break; - case mSCRIPT_TYPE_UINT: - switch (m->type->size) { - case 1: - *val = mSCRIPT_MAKE_U32(*(uint8_t *) rawMember); - break; - case 2: - *val = mSCRIPT_MAKE_U32(*(uint16_t *) rawMember); - break; - case 4: - *val = mSCRIPT_MAKE_U32(*(uint32_t *) rawMember); - break; - case 8: - *val = mSCRIPT_MAKE_U64(*(uint64_t *) rawMember); - break; - default: - return false; - } - break; - case mSCRIPT_TYPE_FLOAT: - switch (m->type->size) { - case 4: - *val = mSCRIPT_MAKE_F32(*(mSCRIPT_TYPE_C_F32 *) rawMember); - break; - case 8: - *val = mSCRIPT_MAKE_F64(*(mSCRIPT_TYPE_C_F64 *) rawMember); - break; - default: - return false; - } - break; - case mSCRIPT_TYPE_FUNCTION: - val->refs = mSCRIPT_VALUE_UNREF; - val->type = m->type; - m->type->alloc(val); - break; - case mSCRIPT_TYPE_OBJECT: - val->refs = mSCRIPT_VALUE_UNREF; - val->value.opaque = rawMember; - if (obj->type->isConst && !m->type->isConst) { - val->type = m->type->constType; - } else { - val->type = m->type; - } - break; - default: + return _accessRawMember(m, obj->value.opaque, obj->type->isConst, val); +} + +bool mScriptObjectGetConst(const struct mScriptValue* obj, const char* member, struct mScriptValue* val) { + if (obj->type->base == mSCRIPT_TYPE_WRAPPER) { + obj = mScriptValueUnwrapConst(obj); + } + if (obj->type->base != mSCRIPT_TYPE_OBJECT) { return false; } - return true; + + const struct mScriptTypeClass* cls = obj->type->details.cls; + if (!cls) { + return false; + } + + struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member); + if (!m) { + return false; + } + + return _accessRawMember(m, obj->value.opaque, true, val); } bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue* val) { @@ -985,6 +1019,17 @@ bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScri return true; } +bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { + if (input->type == type || (input->type->constType == type)) { + output->type = type; + output->value.opaque = input->value.opaque; + output->refs = mSCRIPT_VALUE_UNREF; + output->flags = 0; + return true; + } + return false; +} + bool mScriptPopS32(struct mScriptList* list, int32_t* out) { mSCRIPT_POP(list, S32, val); *out = val; From deff3585bde1a9ae861e4570fdda6ebad2bc3dd1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 May 2022 20:53:40 -0700 Subject: [PATCH 047/105] Scripting: Default "get" handling a la Python and casting a struct into one of its members --- include/mgba/script/types.h | 26 +++++++++++++++++++-- src/script/test/classes.c | 46 ++++++++++++++++++++++++++++++++++++- src/script/test/types.c | 3 +++ src/script/types.c | 42 ++++++++++++++++++++++++++++++++- 4 files changed, 113 insertions(+), 4 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 421a7bfb4..c6b859acb 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -253,17 +253,19 @@ CXX_GUARD_START } \ }, -#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) { \ +#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \ .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ .info = { \ .member = { \ - .name = #NAME, \ + .name = #EXPORTED_NAME, \ .type = mSCRIPT_TYPE_MS_ ## TYPE, \ .offset = offsetof(struct STRUCT, NAME) \ } \ } \ }, +#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \ + mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME) #define mSCRIPT_DEFINE_INHERIT(PARENT) { \ .type = mSCRIPT_CLASS_INIT_INHERIT, \ @@ -378,6 +380,18 @@ CXX_GUARD_START #define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) +#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, _get, _get) + +#define mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(TYPE, CAST_TYPE, MEMBER) { \ + .type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \ + .info = { \ + .castMember = { \ + .type = mSCRIPT_TYPE_MS_ ## CAST_TYPE, \ + .member = #MEMBER \ + } \ + }, \ +}, + #define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } #define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ @@ -480,6 +494,7 @@ enum mScriptClassInitType { mSCRIPT_CLASS_INIT_DOCSTRING, mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, mSCRIPT_CLASS_INIT_INHERIT, + mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, }; enum { @@ -524,12 +539,18 @@ struct mScriptClassMember { size_t offset; }; +struct mScriptClassCastMember { + const struct mScriptType* type; + const char* member; +}; + struct mScriptClassInitDetails { enum mScriptClassInitType type; union { const char* comment; const struct mScriptType* parent; struct mScriptClassMember member; + struct mScriptClassCastMember castMember; } info; }; @@ -538,6 +559,7 @@ struct mScriptTypeClass { const struct mScriptClassInitDetails* details; const struct mScriptType* parent; struct Table instanceMembers; + struct Table castToMembers; }; struct mScriptValue; diff --git a/src/script/test/classes.c b/src/script/test/classes.c index aa1060e93..475514599 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -35,6 +35,9 @@ struct TestD { struct TestC b; }; +struct TestE { +}; + static int32_t testAi0(struct TestA* a) { return a->i; } @@ -59,6 +62,11 @@ static void testAv1(struct TestA* a, int b) { a->i += b; } +static int32_t testGet(struct TestE* e, const char* name) { + UNUSED(e); + return name[0]; +} + #define MEMBER_A_DOCSTRING "Member a" mSCRIPT_DECLARE_STRUCT(TestA); @@ -109,6 +117,13 @@ mSCRIPT_DEFINE_STRUCT(TestD) mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), b) mSCRIPT_DEFINE_END; +mSCRIPT_DECLARE_STRUCT(TestE); +mSCRIPT_DECLARE_STRUCT_METHOD(TestE, S32, _get, testGet, 1, CHARP, name); + +mSCRIPT_DEFINE_STRUCT(TestE) + mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TestE) +mSCRIPT_DEFINE_END; + M_TEST_DEFINE(testALayout) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); @@ -258,6 +273,8 @@ M_TEST_DEFINE(testAGet) { assert_true(mScriptObjectGet(&sval, "hUnaligned", &val)); assert_true(compare.type->equal(&compare, &val)); + assert_false(mScriptObjectGet(&sval, "unknown", &val)); + assert_true(cls->init); mScriptClassDeinit(cls); assert_false(cls->init); @@ -310,6 +327,8 @@ M_TEST_DEFINE(testASet) { assert_false(mScriptObjectSet(&sval, "hUnaligned", &val)); assert_int_equal(s.hUnaligned, 5); + assert_false(mScriptObjectSet(&sval, "unknown", &val)); + assert_true(cls->init); mScriptClassDeinit(cls); assert_false(cls->init); @@ -868,6 +887,29 @@ M_TEST_DEFINE(testDSet) { assert_false(cls->init); } +M_TEST_DEFINE(testEGet) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestE)->details.cls; + + struct TestE s = { + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestE, &s); + struct mScriptValue val; + struct mScriptValue compare; + + compare = mSCRIPT_MAKE_S32('a'); + assert_true(mScriptObjectGet(&sval, "a", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32('b'); + assert_true(mScriptObjectGet(&sval, "b", &val)); + assert_true(compare.type->equal(&compare, &val)); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), cmocka_unit_test(testASignatures), @@ -880,4 +922,6 @@ M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testBSet), cmocka_unit_test(testDLayout), cmocka_unit_test(testDGet), - cmocka_unit_test(testDSet)) + cmocka_unit_test(testDSet), + cmocka_unit_test(testEGet), +) diff --git a/src/script/test/types.c b/src/script/test/types.c index ed1f26a79..dd0e3cdff 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -280,6 +280,9 @@ M_TEST_DEFINE(wrongConst) { .variable = false }; + mScriptClassInit(mSCRIPT_TYPE_MS_S(Test)->details.cls); + mScriptClassInit(mSCRIPT_TYPE_MS_CS(Test)->details.cls); + mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, S(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); diff --git a/src/script/types.c b/src/script/types.c index 4ff7038e8..33ea90f25 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include @@ -793,6 +794,9 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript } HashTableInsert(&cls->instanceMembers, member->name, member); break; + case mSCRIPT_CLASS_INIT_CAST_TO_MEMBER: + HashTableInsert(&cls->castToMembers, detail->info.castMember.type->name, (char*) detail->info.castMember.member); + break; } } } @@ -802,6 +806,7 @@ void mScriptClassInit(struct mScriptTypeClass* cls) { return; } HashTableInit(&cls->instanceMembers, 0, free); + HashTableInit(&cls->castToMembers, 0, NULL); _mScriptClassInit(cls, cls->details, false); @@ -813,6 +818,7 @@ void mScriptClassDeinit(struct mScriptTypeClass* cls) { return; } HashTableDeinit(&cls->instanceMembers); + HashTableDeinit(&cls->castToMembers); cls->init = false; } @@ -913,7 +919,26 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member); if (!m) { - return false; + struct mScriptValue getMember; + m = HashTableLookup(&cls->instanceMembers, "_get"); + if (!m || !_accessRawMember(m, obj->value.opaque, obj->type->isConst, &getMember)) { + return false; + } + struct mScriptFrame frame; + mScriptFrameInit(&frame); + struct mScriptValue* this = mScriptListAppend(&frame.arguments); + this->type = obj->type; + this->refs = mSCRIPT_VALUE_UNREF; + this->flags = 0; + this->value.opaque = obj; + mSCRIPT_PUSH(&frame.arguments, CHARP, member); + if (!mScriptInvoke(&getMember, &frame) || mScriptListSize(&frame.returnValues) != 1) { + mScriptFrameDeinit(&frame); + return false; + } + memcpy(val, mScriptListGetPointer(&frame.returnValues, 0), sizeof(*val)); + mScriptFrameDeinit(&frame); + return true; } return _accessRawMember(m, obj->value.opaque, obj->type->isConst, val); @@ -1027,6 +1052,21 @@ bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptTyp output->flags = 0; return true; } + if (input->type->base != mSCRIPT_TYPE_OBJECT) { + return false; + } + const char* member = HashTableLookup(&input->type->details.cls->castToMembers, type->name); + if (member) { + struct mScriptValue cast; + if (!mScriptObjectGetConst(input, member, &cast)) { + return false; + } + if (cast.type == type) { + memcpy(output, &cast, sizeof(*output)); + return true; + } + return mScriptCast(type, &cast, output); + } return false; } From c6e18b2a5953ad93b2f76062824cb03748a4937f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 May 2022 21:07:11 -0700 Subject: [PATCH 048/105] Scripting: Lua memory fixes --- src/script/engines/lua.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index a33eca20b..e9ef02bbb 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -10,6 +10,8 @@ #include #include +#define MAX_KEY_SIZE 128 + static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*); static void _luaDestroy(struct mScriptEngineContext*); @@ -264,6 +266,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v break; case mSCRIPT_TYPE_STRING: lua_pushstring(luaContext->lua, ((struct mScriptString*) value->value.opaque)->buffer); + mScriptValueDeref(value); break; case mSCRIPT_TYPE_FUNCTION: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -271,6 +274,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v newValue->refs = mSCRIPT_VALUE_UNREF; newValue->type->alloc(newValue); lua_pushcclosure(luaContext->lua, _luaThunk, 1); + mScriptValueDeref(value); break; case mSCRIPT_TYPE_OBJECT: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -502,24 +506,30 @@ int _luaThunk(lua_State* lua) { int _luaGetObject(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); - const char* key = lua_tostring(lua, -1); + char key[MAX_KEY_SIZE]; + const char* keyPtr = lua_tostring(lua, -1); struct mScriptValue* obj = lua_touserdata(lua, -2); struct mScriptValue val; + if (!keyPtr) { + lua_pop(lua, 2); + lua_pushliteral(lua, "Invalid key"); + lua_error(lua); + } + strlcpy(key, keyPtr, sizeof(key)); + lua_pop(lua, 2); + obj = mScriptContextAccessWeakref(luaContext->d.context, obj); if (!obj) { - lua_pop(lua, 2); lua_pushliteral(lua, "Invalid object"); lua_error(lua); } if (!mScriptObjectGet(obj, key, &val)) { - lua_pop(lua, 2); lua_pushliteral(lua, "Invalid key"); lua_error(lua); } - lua_pop(lua, 2); if (!_luaWrap(luaContext, &val)) { lua_pushliteral(lua, "Invalid value"); lua_error(lua); @@ -530,17 +540,25 @@ int _luaGetObject(lua_State* lua) { int _luaSetObject(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); - const char* key = lua_tostring(lua, -2); + char key[MAX_KEY_SIZE]; + const char* keyPtr = lua_tostring(lua, -2); struct mScriptValue* obj = lua_touserdata(lua, -3); struct mScriptValue* val = _luaCoerce(luaContext); + if (!keyPtr) { + lua_pop(lua, 2); + lua_pushliteral(lua, "Invalid key"); + lua_error(lua); + } + strlcpy(key, keyPtr, sizeof(key)); + lua_pop(lua, 2); + obj = mScriptContextAccessWeakref(luaContext->d.context, obj); if (!obj) { lua_pushliteral(lua, "Invalid object"); lua_error(lua); } - lua_pop(lua, 2); if (!val) { lua_pushliteral(lua, "Invalid value"); lua_error(lua); From bbf6d94fe2f8bdafd90ae2a3165ad4d3a642e526 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 May 2022 21:43:03 -0700 Subject: [PATCH 049/105] Scripting: More table scaffolding --- include/mgba/script/types.h | 1 + src/script/engines/lua.c | 56 ++++++++++++++++++++++++++++++++++++- src/script/types.c | 14 ++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index c6b859acb..16e8b3eb3 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -631,6 +631,7 @@ struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value); bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key); struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key); +bool mScriptTableClear(struct mScriptValue* table); void mScriptFrameInit(struct mScriptFrame* frame); void mScriptFrameDeinit(struct mScriptFrame* frame); diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index e9ef02bbb..b7153af1c 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -38,6 +38,7 @@ static int _luaThunk(lua_State* lua); static int _luaGetObject(lua_State* lua); static int _luaSetObject(lua_State* lua); static int _luaGcObject(lua_State* lua); +static int _luaGetTable(lua_State* lua); #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber @@ -96,6 +97,11 @@ static const luaL_Reg _mSTStruct[] = { { NULL, NULL } }; +static const luaL_Reg _mSTTable[] = { + { "__index", _luaGetTable }, + { NULL, NULL } +}; + struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mScriptContext* context) { UNUSED(engine); struct mScriptEngineContextLua* luaContext = calloc(1, sizeof(*luaContext)); @@ -122,6 +128,14 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS #endif lua_pop(luaContext->lua, 1); + luaL_newmetatable(luaContext->lua, "mSTTable"); +#if LUA_VERSION_NUM < 502 + luaL_register(luaContext->lua, NULL, _mSTTable); +#else + luaL_setfuncs(luaContext->lua, _mSTTable, 0); +#endif + lua_pop(luaContext->lua, 1); + return &luaContext->d; } @@ -268,6 +282,15 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v lua_pushstring(luaContext->lua, ((struct mScriptString*) value->value.opaque)->buffer); mScriptValueDeref(value); break; + case mSCRIPT_TYPE_TABLE: + newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + if (needsWeakref) { + *newValue = mSCRIPT_MAKE(WEAKREF, weakref); + } else { + mScriptValueWrap(value, newValue); + } + luaL_setmetatable(luaContext->lua, "mSTTable"); + break; case mSCRIPT_TYPE_FUNCTION: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); newValue->type = value->type; @@ -537,7 +560,6 @@ int _luaGetObject(lua_State* lua) { return 1; } - int _luaSetObject(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); char key[MAX_KEY_SIZE]; @@ -583,3 +605,35 @@ static int _luaGcObject(lua_State* lua) { mScriptValueDeref(val); return 0; } + +int _luaGetTable(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + char key[MAX_KEY_SIZE]; + const char* keyPtr = lua_tostring(lua, -1); + struct mScriptValue* obj = lua_touserdata(lua, -2); + + if (!keyPtr) { + lua_pop(lua, 2); + return 0; + } + strlcpy(key, keyPtr, sizeof(key)); + lua_pop(lua, 2); + + obj = mScriptContextAccessWeakref(luaContext->d.context, obj); + if (!obj) { + lua_pushliteral(lua, "Invalid object"); + lua_error(lua); + } + + struct mScriptValue keyVal = mSCRIPT_MAKE_CHARP(key); + struct mScriptValue* val = mScriptTableLookup(obj, &keyVal); + if (!val) { + return 0; + } + + if (!_luaWrap(luaContext, val)) { + lua_pushliteral(lua, "Invalid value"); + lua_error(lua); + } + return 1; +} diff --git a/src/script/types.c b/src/script/types.c index 33ea90f25..cea72ad71 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -751,6 +751,15 @@ struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScri return HashTableLookupCustom(t, key); } +bool mScriptTableClear(struct mScriptValue* table) { + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return false; + } + struct Table* t = table->value.opaque; + HashTableClear(t); + return true; +} + void mScriptFrameInit(struct mScriptFrame* frame) { mScriptListInit(&frame->arguments, 4); mScriptListInit(&frame->returnValues, 1); @@ -874,6 +883,11 @@ static bool _accessRawMember(struct mScriptClassMember* member, void* raw, bool return false; } break; + case mSCRIPT_TYPE_TABLE: + val->refs = mSCRIPT_VALUE_UNREF; + val->type = mSCRIPT_TYPE_MS_WRAPPER; + val->value.opaque = raw; + break; case mSCRIPT_TYPE_FUNCTION: val->refs = mSCRIPT_VALUE_UNREF; val->flags = 0; From 076299a5f6f27414d3b6aa306457c10796d9621b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 13 May 2022 01:41:52 -0700 Subject: [PATCH 050/105] Scripting: Start bringing up constructors/destructors --- include/mgba/script/types.h | 24 +++++++++++--- src/script/test/classes.c | 33 ++++++++++++++++++++ src/script/types.c | 62 ++++++++++++++++++++++++++++++++++++- 3 files changed, 113 insertions(+), 6 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 16e8b3eb3..08925f514 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -208,7 +208,7 @@ CXX_GUARD_START .size = sizeof(struct STRUCT), \ .name = "struct::" #STRUCT, \ .alloc = NULL, \ - .free = NULL, \ + .free = mScriptObjectFree, \ .cast = mScriptObjectCast, \ .constType = &mSTStructConst_ ## STRUCT, \ }; \ @@ -355,7 +355,6 @@ CXX_GUARD_START return true; \ } \ - #define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) @@ -368,8 +367,8 @@ CXX_GUARD_START #define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) -#define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) { \ - .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ +#define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME) { \ + .type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \ .info = { \ .member = { \ .name = #EXPORTED_NAME, \ @@ -378,9 +377,15 @@ CXX_GUARD_START }, \ }, +#define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) \ + _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME) + #define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) -#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, _get, _get) +#define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init) +#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit) +#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get) +#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set) #define mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(TYPE, CAST_TYPE, MEMBER) { \ .type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \ @@ -495,6 +500,10 @@ enum mScriptClassInitType { mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, mSCRIPT_CLASS_INIT_INHERIT, mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, + mSCRIPT_CLASS_INIT_INIT, + mSCRIPT_CLASS_INIT_DEINIT, + mSCRIPT_CLASS_INIT_GET, + mSCRIPT_CLASS_INIT_SET, }; enum { @@ -560,6 +569,10 @@ struct mScriptTypeClass { const struct mScriptType* parent; struct Table instanceMembers; struct Table castToMembers; + struct mScriptClassMember* alloc; // TODO + struct mScriptClassMember* free; + struct mScriptClassMember* get; + struct mScriptClassMember* set; // TODO }; struct mScriptValue; @@ -643,6 +656,7 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri bool mScriptObjectGetConst(const struct mScriptValue* obj, const char* member, struct mScriptValue*); bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue*); bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) ; +void mScriptObjectFree(struct mScriptValue* obj); bool mScriptPopS32(struct mScriptList* list, int32_t* out); bool mScriptPopU32(struct mScriptList* list, uint32_t* out); diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 475514599..f019b9f91 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -38,6 +38,10 @@ struct TestD { struct TestE { }; +struct TestF { + int* ref; +}; + static int32_t testAi0(struct TestA* a) { return a->i; } @@ -67,6 +71,10 @@ static int32_t testGet(struct TestE* e, const char* name) { return name[0]; } +static void testDeinit(struct TestF* f) { + ++*f->ref; +} + #define MEMBER_A_DOCSTRING "Member a" mSCRIPT_DECLARE_STRUCT(TestA); @@ -124,6 +132,13 @@ mSCRIPT_DEFINE_STRUCT(TestE) mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TestE) mSCRIPT_DEFINE_END; +mSCRIPT_DECLARE_STRUCT(TestF); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestF, _deinit, testDeinit, 0); + +mSCRIPT_DEFINE_STRUCT(TestF) + mSCRIPT_DEFINE_STRUCT_DEINIT(TestF) +mSCRIPT_DEFINE_END; + M_TEST_DEFINE(testALayout) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); @@ -910,6 +925,23 @@ M_TEST_DEFINE(testEGet) { assert_false(cls->init); } +M_TEST_DEFINE(testFDeinit) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestF)->details.cls; + + int ref = 0; + struct TestF* s = calloc(1, sizeof(struct TestF)); + s->ref = &ref; + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(TestF)); + val->value.opaque = s; + val->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + mScriptValueDeref(val); + assert_int_equal(ref, 1); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), cmocka_unit_test(testASignatures), @@ -924,4 +956,5 @@ M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testDGet), cmocka_unit_test(testDSet), cmocka_unit_test(testEGet), + cmocka_unit_test(testFDeinit), ) diff --git a/src/script/types.c b/src/script/types.c index cea72ad71..f2c5efc60 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -806,6 +806,38 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript case mSCRIPT_CLASS_INIT_CAST_TO_MEMBER: HashTableInsert(&cls->castToMembers, detail->info.castMember.type->name, (char*) detail->info.castMember.member); break; + case mSCRIPT_CLASS_INIT_INIT: + cls->alloc = calloc(1, sizeof(*member)); + memcpy(cls->alloc, &detail->info.member, sizeof(*member)); + if (docstring) { + cls->alloc->docstring = docstring; + docstring = NULL; + } + break; + case mSCRIPT_CLASS_INIT_DEINIT: + cls->free = calloc(1, sizeof(*member)); + memcpy(cls->free, &detail->info.member, sizeof(*member)); + if (docstring) { + cls->free->docstring = docstring; + docstring = NULL; + } + break; + case mSCRIPT_CLASS_INIT_GET: + cls->get = calloc(1, sizeof(*member)); + memcpy(cls->get, &detail->info.member, sizeof(*member)); + if (docstring) { + cls->get->docstring = docstring; + docstring = NULL; + } + break; + case mSCRIPT_CLASS_INIT_SET: + cls->set = calloc(1, sizeof(*member)); + memcpy(cls->set, &detail->info.member, sizeof(*member)); + if (docstring) { + cls->set->docstring = docstring; + docstring = NULL; + } + break; } } } @@ -817,6 +849,10 @@ void mScriptClassInit(struct mScriptTypeClass* cls) { HashTableInit(&cls->instanceMembers, 0, free); HashTableInit(&cls->castToMembers, 0, NULL); + cls->alloc = NULL; + cls->free = NULL; + cls->get = NULL; + cls->set = NULL; _mScriptClassInit(cls, cls->details, false); cls->init = true; @@ -934,7 +970,7 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member); if (!m) { struct mScriptValue getMember; - m = HashTableLookup(&cls->instanceMembers, "_get"); + m = cls->get; if (!m || !_accessRawMember(m, obj->value.opaque, obj->type->isConst, &getMember)) { return false; } @@ -1084,6 +1120,30 @@ bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptTyp return false; } +void mScriptObjectFree(struct mScriptValue* value) { + if (value->type->base != mSCRIPT_TYPE_OBJECT) { + return; + } + if (value->flags & mSCRIPT_VALUE_FLAG_FREE_BUFFER) { + mScriptClassInit(value->type->details.cls); + if (value->type->details.cls->free) { + struct mScriptValue deinitMember; + if (_accessRawMember(value->type->details.cls->free, value->value.opaque, value->type->isConst, &deinitMember)) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + struct mScriptValue* this = mScriptListAppend(&frame.arguments); + this->type = mSCRIPT_TYPE_MS_WRAPPER; + this->refs = mSCRIPT_VALUE_UNREF; + this->flags = 0; + this->value.opaque = value; + mScriptInvoke(&deinitMember, &frame); + mScriptFrameDeinit(&frame); + } + } + free(value->value.opaque); + } +} + bool mScriptPopS32(struct mScriptList* list, int32_t* out) { mSCRIPT_POP(list, S32, val); *out = val; From 5eb25876b42cd55978a65a34c61d43689f8b5c44 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 May 2022 21:29:27 -0700 Subject: [PATCH 051/105] Scripting: Add memory adapter for access to memory regions --- src/core/scripting.c | 150 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 146 insertions(+), 4 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 9b4f4534e..7044583b2 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -149,14 +149,95 @@ bool mScriptBridgeLookupSymbol(struct mScriptBridge* sb, const char* name, int32 return info.success; } +struct mScriptMemoryAdapter { + struct mCore* core; + struct mCoreMemoryBlock block; +}; + +struct mScriptCoreAdapter { + struct mCore* core; + struct mScriptValue memory; +}; + +static uint32_t mScriptMemoryAdapterRead8(struct mScriptMemoryAdapter* adapter, uint32_t address) { + uint32_t segmentSize = adapter->block.end - adapter->block.start; + uint32_t segmentAddress = address % segmentSize; + int segment = address / segmentSize; + address = adapter->block.start + segmentAddress; + return adapter->core->rawRead8(adapter->core, address, segment); +} + +static uint32_t mScriptMemoryAdapterRead16(struct mScriptMemoryAdapter* adapter, uint32_t address) { + uint32_t segmentSize = adapter->block.end - adapter->block.start; + uint32_t segmentAddress = address % segmentSize; + int segment = address / segmentSize; + address = adapter->block.start + segmentAddress; + return adapter->core->rawRead16(adapter->core, address, segment); +} + +static uint32_t mScriptMemoryAdapterRead32(struct mScriptMemoryAdapter* adapter, uint32_t address) { + uint32_t segmentSize = adapter->block.end - adapter->block.start; + uint32_t segmentAddress = address % segmentSize; + int segment = address / segmentSize; + address = adapter->block.start + segmentAddress; + return adapter->core->rawRead32(adapter->core, address, segment); +} + +static void mScriptMemoryAdapterWrite8(struct mScriptMemoryAdapter* adapter, uint32_t address, uint8_t value) { + uint32_t segmentSize = adapter->block.end - adapter->block.start; + uint32_t segmentAddress = address % segmentSize; + int segment = address / segmentSize; + address = adapter->block.start + segmentAddress; + adapter->core->rawWrite8(adapter->core, address, segment, value); +} + +static void mScriptMemoryAdapterWrite16(struct mScriptMemoryAdapter* adapter, uint32_t address, uint16_t value) { + uint32_t segmentSize = adapter->block.end - adapter->block.start; + uint32_t segmentAddress = address % segmentSize; + int segment = address / segmentSize; + address = adapter->block.segmentStart + segmentAddress; + adapter->core->rawWrite16(adapter->core, address, segment, value); +} + +static void mScriptMemoryAdapterWrite32(struct mScriptMemoryAdapter* adapter, uint32_t address, uint32_t value) { + uint32_t segmentSize = adapter->block.end - adapter->block.start; + uint32_t segmentAddress = address % segmentSize; + int segment = address / segmentSize; + address = adapter->block.segmentStart + segmentAddress; + adapter->core->rawWrite32(adapter->core, address, segment, value); +} + +mSCRIPT_DECLARE_STRUCT(mScriptMemoryAdapter); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read8, mScriptMemoryAdapterRead8, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read16, mScriptMemoryAdapterRead16, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read32, mScriptMemoryAdapterRead32, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write8, mScriptMemoryAdapterWrite8, 2, U32, address, U8, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write16, mScriptMemoryAdapterWrite16, 2, U32, address, U16, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write32, mScriptMemoryAdapterWrite32, 2, U32, address, U32, value); + +mSCRIPT_DEFINE_STRUCT(mScriptMemoryAdapter) +mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given offset") +mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read8) +mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given offset") +mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read16) +mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given offset") +mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read32) +mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given offset") +mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write8) +mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given offset") +mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write16) +mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset") +mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write32) +mSCRIPT_DEFINE_END; + mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frameCycles, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, step, 0); -mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U8, busRead8, 1, U32, address); -mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U16, busRead16, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead8, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead16, 1, U32, address); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead32, 1, U32, address); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite8, 2, U32, address, U8, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, value); @@ -188,9 +269,70 @@ mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given bus address") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write32, busWrite32) mSCRIPT_DEFINE_END; +static void _mScriptCoreAdapterDeinit(struct mScriptCoreAdapter* adapter) { + adapter->memory.type->free(&adapter->memory); +} + +static struct mScriptValue* _mScriptCoreAdapterGet(struct mScriptCoreAdapter* adapter, const char* name) { + struct mScriptValue val; + struct mScriptValue core = mSCRIPT_MAKE(S(mCore), adapter->core); + if (!mScriptObjectGet(&core, name, &val)) { + return NULL; + } + + struct mScriptValue* ret = malloc(sizeof(*ret)); + memcpy(ret, &val, sizeof(*ret)); + ret->refs = 1; + return ret; +} + +mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WRAPPER, _get, _mScriptCoreAdapterGet, 1, CHARP, name); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0); + +mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) +mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(mScriptCoreAdapter, PS(mCore), _core, core) +mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptCoreAdapter, TABLE, memory) +mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptCoreAdapter) +mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptCoreAdapter) +mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, S(mCore), _core) +mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core) +mSCRIPT_DEFINE_END; + +static void _rebuildMemoryMap(struct mScriptCoreAdapter* adapter) { + mScriptTableClear(&adapter->memory); + + const struct mCoreMemoryBlock* blocks; + size_t nBlocks = adapter->core->listMemoryBlocks(adapter->core, &blocks); + size_t i; + for (i = 0; i < nBlocks; ++i) { + struct mScriptMemoryAdapter* memadapter = calloc(1, sizeof(*memadapter)); + memadapter->core = adapter->core; + memcpy(&memadapter->block, &blocks[i], sizeof(memadapter->block)); + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptMemoryAdapter)); + value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + value->value.opaque = memadapter; + struct mScriptValue* key = mScriptStringCreateFromUTF8(blocks[i].internalName); + mScriptTableInsert(&adapter->memory, key, value); + mScriptValueDeref(key); + mScriptValueDeref(value); + } +} + void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core) { - struct mScriptValue* coreValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mCore)); - coreValue->value.opaque = core; + struct mScriptValue* coreValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCoreAdapter)); + struct mScriptCoreAdapter* adapter = calloc(1, sizeof(*adapter)); + adapter->core = core; + + adapter->memory.refs = mSCRIPT_VALUE_UNREF; + adapter->memory.flags = 0; + adapter->memory.type = mSCRIPT_TYPE_MS_TABLE; + adapter->memory.type->alloc(&adapter->memory); + + _rebuildMemoryMap(adapter); + + coreValue->value.opaque = adapter; + coreValue->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; mScriptContextSetGlobal(context, "emu", coreValue); mScriptValueDeref(coreValue); } From e8e9a3e3c3ada0bdda4f2eb99797d3ad9f862b19 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 May 2022 22:13:18 -0700 Subject: [PATCH 052/105] Scripting: Bring up lists --- include/mgba/script/types.h | 4 ++++ src/script/types.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 08925f514..bc81d3105 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -31,6 +31,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_CHARP const char* #define mSCRIPT_TYPE_C_PTR void* #define mSCRIPT_TYPE_C_CPTR const void* +#define mSCRIPT_TYPE_C_LIST struct mScriptList* #define mSCRIPT_TYPE_C_TABLE Table* #define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue* #define mSCRIPT_TYPE_C_WEAKREF uint32_t @@ -53,6 +54,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_STR opaque #define mSCRIPT_TYPE_FIELD_CHARP copaque #define mSCRIPT_TYPE_FIELD_PTR opaque +#define mSCRIPT_TYPE_FIELD_LIST opaque #define mSCRIPT_TYPE_FIELD_TABLE opaque #define mSCRIPT_TYPE_FIELD_WRAPPER opaque #define mSCRIPT_TYPE_FIELD_WEAKREF u32 @@ -74,6 +76,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_F64 (&mSTFloat64) #define mSCRIPT_TYPE_MS_STR (&mSTString) #define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr) +#define mSCRIPT_TYPE_MS_LIST (&mSTList) #define mSCRIPT_TYPE_MS_TABLE (&mSTTable) #define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper) #define mSCRIPT_TYPE_MS_WEAKREF (&mSTWeakref) @@ -524,6 +527,7 @@ extern const struct mScriptType mSTUInt64; extern const struct mScriptType mSTFloat64; extern const struct mScriptType mSTString; extern const struct mScriptType mSTCharPtr; +extern const struct mScriptType mSTList; extern const struct mScriptType mSTTable; extern const struct mScriptType mSTWrapper; extern const struct mScriptType mSTWeakref; diff --git a/src/script/types.c b/src/script/types.c index f2c5efc60..83e7fbf29 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -9,7 +9,8 @@ #include #include -#define MAX_ALIGNMENT 8 +static void _allocList(struct mScriptValue*); +static void _freeList(struct mScriptValue*); static void _allocTable(struct mScriptValue*); static void _freeTable(struct mScriptValue*); @@ -178,6 +179,15 @@ const struct mScriptType mSTCharPtr = { .equal = _charpEqual, }; +const struct mScriptType mSTList = { + .base = mSCRIPT_TYPE_LIST, + .size = sizeof(struct mScriptList), + .name = "list", + .alloc = _allocList, + .free = _freeList, + .hash = NULL, +}; + const struct mScriptType mSTTable = { .base = mSCRIPT_TYPE_TABLE, .size = sizeof(struct Table), @@ -207,6 +217,23 @@ const struct mScriptType mSTWeakref = { DEFINE_VECTOR(mScriptList, struct mScriptValue) +void _allocList(struct mScriptValue* val) { + val->value.opaque = malloc(sizeof(struct mScriptList)); + mScriptListInit(val->value.opaque, 0); +} + +void _freeList(struct mScriptValue* val) { + size_t i; + for (i = 0; i < mScriptListSize(val->value.opaque); ++i) { + struct mScriptValue* unwrapped = mScriptValueUnwrap(mScriptListGetPointer(val->value.opaque, i)); + if (unwrapped) { + mScriptValueDeref(unwrapped); + } + } + mScriptListDeinit(val->value.opaque); + free(val->value.opaque); +} + void _allocTable(struct mScriptValue* val) { val->value.opaque = malloc(sizeof(struct Table)); struct TableFunctions funcs = { From a59349af8a8f852e920b9c09a28e3f44009932ca Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 May 2022 23:26:03 -0700 Subject: [PATCH 053/105] Scripting: Fix some bugs with context globals --- src/core/scripting.c | 2 -- src/script/context.c | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 7044583b2..52fa9a76b 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -334,7 +334,6 @@ void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core coreValue->value.opaque = adapter; coreValue->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; mScriptContextSetGlobal(context, "emu", coreValue); - mScriptValueDeref(coreValue); } void mScriptContextDetachCore(struct mScriptContext* context) { @@ -367,7 +366,6 @@ void mScriptContextAttachLogger(struct mScriptContext* context, struct mLogger* struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mLogger)); value->value.opaque = logger; mScriptContextSetGlobal(context, "console", value); - mScriptValueDeref(value); } void mScriptContextDetachLogger(struct mScriptContext* context) { diff --git a/src/script/context.c b/src/script/context.c index 93d414360..ba552be41 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -24,6 +24,11 @@ static void _engineContextDestroy(void* ctx) { context->destroy(context); } +static void _engineAddGlobal(const char* key, void* value, void* user) { + struct mScriptEngineContext* context = user; + context->setGlobal(context, key, value); +} + static void _contextAddGlobal(const char* key, void* value, void* user) { UNUSED(key); struct mScriptEngineContext* context = value; @@ -99,6 +104,7 @@ struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* struct mScriptEngineContext* ectx = engine->create(engine, context); if (ectx) { HashTableInsert(&context->engines, engine->name, ectx); + HashTableEnumerate(&context->rootScope, _engineAddGlobal, ectx); } return ectx; } @@ -116,6 +122,7 @@ void mScriptContextSetGlobal(struct mScriptContext* context, const char* key, st mScriptContextClearWeakref(context, oldValue->value.u32); } uint32_t weakref = mScriptContextSetWeakref(context, value); + mScriptValueDeref(value); value = mScriptValueAlloc(mSCRIPT_TYPE_MS_WEAKREF); value->value.u32 = weakref; HashTableInsert(&context->rootScope, key, value); From c14fb54a74ebb95b974692019b6211fa61fc54f9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 May 2022 23:27:00 -0700 Subject: [PATCH 054/105] Scripting: Add callback API --- include/mgba/script/context.h | 6 +++ include/mgba/script/types.h | 1 + src/core/thread.c | 88 +++++++++++++++++++++++++++++++++-- src/script/CMakeLists.txt | 1 + src/script/context.c | 32 +++++++++++++ src/script/stdlib.c | 37 +++++++++++++++ 6 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 src/script/stdlib.c diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index a6b478b17..bc9233025 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -24,6 +24,7 @@ struct mScriptContext { struct mScriptList refPool; struct Table weakrefs; uint32_t nextWeakref; + struct Table callbacks; }; struct mScriptEngine2 { @@ -66,6 +67,11 @@ struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mS struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value); void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); +void mScriptContextAttachStdlib(struct mScriptContext* context); + +void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback); +void mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value); + struct VFile; bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf); bool mScriptContextLoadFile(struct mScriptContext*, const char* path); diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index bc81d3105..6c6b0c47d 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -102,6 +102,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) #define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE) #define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE) +#define mSCRIPT_TYPE_CMP_WRAPPER(TYPE) (true) #define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP_CS(STRUCT) mSCRIPT_TYPE_MS_CS(STRUCT)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME diff --git a/src/core/thread.c b/src/core/thread.c index 2375aea4c..fb773e8ba 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -7,6 +7,9 @@ #include #include +#ifdef ENABLE_SCRIPTING +#include +#endif #include #include #include @@ -183,6 +186,42 @@ void _coreShutdown(void* context) { _changeState(thread->impl, mTHREAD_EXITING, true); } +#ifdef ENABLE_SCRIPTING +#define ADD_CALLBACK(NAME) \ +void _script_ ## NAME(void* context) { \ + struct mCoreThread* threadContext = context; \ + if (!threadContext->scriptContext) { \ + return; \ + } \ + mScriptContextTriggerCallback(threadContext->scriptContext, #NAME); \ +} + +ADD_CALLBACK(frame) +ADD_CALLBACK(crashed) +ADD_CALLBACK(sleep) +ADD_CALLBACK(stop) +ADD_CALLBACK(keysRead) +ADD_CALLBACK(savedataUpdated) +ADD_CALLBACK(alarm) + +#undef ADD_CALLBACK +#define CALLBACK(NAME) _script_ ## NAME + +static void _mCoreThreadAddCallbacks(struct mCoreThread* threadContext) { + struct mCoreCallbacks callbacks = { + .videoFrameEnded = CALLBACK(frame), + .coreCrashed = CALLBACK(crashed), + .sleep = CALLBACK(sleep), + .shutdown = CALLBACK(stop), + .keysRead = CALLBACK(keysRead), + .savedataUpdated = CALLBACK(savedataUpdated), + .alarm = CALLBACK(alarm), + .context = threadContext + }; + threadContext->core->addCoreCallbacks(threadContext->core, &callbacks); +} +#endif + static THREAD_ENTRY _mCoreThreadRun(void* context) { struct mCoreThread* threadContext = context; #ifdef USE_PTHREADS @@ -220,8 +259,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } #ifdef ENABLE_SCRIPTING - if (threadContext->scriptContext) { - mScriptContextAttachCore(threadContext->scriptContext, core); + struct mScriptContext* scriptContext = threadContext->scriptContext; + if (scriptContext) { + mScriptContextAttachCore(scriptContext, core); + _mCoreThreadAddCallbacks(threadContext); } #endif @@ -229,6 +270,18 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { if (threadContext->startCallback) { threadContext->startCallback(threadContext); } +#ifdef ENABLE_SCRIPTING + // startCallback could add a script context + if (!scriptContext) { + scriptContext = threadContext->scriptContext; + if (scriptContext) { + _mCoreThreadAddCallbacks(threadContext); + } + } + if (scriptContext) { + mScriptContextTriggerCallback(scriptContext, "start"); + } +#endif core->reset(core); threadContext->impl->core = core; @@ -238,6 +291,19 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { threadContext->resetCallback(threadContext); } +#ifdef ENABLE_SCRIPTING + // resetCallback could add a script context + if (!scriptContext) { + scriptContext = threadContext->scriptContext; + if (scriptContext) { + _mCoreThreadAddCallbacks(threadContext); + } + } + if (scriptContext) { + mScriptContextTriggerCallback(scriptContext, "reset"); + } +#endif + struct mCoreThreadInternal* impl = threadContext->impl; bool wasPaused = false; int pendingRequests = 0; @@ -283,6 +349,14 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { MutexLock(&impl->stateMutex); } } +#ifdef ENABLE_SCRIPTING + if (!scriptContext) { + scriptContext = threadContext->scriptContext; + if (scriptContext) { + _mCoreThreadAddCallbacks(threadContext); + } + } +#endif if (wasPaused && !(impl->requested & mTHREAD_REQ_PAUSE)) { break; } @@ -324,6 +398,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { if (threadContext->resetCallback) { threadContext->resetCallback(threadContext); } +#ifdef ENABLE_SCRIPTING + if (scriptContext) { + mScriptContextTriggerCallback(scriptContext, "reset"); + } +#endif } if (pendingRequests & mTHREAD_REQ_RUN_ON) { if (threadContext->run) { @@ -344,8 +423,9 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { threadContext->cleanCallback(threadContext); } #ifdef ENABLE_SCRIPTING - if (threadContext->scriptContext) { - mScriptContextDetachCore(threadContext->scriptContext); + if (scriptContext) { + mScriptContextTriggerCallback(scriptContext, "shutdown"); + mScriptContextDetachCore(scriptContext); } #endif core->clearCoreCallbacks(core); diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index aa7cc1562..c579ccb9f 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -1,6 +1,7 @@ include(ExportDirectory) set(SOURCE_FILES context.c + stdlib.c types.c) set(TEST_FILES diff --git a/src/script/context.c b/src/script/context.c index ba552be41..9778a6ebe 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -60,6 +60,7 @@ void mScriptContextInit(struct mScriptContext* context) { mScriptListInit(&context->refPool, 0); TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref); context->nextWeakref = 0; + HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref); } void mScriptContextDeinit(struct mScriptContext* context) { @@ -68,6 +69,7 @@ void mScriptContextDeinit(struct mScriptContext* context) { HashTableDeinit(&context->weakrefs); mScriptContextDrainPool(context); mScriptListDeinit(&context->refPool); + HashTableDeinit(&context->callbacks); } void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* value) { @@ -177,6 +179,36 @@ void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref TableRemove(&context->weakrefs, weakref); } +void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback) { + struct mScriptValue* list = HashTableLookup(&context->callbacks, callback); + if (!list) { + return; + } + size_t i; + for (i = 0; i < mScriptListSize(list->value.opaque); ++i) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + struct mScriptValue* fn = mScriptListGetPointer(list->value.opaque, i); + if (fn->type->base == mSCRIPT_TYPE_WRAPPER) { + fn = mScriptValueUnwrap(fn); + } + mScriptInvoke(fn, &frame); + mScriptFrameDeinit(&frame); + } +} + +void mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) { + if (fn->type->base != mSCRIPT_TYPE_FUNCTION) { + return; + } + struct mScriptValue* list = HashTableLookup(&context->callbacks, callback); + if (!list) { + list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); + HashTableInsert(&context->callbacks, callback, list); + } + mScriptValueWrap(fn, mScriptListAppend(list->value.opaque)); +} + bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) { struct mScriptFileInfo info = { .name = name, diff --git a/src/script/stdlib.c b/src/script/stdlib.c new file mode 100644 index 000000000..08871fb49 --- /dev/null +++ b/src/script/stdlib.c @@ -0,0 +1,37 @@ +/* Copyright (c) 2013-2022 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 + +struct mScriptCallbackAdapter { + struct mScriptContext* context; +}; + +static void _mScriptCallbackAdd(struct mScriptCallbackAdapter* adapter, struct mScriptString* name, struct mScriptValue* fn) { + if (fn->type->base == mSCRIPT_TYPE_WRAPPER) { + fn = mScriptValueUnwrap(fn); + } + mScriptContextAddCallback(adapter->context, name->buffer, fn); +} + +mSCRIPT_DECLARE_STRUCT(mScriptCallbackAdapter); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackAdapter, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function); + +mSCRIPT_DEFINE_STRUCT(mScriptCallbackAdapter) +mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type") +mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackAdapter, add) +mSCRIPT_DEFINE_END; + +void mScriptContextAttachStdlib(struct mScriptContext* context) { + struct mScriptValue* lib; + + lib = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCallbackAdapter)); + lib->value.opaque = calloc(1, sizeof(struct mScriptCallbackAdapter)); + *(struct mScriptCallbackAdapter*) lib->value.opaque = (struct mScriptCallbackAdapter) { + .context = context + }; + lib->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + mScriptContextSetGlobal(context, "callbacks", lib); +} From ca073379fbb461a0065b523ffe655709b4a29b73 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 15 May 2022 02:08:16 -0700 Subject: [PATCH 055/105] Scripting: Add Table iteration --- include/mgba/script/types.h | 5 ++++ src/script/types.c | 55 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 6c6b0c47d..69d7abd66 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -650,6 +650,11 @@ bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, st bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key); struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key); bool mScriptTableClear(struct mScriptValue* table); +bool mScriptTableIteratorStart(struct mScriptValue* table, struct TableIterator*); +bool mScriptTableIteratorNext(struct mScriptValue* table, struct TableIterator*); +struct mScriptValue* mScriptTableIteratorGetKey(struct mScriptValue* table, struct TableIterator*); +struct mScriptValue* mScriptTableIteratorGetValue(struct mScriptValue* table, struct TableIterator*); +bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator*, struct mScriptValue* key); void mScriptFrameInit(struct mScriptFrame* frame); void mScriptFrameDeinit(struct mScriptFrame* frame); diff --git a/src/script/types.c b/src/script/types.c index 83e7fbf29..a6ba0abc4 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -787,6 +787,61 @@ bool mScriptTableClear(struct mScriptValue* table) { return true; } +bool mScriptTableIteratorStart(struct mScriptValue* table, struct TableIterator* iter) { + if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + table = mScriptValueUnwrap(table); + } + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return false; + } + struct Table* t = table->value.opaque; + return HashTableIteratorStart(t, iter); +} + +bool mScriptTableIteratorNext(struct mScriptValue* table, struct TableIterator* iter) { + if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + table = mScriptValueUnwrap(table); + } + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return false; + } + struct Table* t = table->value.opaque; + return HashTableIteratorNext(t, iter); +} + +struct mScriptValue* mScriptTableIteratorGetKey(struct mScriptValue* table, struct TableIterator* iter) { + if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + table = mScriptValueUnwrap(table); + } + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return NULL; + } + struct Table* t = table->value.opaque; + return HashTableIteratorGetCustomKey(t, iter); +} + +struct mScriptValue* mScriptTableIteratorGetValue(struct mScriptValue* table, struct TableIterator* iter) { + if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + table = mScriptValueUnwrap(table); + } + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return NULL; + } + struct Table* t = table->value.opaque; + return HashTableIteratorGetValue(t, iter); +} + +bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator* iter, struct mScriptValue* key) { + if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + table = mScriptValueUnwrap(table); + } + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return false; + } + struct Table* t = table->value.opaque; + return HashTableIteratorLookupCustom(t, iter, key); +} + void mScriptFrameInit(struct mScriptFrame* frame) { mScriptListInit(&frame->arguments, 4); mScriptListInit(&frame->returnValues, 1); From f3ba5f769211302d1d4629de53fa288d184d3d20 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 13 May 2022 00:40:18 -0700 Subject: [PATCH 056/105] Scripting: API cleanup --- include/mgba/core/thread.h | 7 +++---- include/mgba/script/context.h | 4 +++- include/mgba/script/types.h | 3 ++- src/core/scripting.c | 1 - src/core/test/scripting.c | 4 +--- src/core/thread.c | 1 + src/script/context.c | 26 ++++++++++++++++++++------ src/script/engines/lua.c | 19 ++++++++----------- src/script/test/lua.c | 12 +++--------- src/script/types.c | 15 +++++++++++++++ 10 files changed, 56 insertions(+), 36 deletions(-) diff --git a/include/mgba/core/thread.h b/include/mgba/core/thread.h index 72e9de7b5..aa059f652 100644 --- a/include/mgba/core/thread.h +++ b/include/mgba/core/thread.h @@ -11,10 +11,6 @@ CXX_GUARD_START #include -#ifdef ENABLE_SCRIPTING -#include -#include -#endif struct mCoreThread; struct mCore; @@ -27,6 +23,9 @@ struct mThreadLogger { struct mCoreThread* p; }; +#ifdef ENABLE_SCRIPTING +struct mScriptContext; +#endif struct mCoreThreadInternal; struct mCoreThread { // Input diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index bc9233025..8104295d7 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -45,7 +45,7 @@ struct mScriptEngineContext { bool (*setGlobal)(struct mScriptEngineContext*, const char* name, struct mScriptValue*); struct mScriptValue* (*getGlobal)(struct mScriptEngineContext*, const char* name); - bool (*load)(struct mScriptEngineContext*, struct VFile*, const char** error); + bool (*load)(struct mScriptEngineContext*, struct VFile*); bool (*run)(struct mScriptEngineContext*); const char* (*getError)(struct mScriptEngineContext*); }; @@ -60,7 +60,9 @@ struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* void mScriptContextRegisterEngines(struct mScriptContext*); void mScriptContextSetGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value); +struct mScriptValue* mScriptContextGetGlobal(struct mScriptContext*, const char* key); void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key); +struct mScriptValue* mScriptContextEnsureGlobal(struct mScriptContext*, const char* key, const struct mScriptType* type); uint32_t mScriptContextSetWeakref(struct mScriptContext*, struct mScriptValue* value); struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mScriptValue* value); diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 69d7abd66..9c008717b 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -491,7 +491,6 @@ enum mScriptTypeBase { mSCRIPT_TYPE_FUNCTION, mSCRIPT_TYPE_OPAQUE, mSCRIPT_TYPE_OBJECT, - mSCRIPT_TYPE_TUPLE, mSCRIPT_TYPE_LIST, mSCRIPT_TYPE_TABLE, mSCRIPT_TYPE_WRAPPER, @@ -645,6 +644,8 @@ struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val); const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val); struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); +struct mScriptValue* mScriptValueCreateFromUInt(uint32_t value); +struct mScriptValue* mScriptValueCreateFromSInt(int32_t value); bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value); bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key); diff --git a/src/core/scripting.c b/src/core/scripting.c index 52fa9a76b..7618e72f8 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -315,7 +315,6 @@ static void _rebuildMemoryMap(struct mScriptCoreAdapter* adapter) { struct mScriptValue* key = mScriptStringCreateFromUTF8(blocks[i].internalName); mScriptTableInsert(&adapter->memory, key, value); mScriptValueDeref(key); - mScriptValueDeref(value); } } diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index 0f5e55f7f..fd6dbc8ca 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -69,9 +69,7 @@ static const uint8_t _fakeGBROM[0x4000] = { #define LOAD_PROGRAM(PROG) \ do { \ struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - const char* error = NULL; \ - assert_true(lua->load(lua, vf, &error)); \ - assert_null(error); \ + assert_true(lua->load(lua, vf)); \ vf->close(vf); \ } while(0) diff --git a/src/core/thread.c b/src/core/thread.c index fb773e8ba..ee46569cd 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -8,6 +8,7 @@ #include #include #ifdef ENABLE_SCRIPTING +#include #include #endif #include diff --git a/src/script/context.c b/src/script/context.c index 9778a6ebe..2ee2dead0 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -59,7 +59,7 @@ void mScriptContextInit(struct mScriptContext* context) { HashTableInit(&context->engines, 0, _engineContextDestroy); mScriptListInit(&context->refPool, 0); TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref); - context->nextWeakref = 0; + context->nextWeakref = 1; HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref); } @@ -123,10 +123,7 @@ void mScriptContextSetGlobal(struct mScriptContext* context, const char* key, st if (oldValue) { mScriptContextClearWeakref(context, oldValue->value.u32); } - uint32_t weakref = mScriptContextSetWeakref(context, value); - mScriptValueDeref(value); - value = mScriptValueAlloc(mSCRIPT_TYPE_MS_WEAKREF); - value->value.u32 = weakref; + value = mScriptContextMakeWeakref(context, value); HashTableInsert(&context->rootScope, key, value); struct mScriptKVPair pair = { .key = key, @@ -135,6 +132,14 @@ void mScriptContextSetGlobal(struct mScriptContext* context, const char* key, st HashTableEnumerate(&context->engines, _contextAddGlobal, &pair); } +struct mScriptValue* mScriptContextGetGlobal(struct mScriptContext* context, const char* key) { + struct mScriptValue* weakref = HashTableLookup(&context->rootScope, key); + if (!weakref) { + return NULL; + } + return mScriptContextAccessWeakref(context, weakref); +} + void mScriptContextRemoveGlobal(struct mScriptContext* context, const char* key) { if (!HashTableLookup(&context->rootScope, key)) { return; @@ -148,6 +153,15 @@ void mScriptContextRemoveGlobal(struct mScriptContext* context, const char* key) } } +struct mScriptValue* mScriptContextEnsureGlobal(struct mScriptContext* context, const char* key, const struct mScriptType* type) { + struct mScriptValue* value = mScriptContextGetGlobal(context, key); + if (!value) { + mScriptContextSetGlobal(context, key, mScriptValueAlloc(type)); + value = mScriptContextGetGlobal(context, key); + } + return value; +} + uint32_t mScriptContextSetWeakref(struct mScriptContext* context, struct mScriptValue* value) { mScriptValueRef(value); TableInsert(&context->weakrefs, context->nextWeakref, value); @@ -219,7 +233,7 @@ bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, stru if (!info.context) { return false; } - return info.context->load(info.context, vf, NULL); + return info.context->load(info.context, vf); } bool mScriptContextLoadFile(struct mScriptContext* context, const char* path) { diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index b7153af1c..8533b7a22 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -18,7 +18,7 @@ static void _luaDestroy(struct mScriptEngineContext*); static bool _luaIsScript(struct mScriptEngineContext*, const char*, struct VFile*); static struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext*, const char* name); static bool _luaSetGlobal(struct mScriptEngineContext*, const char* name, struct mScriptValue*); -static bool _luaLoad(struct mScriptEngineContext*, struct VFile*, const char** error); +static bool _luaLoad(struct mScriptEngineContext*, struct VFile*); static bool _luaRun(struct mScriptEngineContext*); static const char* _luaGetError(struct mScriptEngineContext*); @@ -333,28 +333,25 @@ static const char* _reader(lua_State* lua, void* context, size_t* size) { return reader->block; } -bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf, const char** error) { +bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; struct mScriptEngineLuaReader data = { .vf = vf }; + if (luaContext->lastError) { + free(luaContext->lastError); + luaContext->lastError = NULL; + } int ret = lua_load(luaContext->lua, _reader, &data, NULL, "t"); switch (ret) { case LUA_OK: - if (error) { - *error = NULL; - } luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); return true; case LUA_ERRSYNTAX: - if (error) { - *error = "Syntax error"; - } + luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1)); + lua_pop(luaContext->lua, 1); break; default: - if (error) { - *error = "Unknown error"; - } break; } return false; diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 437fe4131..d0889ce86 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -15,9 +15,7 @@ #define LOAD_PROGRAM(PROG) \ do { \ struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - const char* error = NULL; \ - assert_true(lua->load(lua, vf, &error)); \ - assert_null(error); \ + assert_true(lua->load(lua, vf)); \ vf->close(vf); \ } while(0) @@ -116,9 +114,7 @@ M_TEST_DEFINE(loadGood) { const char* program = "-- test\n"; struct VFile* vf = VFileFromConstMemory(program, strlen(program)); - const char* error = NULL; - assert_true(lua->load(lua, vf, &error)); - assert_null(error); + assert_true(lua->load(lua, vf)); lua->destroy(lua); mScriptContextDeinit(&context); @@ -132,9 +128,7 @@ M_TEST_DEFINE(loadBadSyntax) { const char* program = "Invalid syntax! )\n"; struct VFile* vf = VFileFromConstMemory(program, strlen(program)); - const char* error = NULL; - assert_false(lua->load(lua, vf, &error)); - assert_non_null(error); + assert_false(lua->load(lua, vf)); lua->destroy(lua); mScriptContextDeinit(&context); diff --git a/src/script/types.c b/src/script/types.c index a6ba0abc4..b455b11de 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -741,6 +741,17 @@ struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { internal->buffer = strdup(string); return val; } +struct mScriptValue* mScriptValueCreateFromSInt(int32_t value) { + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); + val->value.s32 = value; + return val; +} + +struct mScriptValue* mScriptValueCreateFromUInt(uint32_t value) { + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_U32); + val->value.u32 = value; + return val; +} bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value) { if (table->type != mSCRIPT_TYPE_MS_TABLE) { @@ -768,6 +779,9 @@ bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key) { } struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key) { + if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + table = mScriptValueUnwrap(table); + } if (table->type != mSCRIPT_TYPE_MS_TABLE) { return false; } @@ -1003,6 +1017,7 @@ static bool _accessRawMember(struct mScriptClassMember* member, void* raw, bool break; case mSCRIPT_TYPE_TABLE: val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; val->type = mSCRIPT_TYPE_MS_WRAPPER; val->value.opaque = raw; break; From 48f49b74b151ae1ef897fba9cb257dbf8bee0060 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 15 May 2022 15:48:28 -0700 Subject: [PATCH 057/105] Scripting: Make memory blocks weakrefs --- src/core/scripting.c | 71 +++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 7618e72f8..17406dc3c 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -156,6 +156,7 @@ struct mScriptMemoryAdapter { struct mScriptCoreAdapter { struct mCore* core; + struct mScriptContext* context; struct mScriptValue memory; }; @@ -269,7 +270,46 @@ mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given bus address") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write32, busWrite32) mSCRIPT_DEFINE_END; +static void _clearMemoryMap(struct mScriptContext* context, struct mScriptCoreAdapter* adapter, bool clear) { + struct TableIterator iter; + if (mScriptTableIteratorStart(&adapter->memory, &iter)) { + while (true) { + struct mScriptValue* weakref = mScriptTableIteratorGetValue(&adapter->memory, &iter); + if (weakref) { + if (clear) { + mScriptContextClearWeakref(context, weakref->value.s32); + } + mScriptValueDeref(weakref); + } + if (!mScriptTableIteratorNext(&adapter->memory, &iter)) { + break; + } + } + } + mScriptTableClear(&adapter->memory); +} + +static void _rebuildMemoryMap(struct mScriptContext* context, struct mScriptCoreAdapter* adapter) { + _clearMemoryMap(context, adapter, true); + + const struct mCoreMemoryBlock* blocks; + size_t nBlocks = adapter->core->listMemoryBlocks(adapter->core, &blocks); + size_t i; + for (i = 0; i < nBlocks; ++i) { + struct mScriptMemoryAdapter* memadapter = calloc(1, sizeof(*memadapter)); + memadapter->core = adapter->core; + memcpy(&memadapter->block, &blocks[i], sizeof(memadapter->block)); + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptMemoryAdapter)); + value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + value->value.opaque = memadapter; + struct mScriptValue* key = mScriptStringCreateFromUTF8(blocks[i].internalName); + mScriptTableInsert(&adapter->memory, key, mScriptContextMakeWeakref(context, value)); + mScriptValueDeref(key); + } +} + static void _mScriptCoreAdapterDeinit(struct mScriptCoreAdapter* adapter) { + _clearMemoryMap(adapter->context, adapter, false); adapter->memory.type->free(&adapter->memory); } @@ -299,36 +339,18 @@ mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, S(mCore), _core) mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core) mSCRIPT_DEFINE_END; -static void _rebuildMemoryMap(struct mScriptCoreAdapter* adapter) { - mScriptTableClear(&adapter->memory); - - const struct mCoreMemoryBlock* blocks; - size_t nBlocks = adapter->core->listMemoryBlocks(adapter->core, &blocks); - size_t i; - for (i = 0; i < nBlocks; ++i) { - struct mScriptMemoryAdapter* memadapter = calloc(1, sizeof(*memadapter)); - memadapter->core = adapter->core; - memcpy(&memadapter->block, &blocks[i], sizeof(memadapter->block)); - struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptMemoryAdapter)); - value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; - value->value.opaque = memadapter; - struct mScriptValue* key = mScriptStringCreateFromUTF8(blocks[i].internalName); - mScriptTableInsert(&adapter->memory, key, value); - mScriptValueDeref(key); - } -} - void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core) { struct mScriptValue* coreValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCoreAdapter)); struct mScriptCoreAdapter* adapter = calloc(1, sizeof(*adapter)); adapter->core = core; + adapter->context = context; adapter->memory.refs = mSCRIPT_VALUE_UNREF; adapter->memory.flags = 0; adapter->memory.type = mSCRIPT_TYPE_MS_TABLE; adapter->memory.type->alloc(&adapter->memory); - _rebuildMemoryMap(adapter); + _rebuildMemoryMap(context, adapter); coreValue->value.opaque = adapter; coreValue->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; @@ -336,6 +358,15 @@ void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core } void mScriptContextDetachCore(struct mScriptContext* context) { + struct mScriptValue* value = HashTableLookup(&context->rootScope, "emu"); + if (!value) { + return; + } + value = mScriptContextAccessWeakref(context, value); + if (!value) { + return; + } + _clearMemoryMap(context, value->value.opaque, true); mScriptContextRemoveGlobal(context, "emu"); } From fa847b1e63b022463442f09853362c651886e5be Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 13 May 2022 00:42:50 -0700 Subject: [PATCH 058/105] Qt: Add basic scripting view --- src/platform/qt/CMakeLists.txt | 10 +++ src/platform/qt/LogWidget.cpp | 48 ++++++++++- src/platform/qt/LogWidget.h | 24 +++++- src/platform/qt/ScriptingController.cpp | 102 ++++++++++++++++++++++++ src/platform/qt/ScriptingController.h | 57 +++++++++++++ src/platform/qt/ScriptingView.cpp | 51 ++++++++++++ src/platform/qt/ScriptingView.h | 31 +++++++ src/platform/qt/ScriptingView.ui | 97 ++++++++++++++++++++++ src/platform/qt/Window.cpp | 27 ++++++- src/platform/qt/Window.h | 11 +++ 10 files changed, 453 insertions(+), 5 deletions(-) create mode 100644 src/platform/qt/ScriptingController.cpp create mode 100644 src/platform/qt/ScriptingController.h create mode 100644 src/platform/qt/ScriptingView.cpp create mode 100644 src/platform/qt/ScriptingView.h create mode 100644 src/platform/qt/ScriptingView.ui diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 4399bfcfb..f45414220 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -247,11 +247,21 @@ if(USE_DISCORD_RPC) list(APPEND SOURCE_FILES DiscordCoordinator.cpp) endif() +if(ENABLE_SCRIPTING) + list(APPEND SOURCE_FILES + ScriptingController.cpp + ScriptingView.cpp) + + list(APPEND UI_FILES + ScriptingView.ui) +endif() + if(TARGET Qt6::Core) qt_add_resources(RESOURCES resources.qrc) else() qt5_add_resources(RESOURCES resources.qrc) endif() + if(BUILD_UPDATER) file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/updater.qrc INPUT ${CMAKE_CURRENT_SOURCE_DIR}/updater.qrc.in) if(TARGET Qt6::Core) diff --git a/src/platform/qt/LogWidget.cpp b/src/platform/qt/LogWidget.cpp index 136df178c..4b562b3b0 100644 --- a/src/platform/qt/LogWidget.cpp +++ b/src/platform/qt/LogWidget.cpp @@ -11,14 +11,58 @@ using namespace QGBA; +QTextCharFormat LogWidget::s_warn; +QTextCharFormat LogWidget::s_error; +QTextCharFormat LogWidget::s_prompt; + LogWidget::LogWidget(QWidget* parent) - : QTextEdit(parent) + : QPlainTextEdit(parent) { setFont(GBAApp::app()->monospaceFont()); + + QPalette palette = QApplication::palette(); + s_warn.setFontWeight(QFont::DemiBold); + s_warn.setFontItalic(true); + s_warn.setForeground(Qt::yellow); + s_warn.setBackground(QColor(255, 255, 0, 64)); + s_error.setFontWeight(QFont::Bold); + s_error.setForeground(Qt::red); + s_error.setBackground(QColor(255, 0, 0, 64)); + s_prompt.setForeground(palette.brush(QPalette::Disabled, QPalette::Text)); } void LogWidget::log(const QString& line) { moveCursor(QTextCursor::End); - insertPlainText(line); + textCursor().insertText(line, {}); + if (m_newlineTerminated) { + textCursor().insertText("\n"); + } + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} + +void LogWidget::warn(const QString& line) { + moveCursor(QTextCursor::End); + textCursor().insertText(WARN_PREFIX + line, s_warn); + if (m_newlineTerminated) { + textCursor().insertText("\n"); + } + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} + +void LogWidget::error(const QString& line) { + moveCursor(QTextCursor::End); + textCursor().insertText(ERROR_PREFIX + line, s_error); + if (m_newlineTerminated) { + textCursor().insertText("\n"); + } + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} + +void LogWidget::echo(const QString& line) { + moveCursor(QTextCursor::End); + textCursor().insertText(PROMPT_PREFIX + line, s_prompt); + if (m_newlineTerminated) { + textCursor().insertText("\n"); + } verticalScrollBar()->setValue(verticalScrollBar()->maximum()); } diff --git a/src/platform/qt/LogWidget.h b/src/platform/qt/LogWidget.h index 700f7aaf6..d193a318c 100644 --- a/src/platform/qt/LogWidget.h +++ b/src/platform/qt/LogWidget.h @@ -5,16 +5,36 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include +#include namespace QGBA { -class LogWidget : public QTextEdit { +class LogHighlighter; + +class LogWidget : public QPlainTextEdit { +Q_OBJECT + public: + static constexpr const char* WARN_PREFIX = "[WARNING] "; + static constexpr const char* ERROR_PREFIX = "[ERROR] "; + static constexpr const char* PROMPT_PREFIX = "> "; + LogWidget(QWidget* parent = nullptr); + void setNewlineTerminated(bool newlineTerminated) { m_newlineTerminated = newlineTerminated; } + public slots: void log(const QString&); + void warn(const QString&); + void error(const QString&); + void echo(const QString&); + +private: + static QTextCharFormat s_warn; + static QTextCharFormat s_error; + static QTextCharFormat s_prompt; + + bool m_newlineTerminated = false; }; } diff --git a/src/platform/qt/ScriptingController.cpp b/src/platform/qt/ScriptingController.cpp new file mode 100644 index 000000000..21289c6ee --- /dev/null +++ b/src/platform/qt/ScriptingController.cpp @@ -0,0 +1,102 @@ +/* Copyright (c) 2013-2022 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 "ScriptingController.h" + +#include "CoreController.h" + +using namespace QGBA; + +ScriptingController::ScriptingController(QObject* parent) + : QObject(parent) +{ + mScriptContextInit(&m_scriptContext); + mScriptContextAttachStdlib(&m_scriptContext); + mScriptContextRegisterEngines(&m_scriptContext); + + m_logger.p = this; + m_logger.log = [](mLogger* log, int, enum mLogLevel level, const char* format, va_list args) { + Logger* logger = static_cast(log); + va_list argc; + va_copy(argc, args); + QString message = QString::vasprintf(format, argc); + va_end(argc); + switch (level) { + case mLOG_WARN: + emit logger->p->warn(message); + break; + case mLOG_ERROR: + emit logger->p->error(message); + break; + default: + emit logger->p->log(message); + break; + } + }; + + mScriptContextAttachLogger(&m_scriptContext, &m_logger); + + HashTableEnumerate(&m_scriptContext.engines, [](const char* key, void* engine, void* context) { + ScriptingController* self = static_cast(context); + self->m_engines[QString::fromUtf8(key)] = static_cast(engine); + }, this); + + if (m_engines.count() == 1) { + m_activeEngine = *m_engines.begin(); + } +} + +ScriptingController::~ScriptingController() { + clearController(); + mScriptContextDeinit(&m_scriptContext); +} + +void ScriptingController::setController(std::shared_ptr controller) { + if (controller == m_controller) { + return; + } + clearController(); + m_controller = controller; + CoreController::Interrupter interrupter(m_controller); + m_controller->thread()->scriptContext = &m_scriptContext; + if (m_controller->hasStarted()) { + mScriptContextAttachCore(&m_scriptContext, m_controller->thread()->core); + } + connect(m_controller.get(), &CoreController::stopping, this, &ScriptingController::clearController); +} + +bool ScriptingController::loadFile(const QString& path) { + VFileDevice vf(path, QIODevice::ReadOnly); + return load(vf); +} + +bool ScriptingController::load(VFileDevice& vf) { + if (!m_activeEngine) { + return false; + } + CoreController::Interrupter interrupter(m_controller); + if (!m_activeEngine->load(m_activeEngine, vf) || !m_activeEngine->run(m_activeEngine)) { + emit error(QString::fromUtf8(m_activeEngine->getError(m_activeEngine))); + return false; + } + return true; +} + +void ScriptingController::clearController() { + if (!m_controller) { + return; + } + { + CoreController::Interrupter interrupter(m_controller); + mScriptContextDetachCore(&m_scriptContext); + m_controller->thread()->scriptContext = nullptr; + } + m_controller.reset(); +} + +void ScriptingController::runCode(const QString& code) { + VFileDevice vf(code.toUtf8()); + load(vf); +} diff --git a/src/platform/qt/ScriptingController.h b/src/platform/qt/ScriptingController.h new file mode 100644 index 000000000..3e6afffcc --- /dev/null +++ b/src/platform/qt/ScriptingController.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include +#include + +#include +#include + +#include "VFileDevice.h" + +#include + +namespace QGBA { + +class CoreController; +class ScriptingController : public QObject { +Q_OBJECT + +public: + ScriptingController(QObject* parent = nullptr); + ~ScriptingController(); + + void setController(std::shared_ptr controller); + + bool loadFile(const QString& path); + bool load(VFileDevice& vf); + + mScriptContext* context() { return &m_scriptContext; } + +signals: + void log(const QString&); + void warn(const QString&); + void error(const QString&); + +public slots: + void clearController(); + void runCode(const QString& code); + +private: + struct Logger : mLogger { + ScriptingController* p; + } m_logger{}; + + mScriptContext m_scriptContext; + + mScriptEngineContext* m_activeEngine = nullptr; + QHash m_engines; + + std::shared_ptr m_controller; +}; + +} diff --git a/src/platform/qt/ScriptingView.cpp b/src/platform/qt/ScriptingView.cpp new file mode 100644 index 000000000..eb8dac8fe --- /dev/null +++ b/src/platform/qt/ScriptingView.cpp @@ -0,0 +1,51 @@ +/* Copyright (c) 2013-2022 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 "ScriptingView.h" + +#include "GBAApp.h" +#include "ScriptingController.h" + +using namespace QGBA; + +ScriptingView::ScriptingView(ScriptingController* controller, QWidget* parent) + : QMainWindow(parent) + , m_controller(controller) +{ + m_ui.setupUi(this); + + m_ui.prompt->setFont(GBAApp::app()->monospaceFont()); + m_ui.log->setNewlineTerminated(true); + + connect(m_ui.prompt, &QLineEdit::returnPressed, this, &ScriptingView::submitRepl); + connect(m_ui.runButton, &QAbstractButton::clicked, this, &ScriptingView::submitRepl); + connect(m_controller, &ScriptingController::log, m_ui.log, &LogWidget::log); + connect(m_controller, &ScriptingController::warn, m_ui.log, &LogWidget::warn); + connect(m_controller, &ScriptingController::error, m_ui.log, &LogWidget::error); + + connect(m_ui.load, &QAction::triggered, this, &ScriptingView::load); +} + +void ScriptingView::submitRepl() { + m_ui.log->echo(m_ui.prompt->text()); + m_controller->runCode(m_ui.prompt->text()); + m_ui.prompt->clear(); +} + +void ScriptingView::load() { + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select script to load"), getFilters()); + if (!filename.isEmpty()) { + m_controller->loadFile(filename); + } +} + +QString ScriptingView::getFilters() const { + QStringList filters; +#ifdef USE_LUA + filters.append(tr("Lua scripts (*.lua)")); +#endif + filters.append(tr("All files (*.*)")); + return filters.join(";;"); +} diff --git a/src/platform/qt/ScriptingView.h b/src/platform/qt/ScriptingView.h new file mode 100644 index 000000000..969f51c62 --- /dev/null +++ b/src/platform/qt/ScriptingView.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include "ui_ScriptingView.h" + +namespace QGBA { + +class ScriptingController; + +class ScriptingView : public QMainWindow { +Q_OBJECT + +public: + ScriptingView(ScriptingController* controller, QWidget* parent = nullptr); + +private slots: + void submitRepl(); + void load(); + +private: + QString getFilters() const; + Ui::ScriptingView m_ui; + + ScriptingController* m_controller; +}; + +} diff --git a/src/platform/qt/ScriptingView.ui b/src/platform/qt/ScriptingView.ui new file mode 100644 index 000000000..32ea124cf --- /dev/null +++ b/src/platform/qt/ScriptingView.ui @@ -0,0 +1,97 @@ + + + ScriptingView + + + + 0 + 0 + 800 + 600 + + + + Scripting + + + + + + + + + + Run + + + + + + + + 0 + 0 + + + + true + + + + + + + + 0 + 0 + + + + + 180 + 16777215 + + + + + Console + + + + + + + + + + 0 + 0 + 800 + 29 + + + + + File + + + + + + + + + Load script... + + + + + + QGBA::LogWidget + QPlainTextEdit +
LogWidget.h
+
+
+ + +
diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 77ad13fb5..128435831 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -51,6 +51,7 @@ #include "ReportView.h" #include "ROMInfo.h" #include "SaveConverter.h" +#include "ScriptingView.h" #include "SensorView.h" #include "ShaderSelector.h" #include "ShortcutController.h" @@ -653,6 +654,19 @@ void Window::consoleOpen() { } #endif +#ifdef ENABLE_SCRIPTING +void Window::scriptingOpen() { + if (!m_scripting) { + m_scripting = std::make_unique(); + if (m_controller) { + m_scripting->setController(m_controller); + } + } + ScriptingView* view = new ScriptingView(m_scripting.get()); + openView(view); +} +#endif + void Window::keyPressEvent(QKeyEvent* event) { if (event->isAutoRepeat()) { QWidget::keyPressEvent(event); @@ -1642,15 +1656,20 @@ void Window::setupMenu(QMenuBar* menubar) { m_actions.addAction(tr("Settings..."), "settings", this, &Window::openSettingsWindow, "tools")->setRole(Action::Role::SETTINGS); m_actions.addAction(tr("Make portable"), "makePortable", this, &Window::tryMakePortable, "tools"); -#ifdef USE_DEBUGGERS m_actions.addSeparator("tools"); +#ifdef USE_DEBUGGERS m_actions.addAction(tr("Open debugger console..."), "debuggerWindow", this, &Window::consoleOpen, "tools"); #ifdef USE_GDB_STUB Action* gdbWindow = addGameAction(tr("Start &GDB server..."), "gdbWindow", this, &Window::gdbOpen, "tools"); m_platformActions.insert(mPLATFORM_GBA, gdbWindow); #endif #endif +#ifdef ENABLE_SCRIPTING + m_actions.addAction(tr("Scripting..."), "scripting", this, &Window::scriptingOpen, "tools"); +#endif +#if defined(USE_DEBUGGERS) || defined(ENABLE_SCRIPTING) m_actions.addSeparator("tools"); +#endif addGameAction(tr("View &palette..."), "paletteWindow", openControllerTView(), "tools"); addGameAction(tr("View &sprites..."), "spriteWindow", openControllerTView(), "tools"); @@ -2099,6 +2118,12 @@ void Window::setController(CoreController* controller, const QString& fname) { m_pendingPatch = QString(); } +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_scripting->setController(m_controller); + } +#endif + attachDisplay(); m_controller->loadConfig(m_config); m_config->updateOption("showOSD"); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 6cc68150a..667cf77ca 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -23,6 +23,9 @@ #include "LoadSaveState.h" #include "LogController.h" #include "SettingsView.h" +#ifdef ENABLE_SCRIPTING +#include "ScriptingController.h" +#endif namespace QGBA { @@ -113,6 +116,10 @@ public slots: void gdbOpen(); #endif +#ifdef ENABLE_SCRIPTING + void scriptingOpen(); +#endif + protected: virtual void keyPressEvent(QKeyEvent* event) override; virtual void keyReleaseEvent(QKeyEvent* event) override; @@ -252,6 +259,10 @@ private: #ifdef USE_SQLITE3 LibraryController* m_libraryView; #endif + +#ifdef ENABLE_SCRIPTING + std::unique_ptr m_scripting; +#endif }; class WindowBackground : public QWidget { From e228857eeffa1315f226eeb4bd8c889f86a48528 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 13 May 2022 23:18:39 -0700 Subject: [PATCH 059/105] Scripting: Add default arguments --- include/mgba/script/types.h | 138 +++++++++++++++++++++++------------- src/script/test/classes.c | 24 +++++++ src/script/types.c | 17 ++++- 3 files changed, 129 insertions(+), 50 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 9c008717b..af6d8f736 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -284,7 +284,7 @@ CXX_GUARD_START return false; \ } -#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, ...) \ +#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, DEFAULTS, ...) \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \ static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \ .call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \ @@ -303,6 +303,7 @@ CXX_GUARD_START .count = _mSUCC_ ## NPARAMS, \ .entries = { mSCRIPT_TYPE_MS_ ## S(TYPE), _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ .names = { "this", _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ + .defaults = DEFAULTS, \ }, \ .returnType = { \ .count = NRET, \ @@ -315,49 +316,65 @@ CXX_GUARD_START #define _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, RETURN, NAME, CONST, NPARAMS, ...) \ typedef RETURN (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mCOMMA_ ## NPARAMS(CONST struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__))) -#define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ - \ +#define _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, T, NPARAMS, ...) \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \ return true; \ + } + +#define _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, T, NPARAMS, ...) \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ + return true; \ } \ +#define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + #define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, __VA_ARGS__) \ - \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ - return true; \ - } \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) \ - \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \ - return true; \ - } \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, __VA_ARGS__) \ - \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, CS, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ - return true; \ - } \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) @@ -371,6 +388,24 @@ CXX_GUARD_START #define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) +#define mSCRIPT_DECLARE_STRUCT_D_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_CD_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAU(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX] = { \ + mSCRIPT_NO_DEFAULT, + +#define mSCRIPT_DEFINE_DEFAULTS_END } + #define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME) { \ .type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \ .info = { \ @@ -482,6 +517,12 @@ CXX_GUARD_START #define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(S(STRUCT), VALUE) #define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(CS(STRUCT), VALUE) +#define mSCRIPT_NO_DEFAULT { \ + .type = NULL, \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = {0} \ +} + enum mScriptTypeBase { mSCRIPT_TYPE_VOID = 0, mSCRIPT_TYPE_SINT, @@ -532,17 +573,35 @@ extern const struct mScriptType mSTTable; extern const struct mScriptType mSTWrapper; extern const struct mScriptType mSTWeakref; +struct mScriptType; +struct mScriptValue { + const struct mScriptType* type; + int refs; + uint32_t flags; + union { + int32_t s32; + uint32_t u32; + float f32; + int64_t s64; + uint64_t u64; + double f64; + void* opaque; + const void* copaque; + } value; +}; + struct mScriptTypeTuple { size_t count; const struct mScriptType* entries[mSCRIPT_PARAMS_MAX]; const char* names[mSCRIPT_PARAMS_MAX]; + const struct mScriptValue* defaults; bool variable; }; struct mScriptTypeFunction { struct mScriptTypeTuple parameters; struct mScriptTypeTuple returnType; - // TODO: kwargs, defaults + // TODO: kwargs }; struct mScriptClassMember { @@ -579,7 +638,6 @@ struct mScriptTypeClass { struct mScriptClassMember* set; // TODO }; -struct mScriptValue; struct mScriptType { enum mScriptTypeBase base : 8; bool isConst; @@ -600,22 +658,6 @@ struct mScriptType { bool (*cast)(const struct mScriptValue*, const struct mScriptType*, struct mScriptValue*); }; -struct mScriptValue { - const struct mScriptType* type; - int refs; - uint32_t flags; - union { - int32_t s32; - uint32_t u32; - float f32; - int64_t s64; - uint64_t u64; - double f64; - void* opaque; - const void* copaque; - } value; -}; - DECLARE_VECTOR(mScriptList, struct mScriptValue) struct mScriptString { diff --git a/src/script/test/classes.c b/src/script/test/classes.c index f019b9f91..9616229b0 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -66,6 +66,10 @@ static void testAv1(struct TestA* a, int b) { a->i += b; } +static void testAv2(struct TestA* a, int b, int c) { + a->i += b + c; +} + static int32_t testGet(struct TestE* e, const char* name) { UNUSED(e); return name[0]; @@ -90,6 +94,7 @@ mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic0, testAic0, 0); mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic1, testAic1, 1, S32, b); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v0, testAv0, 0); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v1, testAv1, 1, S32, b); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TestA, v2, testAv2, 2, S32, b, S32, c); mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING) @@ -109,8 +114,14 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ic1) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v0) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v1) + mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v2) mSCRIPT_DEFINE_END; +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TestA, v2) + mSCRIPT_NO_DEFAULT, + mSCRIPT_MAKE_S32(0) +mSCRIPT_DEFINE_DEFAULTS_END; + mSCRIPT_DEFINE_STRUCT(TestB) mSCRIPT_DEFINE_INHERIT(TestA) mSCRIPT_DEFINE_STRUCT_MEMBER(TestB, S32, i3) @@ -441,6 +452,19 @@ M_TEST_DEFINE(testAStatic) { mSCRIPT_PUSH(&frame.arguments, S32, 2); assert_true(mScriptInvoke(&val, &frame)); mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "v2", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.arguments, S32, -2); + assert_true(mScriptInvoke(&val, &frame)); + mScriptFrameDeinit(&frame); + assert_true(mScriptObjectGet(&sval, "v2", &val)); + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.arguments, S32, 1); + assert_true(mScriptInvoke(&val, &frame)); + mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "i0", &val)); mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); diff --git a/src/script/types.c b/src/script/types.c index b455b11de..835128c5f 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1297,11 +1297,14 @@ bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* inpu } bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame) { - if (types->count != mScriptListSize(frame) && (!types->variable || mScriptListSize(frame) < types->count)) { + if (types->count < mScriptListSize(frame) && !types->variable) { + return false; + } + if (types->count > mScriptListSize(frame) && !types->variable && !types->defaults) { return false; } size_t i; - for (i = 0; i < types->count; ++i) { + for (i = 0; i < mScriptListSize(frame) && i < types->count; ++i) { if (types->entries[i] == mScriptListGetPointer(frame, i)->type) { continue; } @@ -1316,5 +1319,15 @@ bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList return false; } } + if (types->variable) { + return true; + } + + for (; i < types->count; ++i) { + if (!types->defaults[i].type) { + return false; + } + memcpy(mScriptListAppend(frame), &types->defaults[i], sizeof(struct mScriptValue)); + } return true; } From cb9d45caee7cacd6c8d28bc7e8258c0ca3df165c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 14 May 2022 15:08:18 -0700 Subject: [PATCH 060/105] Scripting: Add savestate slot access --- include/mgba/core/serialize.h | 1 + src/core/core.c | 3 ++ src/core/scripting.c | 65 ++++++++++++++++++++++------------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/include/mgba/core/serialize.h b/include/mgba/core/serialize.h index 5f0780157..59ca1c19f 100644 --- a/include/mgba/core/serialize.h +++ b/include/mgba/core/serialize.h @@ -26,6 +26,7 @@ enum mStateExtdataTag { #define SAVESTATE_CHEATS 4 #define SAVESTATE_RTC 8 #define SAVESTATE_METADATA 16 +#define SAVESTATE_ALL 31 struct mStateExtdataItem { int32_t size; diff --git a/src/core/core.c b/src/core/core.c index 001107228..0879f9254 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -295,6 +295,9 @@ struct VFile* mCoreGetState(struct mCore* core, int slot, bool write) { if (!core->dirs.state) { return NULL; } + if (slot < 0) { + return NULL; + } char name[PATH_MAX + 14]; // Quash warning snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot); return core->dirs.state->openFile(core->dirs.state, name, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); diff --git a/src/core/scripting.c b/src/core/scripting.c index 17406dc3c..d89cbd808 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -244,32 +245,50 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite8, 2, U32, address, U8, valu mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value); -mSCRIPT_DEFINE_STRUCT(mCore) -mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") -mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, currentFrame, frameCounter) -mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per frame") -mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frameCycles) -mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per second") -mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frequency) -mSCRIPT_DEFINE_DOCSTRING("Run until the next frame") -mSCRIPT_DEFINE_STRUCT_METHOD(mCore, runFrame) -mSCRIPT_DEFINE_DOCSTRING("Run a single instruction") -mSCRIPT_DEFINE_STRUCT_METHOD(mCore, step) +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, saveStateSlot, mCoreSaveState, 2, S32, slot, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoadState, 2, S32, slot, S32, flags); -mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given bus address") -mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read8, busRead8) -mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given bus address") -mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read16, busRead16) -mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given bus address") -mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read32, busRead32) -mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given bus address") -mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write8, busWrite8) -mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given bus address") -mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write16, busWrite16) -mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given bus address") -mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write32, busWrite32) +mSCRIPT_DEFINE_STRUCT(mCore) + mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") + mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, currentFrame, frameCounter) + mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per frame") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frameCycles) + mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per second") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frequency) + mSCRIPT_DEFINE_DOCSTRING("Run until the next frame") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, runFrame) + mSCRIPT_DEFINE_DOCSTRING("Run a single instruction") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, step) + + mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given bus address") + mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read8, busRead8) + mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given bus address") + mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read16, busRead16) + mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given bus address") + mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read32, busRead32) + mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given bus address") + mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write8, busWrite8) + mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given bus address") + mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write16, busWrite16) + mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given bus address") + mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write32, busWrite32) + + mSCRIPT_DEFINE_DOCSTRING("Save state to the slot number") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateSlot) + mSCRIPT_DEFINE_DOCSTRING("Load state from the slot number") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateSlot) mSCRIPT_DEFINE_END; +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateSlot) + mSCRIPT_NO_DEFAULT, + mSCRIPT_MAKE_S32(SAVESTATE_ALL) +mSCRIPT_DEFINE_DEFAULTS_END; + +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateSlot) + mSCRIPT_NO_DEFAULT, + mSCRIPT_MAKE_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA) +mSCRIPT_DEFINE_DEFAULTS_END; + static void _clearMemoryMap(struct mScriptContext* context, struct mScriptCoreAdapter* adapter, bool clear) { struct TableIterator iter; if (mScriptTableIteratorStart(&adapter->memory, &iter)) { From 303fc17e773db1d0f055c7d3f98cb0e06998952d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 14 May 2022 15:11:47 -0700 Subject: [PATCH 061/105] Scripting: Add screenshots, delimit categories --- src/core/scripting.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/scripting.c b/src/core/scripting.c index d89cbd808..b1e5dea38 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -232,12 +232,16 @@ mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write32) mSCRIPT_DEFINE_END; +// Info functions mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frameCycles, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0); + +// Run functions mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, step, 0); +// Memory functions mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead8, 1, U32, address); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead16, 1, U32, address); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead32, 1, U32, address); @@ -245,9 +249,13 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite8, 2, U32, address, U8, valu mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value); +// Savestate functions mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, saveStateSlot, mCoreSaveState, 2, S32, slot, S32, flags); mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoadState, 2, S32, slot, S32, flags); +// Miscellaneous functions +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, screenshot, mCoreTakeScreenshot, 0); + mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, currentFrame, frameCounter) @@ -277,6 +285,9 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateSlot) mSCRIPT_DEFINE_DOCSTRING("Load state from the slot number") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateSlot) + + mSCRIPT_DEFINE_DOCSTRING("Save a screenshot") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshot) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateSlot) From 5c84278667eb022c48f9e0940ae2016e6285ef41 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 14 May 2022 19:26:07 -0700 Subject: [PATCH 062/105] Scripting: Start exporting some constants --- include/mgba/script/context.h | 10 ++++++++++ src/script/context.c | 24 +++++++++++++++++++----- src/script/stdlib.c | 20 ++++++++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 8104295d7..74385c291 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -14,6 +14,9 @@ CXX_GUARD_START #include #include +#define mSCRIPT_CONSTANT_PAIR(NS, CONST) { #CONST, mScriptValueCreateFromSInt(NS ## _ ## CONST) } +#define mSCRIPT_CONSTANT_SENTINEL { NULL, NULL } + struct mScriptFrame; struct mScriptFunction; struct mScriptEngineContext; @@ -25,6 +28,7 @@ struct mScriptContext { struct Table weakrefs; uint32_t nextWeakref; struct Table callbacks; + struct mScriptValue* constants; }; struct mScriptEngine2 { @@ -50,6 +54,11 @@ struct mScriptEngineContext { const char* (*getError)(struct mScriptEngineContext*); }; +struct mScriptKVPair { + const char* key; + struct mScriptValue* value; +}; + void mScriptContextInit(struct mScriptContext*); void mScriptContextDeinit(struct mScriptContext*); @@ -70,6 +79,7 @@ struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); void mScriptContextAttachStdlib(struct mScriptContext* context); +void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants); void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback); void mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value); diff --git a/src/script/context.c b/src/script/context.c index 2ee2dead0..b3ec04ffd 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -8,11 +8,6 @@ #include #endif -struct mScriptKVPair { - const char* key; - struct mScriptValue* value; -}; - struct mScriptFileInfo { const char* name; struct VFile* vf; @@ -61,6 +56,7 @@ void mScriptContextInit(struct mScriptContext* context) { TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref); context->nextWeakref = 1; HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref); + context->constants = NULL; } void mScriptContextDeinit(struct mScriptContext* context) { @@ -223,6 +219,24 @@ void mScriptContextAddCallback(struct mScriptContext* context, const char* callb mScriptValueWrap(fn, mScriptListAppend(list->value.opaque)); } +void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants) { + if (!context->constants) { + context->constants = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + } + struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + size_t i; + for (i = 0; constants[i].key; ++i) { + struct mScriptValue* key = mScriptStringCreateFromUTF8(constants[i].key); + mScriptTableInsert(table, key, constants[i].value); + mScriptValueDeref(key); + mScriptValueDeref(constants[i].value); + } + struct mScriptValue* key = mScriptStringCreateFromUTF8(nspace); + mScriptTableInsert(context->constants, key, table); + mScriptValueDeref(key); + mScriptValueDeref(table); +} + bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) { struct mScriptFileInfo info = { .name = name, diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 08871fb49..78d57deb8 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -5,6 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include +#include + struct mScriptCallbackAdapter { struct mScriptContext* context; }; @@ -34,4 +37,21 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { }; lib->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; mScriptContextSetGlobal(context, "callbacks", lib); + + mScriptContextExportConstants(context, "SAVESTATE", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(SAVESTATE, SCREENSHOT), + mSCRIPT_CONSTANT_PAIR(SAVESTATE, SAVEDATA), + mSCRIPT_CONSTANT_PAIR(SAVESTATE, CHEATS), + mSCRIPT_CONSTANT_PAIR(SAVESTATE, RTC), + mSCRIPT_CONSTANT_PAIR(SAVESTATE, METADATA), + mSCRIPT_CONSTANT_PAIR(SAVESTATE, ALL), + mSCRIPT_CONSTANT_SENTINEL + }); + mScriptContextExportConstants(context, "PLATFORM", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mPLATFORM, NONE), + mSCRIPT_CONSTANT_PAIR(mPLATFORM, GBA), + mSCRIPT_CONSTANT_PAIR(mPLATFORM, GB), + mSCRIPT_CONSTANT_SENTINEL + }); + mScriptContextSetGlobal(context, "C", context->constants); } From 98f5298e041f5b8c08d0d656bab6aab8ca469706 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 15 May 2022 02:08:55 -0700 Subject: [PATCH 063/105] Scripting: Add __pairs implementation for tables --- src/script/engines/lua.c | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 8533b7a22..df0ef9eed 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -39,6 +39,7 @@ static int _luaGetObject(lua_State* lua); static int _luaSetObject(lua_State* lua); static int _luaGcObject(lua_State* lua); static int _luaGetTable(lua_State* lua); +static int _luaPairsTable(lua_State* lua); #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber @@ -99,6 +100,7 @@ static const luaL_Reg _mSTStruct[] = { static const luaL_Reg _mSTTable[] = { { "__index", _luaGetTable }, + { "__pairs", _luaPairsTable }, { NULL, NULL } }; @@ -634,3 +636,56 @@ int _luaGetTable(lua_State* lua) { } return 1; } + +static int _luaNextTable(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + char key[MAX_KEY_SIZE]; + const char* keyPtr = lua_tostring(lua, -1); + struct mScriptValue* table = lua_touserdata(lua, -2); + struct mScriptValue keyVal; + + if (keyPtr) { + strlcpy(key, keyPtr, sizeof(key)); + keyVal = mSCRIPT_MAKE_CHARP(key); + } + lua_pop(lua, 2); + + table = mScriptContextAccessWeakref(luaContext->d.context, table); + if (!table) { + lua_pushliteral(lua, "Invalid object"); + lua_error(lua); + } + + struct TableIterator iter; + if (keyPtr) { + if (!mScriptTableIteratorLookup(table, &iter, &keyVal)) { + return 0; + } + if (!mScriptTableIteratorNext(table, &iter)) { + return 0; + } + } else { + if (!mScriptTableIteratorStart(table, &iter)) { + return 0; + } + } + + if (!_luaWrap(luaContext, mScriptTableIteratorGetKey(table, &iter))) { + lua_pushliteral(lua, "Iteration error"); + lua_error(lua); + } + + if (!_luaWrap(luaContext, mScriptTableIteratorGetValue(table, &iter))) { + lua_pushliteral(lua, "Iteration error"); + lua_error(lua); + } + + return 2; +} + +int _luaPairsTable(lua_State* lua) { + lua_pushcfunction(lua, _luaNextTable); + lua_insert(lua, -2); + lua_pushnil(lua); + return 3; +} From 1895ed34fcbfa4be5804ac8f6e75b294d335716f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 15 May 2022 19:29:25 -0700 Subject: [PATCH 064/105] Scripting: Add making strings from non-UTF8-clean text --- include/mgba/script/types.h | 1 + src/script/types.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index af6d8f736..87953723a 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -686,6 +686,7 @@ struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val); const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val); struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); +struct mScriptValue* mScriptStringCreateFromASCII(const char* string); struct mScriptValue* mScriptValueCreateFromUInt(uint32_t value); struct mScriptValue* mScriptValueCreateFromSInt(int32_t value); diff --git a/src/script/types.c b/src/script/types.c index 835128c5f..1152e28ef 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -7,6 +7,7 @@ #include #include +#include #include static void _allocList(struct mScriptValue*); @@ -738,9 +739,20 @@ struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); struct mScriptString* internal = val->value.opaque; internal->size = strlen(string); + internal->length = utf8strlen(string); internal->buffer = strdup(string); return val; } + +struct mScriptValue* mScriptStringCreateFromASCII(const char* string) { + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); + struct mScriptString* internal = val->value.opaque; + internal->size = strlen(string); + internal->length = strlen(string); + internal->buffer = latin1ToUtf8(string, internal->size + 1); + return val; +} + struct mScriptValue* mScriptValueCreateFromSInt(int32_t value) { struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); val->value.s32 = value; From e70a9b2f41a17df12aededa8841f62188ac5f32d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 15 May 2022 19:29:42 -0700 Subject: [PATCH 065/105] Scripting: Add more informational functions --- src/core/scripting.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/core/scripting.c b/src/core/scripting.c index b1e5dea38..39b3ffb25 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -232,10 +232,25 @@ mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write32) mSCRIPT_DEFINE_END; +struct mScriptValue* _mScriptCoreGetGameTitle(const struct mCore* core) { + char title[32] = {0}; + core->getGameTitle(core, title); + return mScriptStringCreateFromASCII(title); +} + +struct mScriptValue* _mScriptCoreGetGameCode(const struct mCore* core) { + char code[16] = {0}; + core->getGameCode(core, code); + return mScriptStringCreateFromASCII(code); +} + // Info functions +mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, platform, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frameCycles, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0); +mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WRAPPER, getGameTitle, _mScriptCoreGetGameTitle, 0); +mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WRAPPER, getGameCode, _mScriptCoreGetGameCode, 0); // Run functions mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0); @@ -257,12 +272,20 @@ mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoad mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, screenshot, mCoreTakeScreenshot, 0); mSCRIPT_DEFINE_STRUCT(mCore) + mSCRIPT_DEFINE_DOCSTRING("Get which platform is being emulated") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, platform) mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, currentFrame, frameCounter) mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per frame") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frameCycles) mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per second") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frequency) + + mSCRIPT_DEFINE_DOCSTRING("Get internal title of the game from the ROM header") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getGameTitle) + mSCRIPT_DEFINE_DOCSTRING("Get internal product code for the game from the ROM header") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getGameCode) + mSCRIPT_DEFINE_DOCSTRING("Run until the next frame") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, runFrame) mSCRIPT_DEFINE_DOCSTRING("Run a single instruction") From 82c97e5dc3b5586a60e29e08c5d1e3afead8cd34 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 May 2022 01:07:45 -0700 Subject: [PATCH 066/105] Scripting: Add key input functions --- src/core/scripting.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/core/scripting.c b/src/core/scripting.c index 39b3ffb25..70f3e50fa 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -256,6 +256,12 @@ mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WRAPPER, getGameCode, _mScriptCoreGetGame mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, step, 0); +// Key functions +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, setKeys, 1, U32, keys); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, addKeys, 1, U32, keys); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, clearKeys, 1, U32, keys); +mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, getKeys, 0); + // Memory functions mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead8, 1, U32, address); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead16, 1, U32, address); @@ -291,6 +297,15 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Run a single instruction") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, step) + mSCRIPT_DEFINE_DOCSTRING("Set the currently active keys") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, setKeys) + mSCRIPT_DEFINE_DOCSTRING("Add keys to the currently active key list") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, addKeys) + mSCRIPT_DEFINE_DOCSTRING("Remove keys from the currently active key list") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, clearKeys) + mSCRIPT_DEFINE_DOCSTRING("Get the currently active keys") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getKeys) + mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given bus address") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read8, busRead8) mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given bus address") From 304a8d165586f9e9222861e3b7678b6570e7f529 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 May 2022 16:11:25 -0700 Subject: [PATCH 067/105] Scripting: Split macros into separate header --- include/mgba/core/scripting.h | 1 + include/mgba/script/macros.h | 430 ++++++++++++++++++++++++++++++++++ include/mgba/script/types.h | 415 -------------------------------- src/script/engines/lua.c | 1 + src/script/stdlib.c | 1 + src/script/test/classes.c | 1 + src/script/test/lua.c | 1 + src/script/test/types.c | 1 + src/script/types.c | 1 + 9 files changed, 437 insertions(+), 415 deletions(-) create mode 100644 include/mgba/script/macros.h diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h index fbb560237..76ac10915 100644 --- a/include/mgba/core/scripting.h +++ b/include/mgba/core/scripting.h @@ -14,6 +14,7 @@ CXX_GUARD_START #ifdef USE_DEBUGGERS #include #endif +#include #include mLOG_DECLARE_CATEGORY(SCRIPT); diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h new file mode 100644 index 000000000..1da18b59c --- /dev/null +++ b/include/mgba/script/macros.h @@ -0,0 +1,430 @@ +/* Copyright (c) 2013-2022 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/. */ +#ifndef M_SCRIPT_MACROS_H +#define M_SCRIPT_MACROS_H + +#include + +CXX_GUARD_START + +#define mSCRIPT_POP(STACK, TYPE, NAME) \ + mSCRIPT_TYPE_C_ ## TYPE NAME; \ + do { \ + struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ + bool deref = true; \ + if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ + if (_val->type == mSCRIPT_TYPE_MS_WRAPPER) { \ + _val = mScriptValueUnwrap(_val); \ + deref = false; \ + if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ + return false; \ + } \ + } else { \ + return false; \ + } \ + } \ + NAME = _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE; \ + if (deref) { \ + mScriptValueDeref(_val); \ + } \ + mScriptListResize(STACK, -1); \ + } while (0) + +#define mSCRIPT_POP_0(...) +#define mSCRIPT_POP_1(FRAME, T0) mSCRIPT_POP(FRAME, T0, p0) +#define mSCRIPT_POP_2(FRAME, T0, T1) mSCRIPT_POP(FRAME, T1, p1); mSCRIPT_POP_1(FRAME, T0) +#define mSCRIPT_POP_3(FRAME, T0, T1, T2) mSCRIPT_POP(FRAME, T2, p2); mSCRIPT_POP_2(FRAME, T0, T1) +#define mSCRIPT_POP_4(FRAME, T0, T1, T2, T3) mSCRIPT_POP(FRAME, T3, p3); mSCRIPT_POP_3(FRAME, T0, T1, T2) +#define mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4) mSCRIPT_POP(FRAME, T4, p4); mSCRIPT_POP_4(FRAME, T0, T1, T2, T3) +#define mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) mSCRIPT_POP(FRAME, T5, p5); mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4) +#define mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) mSCRIPT_POP(FRAME, T6, p6); mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) +#define mSCRIPT_POP_8(FRAME, T0, T1, T2, T3, T4, T5, T6, T7) mSCRIPT_POP(FRAME, T7, p7); mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) + +#define mSCRIPT_PUSH(STACK, TYPE, NAME) \ + do { \ + struct mScriptValue* _val = mScriptListAppend(STACK); \ + _val->type = mSCRIPT_TYPE_MS_ ## TYPE; \ + _val->refs = mSCRIPT_VALUE_UNREF; \ + _val->flags = 0; \ + _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE = NAME; \ + } while (0) + +#define mSCRIPT_ARG_NAMES_0 +#define mSCRIPT_ARG_NAMES_1 p0 +#define mSCRIPT_ARG_NAMES_2 p0, p1 +#define mSCRIPT_ARG_NAMES_3 p0, p1, p2 +#define mSCRIPT_ARG_NAMES_4 p0, p1, p2, p3 +#define mSCRIPT_ARG_NAMES_5 p0, p1, p2, p3, p4 +#define mSCRIPT_ARG_NAMES_6 p0, p1, p2, p3, p4, p5 +#define mSCRIPT_ARG_NAMES_7 p0, p1, p2, p3, p4, p5, p6 +#define mSCRIPT_ARG_NAMES_8 p0, p1, p2, p3, p4, p5, p6, p7 + +#define mSCRIPT_PREFIX_0(PREFIX, ...) +#define mSCRIPT_PREFIX_1(PREFIX, T0) PREFIX ## T0 +#define mSCRIPT_PREFIX_2(PREFIX, T0, T1) PREFIX ## T0, PREFIX ## T1 +#define mSCRIPT_PREFIX_3(PREFIX, T0, T1, T2) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2 +#define mSCRIPT_PREFIX_4(PREFIX, T0, T1, T2, T3) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3 +#define mSCRIPT_PREFIX_5(PREFIX, T0, T1, T2, T3, T4) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4 +#define mSCRIPT_PREFIX_6(PREFIX, T0, T1, T2, T3, T4, T5) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5 +#define mSCRIPT_PREFIX_7(PREFIX, T0, T1, T2, T3, T4, T5, T6) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6 +#define mSCRIPT_PREFIX_8(PREFIX, T0, T1, T2, T3, T4, T5, T6, T7) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6, PREFIX ## T7 +#define mSCRIPT_PREFIX_N(N) mSCRIPT_PREFIX_ ## N + +#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) +#define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ + mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ + mSCRIPT_PUSH(&frame->returnValues, RETURN, out) + +#define mSCRIPT_DECLARE_STRUCT(STRUCT) \ + extern const struct mScriptType mSTStruct_ ## STRUCT; \ + extern const struct mScriptType mSTStructConst_ ## STRUCT; \ + extern const struct mScriptType mSTStructPtr_ ## STRUCT; \ + extern const struct mScriptType mSTStructPtrConst_ ## STRUCT; + +#define mSCRIPT_DEFINE_STRUCT(STRUCT) \ + const struct mScriptType mSTStruct_ ## STRUCT; \ + const struct mScriptType mSTStructConst_ ## STRUCT; \ + const struct mScriptType mSTStructPtr_ ## STRUCT; \ + const struct mScriptType mSTStructPtrConst_ ## STRUCT; \ + static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT; \ + static bool _mSTStructPtrCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \ + if (input->type == type || (input->type->constType == type)) { \ + output->type = type; \ + output->value.opaque = input->value.opaque; \ + return true; \ + } \ + if (input->type != &mSTStructPtr_ ## STRUCT && input->type != &mSTStructPtrConst_ ## STRUCT) { \ + return false; \ + } \ + if (type == &mSTStructConst_ ## STRUCT || (!input->type->isConst && type == &mSTStruct_ ## STRUCT)) { \ + output->type = type; \ + output->value.opaque = *(void**) input->value.opaque; \ + return true; \ + } \ + return false; \ + } \ + const struct mScriptType mSTStruct_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OBJECT, \ + .details = { \ + .cls = &_mSTStructDetails_ ## STRUCT \ + }, \ + .size = sizeof(struct STRUCT), \ + .name = "struct::" #STRUCT, \ + .alloc = NULL, \ + .free = mScriptObjectFree, \ + .cast = mScriptObjectCast, \ + .constType = &mSTStructConst_ ## STRUCT, \ + }; \ + const struct mScriptType mSTStructConst_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OBJECT, \ + .isConst = true, \ + .details = { \ + .cls = &_mSTStructDetails_ ## STRUCT \ + }, \ + .size = sizeof(struct STRUCT), \ + .name = "const struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = mScriptObjectCast, \ + }; \ + const struct mScriptType mSTStructPtr_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OPAQUE, \ + .size = sizeof(void*), \ + .name = "ptr struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = _mSTStructPtrCast_ ## STRUCT, \ + .constType = &mSTStructPtrConst_ ## STRUCT, \ + }; \ + const struct mScriptType mSTStructPtrConst_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OPAQUE, \ + .isConst = true, \ + .size = sizeof(void*), \ + .name = "ptr const struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = _mSTStructPtrCast_ ## STRUCT, \ + }; \ + static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ + .init = false, \ + .details = (const struct mScriptClassInitDetails[]) { + +#define mSCRIPT_DEFINE_DOCSTRING(DOCSTRING) { \ + .type = mSCRIPT_CLASS_INIT_DOCSTRING, \ + .info = { \ + .comment = DOCSTRING \ + } \ +}, + +#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \ + .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ + .info = { \ + .member = { \ + .name = #EXPORTED_NAME, \ + .type = mSCRIPT_TYPE_MS_ ## TYPE, \ + .offset = offsetof(struct STRUCT, NAME) \ + } \ + } \ +}, + +#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \ + mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME) + +#define mSCRIPT_DEFINE_INHERIT(PARENT) { \ + .type = mSCRIPT_CLASS_INIT_INHERIT, \ + .info = { \ + .parent = mSCRIPT_TYPE_MS_S(PARENT) \ + } \ +}, + +#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \ + _mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } + +#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, DEFAULTS, ...) \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \ + static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \ + .call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \ + }; \ + \ + static void _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME(struct mScriptValue* val) { \ + val->value.copaque = &_mSTStructBindingFunction_ ## TYPE ## _ ## NAME; \ + }\ + static const struct mScriptType _mSTStructBindingType_ ## TYPE ## _ ## NAME = { \ + .base = mSCRIPT_TYPE_FUNCTION, \ + .name = "struct::" #TYPE "." #NAME, \ + .alloc = _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME, \ + .details = { \ + .function = { \ + .parameters = { \ + .count = _mSUCC_ ## NPARAMS, \ + .entries = { mSCRIPT_TYPE_MS_ ## S(TYPE), _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ + .names = { "this", _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ + .defaults = DEFAULTS, \ + }, \ + .returnType = { \ + .count = NRET, \ + .entries = { RETURN } \ + }, \ + }, \ + } \ + }; + +#define _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, RETURN, NAME, CONST, NPARAMS, ...) \ + typedef RETURN (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mCOMMA_ ## NPARAMS(CONST struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__))) + +#define _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, T, NPARAMS, ...) \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \ + return true; \ + } + +#define _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, T, NPARAMS, ...) \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ + return true; \ + } \ + +#define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TYPE, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_CD_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_D_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_CD_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ + mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAU(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX] = { \ + mSCRIPT_NO_DEFAULT, + +#define mSCRIPT_DEFINE_DEFAULTS_END } + +#define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME) { \ + .type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \ + .info = { \ + .member = { \ + .name = #EXPORTED_NAME, \ + .type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME \ + } \ + }, \ +}, + +#define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) \ + _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME) + +#define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) + +#define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init) +#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit) +#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get) +#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set) + +#define mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(TYPE, CAST_TYPE, MEMBER) { \ + .type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \ + .info = { \ + .castMember = { \ + .type = mSCRIPT_TYPE_MS_ ## CAST_TYPE, \ + .member = #MEMBER \ + } \ + }, \ +}, + +#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } + +#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ + static struct mScriptFunction _function_ ## NAME = { \ + .call = _binding_ ## NAME \ + }; \ + static void _alloc_ ## NAME(struct mScriptValue* val) { \ + val->value.copaque = &_function_ ## NAME; \ + } \ + static const struct mScriptType _type_ ## NAME = { \ + .base = mSCRIPT_TYPE_FUNCTION, \ + .name = "function::" #NAME, \ + .alloc = _alloc_ ## NAME, \ + .details = { \ + .function = { \ + .parameters = { \ + .count = NPARAMS, \ + .entries = { _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ + .names = { _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ + }, \ + .returnType = { \ + .count = NRET, \ + .entries = { RETURN } \ + }, \ + }, \ + } \ + }; \ + const struct mScriptValue NAME = { \ + .type = &_type_ ## NAME, \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = { \ + .copaque = &_function_ ## NAME \ + } \ + } + +#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \ + static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } \ + _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ + return true; \ + } \ + _mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) + +#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \ + static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } \ + _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ + return true; \ + } \ + _mSCRIPT_BIND_FUNCTION(NAME, 0, , NPARAMS, __VA_ARGS__) + +#define mSCRIPT_MAKE(TYPE, VALUE) (struct mScriptValue) { \ + .type = (mSCRIPT_TYPE_MS_ ## TYPE), \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = { \ + .mSCRIPT_TYPE_FIELD_ ## TYPE = (VALUE) \ + }, \ + } \ + +#define mSCRIPT_MAKE_S8(VALUE) mSCRIPT_MAKE(S8, VALUE) +#define mSCRIPT_MAKE_U8(VALUE) mSCRIPT_MAKE(U8, VALUE) +#define mSCRIPT_MAKE_S16(VALUE) mSCRIPT_MAKE(S16, VALUE) +#define mSCRIPT_MAKE_U16(VALUE) mSCRIPT_MAKE(U16, VALUE) +#define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(S32, VALUE) +#define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(U32, VALUE) +#define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(F32, VALUE) +#define mSCRIPT_MAKE_S64(VALUE) mSCRIPT_MAKE(S64, VALUE) +#define mSCRIPT_MAKE_U64(VALUE) mSCRIPT_MAKE(U64, VALUE) +#define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(F64, VALUE) +#define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(CHARP, VALUE) +#define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(S(STRUCT), VALUE) +#define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(CS(STRUCT), VALUE) + +#define mSCRIPT_NO_DEFAULT { \ + .type = NULL, \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = {0} \ +} + +CXX_GUARD_END + +#endif diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 87953723a..27733143f 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -108,421 +108,6 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1) -#define mSCRIPT_POP(STACK, TYPE, NAME) \ - mSCRIPT_TYPE_C_ ## TYPE NAME; \ - do { \ - struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ - bool deref = true; \ - if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ - if (_val->type == mSCRIPT_TYPE_MS_WRAPPER) { \ - _val = mScriptValueUnwrap(_val); \ - deref = false; \ - if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ - return false; \ - } \ - } else { \ - return false; \ - } \ - } \ - NAME = _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE; \ - if (deref) { \ - mScriptValueDeref(_val); \ - } \ - mScriptListResize(STACK, -1); \ - } while (0) - -#define mSCRIPT_POP_0(...) -#define mSCRIPT_POP_1(FRAME, T0) mSCRIPT_POP(FRAME, T0, p0) -#define mSCRIPT_POP_2(FRAME, T0, T1) mSCRIPT_POP(FRAME, T1, p1); mSCRIPT_POP_1(FRAME, T0) -#define mSCRIPT_POP_3(FRAME, T0, T1, T2) mSCRIPT_POP(FRAME, T2, p2); mSCRIPT_POP_2(FRAME, T0, T1) -#define mSCRIPT_POP_4(FRAME, T0, T1, T2, T3) mSCRIPT_POP(FRAME, T3, p3); mSCRIPT_POP_3(FRAME, T0, T1, T2) -#define mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4) mSCRIPT_POP(FRAME, T4, p4); mSCRIPT_POP_4(FRAME, T0, T1, T2, T3) -#define mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) mSCRIPT_POP(FRAME, T5, p5); mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4) -#define mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) mSCRIPT_POP(FRAME, T6, p6); mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) -#define mSCRIPT_POP_8(FRAME, T0, T1, T2, T3, T4, T5, T6, T7) mSCRIPT_POP(FRAME, T7, p7); mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) - -#define mSCRIPT_PUSH(STACK, TYPE, NAME) \ - do { \ - struct mScriptValue* _val = mScriptListAppend(STACK); \ - _val->type = mSCRIPT_TYPE_MS_ ## TYPE; \ - _val->refs = mSCRIPT_VALUE_UNREF; \ - _val->flags = 0; \ - _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE = NAME; \ - } while (0) - -#define mSCRIPT_ARG_NAMES_0 -#define mSCRIPT_ARG_NAMES_1 p0 -#define mSCRIPT_ARG_NAMES_2 p0, p1 -#define mSCRIPT_ARG_NAMES_3 p0, p1, p2 -#define mSCRIPT_ARG_NAMES_4 p0, p1, p2, p3 -#define mSCRIPT_ARG_NAMES_5 p0, p1, p2, p3, p4 -#define mSCRIPT_ARG_NAMES_6 p0, p1, p2, p3, p4, p5 -#define mSCRIPT_ARG_NAMES_7 p0, p1, p2, p3, p4, p5, p6 -#define mSCRIPT_ARG_NAMES_8 p0, p1, p2, p3, p4, p5, p6, p7 - -#define mSCRIPT_PREFIX_0(PREFIX, ...) -#define mSCRIPT_PREFIX_1(PREFIX, T0) PREFIX ## T0 -#define mSCRIPT_PREFIX_2(PREFIX, T0, T1) PREFIX ## T0, PREFIX ## T1 -#define mSCRIPT_PREFIX_3(PREFIX, T0, T1, T2) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2 -#define mSCRIPT_PREFIX_4(PREFIX, T0, T1, T2, T3) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3 -#define mSCRIPT_PREFIX_5(PREFIX, T0, T1, T2, T3, T4) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4 -#define mSCRIPT_PREFIX_6(PREFIX, T0, T1, T2, T3, T4, T5) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5 -#define mSCRIPT_PREFIX_7(PREFIX, T0, T1, T2, T3, T4, T5, T6) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6 -#define mSCRIPT_PREFIX_8(PREFIX, T0, T1, T2, T3, T4, T5, T6, T7) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6, PREFIX ## T7 -#define mSCRIPT_PREFIX_N(N) mSCRIPT_PREFIX_ ## N - -#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) -#define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ - mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ - mSCRIPT_PUSH(&frame->returnValues, RETURN, out) - -#define mSCRIPT_DECLARE_STRUCT(STRUCT) \ - extern const struct mScriptType mSTStruct_ ## STRUCT; \ - extern const struct mScriptType mSTStructConst_ ## STRUCT; \ - extern const struct mScriptType mSTStructPtr_ ## STRUCT; \ - extern const struct mScriptType mSTStructPtrConst_ ## STRUCT; - -#define mSCRIPT_DEFINE_STRUCT(STRUCT) \ - const struct mScriptType mSTStruct_ ## STRUCT; \ - const struct mScriptType mSTStructConst_ ## STRUCT; \ - const struct mScriptType mSTStructPtr_ ## STRUCT; \ - const struct mScriptType mSTStructPtrConst_ ## STRUCT; \ - static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT; \ - static bool _mSTStructPtrCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \ - if (input->type == type || (input->type->constType == type)) { \ - output->type = type; \ - output->value.opaque = input->value.opaque; \ - return true; \ - } \ - if (input->type != &mSTStructPtr_ ## STRUCT && input->type != &mSTStructPtrConst_ ## STRUCT) { \ - return false; \ - } \ - if (type == &mSTStructConst_ ## STRUCT || (!input->type->isConst && type == &mSTStruct_ ## STRUCT)) { \ - output->type = type; \ - output->value.opaque = *(void**) input->value.opaque; \ - return true; \ - } \ - return false; \ - } \ - const struct mScriptType mSTStruct_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_OBJECT, \ - .details = { \ - .cls = &_mSTStructDetails_ ## STRUCT \ - }, \ - .size = sizeof(struct STRUCT), \ - .name = "struct::" #STRUCT, \ - .alloc = NULL, \ - .free = mScriptObjectFree, \ - .cast = mScriptObjectCast, \ - .constType = &mSTStructConst_ ## STRUCT, \ - }; \ - const struct mScriptType mSTStructConst_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_OBJECT, \ - .isConst = true, \ - .details = { \ - .cls = &_mSTStructDetails_ ## STRUCT \ - }, \ - .size = sizeof(struct STRUCT), \ - .name = "const struct::" #STRUCT, \ - .alloc = NULL, \ - .free = NULL, \ - .cast = mScriptObjectCast, \ - }; \ - const struct mScriptType mSTStructPtr_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_OPAQUE, \ - .size = sizeof(void*), \ - .name = "ptr struct::" #STRUCT, \ - .alloc = NULL, \ - .free = NULL, \ - .cast = _mSTStructPtrCast_ ## STRUCT, \ - .constType = &mSTStructPtrConst_ ## STRUCT, \ - }; \ - const struct mScriptType mSTStructPtrConst_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_OPAQUE, \ - .isConst = true, \ - .size = sizeof(void*), \ - .name = "ptr const struct::" #STRUCT, \ - .alloc = NULL, \ - .free = NULL, \ - .cast = _mSTStructPtrCast_ ## STRUCT, \ - }; \ - static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ - .init = false, \ - .details = (const struct mScriptClassInitDetails[]) { - -#define mSCRIPT_DEFINE_DOCSTRING(DOCSTRING) { \ - .type = mSCRIPT_CLASS_INIT_DOCSTRING, \ - .info = { \ - .comment = DOCSTRING \ - } \ -}, - -#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \ - .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ - .info = { \ - .member = { \ - .name = #EXPORTED_NAME, \ - .type = mSCRIPT_TYPE_MS_ ## TYPE, \ - .offset = offsetof(struct STRUCT, NAME) \ - } \ - } \ -}, - -#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \ - mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME) - -#define mSCRIPT_DEFINE_INHERIT(PARENT) { \ - .type = mSCRIPT_CLASS_INIT_INHERIT, \ - .info = { \ - .parent = mSCRIPT_TYPE_MS_S(PARENT) \ - } \ -}, - -#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \ - _mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ - return false; \ - } - -#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, DEFAULTS, ...) \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \ - static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \ - .call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \ - }; \ - \ - static void _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME(struct mScriptValue* val) { \ - val->value.copaque = &_mSTStructBindingFunction_ ## TYPE ## _ ## NAME; \ - }\ - static const struct mScriptType _mSTStructBindingType_ ## TYPE ## _ ## NAME = { \ - .base = mSCRIPT_TYPE_FUNCTION, \ - .name = "struct::" #TYPE "." #NAME, \ - .alloc = _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME, \ - .details = { \ - .function = { \ - .parameters = { \ - .count = _mSUCC_ ## NPARAMS, \ - .entries = { mSCRIPT_TYPE_MS_ ## S(TYPE), _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ - .names = { "this", _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ - .defaults = DEFAULTS, \ - }, \ - .returnType = { \ - .count = NRET, \ - .entries = { RETURN } \ - }, \ - }, \ - } \ - }; - -#define _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, RETURN, NAME, CONST, NPARAMS, ...) \ - typedef RETURN (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mCOMMA_ ## NPARAMS(CONST struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__))) - -#define _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, T, NPARAMS, ...) \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \ - return true; \ - } - -#define _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, T, NPARAMS, ...) \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ - return true; \ - } \ - -#define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, NULL, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, NULL, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_CD_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_D_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_CD_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAU(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX] = { \ - mSCRIPT_NO_DEFAULT, - -#define mSCRIPT_DEFINE_DEFAULTS_END } - -#define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME) { \ - .type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \ - .info = { \ - .member = { \ - .name = #EXPORTED_NAME, \ - .type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME \ - } \ - }, \ -}, - -#define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) \ - _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME) - -#define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) - -#define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init) -#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit) -#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get) -#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set) - -#define mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(TYPE, CAST_TYPE, MEMBER) { \ - .type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \ - .info = { \ - .castMember = { \ - .type = mSCRIPT_TYPE_MS_ ## CAST_TYPE, \ - .member = #MEMBER \ - } \ - }, \ -}, - -#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } - -#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ - static struct mScriptFunction _function_ ## NAME = { \ - .call = _binding_ ## NAME \ - }; \ - static void _alloc_ ## NAME(struct mScriptValue* val) { \ - val->value.copaque = &_function_ ## NAME; \ - } \ - static const struct mScriptType _type_ ## NAME = { \ - .base = mSCRIPT_TYPE_FUNCTION, \ - .name = "function::" #NAME, \ - .alloc = _alloc_ ## NAME, \ - .details = { \ - .function = { \ - .parameters = { \ - .count = NPARAMS, \ - .entries = { _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ - .names = { _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ - }, \ - .returnType = { \ - .count = NRET, \ - .entries = { RETURN } \ - }, \ - }, \ - } \ - }; \ - const struct mScriptValue NAME = { \ - .type = &_type_ ## NAME, \ - .refs = mSCRIPT_VALUE_UNREF, \ - .value = { \ - .copaque = &_function_ ## NAME \ - } \ - } - -#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \ - static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ - return false; \ - } \ - _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ - return true; \ - } \ - _mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) - -#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \ - static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ - return false; \ - } \ - _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ - return true; \ - } \ - _mSCRIPT_BIND_FUNCTION(NAME, 0, , NPARAMS, __VA_ARGS__) - -#define mSCRIPT_MAKE(TYPE, VALUE) (struct mScriptValue) { \ - .type = (mSCRIPT_TYPE_MS_ ## TYPE), \ - .refs = mSCRIPT_VALUE_UNREF, \ - .value = { \ - .mSCRIPT_TYPE_FIELD_ ## TYPE = (VALUE) \ - }, \ - } \ - -#define mSCRIPT_MAKE_S8(VALUE) mSCRIPT_MAKE(S8, VALUE) -#define mSCRIPT_MAKE_U8(VALUE) mSCRIPT_MAKE(U8, VALUE) -#define mSCRIPT_MAKE_S16(VALUE) mSCRIPT_MAKE(S16, VALUE) -#define mSCRIPT_MAKE_U16(VALUE) mSCRIPT_MAKE(U16, VALUE) -#define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(S32, VALUE) -#define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(U32, VALUE) -#define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(F32, VALUE) -#define mSCRIPT_MAKE_S64(VALUE) mSCRIPT_MAKE(S64, VALUE) -#define mSCRIPT_MAKE_U64(VALUE) mSCRIPT_MAKE(U64, VALUE) -#define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(F64, VALUE) -#define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(CHARP, VALUE) -#define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(S(STRUCT), VALUE) -#define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(CS(STRUCT), VALUE) - -#define mSCRIPT_NO_DEFAULT { \ - .type = NULL, \ - .refs = mSCRIPT_VALUE_UNREF, \ - .value = {0} \ -} - enum mScriptTypeBase { mSCRIPT_TYPE_VOID = 0, mSCRIPT_TYPE_SINT, diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index df0ef9eed..b26795e76 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 78d57deb8..0992c601e 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -7,6 +7,7 @@ #include #include +#include struct mScriptCallbackAdapter { struct mScriptContext* context; diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 9616229b0..476e24455 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -6,6 +6,7 @@ #include "util/test/suite.h" #include +#include #include struct TestA { diff --git a/src/script/test/lua.c b/src/script/test/lua.c index d0889ce86..7ef6a1ee0 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -6,6 +6,7 @@ #include "util/test/suite.h" #include +#include #define SETUP_LUA \ struct mScriptContext context; \ diff --git a/src/script/test/types.c b/src/script/test/types.c index dd0e3cdff..45a9a2d25 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -6,6 +6,7 @@ #include "util/test/suite.h" #include +#include #include struct Test { diff --git a/src/script/types.c b/src/script/types.c index 1152e28ef..c72f2cf0a 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include From 93cadacb18240b4e4dc4617e82c17e1d22a8098f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 May 2022 16:39:44 -0700 Subject: [PATCH 068/105] Scripting: Allow null values to be wrapped in Lua as nil --- src/script/engines/lua.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index b26795e76..b8d596f05 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -241,14 +241,26 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { } bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* value) { + if (!value) { + lua_pushnil(luaContext->lua); + return true; + } uint32_t weakref; bool needsWeakref = false; if (value->type == mSCRIPT_TYPE_MS_WRAPPER) { value = mScriptValueUnwrap(value); + if (!value) { + lua_pushnil(luaContext->lua); + return true; + } } if (value->type == mSCRIPT_TYPE_MS_WEAKREF) { weakref = value->value.u32; value = mScriptContextAccessWeakref(luaContext->d.context, value); + if (!value) { + lua_pushnil(luaContext->lua); + return true; + } needsWeakref = true; } bool ok = true; From 4a345fb07c61543d2624af797fb49c7db55899fb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 May 2022 16:40:01 -0700 Subject: [PATCH 069/105] Scripting: Add casting STR to CHARP --- src/script/types.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/script/types.c b/src/script/types.c index c72f2cf0a..297a2b3b8 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -21,6 +21,7 @@ static void _deinitTableValue(void*); static void _allocString(struct mScriptValue*); static void _freeString(struct mScriptValue*); static uint32_t _hashString(const struct mScriptValue*); +static bool _stringCast(const struct mScriptValue*, const struct mScriptType*, struct mScriptValue*); static bool _castScalar(const struct mScriptValue*, const struct mScriptType*, struct mScriptValue*); static uint32_t _hashScalar(const struct mScriptValue*); @@ -169,6 +170,7 @@ const struct mScriptType mSTString = { .free = _freeString, .hash = _hashString, .equal = _stringEqual, + .cast = _stringCast, }; const struct mScriptType mSTCharPtr = { @@ -179,6 +181,7 @@ const struct mScriptType mSTCharPtr = { .free = NULL, .hash = _hashString, .equal = _charpEqual, + .cast = _stringCast, }; const struct mScriptType mSTList = { @@ -272,6 +275,24 @@ static void _freeString(struct mScriptValue* val) { free(string); } +static bool _stringCast(const struct mScriptValue* in, const struct mScriptType* type, struct mScriptValue* out) { + if (in->type == type) { + out->type = type; + out->refs = mSCRIPT_VALUE_UNREF; + out->flags = 0; + out->value.opaque = in->value.opaque; + return true; + } + if (in->type == mSCRIPT_TYPE_MS_STR && type == mSCRIPT_TYPE_MS_CHARP) { + out->type = type; + out->refs = mSCRIPT_VALUE_UNREF; + out->flags = 0; + out->value.opaque = ((struct mScriptString*) in->value.opaque)->buffer; + return true; + } + return false; +} + static uint32_t _hashString(const struct mScriptValue* val) { const char* buffer = 0; size_t size = 0; From 31a7f09b8a9bfb69623986d1fa6a5e426fd151b4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 May 2022 16:40:27 -0700 Subject: [PATCH 070/105] Scripting: Add register read/write --- src/core/scripting.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 70f3e50fa..7d739474c 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -232,18 +232,32 @@ mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write32) mSCRIPT_DEFINE_END; -struct mScriptValue* _mScriptCoreGetGameTitle(const struct mCore* core) { +static struct mScriptValue* _mScriptCoreGetGameTitle(const struct mCore* core) { char title[32] = {0}; core->getGameTitle(core, title); return mScriptStringCreateFromASCII(title); } -struct mScriptValue* _mScriptCoreGetGameCode(const struct mCore* core) { +static struct mScriptValue* _mScriptCoreGetGameCode(const struct mCore* core) { char code[16] = {0}; core->getGameCode(core, code); return mScriptStringCreateFromASCII(code); } +static struct mScriptValue* _mScriptCoreReadRegister(const struct mCore* core, const char* regName) { + int32_t out; + if (!core->readRegister(core, regName, &out)) { + return NULL; + } + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); + value->value.s32 = out; + return value; +} + +static void _mScriptCoreWriteRegister(struct mCore* core, const char* regName, int32_t in) { + core->writeRegister(core, regName, &in); +} + // Info functions mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, platform, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); @@ -270,6 +284,10 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite8, 2, U32, address, U8, valu mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value); +// Register functions +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WRAPPER, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegister, 2, CHARP, regName, S32, value); + // Savestate functions mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, saveStateSlot, mCoreSaveState, 2, S32, slot, S32, flags); mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoadState, 2, S32, slot, S32, flags); @@ -319,6 +337,11 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given bus address") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write32, busWrite32) + mSCRIPT_DEFINE_DOCSTRING("Read the value of the register with the given name") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, readRegister) + mSCRIPT_DEFINE_DOCSTRING("Write the value of the register with the given name") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, writeRegister) + mSCRIPT_DEFINE_DOCSTRING("Save state to the slot number") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateSlot) mSCRIPT_DEFINE_DOCSTRING("Load state from the slot number") From 9c6068a526cd77c42a98ba2ab69abcc414bf3dee Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 17 May 2022 21:22:18 -0700 Subject: [PATCH 071/105] Scripting: Start bringing up lists in lua --- src/script/engines/lua.c | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index b8d596f05..238a40af5 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -41,6 +41,8 @@ static int _luaSetObject(lua_State* lua); static int _luaGcObject(lua_State* lua); static int _luaGetTable(lua_State* lua); static int _luaPairsTable(lua_State* lua); +static int _luaGetList(lua_State* lua); +static int _luaLenList(lua_State* lua); #if LUA_VERSION_NUM < 503 #define lua_pushinteger lua_pushnumber @@ -105,6 +107,13 @@ static const luaL_Reg _mSTTable[] = { { NULL, NULL } }; +static const luaL_Reg _mSTList[] = { + { "__index", _luaGetList }, + { "__len", _luaLenList }, + { "__gc", _luaGcObject }, + { NULL, NULL } +}; + struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mScriptContext* context) { UNUSED(engine); struct mScriptEngineContextLua* luaContext = calloc(1, sizeof(*luaContext)); @@ -139,6 +148,14 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS #endif lua_pop(luaContext->lua, 1); + luaL_newmetatable(luaContext->lua, "mSTList"); +#if LUA_VERSION_NUM < 502 + luaL_register(luaContext->lua, NULL, _mSTList); +#else + luaL_setfuncs(luaContext->lua, _mSTList, 0); +#endif + lua_pop(luaContext->lua, 1); + return &luaContext->d; } @@ -297,6 +314,16 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v lua_pushstring(luaContext->lua, ((struct mScriptString*) value->value.opaque)->buffer); mScriptValueDeref(value); break; + case mSCRIPT_TYPE_LIST: + newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + if (needsWeakref) { + *newValue = mSCRIPT_MAKE(WEAKREF, weakref); + } else { + mScriptValueWrap(value, newValue); + mScriptValueDeref(value); + } + luaL_setmetatable(luaContext->lua, "mSTList"); + break; case mSCRIPT_TYPE_TABLE: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); if (needsWeakref) { @@ -702,3 +729,60 @@ int _luaPairsTable(lua_State* lua) { lua_pushnil(lua); return 3; } + +int _luaGetList(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + ssize_t index; +#if LUA_VERSION_NUM >= 503 + index = lua_tointeger(luaContext->lua, -1); +#else + index = lua_tonumber(luaContext->lua, -1); +#endif + struct mScriptValue* obj = lua_touserdata(lua, -2); + lua_pop(lua, 2); + + obj = mScriptContextAccessWeakref(luaContext->d.context, obj); + if (obj->type == mSCRIPT_TYPE_MS_WRAPPER) { + obj = mScriptValueUnwrap(obj); + } + if (!obj || obj->type != mSCRIPT_TYPE_MS_LIST) { + lua_pushliteral(lua, "Invalid object"); + lua_error(lua); + } + struct mScriptList* list = obj->value.opaque; + + // Lua indexes from 1 + if (index < 1) { + lua_pushliteral(lua, "Invalid index"); + lua_error(lua); + } + if ((size_t) index > mScriptListSize(list)) { + return 0; + } + --index; + + struct mScriptValue* val = mScriptListGetPointer(list, index); + if (!_luaWrap(luaContext, val)) { + lua_pushliteral(lua, "Invalid value"); + lua_error(lua); + } + return 1; +} + +static int _luaLenList(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + struct mScriptValue* obj = lua_touserdata(lua, -1); + lua_pop(lua, 1); + + obj = mScriptContextAccessWeakref(luaContext->d.context, obj); + if (obj->type == mSCRIPT_TYPE_MS_WRAPPER) { + obj = mScriptValueUnwrap(obj); + } + if (!obj || obj->type != mSCRIPT_TYPE_MS_LIST) { + lua_pushliteral(lua, "Invalid object"); + lua_error(lua); + } + struct mScriptList* list = obj->value.opaque; + lua_pushinteger(lua, mScriptListSize(list)); + return 1; +} From f570786d782b50b901720edc1d5938b31bedd2ef Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 17 May 2022 22:07:45 -0700 Subject: [PATCH 072/105] Scripting: Add memory range reading --- src/core/scripting.c | 54 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 7d739474c..5c2c22d0d 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -185,6 +185,20 @@ static uint32_t mScriptMemoryAdapterRead32(struct mScriptMemoryAdapter* adapter, return adapter->core->rawRead32(adapter->core, address, segment); } +static struct mScriptValue* mScriptMemoryAdapterReadRange(struct mScriptMemoryAdapter* adapter, uint32_t address, uint32_t length) { + uint32_t segmentSize = adapter->block.end - adapter->block.start; + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); + struct mScriptList* list = value->value.opaque; + uint32_t i; + for (i = 0; i < length; ++i, ++address) { + uint32_t segmentAddress = address % segmentSize; + int segment = address / segmentSize; + segmentAddress += adapter->block.start; + *mScriptListAppend(list) = mSCRIPT_MAKE_U32(adapter->core->rawRead8(adapter->core, segmentAddress, segment)); + } + return value; +} + static void mScriptMemoryAdapterWrite8(struct mScriptMemoryAdapter* adapter, uint32_t address, uint8_t value) { uint32_t segmentSize = adapter->block.end - adapter->block.start; uint32_t segmentAddress = address % segmentSize; @@ -213,23 +227,26 @@ mSCRIPT_DECLARE_STRUCT(mScriptMemoryAdapter); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read8, mScriptMemoryAdapterRead8, 1, U32, address); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read16, mScriptMemoryAdapterRead16, 1, U32, address); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read32, mScriptMemoryAdapterRead32, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, WRAPPER, readRange, mScriptMemoryAdapterReadRange, 2, U32, address, U32, length); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write8, mScriptMemoryAdapterWrite8, 2, U32, address, U8, value); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write16, mScriptMemoryAdapterWrite16, 2, U32, address, U16, value); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write32, mScriptMemoryAdapterWrite32, 2, U32, address, U32, value); mSCRIPT_DEFINE_STRUCT(mScriptMemoryAdapter) -mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given offset") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read8) -mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given offset") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read16) -mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given offset") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read32) -mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given offset") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write8) -mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given offset") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write16) -mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write32) + mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given offset") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read8) + mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given offset") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read16) + mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given offset") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read32) + mSCRIPT_DEFINE_DOCSTRING("Read byte range from the given offset") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, readRange) + mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given offset") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write8) + mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given offset") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write16) + mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write32) mSCRIPT_DEFINE_END; static struct mScriptValue* _mScriptCoreGetGameTitle(const struct mCore* core) { @@ -244,6 +261,16 @@ static struct mScriptValue* _mScriptCoreGetGameCode(const struct mCore* core) { return mScriptStringCreateFromASCII(code); } +static struct mScriptValue* _mScriptCoreReadRange(struct mCore* core, uint32_t address, uint32_t length) { + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); + struct mScriptList* list = value->value.opaque; + uint32_t i; + for (i = 0; i < length; ++i, ++address) { + *mScriptListAppend(list) = mSCRIPT_MAKE_U32(core->busRead8(core, address)); + } + return value; +} + static struct mScriptValue* _mScriptCoreReadRegister(const struct mCore* core, const char* regName) { int32_t out; if (!core->readRegister(core, regName, &out)) { @@ -280,6 +307,7 @@ mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, getKeys, 0); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead8, 1, U32, address); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead16, 1, U32, address); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead32, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WRAPPER, readRange, _mScriptCoreReadRange, 2, U32, address, U32, length); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite8, 2, U32, address, U8, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value); @@ -330,6 +358,8 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read16, busRead16) mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given bus address") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read32, busRead32) + mSCRIPT_DEFINE_DOCSTRING("Read byte range from the given offset") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, readRange) mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given bus address") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write8, busWrite8) mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given bus address") From 28d7bfdffcfb1ac2e2efc94d2612580530dfe27a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 May 2022 00:16:40 -0700 Subject: [PATCH 073/105] Scripting: Add prototype "buffer" pseudo-TUI interface --- include/mgba/core/scripting.h | 23 +++ include/mgba/script/macros.h | 2 + src/core/scripting.c | 61 +++++++ src/platform/qt/CMakeLists.txt | 1 + src/platform/qt/ScriptingController.cpp | 9 + src/platform/qt/ScriptingController.h | 5 + src/platform/qt/ScriptingTextBuffer.cpp | 220 ++++++++++++++++++++++++ src/platform/qt/ScriptingTextBuffer.h | 65 +++++++ src/platform/qt/ScriptingView.cpp | 22 +++ src/platform/qt/ScriptingView.h | 6 + src/platform/qt/ScriptingView.ui | 65 +++---- 11 files changed, 449 insertions(+), 30 deletions(-) create mode 100644 src/platform/qt/ScriptingTextBuffer.cpp create mode 100644 src/platform/qt/ScriptingTextBuffer.h diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h index 76ac10915..7a38e55c6 100644 --- a/include/mgba/core/scripting.h +++ b/include/mgba/core/scripting.h @@ -21,8 +21,10 @@ mLOG_DECLARE_CATEGORY(SCRIPT); struct mCore; struct mLogger; +struct mScriptTextBuffer; mSCRIPT_DECLARE_STRUCT(mCore); mSCRIPT_DECLARE_STRUCT(mLogger); +mSCRIPT_DECLARE_STRUCT(mScriptTextBuffer); struct mScriptBridge; struct VFile; @@ -41,6 +43,24 @@ struct mScriptEngine { #endif }; +struct mScriptTextBuffer { + void (*init)(struct mScriptTextBuffer*, const char* name); + void (*deinit)(struct mScriptTextBuffer*); + + void (*setName)(struct mScriptTextBuffer*, const char* text); + + uint32_t (*getX)(const struct mScriptTextBuffer*); + uint32_t (*getY)(const struct mScriptTextBuffer*); + uint32_t (*cols)(const struct mScriptTextBuffer*); + uint32_t (*rows)(const struct mScriptTextBuffer*); + + void (*print)(struct mScriptTextBuffer*, const char* text); + void (*clear)(struct mScriptTextBuffer*); + void (*setSize)(struct mScriptTextBuffer*, uint32_t cols, uint32_t rows); + void (*moveCursor)(struct mScriptTextBuffer*, uint32_t x, uint32_t y); + void (*advance)(struct mScriptTextBuffer*, int32_t); +}; + struct mScriptBridge* mScriptBridgeCreate(void); void mScriptBridgeDestroy(struct mScriptBridge*); @@ -64,6 +84,9 @@ void mScriptContextDetachCore(struct mScriptContext*); void mScriptContextAttachLogger(struct mScriptContext*, struct mLogger*); void mScriptContextDetachLogger(struct mScriptContext*); +typedef struct mScriptTextBuffer* (*mScriptContextBufferFactory)(void*); +void mScriptContextSetTextBufferFactory(struct mScriptContext*, mScriptContextBufferFactory factory, void* cbContext); + CXX_GUARD_END #endif diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 1da18b59c..1aa6fb87c 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -324,7 +324,9 @@ CXX_GUARD_START #define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) #define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init) +#define mSCRIPT_DEFINE_STRUCT_INIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, NAME) #define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit) +#define mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, NAME) #define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get) #define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set) diff --git a/src/core/scripting.c b/src/core/scripting.c index 5c2c22d0d..e86309b57 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -161,6 +161,11 @@ struct mScriptCoreAdapter { struct mScriptValue memory; }; +struct mScriptUILibrary { + mScriptContextBufferFactory textBufferFactory; + void* textBufferContext; +}; + static uint32_t mScriptMemoryAdapterRead8(struct mScriptMemoryAdapter* adapter, uint32_t address) { uint32_t segmentSize = adapter->block.end - adapter->block.start; uint32_t segmentAddress = address % segmentSize; @@ -522,3 +527,59 @@ void mScriptContextAttachLogger(struct mScriptContext* context, struct mLogger* void mScriptContextDetachLogger(struct mScriptContext* context) { mScriptContextRemoveGlobal(context, "console"); } + +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, deinit, 0); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, getX, 0); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, getY, 0); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, cols, 0); +mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, rows, 0); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, print, 1, CHARP, text); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, clear, 0); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setSize, 2, U32, cols, U32, rows); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, moveCursor, 2, U32, x, U32, y); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, advance, 1, S32, adv); +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setName, 1, CHARP, name); + +mSCRIPT_DEFINE_STRUCT(mScriptTextBuffer) + mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(mScriptTextBuffer, deinit) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getX) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getY) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, cols) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, rows) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, print) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, clear) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setSize) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, moveCursor) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, advance) + mSCRIPT_DEFINE_DOCSTRING("Set the user-visible name of this buffer") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setName) +mSCRIPT_DEFINE_END; + +struct mScriptTextBuffer* _mScriptUICreateBuffer(struct mScriptUILibrary* lib, const char* name) { + struct mScriptTextBuffer* buffer = lib->textBufferFactory(lib->textBufferContext); + buffer->init(buffer, name); + return buffer; +} + +mSCRIPT_DECLARE_STRUCT(mScriptUILibrary); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptUILibrary, S(mScriptTextBuffer), createBuffer, _mScriptUICreateBuffer, 1, CHARP, name); + +mSCRIPT_DEFINE_STRUCT(mScriptUILibrary) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptUILibrary, createBuffer) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptUILibrary, createBuffer) + mSCRIPT_MAKE_CHARP(NULL) +mSCRIPT_DEFINE_DEFAULTS_END; + +void mScriptContextSetTextBufferFactory(struct mScriptContext* context, mScriptContextBufferFactory factory, void* cbContext) { + struct mScriptValue* value = mScriptContextEnsureGlobal(context, "ui", mSCRIPT_TYPE_MS_S(mScriptUILibrary)); + struct mScriptUILibrary* uiLib = value->value.opaque; + if (!uiLib) { + uiLib = calloc(1, sizeof(*uiLib)); + value->value.opaque = uiLib; + value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + } + uiLib->textBufferFactory = factory; + uiLib->textBufferContext = cbContext; +} diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index f45414220..dd78b01df 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -250,6 +250,7 @@ endif() if(ENABLE_SCRIPTING) list(APPEND SOURCE_FILES ScriptingController.cpp + ScriptingTextBuffer.cpp ScriptingView.cpp) list(APPEND UI_FILES diff --git a/src/platform/qt/ScriptingController.cpp b/src/platform/qt/ScriptingController.cpp index 21289c6ee..53fed8ec2 100644 --- a/src/platform/qt/ScriptingController.cpp +++ b/src/platform/qt/ScriptingController.cpp @@ -6,6 +6,7 @@ #include "ScriptingController.h" #include "CoreController.h" +#include "ScriptingTextBuffer.h" using namespace QGBA; @@ -37,6 +38,7 @@ ScriptingController::ScriptingController(QObject* parent) }; mScriptContextAttachLogger(&m_scriptContext, &m_logger); + mScriptContextSetTextBufferFactory(&m_scriptContext, &ScriptingController::createTextBuffer, this); HashTableEnumerate(&m_scriptContext.engines, [](const char* key, void* engine, void* context) { ScriptingController* self = static_cast(context); @@ -100,3 +102,10 @@ void ScriptingController::runCode(const QString& code) { VFileDevice vf(code.toUtf8()); load(vf); } + +mScriptTextBuffer* ScriptingController::createTextBuffer(void* context) { + ScriptingController* self = static_cast(context); + ScriptingTextBuffer* buffer = new ScriptingTextBuffer(self); + emit self->textBufferCreated(buffer); + return buffer->textBuffer(); +} diff --git a/src/platform/qt/ScriptingController.h b/src/platform/qt/ScriptingController.h index 3e6afffcc..d220c0377 100644 --- a/src/platform/qt/ScriptingController.h +++ b/src/platform/qt/ScriptingController.h @@ -18,6 +18,8 @@ namespace QGBA { class CoreController; +class ScriptingTextBuffer; + class ScriptingController : public QObject { Q_OBJECT @@ -36,12 +38,15 @@ signals: void log(const QString&); void warn(const QString&); void error(const QString&); + void textBufferCreated(ScriptingTextBuffer*); public slots: void clearController(); void runCode(const QString& code); private: + static mScriptTextBuffer* createTextBuffer(void* context); + struct Logger : mLogger { ScriptingController* p; } m_logger{}; diff --git a/src/platform/qt/ScriptingTextBuffer.cpp b/src/platform/qt/ScriptingTextBuffer.cpp new file mode 100644 index 000000000..7a4785d01 --- /dev/null +++ b/src/platform/qt/ScriptingTextBuffer.cpp @@ -0,0 +1,220 @@ +/* Copyright (c) 2013-2022 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 "ScriptingTextBuffer.h" + +#include "GBAApp.h" + +#include +#include +#include + +using namespace QGBA; + +ScriptingTextBuffer::ScriptingTextBuffer(QObject* parent) + : QObject(parent) +{ + m_shim.init = &ScriptingTextBuffer::init; + m_shim.deinit = &ScriptingTextBuffer::deinit; + m_shim.setName = &ScriptingTextBuffer::setName; + m_shim.getX = &ScriptingTextBuffer::getX; + m_shim.getY = &ScriptingTextBuffer::getY; + m_shim.cols = &ScriptingTextBuffer::cols; + m_shim.rows = &ScriptingTextBuffer::rows; + m_shim.print = &ScriptingTextBuffer::print; + m_shim.clear = &ScriptingTextBuffer::clear; + m_shim.setSize = &ScriptingTextBuffer::setSize; + m_shim.moveCursor = &ScriptingTextBuffer::moveCursor; + m_shim.advance = &ScriptingTextBuffer::advance; + m_shim.p = this; + m_shim.cursor = QTextCursor(&m_document); + + auto layout = new QPlainTextDocumentLayout(&m_document); + m_document.setDocumentLayout(layout); + m_document.setDefaultFont(GBAApp::app()->monospaceFont()); + m_document.setMaximumBlockCount(m_dims.height()); + + QTextOption textOption; + textOption.setWrapMode(QTextOption::NoWrap); + m_document.setDefaultTextOption(textOption); + setBufferName(tr("Untitled buffer")); +} + +void ScriptingTextBuffer::setBufferName(const QString& name) { + m_name = name; + m_document.setMetaInformation(QTextDocument::DocumentTitle, name); + emit bufferNameChanged(name); +} + +void ScriptingTextBuffer::print(const QString& text) { + QMutexLocker locker(&m_mutex); + QString split(text); + m_shim.cursor.beginEditBlock(); + while (m_shim.cursor.positionInBlock() + split.length() > m_dims.width()) { + int cut = m_dims.width() - m_shim.cursor.positionInBlock(); + if (!m_shim.cursor.atBlockEnd()) { + m_shim.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + } + m_shim.cursor.insertText(split.left(cut)); + if (m_shim.cursor.atEnd()) { + m_shim.cursor.insertBlock(); + } else { + m_shim.cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, 1); + } + split = split.mid(cut); + } + if (!m_shim.cursor.atBlockEnd()) { + m_shim.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, split.length()); + } + m_shim.cursor.insertText(split); + m_shim.cursor.endEditBlock(); +} + +void ScriptingTextBuffer::clear() { + QMutexLocker locker(&m_mutex); + m_document.clear(); + m_document.setMetaInformation(QTextDocument::DocumentTitle, m_name); + m_shim.cursor = QTextCursor(&m_document); +} + +void ScriptingTextBuffer::setSize(const QSize& size) { + QMutexLocker locker(&m_mutex); + m_dims = size; + m_document.setMaximumBlockCount(m_dims.height()); + for (int i = 0; i < m_document.blockCount(); ++i) { + if (m_document.findBlockByNumber(i).length() - 1 > m_dims.width()) { + QTextCursor deleter(m_document.findBlockByNumber(i)); + deleter.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, size.width()); + deleter.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + deleter.removeSelectedText(); + } + } +} + +void ScriptingTextBuffer::moveCursor(const QPoint& pos) { + QMutexLocker locker(&m_mutex); + m_shim.cursor.movePosition(QTextCursor::Start); + int y = pos.y(); + if (y >= m_dims.height()) { + y = m_dims.height() - 1; + } + if (y >= m_document.blockCount()) { + m_shim.cursor.movePosition(QTextCursor::End); + while (y >= m_document.blockCount()) { + m_shim.cursor.insertBlock(); + } + } else { + m_shim.cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, y); + } + + int x = pos.x(); + if (x >= m_dims.width()) { + x = m_dims.width() - 1; + } + + if (x >= m_shim.cursor.block().length()) { + m_shim.cursor.movePosition(QTextCursor::EndOfBlock); + m_shim.cursor.insertText(QString(x - m_shim.cursor.block().length() + 1, QChar(' '))); + } else { + m_shim.cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, x); + } +} + +void ScriptingTextBuffer::advance(int increment) { + QMutexLocker locker(&m_mutex); + int x = m_shim.cursor.positionInBlock(); + int y = m_shim.cursor.blockNumber(); + x += increment; + if (x > 0) { + y += x / m_dims.width(); + x %= m_dims.width(); + } else if (x < 0) { + y += (x - m_dims.width() + 1) / m_dims.width(); + x %= m_dims.width(); + if (x) { + x += m_dims.width(); + } + } + locker.unlock(); + moveCursor({x, y}); +} + +void ScriptingTextBuffer::init(struct mScriptTextBuffer* buffer, const char* name) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + if (name) { + QMetaObject::invokeMethod(self, "setBufferName", Q_ARG(const QString&, QString::fromUtf8(name))); + } + QMetaObject::invokeMethod(self, "clear"); +} + +void ScriptingTextBuffer::deinit(struct mScriptTextBuffer*) { + // TODO +} + +void ScriptingTextBuffer::setName(struct mScriptTextBuffer* buffer, const char* name) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + QMetaObject::invokeMethod(self, "setBufferName", Q_ARG(const QString&, QString::fromUtf8(name))); +} + +uint32_t ScriptingTextBuffer::getX(const struct mScriptTextBuffer* buffer) { + const ScriptingBufferShim* self = static_cast(buffer); + QMutexLocker locker(&self->p->m_mutex); + return self->cursor.positionInBlock(); +} + +uint32_t ScriptingTextBuffer::getY(const struct mScriptTextBuffer* buffer) { + const ScriptingBufferShim* self = static_cast(buffer); + QMutexLocker locker(&self->p->m_mutex); + return self->cursor.blockNumber(); +} + +uint32_t ScriptingTextBuffer::cols(const struct mScriptTextBuffer* buffer) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + QMutexLocker locker(&self->m_mutex); + return self->m_dims.width(); +} + +uint32_t ScriptingTextBuffer::rows(const struct mScriptTextBuffer* buffer) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + QMutexLocker locker(&self->m_mutex); + return self->m_dims.height(); +} + +void ScriptingTextBuffer::print(struct mScriptTextBuffer* buffer, const char* text) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + QMetaObject::invokeMethod(self, "print", Q_ARG(const QString&, QString::fromUtf8(text))); +} + +void ScriptingTextBuffer::clear(struct mScriptTextBuffer* buffer) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + QMetaObject::invokeMethod(self, "clear"); +} + +void ScriptingTextBuffer::setSize(struct mScriptTextBuffer* buffer, uint32_t cols, uint32_t rows) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + if (cols > 500) { + cols = 500; + } + if (rows > 10000) { + rows = 10000; + } + QMetaObject::invokeMethod(self, "setSize", Q_ARG(QSize, QSize(cols, rows))); +} + +void ScriptingTextBuffer::moveCursor(struct mScriptTextBuffer* buffer, uint32_t x, uint32_t y) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + if (x > INT_MAX) { + x = INT_MAX; + } + if (y > INT_MAX) { + y = INT_MAX; + } + QMetaObject::invokeMethod(self, "moveCursor", Q_ARG(QPoint, QPoint(x, y))); +} + +void ScriptingTextBuffer::advance(struct mScriptTextBuffer* buffer, int32_t adv) { + ScriptingTextBuffer* self = static_cast(buffer)->p; + emit self->advance(adv); +} diff --git a/src/platform/qt/ScriptingTextBuffer.h b/src/platform/qt/ScriptingTextBuffer.h new file mode 100644 index 000000000..c771003f2 --- /dev/null +++ b/src/platform/qt/ScriptingTextBuffer.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include +#include +#include +#include + +#include + +namespace QGBA { + +class ScriptingTextBuffer : public QObject { +Q_OBJECT + +public: + ScriptingTextBuffer(QObject* parent); + + QTextDocument* document() { return &m_document; }; + mScriptTextBuffer* textBuffer() { return &m_shim; } + +public slots: + void setBufferName(const QString&); + void print(const QString&); + void clear(); + void setSize(const QSize&); + void moveCursor(const QPoint&); + void advance(int); + +signals: + void bufferNameChanged(const QString&); + +private: + struct ScriptingBufferShim : public mScriptTextBuffer { + ScriptingTextBuffer* p; + QTextCursor cursor; + } m_shim; + QTextDocument m_document; + QMutex m_mutex; + QString m_name; + + static void init(struct mScriptTextBuffer*, const char* name); + static void deinit(struct mScriptTextBuffer*); + + static void setName(struct mScriptTextBuffer*, const char* name); + + static uint32_t getX(const struct mScriptTextBuffer*); + static uint32_t getY(const struct mScriptTextBuffer*); + static uint32_t cols(const struct mScriptTextBuffer*); + static uint32_t rows(const struct mScriptTextBuffer*); + + static void print(struct mScriptTextBuffer*, const char* text); + static void clear(struct mScriptTextBuffer*); + static void setSize(struct mScriptTextBuffer*, uint32_t cols, uint32_t rows); + static void moveCursor(struct mScriptTextBuffer*, uint32_t x, uint32_t y); + static void advance(struct mScriptTextBuffer*, int32_t); + + QSize m_dims{80, 24}; +}; + +} diff --git a/src/platform/qt/ScriptingView.cpp b/src/platform/qt/ScriptingView.cpp index eb8dac8fe..b870d0e61 100644 --- a/src/platform/qt/ScriptingView.cpp +++ b/src/platform/qt/ScriptingView.cpp @@ -7,6 +7,7 @@ #include "GBAApp.h" #include "ScriptingController.h" +#include "ScriptingTextBuffer.h" using namespace QGBA; @@ -24,7 +25,9 @@ ScriptingView::ScriptingView(ScriptingController* controller, QWidget* parent) connect(m_controller, &ScriptingController::log, m_ui.log, &LogWidget::log); connect(m_controller, &ScriptingController::warn, m_ui.log, &LogWidget::warn); connect(m_controller, &ScriptingController::error, m_ui.log, &LogWidget::error); + connect(m_controller, &ScriptingController::textBufferCreated, this, &ScriptingView::addTextBuffer); + connect(m_ui.buffers, &QListWidget::currentRowChanged, this, &ScriptingView::selectBuffer); connect(m_ui.load, &QAction::triggered, this, &ScriptingView::load); } @@ -41,6 +44,25 @@ void ScriptingView::load() { } } +void ScriptingView::addTextBuffer(ScriptingTextBuffer* buffer) { + QTextDocument* document = buffer->document(); + m_textBuffers.append(buffer); + QListWidgetItem* item = new QListWidgetItem(document->metaInformation(QTextDocument::DocumentTitle)); + connect(buffer, &ScriptingTextBuffer::bufferNameChanged, this, [item](const QString& name) { + item->setText(name); + }); + connect(buffer, &QObject::destroyed, this, [this, buffer, item]() { + m_textBuffers.removeAll(buffer); + m_ui.buffers->removeItemWidget(item); + }); + m_ui.buffers->addItem(item); + m_ui.buffers->setCurrentItem(item); +} + +void ScriptingView::selectBuffer(int index) { + m_ui.buffer->setDocument(m_textBuffers[index]->document()); +} + QString ScriptingView::getFilters() const { QStringList filters; #ifdef USE_LUA diff --git a/src/platform/qt/ScriptingView.h b/src/platform/qt/ScriptingView.h index 969f51c62..5f7668dd2 100644 --- a/src/platform/qt/ScriptingView.h +++ b/src/platform/qt/ScriptingView.h @@ -10,6 +10,7 @@ namespace QGBA { class ScriptingController; +class ScriptingTextBuffer; class ScriptingView : public QMainWindow { Q_OBJECT @@ -21,11 +22,16 @@ private slots: void submitRepl(); void load(); + void addTextBuffer(ScriptingTextBuffer*); + void selectBuffer(int); + private: QString getFilters() const; + Ui::ScriptingView m_ui; ScriptingController* m_controller; + QList m_textBuffers; }; } diff --git a/src/platform/qt/ScriptingView.ui b/src/platform/qt/ScriptingView.ui index 32ea124cf..a0cf7ed1a 100644 --- a/src/platform/qt/ScriptingView.ui +++ b/src/platform/qt/ScriptingView.ui @@ -15,31 +15,8 @@ - - - - - - - Run - - - - - - - - 0 - 0 - - - - true - - - - - + + 0 @@ -52,11 +29,39 @@ 16777215 - - - Console - - + + + + + + QPlainTextEdit::NoWrap + + + true + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + Run + From d15bd4969eefa3655196c113968d19fc8f1fc052 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 May 2022 00:24:30 -0700 Subject: [PATCH 074/105] Res: Add WIP example Pokemon script --- res/scripts/pokemon.lua | 298 +++++++++++++++++++++++++++++++++ src/platform/qt/CMakeLists.txt | 4 + 2 files changed, 302 insertions(+) create mode 100644 res/scripts/pokemon.lua diff --git a/res/scripts/pokemon.lua b/res/scripts/pokemon.lua new file mode 100644 index 000000000..635ecf9d0 --- /dev/null +++ b/res/scripts/pokemon.lua @@ -0,0 +1,298 @@ +gen3CharmapEn = { + " ", "À", "Á", "Â", "Ç", "È", "É", "Ê", "Ë", "Ì", "こ", "Î", "Ï", "Ò", "Ó", "Ô", + "Œ", "Ù", "Ú", "Û", "Ñ", "ß", "à", "á", "ね", "ç", "è", "é", "ê", "ë", "ì", "ま", + "î", "ï", "ò", "ó", "ô", "œ", "ù", "ú", "û", "ñ", "º", "ª", "�", "&", "+", "あ", + "ぃ", "ぅ", "ぇ", "ぉ", "v", "=", "ょ", "が", "ぎ", "ぐ", "げ", "ご", "ざ", "じ", "ず", "ぜ", + "ぞ", "だ", "ぢ", "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ", + "っ", "¿", "¡", "P\u{200d}k", "M\u{200d}n", "P\u{200d}o", "K\u{200d}é", "�", "�", "�", "Í", "%", "(", ")", "セ", "ソ", + "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "â", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "í", + "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "⬆", "⬇", "⬅", "➡", "ヲ", "ン", "ァ", + "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ", + "ゾ", "ダ", "ヂ", "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", "パ", "ピ", "プ", "ペ", "ポ", + "ッ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", ".", "-", "・", + "…", "“", "”", "‘", "’", "♂", "♀", "$", ",", "×", "/", "A", "B", "C", "D", "E", + "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", + "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", + "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "▶", + ":", "Ä", "Ö", "Ü", "ä", "ö", "ü", "⬆", "⬇", "⬅", "�", "�", "�", "�", "�", "" +} + +function readBoxMonGen3(game, address) + local mon = {} + mon.personality = emu:read32(address + 0) + mon.otId = emu:read32(address + 4) + mon.nickname = game:toString(emu:readRange(address + 8, game._monNameLength)) + mon.language = emu:read8(address + 18) + local flags = emu:read8(address + 19) + mon.isBadEgg = flags & 1 + mon.hasSpecies = (flags >> 1) & 1 + mon.isEgg = (flags >> 2) & 1 + mon.otName = game:toString(emu:readRange(address + 20, game._playerNameLength)) + mon.markings = emu:read8(address + 27) + + local key = mon.otId ~ mon.personality + local substructSelector = { + [ 0] = {0, 1, 2, 3}, + [ 1] = {0, 1, 3, 2}, + [ 2] = {0, 2, 1, 3}, + [ 3] = {0, 3, 1, 2}, + [ 4] = {0, 2, 3, 1}, + [ 5] = {0, 3, 2, 1}, + [ 6] = {1, 0, 2, 3}, + [ 7] = {1, 0, 3, 2}, + [ 8] = {2, 0, 1, 3}, + [ 9] = {3, 0, 1, 2}, + [10] = {2, 0, 3, 1}, + [11] = {3, 0, 2, 1}, + [12] = {1, 2, 0, 3}, + [13] = {1, 3, 0, 2}, + [14] = {2, 1, 0, 3}, + [15] = {3, 1, 0, 2}, + [16] = {2, 3, 0, 1}, + [17] = {3, 2, 0, 1}, + [18] = {1, 2, 3, 0}, + [19] = {1, 3, 2, 0}, + [20] = {2, 1, 3, 0}, + [21] = {3, 1, 2, 0}, + [22] = {2, 3, 1, 0}, + [23] = {3, 2, 1, 0}, + } + + local pSel = substructSelector[mon.personality % 24] + local ss0 = {} + local ss1 = {} + local ss2 = {} + local ss3 = {} + + for i = 0, 2 do + ss0[i] = emu:read32(address + 32 + pSel[1] * 12 + i * 4) ~ key + ss1[i] = emu:read32(address + 32 + pSel[2] * 12 + i * 4) ~ key + ss2[i] = emu:read32(address + 32 + pSel[3] * 12 + i * 4) ~ key + ss3[i] = emu:read32(address + 32 + pSel[4] * 12 + i * 4) ~ key + end + + mon.species = ss0[0] & 0xFFFF + mon.heldItem = ss0[0] >> 16 + mon.experience = ss0[1] + mon.ppBonuses = ss0[2] & 0xFF + mon.friendship = (ss0[2] >> 8) & 0xFF + + mon.moves = { + ss1[0] & 0xFFFF, + ss1[0] >> 16, + ss1[1] & 0xFFFF, + ss1[1] >> 16 + } + mon.pp = { + ss1[2] & 0xFF, + (ss1[2] >> 8) & 0xFF, + (ss1[2] >> 16) & 0xFF, + ss1[2] >> 24 + } + + mon.hpEV = ss2[0] & 0xFF + mon.attackEV = (ss2[0] >> 8) & 0xFF + mon.defenseEV = (ss2[0] >> 16) & 0xFF + mon.speedEV = ss2[0] >> 24 + mon.spAttackEV = ss2[1] & 0xFF + mon.spDefenseEV = (ss2[1] >> 8) & 0xFF + mon.cool = (ss2[1] >> 16) & 0xFF + mon.beauty = ss2[1] >> 24 + mon.cute = ss2[2] & 0xFF + mon.smart = (ss2[2] >> 8) & 0xFF + mon.tough = (ss2[2] >> 16) & 0xFF + mon.sheen = ss2[2] >> 24 + + mon.pokerus = ss3[0] & 0xFF + mon.metLocation = (ss3[0] >> 8) & 0xFF + flags = ss3[0] >> 16 + mon.metLevel = flags & 0x7F + mon.metGame = (flags >> 7) & 0xF + mon.pokeball = (flags >> 11) & 0xF + mon.otGender = (flags >> 15) & 0x1 + flags = ss3[1] + mon.hpIV = flags & 0x1F + mon.attackIV = (flags >> 5) & 0x1F + mon.defenseIV = (flags >> 10) & 0x1F + mon.speedIV = (flags >> 15) & 0x1F + mon.spAttackIV = (flags >> 20) & 0x1F + mon.spDefenseIV = (flags >> 25) & 0x1F + -- Bit 30 is another "isEgg" bit + mon.altAbility = (flags >> 31) & 1 + flags = ss3[2] + mon.coolRibbon = flags & 7 + mon.beautyRibbon = (flags >> 3) & 7 + mon.cuteRibbon = (flags >> 6) & 7 + mon.smartRibbon = (flags >> 9) & 7 + mon.toughRibbon = (flags >> 12) & 7 + mon.championRibbon = (flags >> 15) & 1 + mon.winningRibbon = (flags >> 16) & 1 + mon.victoryRibbon = (flags >> 17) & 1 + mon.artistRibbon = (flags >> 18) & 1 + mon.effortRibbon = (flags >> 19) & 1 + mon.marineRibbon = (flags >> 20) & 1 + mon.landRibbon = (flags >> 21) & 1 + mon.skyRibbon = (flags >> 22) & 1 + mon.countryRibbon = (flags >> 23) & 1 + mon.nationalRibbon = (flags >> 24) & 1 + mon.earthRibbon = (flags >> 25) & 1 + mon.worldRibbon = (flags >> 26) & 1 + mon.eventLegal = (flags >> 27) & 0x1F + return mon +end + +function readPartyMonGen3(game, address) + local mon = game:_readBoxMon(address) + mon.status = emu:read32(address + 80) + mon.level = emu:read8(address + 84) + mon.mail = emu:read32(address + 85) + mon.hp = emu:read16(address + 86) + mon.maxHP = emu:read16(address + 88) + mon.attack = emu:read16(address + 90) + mon.defense = emu:read16(address + 92) + mon.speed = emu:read16(address + 94) + mon.spAttack = emu:read16(address + 96) + mon.spDefense = emu:read16(address + 98) + return mon +end + +function getParty(game) + local party = {} + for i = 1, emu:read8(game._partyCount) do + party[i] = game:_readPartyMon(game._party + (i - 1) * game._partyMonSize) + end + return party +end + +function toString(game, rawstring) + local string = "" + for _, char in ipairs(rawstring) do + if char == game._terminator then + break + end + string = string..game._charmap[char + 1] + end + return string +end + +function getSpeciesName(game, id) + local pointer = game._speciesNameTable + (game._monNameLength + 1) * id + return game:toString(emu:readRange(pointer, game._monNameLength)) +end + +local gameGSEn = { + name="Gold/Silver (USA)", +} + +local gameCrystalEn = { + name="Crystal (USA)", +} + +local gameRubyEn = { + name="Ruby (USA)", + _party=0x3004360, + _partyCount=0x3004350, + _speciesNameTable=0x81f716c, + _boxMonSize=80, + _partyMonSize=100, + _monNameLength=10, + _playerNameLength=7, + _charmap=gen3CharmapEn, + _readBoxMon=readBoxMonGen3, + _readPartyMon=readPartyMonGen3, + _terminator=0xFF, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, +} + +local gameSapphireEn = { + name="Sapphire (USA)", + _party=0x3004360, + _partyCount=0x3004350, + _speciesNameTable=0x81f70fc, + _boxMonSize=80, + _partyMonSize=100, + _monNameLength=10, + _playerNameLength=7, + _charmap=gen3CharmapEn, + _readBoxMon=readBoxMonGen3, + _readPartyMon=readPartyMonGen3, + _terminator=0xFF, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, +} + + +local gameEmeraldEn = { + name="Emerald (USA)", + _party=0x20244ec, + _partyCount=0x20244e9, + _speciesNameTable=0x8318570, + _boxMonSize=80, + _partyMonSize=100, + _monNameLength=10, + _playerNameLength=7, + _charmap=gen3CharmapEn, + _readBoxMon=readBoxMonGen3, + _readPartyMon=readPartyMonGen3, + _terminator=0xFF, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, +} + +local gameFRLGEn = { + name="FireRed/LeafGreen (USA)", + _party=0x2024284, + _partyCount=0x2024029, + _boxMonSize=80, + _partyMonSize=100, + _monNameLength=10, + _playerNameLength=7, + _charmap=gen3CharmapEn, + _readBoxMon=readBoxMonGen3, + _readPartyMon=readPartyMonGen3, + _terminator=0xFF, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, +} + +gameCodes = { + ["DMG-AAUE"]=gameGSEn, + ["DMG-AAXE"]=gameGSEn, + ["DMG-BYTE"]=gameCrystalEn, + ["AGB-AXVE"]=gameRubyEn, + ["AGB-AXPE"]=gameSapphireEn, + ["AGB-BPEE"]=gameEmeraldEn, + ["AGB-BPRE"]=gameFRLGEn, + ["AGB-BPGE"]=gameFRLGEn, +} + +function printPartyStatus(game, buffer) + buffer:clear() + for _, mon in ipairs(game:getParty()) do + buffer:print(string.format("%-10s (Lv%3i %10s): %3i/%3i\n", + mon.nickname, + mon.level, + game:getSpeciesName(mon.species), + mon.hp, + mon.maxHP)) + end +end + +function detectGame() + game = gameCodes[emu:getGameCode()] + if not game then + console:error("Unknown game!") + else + console:log("Found game: " .. game.name) + end +end + +callbacks:add("start", detectGame) +if emu then + detectGame() +end diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index dd78b01df..66576d49d 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -302,6 +302,10 @@ endif() if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY) install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) endif() +if(ENABLE_SCRIPTING AND USE_LUA) + message(STATUS ${CMAKE_SOURCE_DIR}/res/scripts) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/scripts DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) +endif() install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt) if(NOT WIN32 AND NOT APPLE) if(DATADIR MATCHES "^\\.[.\\]") From a747545014a173529265c2c42febe6ffbb3fa14a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 May 2022 20:31:38 -0700 Subject: [PATCH 075/105] Scripting: Improve Lua error reporting --- src/script/engines/lua.c | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 238a40af5..f33e0ef4c 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -544,21 +544,21 @@ int _luaThunk(lua_State* lua) { if (!_luaPopFrame(luaContext, &frame.arguments)) { mScriptContextDrainPool(luaContext->d.context); mScriptFrameDeinit(&frame); - lua_pushliteral(lua, "Error calling function (setting arguments)"); + luaL_traceback(lua, lua, "Error calling function (translating arguments into runtime)", 1); lua_error(lua); } struct mScriptValue* fn = lua_touserdata(lua, lua_upvalueindex(1)); if (!fn || !mScriptInvoke(fn, &frame)) { mScriptFrameDeinit(&frame); - lua_pushliteral(lua, "Error calling function (invoking)"); + luaL_traceback(lua, lua, "Error calling function (invoking failed)", 1); lua_error(lua); } mScriptContextDrainPool(luaContext->d.context); if (!_luaPushFrame(luaContext, &frame.returnValues)) { mScriptFrameDeinit(&frame); - lua_pushliteral(lua, "Error calling function (getting return values)"); + luaL_traceback(lua, lua, "Error calling function (translating return values from runtime)", 1); lua_error(lua); } mScriptFrameDeinit(&frame); @@ -575,7 +575,7 @@ int _luaGetObject(lua_State* lua) { if (!keyPtr) { lua_pop(lua, 2); - lua_pushliteral(lua, "Invalid key"); + luaL_traceback(lua, lua, "Invalid key", 1); lua_error(lua); } strlcpy(key, keyPtr, sizeof(key)); @@ -583,17 +583,19 @@ int _luaGetObject(lua_State* lua) { obj = mScriptContextAccessWeakref(luaContext->d.context, obj); if (!obj) { - lua_pushliteral(lua, "Invalid object"); + luaL_traceback(lua, lua, "Invalid object", 1); lua_error(lua); } if (!mScriptObjectGet(obj, key, &val)) { - lua_pushliteral(lua, "Invalid key"); + char error[MAX_KEY_SIZE + 16]; + snprintf(error, sizeof(error), "Invalid key '%s'", key); + luaL_traceback(lua, lua, "Invalid key", 1); lua_error(lua); } if (!_luaWrap(luaContext, &val)) { - lua_pushliteral(lua, "Invalid value"); + luaL_traceback(lua, lua, "Error translating value from runtime", 1); lua_error(lua); } return 1; @@ -608,7 +610,7 @@ int _luaSetObject(lua_State* lua) { if (!keyPtr) { lua_pop(lua, 2); - lua_pushliteral(lua, "Invalid key"); + luaL_traceback(lua, lua, "Invalid key", 1); lua_error(lua); } strlcpy(key, keyPtr, sizeof(key)); @@ -616,18 +618,20 @@ int _luaSetObject(lua_State* lua) { obj = mScriptContextAccessWeakref(luaContext->d.context, obj); if (!obj) { - lua_pushliteral(lua, "Invalid object"); + luaL_traceback(lua, lua, "Invalid object", 1); lua_error(lua); } if (!val) { - lua_pushliteral(lua, "Invalid value"); + luaL_traceback(lua, lua, "Error translating value to runtime", 1); lua_error(lua); } if (!mScriptObjectSet(obj, key, val)) { mScriptValueDeref(val); - lua_pushliteral(lua, "Invalid key"); + char error[MAX_KEY_SIZE + 16]; + snprintf(error, sizeof(error), "Invalid key '%s'", key); + luaL_traceback(lua, lua, "Invalid key", 1); lua_error(lua); } mScriptValueDeref(val); @@ -660,7 +664,7 @@ int _luaGetTable(lua_State* lua) { obj = mScriptContextAccessWeakref(luaContext->d.context, obj); if (!obj) { - lua_pushliteral(lua, "Invalid object"); + luaL_traceback(lua, lua, "Invalid table", 1); lua_error(lua); } @@ -671,7 +675,7 @@ int _luaGetTable(lua_State* lua) { } if (!_luaWrap(luaContext, val)) { - lua_pushliteral(lua, "Invalid value"); + luaL_traceback(lua, lua, "Error translating value from runtime", 1); lua_error(lua); } return 1; @@ -692,7 +696,7 @@ static int _luaNextTable(lua_State* lua) { table = mScriptContextAccessWeakref(luaContext->d.context, table); if (!table) { - lua_pushliteral(lua, "Invalid object"); + luaL_traceback(lua, lua, "Invalid table", 1); lua_error(lua); } @@ -711,12 +715,12 @@ static int _luaNextTable(lua_State* lua) { } if (!_luaWrap(luaContext, mScriptTableIteratorGetKey(table, &iter))) { - lua_pushliteral(lua, "Iteration error"); + luaL_traceback(lua, lua, "Iteration error", 1); lua_error(lua); } if (!_luaWrap(luaContext, mScriptTableIteratorGetValue(table, &iter))) { - lua_pushliteral(lua, "Iteration error"); + luaL_traceback(lua, lua, "Iteration error", 1); lua_error(lua); } @@ -746,14 +750,14 @@ int _luaGetList(lua_State* lua) { obj = mScriptValueUnwrap(obj); } if (!obj || obj->type != mSCRIPT_TYPE_MS_LIST) { - lua_pushliteral(lua, "Invalid object"); + luaL_traceback(lua, lua, "Invalid list", 1); lua_error(lua); } struct mScriptList* list = obj->value.opaque; // Lua indexes from 1 if (index < 1) { - lua_pushliteral(lua, "Invalid index"); + luaL_traceback(lua, lua, "Invalid index", 1); lua_error(lua); } if ((size_t) index > mScriptListSize(list)) { @@ -763,7 +767,7 @@ int _luaGetList(lua_State* lua) { struct mScriptValue* val = mScriptListGetPointer(list, index); if (!_luaWrap(luaContext, val)) { - lua_pushliteral(lua, "Invalid value"); + luaL_traceback(lua, lua, "Error translating value from runtime", 1); lua_error(lua); } return 1; @@ -779,7 +783,7 @@ static int _luaLenList(lua_State* lua) { obj = mScriptValueUnwrap(obj); } if (!obj || obj->type != mSCRIPT_TYPE_MS_LIST) { - lua_pushliteral(lua, "Invalid object"); + luaL_traceback(lua, lua, "Invalid list", 1); lua_error(lua); } struct mScriptList* list = obj->value.opaque; From 7e36a71953356001d44fd165a7c03312b5472ed0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 May 2022 21:09:48 -0700 Subject: [PATCH 076/105] Scripting: Fix segment addressing --- src/core/scripting.c | 69 ++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index e86309b57..12ba0046a 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -166,66 +166,67 @@ struct mScriptUILibrary { void* textBufferContext; }; +#define CALCULATE_SEGMENT_INFO \ + uint32_t segmentSize = adapter->block.end - adapter->block.start; \ + uint32_t segmentStart = adapter->block.segmentStart - adapter->block.start; \ + if (adapter->block.segmentStart) { \ + segmentSize -= segmentStart; \ + } + +#define CALCULATE_SEGMENT_ADDRESS \ + uint32_t segmentAddress = address % segmentSize; \ + int segment = address / segmentSize; \ + segmentAddress += adapter->block.start; \ + if (adapter->block.segmentStart && segment) { \ + segmentAddress += segmentStart; \ + } + static uint32_t mScriptMemoryAdapterRead8(struct mScriptMemoryAdapter* adapter, uint32_t address) { - uint32_t segmentSize = adapter->block.end - adapter->block.start; - uint32_t segmentAddress = address % segmentSize; - int segment = address / segmentSize; - address = adapter->block.start + segmentAddress; - return adapter->core->rawRead8(adapter->core, address, segment); + CALCULATE_SEGMENT_INFO; + CALCULATE_SEGMENT_ADDRESS; + return adapter->core->rawRead8(adapter->core, segmentAddress, segment); } static uint32_t mScriptMemoryAdapterRead16(struct mScriptMemoryAdapter* adapter, uint32_t address) { - uint32_t segmentSize = adapter->block.end - adapter->block.start; - uint32_t segmentAddress = address % segmentSize; - int segment = address / segmentSize; - address = adapter->block.start + segmentAddress; - return adapter->core->rawRead16(adapter->core, address, segment); + CALCULATE_SEGMENT_INFO; + CALCULATE_SEGMENT_ADDRESS; + return adapter->core->rawRead16(adapter->core, segmentAddress, segment); } static uint32_t mScriptMemoryAdapterRead32(struct mScriptMemoryAdapter* adapter, uint32_t address) { - uint32_t segmentSize = adapter->block.end - adapter->block.start; - uint32_t segmentAddress = address % segmentSize; - int segment = address / segmentSize; - address = adapter->block.start + segmentAddress; - return adapter->core->rawRead32(adapter->core, address, segment); + CALCULATE_SEGMENT_INFO; + CALCULATE_SEGMENT_ADDRESS; + return adapter->core->rawRead32(adapter->core, segmentAddress, segment); } static struct mScriptValue* mScriptMemoryAdapterReadRange(struct mScriptMemoryAdapter* adapter, uint32_t address, uint32_t length) { - uint32_t segmentSize = adapter->block.end - adapter->block.start; + CALCULATE_SEGMENT_INFO; struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); struct mScriptList* list = value->value.opaque; uint32_t i; for (i = 0; i < length; ++i, ++address) { - uint32_t segmentAddress = address % segmentSize; - int segment = address / segmentSize; - segmentAddress += adapter->block.start; + CALCULATE_SEGMENT_ADDRESS; *mScriptListAppend(list) = mSCRIPT_MAKE_U32(adapter->core->rawRead8(adapter->core, segmentAddress, segment)); } return value; } static void mScriptMemoryAdapterWrite8(struct mScriptMemoryAdapter* adapter, uint32_t address, uint8_t value) { - uint32_t segmentSize = adapter->block.end - adapter->block.start; - uint32_t segmentAddress = address % segmentSize; - int segment = address / segmentSize; - address = adapter->block.start + segmentAddress; - adapter->core->rawWrite8(adapter->core, address, segment, value); + CALCULATE_SEGMENT_INFO; + CALCULATE_SEGMENT_ADDRESS; + adapter->core->rawWrite8(adapter->core, address, segmentAddress, value); } static void mScriptMemoryAdapterWrite16(struct mScriptMemoryAdapter* adapter, uint32_t address, uint16_t value) { - uint32_t segmentSize = adapter->block.end - adapter->block.start; - uint32_t segmentAddress = address % segmentSize; - int segment = address / segmentSize; - address = adapter->block.segmentStart + segmentAddress; - adapter->core->rawWrite16(adapter->core, address, segment, value); + CALCULATE_SEGMENT_INFO; + CALCULATE_SEGMENT_ADDRESS; + adapter->core->rawWrite16(adapter->core, address, segmentAddress, value); } static void mScriptMemoryAdapterWrite32(struct mScriptMemoryAdapter* adapter, uint32_t address, uint32_t value) { - uint32_t segmentSize = adapter->block.end - adapter->block.start; - uint32_t segmentAddress = address % segmentSize; - int segment = address / segmentSize; - address = adapter->block.segmentStart + segmentAddress; - adapter->core->rawWrite32(adapter->core, address, segment, value); + CALCULATE_SEGMENT_INFO; + CALCULATE_SEGMENT_ADDRESS; + adapter->core->rawWrite32(adapter->core, address, segmentAddress, value); } mSCRIPT_DECLARE_STRUCT(mScriptMemoryAdapter); From 304d8d1a4dd134b4927b3e8f76f0e2e510e8c786 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 May 2022 22:32:10 -0700 Subject: [PATCH 077/105] Scripting: More API cleanup --- include/mgba/core/scripting.h | 2 +- include/mgba/script/types.h | 11 +++- src/core/scripting.c | 115 ++++++++++++++++++++-------------- src/core/test/scripting.c | 11 ---- src/script/context.c | 6 +- src/script/engines/lua.c | 11 ++-- src/script/types.c | 89 +++++++++++++++----------- 7 files changed, 137 insertions(+), 108 deletions(-) diff --git a/include/mgba/core/scripting.h b/include/mgba/core/scripting.h index 7a38e55c6..6c0943e46 100644 --- a/include/mgba/core/scripting.h +++ b/include/mgba/core/scripting.h @@ -20,10 +20,10 @@ CXX_GUARD_START mLOG_DECLARE_CATEGORY(SCRIPT); struct mCore; -struct mLogger; struct mScriptTextBuffer; mSCRIPT_DECLARE_STRUCT(mCore); mSCRIPT_DECLARE_STRUCT(mLogger); +mSCRIPT_DECLARE_STRUCT(mScriptConsole); mSCRIPT_DECLARE_STRUCT(mScriptTextBuffer); struct mScriptBridge; diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 27733143f..239772483 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -51,11 +51,11 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_S64 s64 #define mSCRIPT_TYPE_FIELD_U64 u64 #define mSCRIPT_TYPE_FIELD_F64 f64 -#define mSCRIPT_TYPE_FIELD_STR opaque +#define mSCRIPT_TYPE_FIELD_STR string #define mSCRIPT_TYPE_FIELD_CHARP copaque #define mSCRIPT_TYPE_FIELD_PTR opaque -#define mSCRIPT_TYPE_FIELD_LIST opaque -#define mSCRIPT_TYPE_FIELD_TABLE opaque +#define mSCRIPT_TYPE_FIELD_LIST list +#define mSCRIPT_TYPE_FIELD_TABLE table #define mSCRIPT_TYPE_FIELD_WRAPPER opaque #define mSCRIPT_TYPE_FIELD_WEAKREF u32 #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque @@ -172,6 +172,9 @@ struct mScriptValue { double f64; void* opaque; const void* copaque; + struct mScriptString* string; + struct Table* table; + struct mScriptList* list; } value; }; @@ -270,6 +273,8 @@ void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out); struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val); const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val); +struct mScriptValue* mScriptStringCreateEmpty(size_t size); +struct mScriptValue* mScriptStringCreateFromBytes(const void* string, size_t size); struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); struct mScriptValue* mScriptStringCreateFromASCII(const char* string); struct mScriptValue* mScriptValueCreateFromUInt(uint32_t value); diff --git a/src/core/scripting.c b/src/core/scripting.c index 12ba0046a..b8ff9fb11 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -161,7 +161,8 @@ struct mScriptCoreAdapter { struct mScriptValue memory; }; -struct mScriptUILibrary { +struct mScriptConsole { + struct mLogger* logger; mScriptContextBufferFactory textBufferFactory; void* textBufferContext; }; @@ -201,12 +202,12 @@ static uint32_t mScriptMemoryAdapterRead32(struct mScriptMemoryAdapter* adapter, static struct mScriptValue* mScriptMemoryAdapterReadRange(struct mScriptMemoryAdapter* adapter, uint32_t address, uint32_t length) { CALCULATE_SEGMENT_INFO; - struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); - struct mScriptList* list = value->value.opaque; + struct mScriptValue* value = mScriptStringCreateEmpty(length); + char* buffer = value->value.string->buffer; uint32_t i; for (i = 0; i < length; ++i, ++address) { CALCULATE_SEGMENT_ADDRESS; - *mScriptListAppend(list) = mSCRIPT_MAKE_U32(adapter->core->rawRead8(adapter->core, segmentAddress, segment)); + buffer[i] = adapter->core->rawRead8(adapter->core, segmentAddress, segment); } return value; } @@ -268,11 +269,11 @@ static struct mScriptValue* _mScriptCoreGetGameCode(const struct mCore* core) { } static struct mScriptValue* _mScriptCoreReadRange(struct mCore* core, uint32_t address, uint32_t length) { - struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); - struct mScriptList* list = value->value.opaque; + struct mScriptValue* value = mScriptStringCreateEmpty(length); + char* buffer = value->value.string->buffer; uint32_t i; for (i = 0; i < length; ++i, ++address) { - *mScriptListAppend(list) = mSCRIPT_MAKE_U32(core->busRead8(core, address)); + buffer[i] = core->busRead8(core, address); } return value; } @@ -497,36 +498,70 @@ void mScriptContextDetachCore(struct mScriptContext* context) { mScriptContextRemoveGlobal(context, "emu"); } -void mScriptLog(struct mLogger* logger, struct mScriptString* msg) { - mLogExplicit(logger, _mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg->buffer); +static struct mScriptTextBuffer* _mScriptConsoleCreateBuffer(struct mScriptConsole* lib, const char* name) { + struct mScriptTextBuffer* buffer = lib->textBufferFactory(lib->textBufferContext); + buffer->init(buffer, name); + return buffer; } -void mScriptWarn(struct mLogger* logger, struct mScriptString* msg) { - mLogExplicit(logger, _mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer); +static void mScriptConsoleLog(struct mScriptConsole* console, struct mScriptString* msg) { + if (console->logger) { + mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg->buffer); + } else { + mLog(_mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg->buffer); + } } -void mScriptError(struct mLogger* logger, struct mScriptString* msg) { - mLogExplicit(logger, _mLOG_CAT_SCRIPT, mLOG_ERROR, "%s", msg->buffer); +static void mScriptConsoleWarn(struct mScriptConsole* console, struct mScriptString* msg) { + if (console->logger) { + mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer); + } else { + mLog(_mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer); + } } -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mLogger, log, mScriptLog, 1, STR, msg); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mLogger, warn, mScriptWarn, 1, STR, msg); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mLogger, error, mScriptError, 1, STR, msg); +static void mScriptConsoleError(struct mScriptConsole* console, struct mScriptString* msg) { + if (console->logger) { + mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_ERROR, "%s", msg->buffer); + } else { + mLog(_mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer); + } +} -mSCRIPT_DEFINE_STRUCT(mLogger) -mSCRIPT_DEFINE_STRUCT_METHOD(mLogger, log) -mSCRIPT_DEFINE_STRUCT_METHOD(mLogger, warn) -mSCRIPT_DEFINE_STRUCT_METHOD(mLogger, error) +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, log, mScriptConsoleLog, 1, STR, msg); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, warn, mScriptConsoleWarn, 1, STR, msg); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, error, mScriptConsoleError, 1, STR, msg); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptConsole, S(mScriptTextBuffer), createBuffer, _mScriptConsoleCreateBuffer, 1, CHARP, name); + +mSCRIPT_DEFINE_STRUCT(mScriptConsole) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, log) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, warn) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, error) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, createBuffer) mSCRIPT_DEFINE_END; +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptConsole, createBuffer) + mSCRIPT_MAKE_CHARP(NULL) +mSCRIPT_DEFINE_DEFAULTS_END; + void mScriptContextAttachLogger(struct mScriptContext* context, struct mLogger* logger) { - struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mLogger)); - value->value.opaque = logger; - mScriptContextSetGlobal(context, "console", value); + 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; + } + console->logger = logger; } void mScriptContextDetachLogger(struct mScriptContext* context) { - mScriptContextRemoveGlobal(context, "console"); + struct mScriptValue* value = mScriptContextGetGlobal(context, "console"); + if (!value) { + return; + } + struct mScriptConsole* console = value->value.opaque; + console->logger = mLogGetContext(); } mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, deinit, 0); @@ -556,31 +591,15 @@ mSCRIPT_DEFINE_STRUCT(mScriptTextBuffer) mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setName) mSCRIPT_DEFINE_END; -struct mScriptTextBuffer* _mScriptUICreateBuffer(struct mScriptUILibrary* lib, const char* name) { - struct mScriptTextBuffer* buffer = lib->textBufferFactory(lib->textBufferContext); - buffer->init(buffer, name); - return buffer; -} - -mSCRIPT_DECLARE_STRUCT(mScriptUILibrary); -mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptUILibrary, S(mScriptTextBuffer), createBuffer, _mScriptUICreateBuffer, 1, CHARP, name); - -mSCRIPT_DEFINE_STRUCT(mScriptUILibrary) - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptUILibrary, createBuffer) -mSCRIPT_DEFINE_END; - -mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptUILibrary, createBuffer) - mSCRIPT_MAKE_CHARP(NULL) -mSCRIPT_DEFINE_DEFAULTS_END; - void mScriptContextSetTextBufferFactory(struct mScriptContext* context, mScriptContextBufferFactory factory, void* cbContext) { - struct mScriptValue* value = mScriptContextEnsureGlobal(context, "ui", mSCRIPT_TYPE_MS_S(mScriptUILibrary)); - struct mScriptUILibrary* uiLib = value->value.opaque; - if (!uiLib) { - uiLib = calloc(1, sizeof(*uiLib)); - value->value.opaque = uiLib; + struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole)); + struct mScriptConsole* console = value->value.opaque; + if (!console) { + console = calloc(1, sizeof(*console)); + console->logger = mLogGetContext(); + value->value.opaque = console; value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; } - uiLib->textBufferFactory = factory; - uiLib->textBufferContext = cbContext; + console->textBufferFactory = factory; + console->textBufferContext = cbContext; } diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index fd6dbc8ca..3d2b8c4fa 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -313,17 +313,6 @@ M_TEST_DEFINE(logging) { assert_string_equal(logger.error, "error"); mScriptContextDetachLogger(&context); - - LOAD_PROGRAM( - "assert(not console)\n" - ); - assert_true(lua->run(lua)); - - LOAD_PROGRAM( - "a:log(\"l2\")\n" - ); - assert_false(lua->run(lua)); - mScriptTestLoggerDeinit(&logger); mScriptContextDeinit(&context); } diff --git a/src/script/context.c b/src/script/context.c index b3ec04ffd..af7fef3b8 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -195,10 +195,10 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c return; } size_t i; - for (i = 0; i < mScriptListSize(list->value.opaque); ++i) { + for (i = 0; i < mScriptListSize(list->value.list); ++i) { struct mScriptFrame frame; mScriptFrameInit(&frame); - struct mScriptValue* fn = mScriptListGetPointer(list->value.opaque, i); + struct mScriptValue* fn = mScriptListGetPointer(list->value.list, i); if (fn->type->base == mSCRIPT_TYPE_WRAPPER) { fn = mScriptValueUnwrap(fn); } @@ -216,7 +216,7 @@ void mScriptContextAddCallback(struct mScriptContext* context, const char* callb list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); HashTableInsert(&context->callbacks, callback, list); } - mScriptValueWrap(fn, mScriptListAppend(list->value.opaque)); + mScriptValueWrap(fn, mScriptListAppend(list->value.list)); } void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants) { diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index f33e0ef4c..90005eed6 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -216,6 +216,8 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { return NULL; } + size_t size; + const void* buffer; struct mScriptValue* value = NULL; switch (lua_type(luaContext->lua, -1)) { case LUA_TNUMBER: @@ -234,7 +236,8 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { value->value.s32 = lua_toboolean(luaContext->lua, -1); break; case LUA_TSTRING: - value = mScriptStringCreateFromUTF8(lua_tostring(luaContext->lua, -1)); + buffer = lua_tolstring(luaContext->lua, -1, &size); + value = mScriptStringCreateFromBytes(buffer, size); mScriptValueWrap(value, mScriptListAppend(&luaContext->d.context->refPool)); mScriptValueDeref(value); break; @@ -311,7 +314,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } break; case mSCRIPT_TYPE_STRING: - lua_pushstring(luaContext->lua, ((struct mScriptString*) value->value.opaque)->buffer); + lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size); mScriptValueDeref(value); break; case mSCRIPT_TYPE_LIST: @@ -753,7 +756,7 @@ int _luaGetList(lua_State* lua) { luaL_traceback(lua, lua, "Invalid list", 1); lua_error(lua); } - struct mScriptList* list = obj->value.opaque; + struct mScriptList* list = obj->value.list; // Lua indexes from 1 if (index < 1) { @@ -786,7 +789,7 @@ static int _luaLenList(lua_State* lua) { luaL_traceback(lua, lua, "Invalid list", 1); lua_error(lua); } - struct mScriptList* list = obj->value.opaque; + struct mScriptList* list = obj->value.list; lua_pushinteger(lua, mScriptListSize(list)); return 1; } diff --git a/src/script/types.c b/src/script/types.c index 297a2b3b8..f4a268ac4 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -182,6 +182,7 @@ const struct mScriptType mSTCharPtr = { .hash = _hashString, .equal = _charpEqual, .cast = _stringCast, + .isConst = true, }; const struct mScriptType mSTList = { @@ -223,24 +224,24 @@ const struct mScriptType mSTWeakref = { DEFINE_VECTOR(mScriptList, struct mScriptValue) void _allocList(struct mScriptValue* val) { - val->value.opaque = malloc(sizeof(struct mScriptList)); - mScriptListInit(val->value.opaque, 0); + val->value.list = malloc(sizeof(struct mScriptList)); + mScriptListInit(val->value.list, 0); } void _freeList(struct mScriptValue* val) { size_t i; - for (i = 0; i < mScriptListSize(val->value.opaque); ++i) { - struct mScriptValue* unwrapped = mScriptValueUnwrap(mScriptListGetPointer(val->value.opaque, i)); + for (i = 0; i < mScriptListSize(val->value.list); ++i) { + struct mScriptValue* unwrapped = mScriptValueUnwrap(mScriptListGetPointer(val->value.list, i)); if (unwrapped) { mScriptValueDeref(unwrapped); } } - mScriptListDeinit(val->value.opaque); - free(val->value.opaque); + mScriptListDeinit(val->value.list); + free(val->value.list); } void _allocTable(struct mScriptValue* val) { - val->value.opaque = malloc(sizeof(struct Table)); + val->value.table = malloc(sizeof(struct Table)); struct TableFunctions funcs = { .deinitializer = _deinitTableValue, .hash = _valHash, @@ -248,12 +249,12 @@ void _allocTable(struct mScriptValue* val) { .ref = _valRef, .deref = _valDeref }; - HashTableInitCustom(val->value.opaque, 0, &funcs); + HashTableInitCustom(val->value.table, 0, &funcs); } void _freeTable(struct mScriptValue* val) { - HashTableDeinit(val->value.opaque); - free(val->value.opaque); + HashTableDeinit(val->value.table); + free(val->value.table); } void _deinitTableValue(void* val) { @@ -264,11 +265,11 @@ static void _allocString(struct mScriptValue* val) { struct mScriptString* string = calloc(1, sizeof(*string)); string->size = 0; string->buffer = NULL; - val->value.opaque = string; + val->value.string = string; } static void _freeString(struct mScriptValue* val) { - struct mScriptString* string = val->value.opaque; + struct mScriptString* string = val->value.string; if (string->size) { free(string->buffer); } @@ -287,7 +288,7 @@ static bool _stringCast(const struct mScriptValue* in, const struct mScriptType* out->type = type; out->refs = mSCRIPT_VALUE_UNREF; out->flags = 0; - out->value.opaque = ((struct mScriptString*) in->value.opaque)->buffer; + out->value.opaque = in->value.string->buffer; return true; } return false; @@ -297,7 +298,7 @@ static uint32_t _hashString(const struct mScriptValue* val) { const char* buffer = 0; size_t size = 0; if (val->type == &mSTString) { - struct mScriptString* string = val->value.opaque; + struct mScriptString* string = val->value.string; buffer = string->buffer; size = string->size; } else if (val->type == &mSTCharPtr) { @@ -642,7 +643,7 @@ bool _charpEqual(const struct mScriptValue* a, const struct mScriptValue* b) { valB = b->value.opaque; lenB = strlen(valB); } else if (b->type == &mSTString) { - struct mScriptString* stringB = b->value.opaque; + struct mScriptString* stringB = b->value.string; valB = stringB->buffer; lenB = stringB->size; } else { @@ -657,7 +658,7 @@ bool _charpEqual(const struct mScriptValue* a, const struct mScriptValue* b) { } bool _stringEqual(const struct mScriptValue* a, const struct mScriptValue* b) { - struct mScriptString* stringA = a->value.opaque; + struct mScriptString* stringA = a->value.string; const char* valA = stringA->buffer; const char* valB; size_t lenA = stringA->size; @@ -669,7 +670,7 @@ bool _stringEqual(const struct mScriptValue* a, const struct mScriptValue* b) { valB = b->value.opaque; lenB = strlen(valB); } else if (b->type == &mSTString) { - struct mScriptString* stringB = b->value.opaque; + struct mScriptString* stringB = b->value.string; valB = stringB->buffer; lenB = stringB->size; } else { @@ -757,9 +758,30 @@ const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* va return NULL; } -struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { +struct mScriptValue* mScriptStringCreateEmpty(size_t size) { struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); struct mScriptString* internal = val->value.opaque; + internal->size = size; + internal->length = 0; + internal->buffer = malloc(size + 1); + memset(internal->buffer, 0, size + 1); + return val; +} + +struct mScriptValue* mScriptStringCreateFromBytes(const void* string, size_t size) { + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); + struct mScriptString* internal = val->value.opaque; + internal->size = size; + internal->length = 0; + internal->buffer = malloc(size + 1); + memcpy(internal->buffer, string, size); + internal->buffer[size] = '\0'; + return val; +} + +struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); + struct mScriptString* internal = val->value.string; internal->size = strlen(string); internal->length = utf8strlen(string); internal->buffer = strdup(string); @@ -768,7 +790,7 @@ struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { struct mScriptValue* mScriptStringCreateFromASCII(const char* string) { struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); - struct mScriptString* internal = val->value.opaque; + struct mScriptString* internal = val->value.string; internal->size = strlen(string); internal->length = strlen(string); internal->buffer = latin1ToUtf8(string, internal->size + 1); @@ -794,9 +816,8 @@ bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, st if (!key->type->hash) { return false; } - struct Table* t = table->value.opaque; mScriptValueRef(value); - HashTableInsertCustom(t, key, value); + HashTableInsertCustom(table->value.table, key, value); return true; } @@ -807,8 +828,7 @@ bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key) { if (!key->type->hash) { return false; } - struct Table* t = table->value.opaque; - HashTableRemoveCustom(t, key); + HashTableRemoveCustom(table->value.table, key); return true; } @@ -822,16 +842,14 @@ struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScri if (!key->type->hash) { return false; } - struct Table* t = table->value.opaque; - return HashTableLookupCustom(t, key); + return HashTableLookupCustom(table->value.table, key); } bool mScriptTableClear(struct mScriptValue* table) { if (table->type != mSCRIPT_TYPE_MS_TABLE) { return false; } - struct Table* t = table->value.opaque; - HashTableClear(t); + HashTableClear(table->value.table); return true; } @@ -842,8 +860,7 @@ bool mScriptTableIteratorStart(struct mScriptValue* table, struct TableIterator* if (table->type != mSCRIPT_TYPE_MS_TABLE) { return false; } - struct Table* t = table->value.opaque; - return HashTableIteratorStart(t, iter); + return HashTableIteratorStart(table->value.table, iter); } bool mScriptTableIteratorNext(struct mScriptValue* table, struct TableIterator* iter) { @@ -853,8 +870,7 @@ bool mScriptTableIteratorNext(struct mScriptValue* table, struct TableIterator* if (table->type != mSCRIPT_TYPE_MS_TABLE) { return false; } - struct Table* t = table->value.opaque; - return HashTableIteratorNext(t, iter); + return HashTableIteratorNext(table->value.table, iter); } struct mScriptValue* mScriptTableIteratorGetKey(struct mScriptValue* table, struct TableIterator* iter) { @@ -864,8 +880,7 @@ struct mScriptValue* mScriptTableIteratorGetKey(struct mScriptValue* table, stru if (table->type != mSCRIPT_TYPE_MS_TABLE) { return NULL; } - struct Table* t = table->value.opaque; - return HashTableIteratorGetCustomKey(t, iter); + return HashTableIteratorGetCustomKey(table->value.table, iter); } struct mScriptValue* mScriptTableIteratorGetValue(struct mScriptValue* table, struct TableIterator* iter) { @@ -875,8 +890,7 @@ struct mScriptValue* mScriptTableIteratorGetValue(struct mScriptValue* table, st if (table->type != mSCRIPT_TYPE_MS_TABLE) { return NULL; } - struct Table* t = table->value.opaque; - return HashTableIteratorGetValue(t, iter); + return HashTableIteratorGetValue(table->value.table, iter); } bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator* iter, struct mScriptValue* key) { @@ -886,8 +900,7 @@ bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator if (table->type != mSCRIPT_TYPE_MS_TABLE) { return false; } - struct Table* t = table->value.opaque; - return HashTableIteratorLookupCustom(t, iter, key); + return HashTableIteratorLookupCustom(table->value.table, iter, key); } void mScriptFrameInit(struct mScriptFrame* frame) { @@ -1053,7 +1066,7 @@ static bool _accessRawMember(struct mScriptClassMember* member, void* raw, bool val->refs = mSCRIPT_VALUE_UNREF; val->flags = 0; val->type = mSCRIPT_TYPE_MS_WRAPPER; - val->value.opaque = raw; + val->value.table = raw; break; case mSCRIPT_TYPE_FUNCTION: val->refs = mSCRIPT_VALUE_UNREF; From 7bc7656988296aae9d9028df2e707dbdac9cfc65 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 20 May 2022 02:31:12 -0700 Subject: [PATCH 078/105] Scripting: Memory fixes --- src/script/context.c | 2 +- src/script/engines/lua.c | 25 ++++++++++++++++--------- src/script/stdlib.c | 1 + 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/script/context.c b/src/script/context.c index af7fef3b8..352fbed91 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -60,12 +60,12 @@ void mScriptContextInit(struct mScriptContext* context) { } void mScriptContextDeinit(struct mScriptContext* context) { - HashTableDeinit(&context->engines); HashTableDeinit(&context->rootScope); HashTableDeinit(&context->weakrefs); mScriptContextDrainPool(context); mScriptListDeinit(&context->refPool); HashTableDeinit(&context->callbacks); + HashTableDeinit(&context->engines); } void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* value) { diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 90005eed6..8710d14e5 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -26,7 +26,7 @@ static const char* _luaGetError(struct mScriptEngineContext*); static bool _luaCall(struct mScriptFrame*, void* context); struct mScriptEngineContextLua; -static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptList*); +static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptList*, bool internal); static bool _luaPopFrame(struct mScriptEngineContextLua*, struct mScriptList*); static bool _luaInvoke(struct mScriptEngineContextLua*, struct mScriptFrame*); @@ -238,10 +238,10 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { case LUA_TSTRING: buffer = lua_tolstring(luaContext->lua, -1, &size); value = mScriptStringCreateFromBytes(buffer, size); - mScriptValueWrap(value, mScriptListAppend(&luaContext->d.context->refPool)); - mScriptValueDeref(value); + mScriptContextFillPool(luaContext->d.context, value); break; case LUA_TFUNCTION: + // This function pops the value internally via luaL_ref return _luaCoerceFunction(luaContext); case LUA_TUSERDATA: if (!lua_getmetatable(luaContext->lua, -1)) { @@ -255,6 +255,7 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { lua_pop(luaContext->lua, 2); value = lua_touserdata(luaContext->lua, -1); value = mScriptContextAccessWeakref(luaContext->d.context, value); + break; } lua_pop(luaContext->lua, 1); return value; @@ -315,7 +316,6 @@ 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); - mScriptValueDeref(value); break; case mSCRIPT_TYPE_LIST: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -333,6 +333,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v *newValue = mSCRIPT_MAKE(WEAKREF, weakref); } else { mScriptValueWrap(value, newValue); + mScriptValueDeref(value); } luaL_setmetatable(luaContext->lua, "mSTTable"); break; @@ -350,6 +351,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v *newValue = mSCRIPT_MAKE(WEAKREF, weakref); } else { mScriptValueWrap(value, newValue); + mScriptValueDeref(value); } luaL_setmetatable(luaContext->lua, "mSTStruct"); break; @@ -413,12 +415,17 @@ const char* _luaGetError(struct mScriptEngineContext* context) { return luaContext->lastError; } -bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame) { +bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame, bool internal) { bool ok = true; if (frame) { size_t i; for (i = 0; i < mScriptListSize(frame); ++i) { - if (!_luaWrap(luaContext, mScriptListGetPointer(frame, i))) { + struct mScriptValue* value = mScriptListGetPointer(frame, i); + if (internal && value->type->base == mSCRIPT_TYPE_WRAPPER) { + value = mScriptValueUnwrap(value); + mScriptContextFillPool(luaContext->d.context, value); + } + if (!_luaWrap(luaContext, value)) { ok = false; break; } @@ -480,7 +487,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* luaContext->lastError = NULL; } - if (frame && !_luaPushFrame(luaContext, &frame->arguments)) { + if (frame && !_luaPushFrame(luaContext, &frame->arguments, false)) { return false; } @@ -557,13 +564,13 @@ int _luaThunk(lua_State* lua) { luaL_traceback(lua, lua, "Error calling function (invoking failed)", 1); lua_error(lua); } - mScriptContextDrainPool(luaContext->d.context); - if (!_luaPushFrame(luaContext, &frame.returnValues)) { + if (!_luaPushFrame(luaContext, &frame.returnValues, true)) { mScriptFrameDeinit(&frame); luaL_traceback(lua, lua, "Error calling function (translating return values from runtime)", 1); lua_error(lua); } + mScriptContextDrainPool(luaContext->d.context); mScriptFrameDeinit(&frame); return lua_gettop(luaContext->lua); diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 0992c601e..fcbb4a83f 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -18,6 +18,7 @@ static void _mScriptCallbackAdd(struct mScriptCallbackAdapter* adapter, struct m fn = mScriptValueUnwrap(fn); } mScriptContextAddCallback(adapter->context, name->buffer, fn); + mScriptValueDeref(fn); } mSCRIPT_DECLARE_STRUCT(mScriptCallbackAdapter); From 7a6b16dc41c5a34f45449be14a9cc525a4e657e1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 20 May 2022 02:31:24 -0700 Subject: [PATCH 079/105] Scripting: Fix up Lua tables --- include/mgba/script/types.h | 1 + src/core/test/scripting.c | 7 +++ src/script/engines/lua.c | 68 ++++++++++++++++++---- src/script/test/lua.c | 112 +++++++++++++++++++++++++++++++----- src/script/types.c | 7 +++ 5 files changed, 172 insertions(+), 23 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 239772483..7a6bc6513 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -284,6 +284,7 @@ bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, st bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key); struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key); bool mScriptTableClear(struct mScriptValue* table); +size_t mScriptTableSize(struct mScriptValue* table); bool mScriptTableIteratorStart(struct mScriptValue* table, struct TableIterator*); bool mScriptTableIteratorNext(struct mScriptValue* table, struct TableIterator*); struct mScriptValue* mScriptTableIteratorGetKey(struct mScriptValue* table, struct TableIterator*); diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index 3d2b8c4fa..308f053d3 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -186,7 +186,9 @@ M_TEST_DEFINE(detach) { LOAD_PROGRAM( "assert(emu)\n" + "assert(emu.memory)\n" "a = emu\n" + "b = emu.memory\n" ); assert_true(lua->run(lua)); @@ -202,6 +204,11 @@ M_TEST_DEFINE(detach) { ); assert_false(lua->run(lua)); + LOAD_PROGRAM( + "assert(memory.cart0)\n" + ); + assert_false(lua->run(lua)); + TEARDOWN_CORE; mScriptContextDeinit(&context); } diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 8710d14e5..e5de3a05c 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -40,6 +40,7 @@ static int _luaGetObject(lua_State* lua); static int _luaSetObject(lua_State* lua); static int _luaGcObject(lua_State* lua); static int _luaGetTable(lua_State* lua); +static int _luaLenTable(lua_State* lua); static int _luaPairsTable(lua_State* lua); static int _luaGetList(lua_State* lua); static int _luaLenList(lua_State* lua); @@ -103,6 +104,7 @@ static const luaL_Reg _mSTStruct[] = { static const luaL_Reg _mSTTable[] = { { "__index", _luaGetTable }, + { "__len", _luaLenTable }, { "__pairs", _luaPairsTable }, { NULL, NULL } }; @@ -662,14 +664,24 @@ static int _luaGcObject(lua_State* lua) { int _luaGetTable(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); char key[MAX_KEY_SIZE]; - const char* keyPtr = lua_tostring(lua, -1); - struct mScriptValue* obj = lua_touserdata(lua, -2); - - if (!keyPtr) { + int type = lua_type(luaContext->lua, -1); + const char* keyPtr = NULL; + int64_t intKey; + switch (type) { + case LUA_TNUMBER: + intKey = lua_tointeger(luaContext->lua, -1); + break; + case LUA_TSTRING: + keyPtr = lua_tostring(lua, -1); + break; + default: lua_pop(lua, 2); return 0; } - strlcpy(key, keyPtr, sizeof(key)); + struct mScriptValue* obj = lua_touserdata(lua, -2); + if (keyPtr) { + strlcpy(key, keyPtr, sizeof(key)); + } lua_pop(lua, 2); obj = mScriptContextAccessWeakref(luaContext->d.context, obj); @@ -678,7 +690,15 @@ int _luaGetTable(lua_State* lua) { lua_error(lua); } - struct mScriptValue keyVal = mSCRIPT_MAKE_CHARP(key); + struct mScriptValue keyVal; + switch (type) { + case LUA_TNUMBER: + keyVal = mSCRIPT_MAKE_S64(intKey); + break; + case LUA_TSTRING: + keyVal = mSCRIPT_MAKE_CHARP(key); + break; + } struct mScriptValue* val = mScriptTableLookup(obj, &keyVal); if (!val) { return 0; @@ -691,13 +711,41 @@ int _luaGetTable(lua_State* lua) { return 1; } +int _luaLenTable(lua_State* lua) { + struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); + struct mScriptValue* obj = lua_touserdata(lua, -1); + lua_pop(lua, 1); + + obj = mScriptContextAccessWeakref(luaContext->d.context, obj); + if (!obj) { + luaL_traceback(lua, lua, "Invalid table", 1); + lua_error(lua); + } + + struct mScriptValue val = mSCRIPT_MAKE_U64(mScriptTableSize(obj)); + + if (!_luaWrap(luaContext, &val)) { + luaL_traceback(lua, lua, "Error translating value from runtime", 1); + lua_error(lua); + } + return 1; +} + static int _luaNextTable(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); char key[MAX_KEY_SIZE]; - const char* keyPtr = lua_tostring(lua, -1); + int type = lua_type(luaContext->lua, -1); + const char* keyPtr = NULL; + struct mScriptValue keyVal = {0}; + switch (type) { + case LUA_TNUMBER: + keyVal = mSCRIPT_MAKE_S64(lua_tointeger(luaContext->lua, -1)); + break; + case LUA_TSTRING: + keyPtr = lua_tostring(lua, -1); + break; + } struct mScriptValue* table = lua_touserdata(lua, -2); - struct mScriptValue keyVal; - if (keyPtr) { strlcpy(key, keyPtr, sizeof(key)); keyVal = mSCRIPT_MAKE_CHARP(key); @@ -711,7 +759,7 @@ static int _luaNextTable(lua_State* lua) { } struct TableIterator iter; - if (keyPtr) { + if (keyVal.type) { if (!mScriptTableIteratorLookup(table, &iter, &keyVal)) { return 0; } diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 7ef6a1ee0..fc91ad742 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -20,6 +20,10 @@ vf->close(vf); \ } while(0) +#define TEST_PROGRAM(PROG) \ + LOAD_PROGRAM(PROG); \ + assert_true(lua->run(lua)); \ + struct Test { int32_t i; int32_t (*ifn0)(struct Test*); @@ -154,16 +158,14 @@ M_TEST_DEFINE(getGlobal) { struct mScriptValue a = mSCRIPT_MAKE_S32(1); struct mScriptValue* val; - LOAD_PROGRAM("a = 1"); - assert_true(lua->run(lua)); + TEST_PROGRAM("a = 1"); val = lua->getGlobal(lua, "a"); assert_non_null(val); assert_true(a.type->equal(&a, val)); mScriptValueDeref(val); - LOAD_PROGRAM("b = 1"); - assert_true(lua->run(lua)); + TEST_PROGRAM("b = 1"); val = lua->getGlobal(lua, "a"); assert_non_null(val); @@ -176,8 +178,7 @@ M_TEST_DEFINE(getGlobal) { mScriptValueDeref(val); a = mSCRIPT_MAKE_S32(2); - LOAD_PROGRAM("a = 2"); - assert_true(lua->run(lua)); + TEST_PROGRAM("a = 2"); val = lua->getGlobal(lua, "a"); assert_non_null(val); @@ -185,8 +186,7 @@ M_TEST_DEFINE(getGlobal) { mScriptValueDeref(val); a = mSCRIPT_MAKE_S32(3); - LOAD_PROGRAM("b = a + b"); - assert_true(lua->run(lua)); + TEST_PROGRAM("b = a + b"); val = lua->getGlobal(lua, "b"); assert_non_null(val); @@ -250,8 +250,8 @@ M_TEST_DEFINE(callLuaFunc) { struct mScriptValue* fn; - LOAD_PROGRAM("function a(b) return b + 1 end; function c(d, e) return d + e end"); - assert_true(lua->run(lua)); + TEST_PROGRAM("function a(b) return b + 1 end; function c(d, e) return d + e end"); + assert_null(lua->getError(lua)); fn = lua->getGlobal(lua, "a"); assert_non_null(fn); @@ -291,11 +291,10 @@ M_TEST_DEFINE(callCFunc) { struct mScriptValue a = mSCRIPT_MAKE_S32(1); struct mScriptValue* val; - LOAD_PROGRAM("a = b(1); c = d(1, 2)"); - assert_true(lua->setGlobal(lua, "b", &boundIdentityInt)); assert_true(lua->setGlobal(lua, "d", &boundAddInts)); - assert_true(lua->run(lua)); + TEST_PROGRAM("a = b(1); c = d(1, 2)"); + assert_null(lua->getError(lua)); val = lua->getGlobal(lua, "a"); assert_non_null(val); @@ -535,6 +534,91 @@ M_TEST_DEFINE(errorReporting) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(tableLookup) { + SETUP_LUA; + + assert_null(lua->getError(lua)); + struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + assert_non_null(table); + struct mScriptValue* val; + + mScriptContextSetGlobal(&context, "t", table); + + val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S64); + val->value.s64 = 0; + assert_true(mScriptTableInsert(table, &mSCRIPT_MAKE_S64(0), val)); + mScriptValueDeref(val); + + val = mScriptStringCreateFromASCII("t"); + assert_true(mScriptTableInsert(table, &mSCRIPT_MAKE_CHARP("t"), val)); + mScriptValueDeref(val); + + val = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + assert_true(mScriptTableInsert(table, &mSCRIPT_MAKE_CHARP("sub"), val)); + mScriptValueDeref(val); + + table = val; + val = mScriptStringCreateFromASCII("t"); + assert_true(mScriptTableInsert(table, &mSCRIPT_MAKE_CHARP("t"), val)); + mScriptValueDeref(val); + + TEST_PROGRAM("assert(t)"); + TEST_PROGRAM("assert(t['t'] ~= nil)"); + TEST_PROGRAM("assert(t['t'] == 't')"); + TEST_PROGRAM("assert(t.t == 't')"); + TEST_PROGRAM("assert(t['x'] == nil)"); + TEST_PROGRAM("assert(t.x == nil)"); + TEST_PROGRAM("assert(t.sub ~= nil)"); + TEST_PROGRAM("assert(t.sub.t ~= nil)"); + TEST_PROGRAM("assert(t.sub.t == 't')"); + TEST_PROGRAM("assert(t[0] ~= nil)"); + TEST_PROGRAM("assert(t[0] == 0)"); + TEST_PROGRAM("assert(t[1] == nil)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(tableIterate) { + SETUP_LUA; + + assert_null(lua->getError(lua)); + struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + assert_non_null(table); + struct mScriptValue* val; + struct mScriptValue* key; + + mScriptContextSetGlobal(&context, "t", table); + + int i; + for (i = 0; i < 50; ++i) { + val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S64); + val->value.s64 = 1LL << i; + key = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32); + key->value.s32 = i; + assert_true(mScriptTableInsert(table, key, val)); + mScriptValueDeref(key); + mScriptValueDeref(val); + } + assert_int_equal(mScriptTableSize(table), 50); + + TEST_PROGRAM("assert(t)"); + TEST_PROGRAM("assert(#t == 50)"); + TEST_PROGRAM( + "i = 0\n" + "z = 0\n" + "for k, v in pairs(t) do\n" + " i = i + 1\n" + " z = z + v\n" + " assert((1 << k) == v)\n" + "end\n" + ); + + TEST_PROGRAM("assert(i == #t)"); + TEST_PROGRAM("assert(z == (1 << #t) - 1)"); + + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(create), cmocka_unit_test(loadGood), @@ -548,4 +632,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(globalStructFieldSet), cmocka_unit_test(globalStructMethods), cmocka_unit_test(errorReporting), + cmocka_unit_test(tableLookup), + cmocka_unit_test(tableIterate), ) diff --git a/src/script/types.c b/src/script/types.c index f4a268ac4..0c20b9257 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -853,6 +853,13 @@ bool mScriptTableClear(struct mScriptValue* table) { return true; } +size_t mScriptTableSize(struct mScriptValue* table) { + if (table->type != mSCRIPT_TYPE_MS_TABLE) { + return 0; + } + return HashTableSize(table->value.table); +} + bool mScriptTableIteratorStart(struct mScriptValue* table, struct TableIterator* iter) { if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { table = mScriptValueUnwrap(table); From b84a549e8cca85ed6524dcfc0de56a482fa18c57 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 May 2022 23:35:58 -0700 Subject: [PATCH 080/105] Scripting: Expose mCore.checksum function --- src/core/scripting.c | 30 ++++++++++++++++++++++++++++++ src/script/stdlib.c | 4 ++++ 2 files changed, 34 insertions(+) diff --git a/src/core/scripting.c b/src/core/scripting.c index b8ff9fb11..496242c69 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -268,6 +268,29 @@ static struct mScriptValue* _mScriptCoreGetGameCode(const struct mCore* core) { return mScriptStringCreateFromASCII(code); } +static struct mScriptValue* _mScriptCoreChecksum(const struct mCore* core, int t) { + enum mCoreChecksumType type = (enum mCoreChecksumType) t; + size_t size = 0; + switch (type) { + case mCHECKSUM_CRC32: + size = 4; + break; + } + if (!size) { + return NULL; + } + void* data = calloc(1, size); + core->checksum(core, data, type); + if (type == mCHECKSUM_CRC32) { + // This checksum is endian-dependent...let's just make it big endian for Lua + uint32_t* crc = data; + STORE_32BE(*crc, 0, crc); + } + struct mScriptValue* ret = mScriptStringCreateFromBytes(data, size); + free(data); + return ret; +} + static struct mScriptValue* _mScriptCoreReadRange(struct mCore* core, uint32_t address, uint32_t length) { struct mScriptValue* value = mScriptStringCreateEmpty(length); char* buffer = value->value.string->buffer; @@ -299,6 +322,7 @@ mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frameCycles, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0); mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WRAPPER, getGameTitle, _mScriptCoreGetGameTitle, 0); mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WRAPPER, getGameCode, _mScriptCoreGetGameCode, 0); +mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(mCore, WRAPPER, checksum, _mScriptCoreChecksum, 1, S32, type); // Run functions mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0); @@ -339,6 +363,8 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frameCycles) mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per second") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frequency) + mSCRIPT_DEFINE_DOCSTRING("Get the checksum of the loaded ROM") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, checksum) mSCRIPT_DEFINE_DOCSTRING("Get internal title of the game from the ROM header") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getGameTitle) @@ -388,6 +414,10 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshot) mSCRIPT_DEFINE_END; +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, checksum) + mSCRIPT_MAKE_S32(mCHECKSUM_CRC32) +mSCRIPT_DEFINE_DEFAULTS_END; + mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateSlot) mSCRIPT_NO_DEFAULT, mSCRIPT_MAKE_S32(SAVESTATE_ALL) diff --git a/src/script/stdlib.c b/src/script/stdlib.c index fcbb4a83f..d1be3e6e1 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -55,5 +55,9 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(mPLATFORM, GB), mSCRIPT_CONSTANT_SENTINEL }); + mScriptContextExportConstants(context, "CHECKSUM", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mCHECKSUM, CRC32), + mSCRIPT_CONSTANT_SENTINEL + }); mScriptContextSetGlobal(context, "C", context->constants); } From 62d5d788fc020808c4565af3287e0f6f27aa87c5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 May 2022 21:47:04 -0700 Subject: [PATCH 081/105] Res: Increase Pokemon script compatibility --- res/scripts/pokemon.lua | 334 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 312 insertions(+), 22 deletions(-) diff --git a/res/scripts/pokemon.lua b/res/scripts/pokemon.lua index 635ecf9d0..606dfe8d0 100644 --- a/res/scripts/pokemon.lua +++ b/res/scripts/pokemon.lua @@ -1,4 +1,23 @@ -gen3CharmapEn = { +gbCharmapEn = { [0]= + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", " ", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", + "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "(", ")", ":", ";", "[", "]", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", + "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "é", "ʼd", "ʼl", "ʼs", "ʼt", "ʼv", + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", + "'", "P\u{200d}k", "M\u{200d}n", "-", "ʼr", "ʼm", "?", "!", ".", "ァ", "ゥ", "ェ", "▹", "▸", "▾", "♂", + "$", "×", ".", "/", ",", "♀", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" +} + +gen3CharmapEn = { [0]= " ", "À", "Á", "Â", "Ç", "È", "É", "Ê", "Ë", "Ì", "こ", "Î", "Ï", "Ò", "Ó", "Ô", "Œ", "Ù", "Ú", "Û", "Ñ", "ß", "à", "á", "ね", "ç", "è", "é", "ê", "ë", "ì", "ま", "î", "ï", "ò", "ó", "ô", "œ", "ù", "ú", "û", "ñ", "º", "ª", "�", "&", "+", "あ", @@ -17,6 +36,116 @@ gen3CharmapEn = { ":", "Ä", "Ö", "Ü", "ä", "ö", "ü", "⬆", "⬇", "⬅", "�", "�", "�", "�", "�", "" } +function _read16BE(emu, address) + return (emu:read8(address) << 8) | emu:read8(address + 1) +end + +function readBoxMonGen1(game, address, nameAddress, otAddress) + local mon = {} + mon.species = emu.memory.cart0:read8(game._speciesIndex + emu:read8(address + 0) - 1) + mon.hp = _read16BE(emu, address + 1) + mon.level = emu:read8(address + 3) + mon.status = emu:read8(address + 4) + mon.type1 = emu:read8(address + 5) + mon.type2 = emu:read8(address + 6) + mon.catchRate = emu:read8(address + 7) + mon.moves = { + emu:read8(address + 8), + emu:read8(address + 9), + emu:read8(address + 10), + emu:read8(address + 11), + } + mon.otId = _read16BE(emu, address + 12) + mon.experience = (_read16BE(emu, address + 14) << 8)| emu:read8(address + 16) + mon.hpEV = _read16BE(emu, address + 17) + mon.attackEV = _read16BE(emu, address + 19) + mon.defenseEV = _read16BE(emu, address + 21) + mon.speedEV = _read16BE(emu, address + 23) + mon.spAttackEV = _read16BE(emu, address + 25) + mon.spDefenseEV = mon.spAttackEv + local iv = _read16BE(emu, address + 27) + mon.attackIV = (iv >> 4) & 0xF + mon.defenseIV = iv & 0xF + mon.speedIV = (iv >> 12) & 0xF + mon.spAttackIV = (iv >> 8) & 0xF + mon.spDefenseIV = mon.spAttackIV + mon.pp = { + emu:read8(address + 28), + emu:read8(address + 29), + emu:read8(address + 30), + emu:read8(address + 31), + } + mon.nickname = game:toString(emu:readRange(nameAddress, game._monNameLength)) + mon.otName = game:toString(emu:readRange(otAddress, game._playerNameLength)) + return mon +end + +function readPartyMonGen1(game, address, nameAddress, otAddress) + local mon = game:_readBoxMon(address, nameAddress, otAddress) + mon.level = emu:read8(address + 33) + mon.maxHP = _read16BE(emu, address + 34) + mon.attack = _read16BE(emu, address + 36) + mon.defense = _read16BE(emu, address + 38) + mon.speed = _read16BE(emu, address + 40) + mon.spAttack = _read16BE(emu, address + 42) + mon.spDefense = mon.spAttack + return mon +end + +function readBoxMonGen2(game, address, nameAddress, otAddress) + local mon = {} + mon.species = emu:read8(address + 0) + mon.item = emu:read8(address + 1) + mon.moves = { + emu:read8(address + 2), + emu:read8(address + 3), + emu:read8(address + 4), + emu:read8(address + 5), + } + mon.otId = _read16BE(emu, address + 6) + mon.experience = (_read16BE(emu, address + 8) << 8)| emu:read8(address + 10) + mon.hpEV = _read16BE(emu, address + 11) + mon.attackEV = _read16BE(emu, address + 13) + mon.defenseEV = _read16BE(emu, address + 15) + mon.speedEV = _read16BE(emu, address + 17) + mon.spAttackEV = _read16BE(emu, address + 19) + mon.spDefenseEV = mon.spAttackEv + local iv = _read16BE(emu, address + 21) + mon.attackIV = (iv >> 4) & 0xF + mon.defenseIV = iv & 0xF + mon.speedIV = (iv >> 12) & 0xF + mon.spAttackIV = (iv >> 8) & 0xF + mon.spDefenseIV = mon.spAttackIV + mon.pp = { + emu:read8(address + 23), + emu:read8(address + 24), + emu:read8(address + 25), + emu:read8(address + 26), + } + mon.friendship = emu:read8(address + 27) + mon.pokerus = emu:read8(address + 28) + local caughtData = _read16BE(emu, address + 29) + mon.metLocation = (caughtData >> 8) & 0x7F + mon.metLevel = caughtData & 0x1F + mon.level = emu:read8(address + 31) + mon.nickname = game:toString(emu:readRange(nameAddress, game._monNameLength)) + mon.otName = game:toString(emu:readRange(otAddress, game._playerNameLength)) + return mon +end + +function readPartyMonGen2(game, address, nameAddress, otAddress) + local mon = game:_readBoxMon(address, nameAddress, otAddress) + mon.status = emu:read8(address + 32) + mon.hp = _read16BE(emu, address + 34) + mon.maxHP = _read16BE(emu, address + 36) + mon.attack = _read16BE(emu, address + 38) + mon.defense = _read16BE(emu, address + 40) + mon.speed = _read16BE(emu, address + 42) + mon.spAttack = _read16BE(emu, address + 44) + mon.spDefense = _read16BE(emu, address + 46) + return mon +end + function readBoxMonGen3(game, address) local mon = {} mon.personality = emu:read32(address + 0) @@ -158,45 +287,127 @@ end function getParty(game) local party = {} + local monStart = game._party + local nameStart = game._partyNames + local otStart = game._partyOt for i = 1, emu:read8(game._partyCount) do - party[i] = game:_readPartyMon(game._party + (i - 1) * game._partyMonSize) + party[i] = game:_readPartyMon(monStart, nameStart, otStart) + monStart = monStart + game._partyMonSize + if game._partyNames then + nameStart = nameStart + game._monNameLength + 1 + end + if game._partyOt then + otStart = otStart + game._playerNameLength + 1 + end end return party end function toString(game, rawstring) local string = "" - for _, char in ipairs(rawstring) do + for _, char in ipairs({rawstring:byte(1, #rawstring)}) do if char == game._terminator then break end - string = string..game._charmap[char + 1] + string = string..game._charmap[char] end return string end function getSpeciesName(game, id) - local pointer = game._speciesNameTable + (game._monNameLength + 1) * id - return game:toString(emu:readRange(pointer, game._monNameLength)) + if game._speciesIndex then + local index = game._index + if not index then + index = {} + for i = 0, 255 do + index[emu.memory.cart0:read8(game._speciesIndex + i)] = i + end + game._index = index + end + id = index[id] + end + local pointer = game._speciesNameTable + (game._speciesNameLength) * id + return game:toString(emu.memory.cart0:readRange(pointer, game._monNameLength)) end +local gameRBEn = { + name="Red/Blue (USA)", + _party=0xd16b, + _partyCount=0xd163, + _partyNames=0xd2b5, + _partyOt=0xd273, + _speciesNameTable=0x1c21e, + _speciesIndex=0x41024, + _boxMonSize=33, + _partyMonSize=44, + _terminator=0x50, + _monNameLength=10, + _speciesNameLength=10, + _playerNameLength=10, + _charmap=gbCharmapEn, + _readBoxMon=readBoxMonGen1, + _readPartyMon=readPartyMonGen1, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, +} + +local gameYellowEn = { + name="Yellow (USA)", +} + local gameGSEn = { name="Gold/Silver (USA)", + _party=0xda2a, + _partyCount=0xda22, + _partyNames=0xdb8c, + _partyOt=0xdb4a, + _speciesNameTable=0x1b0b6a, + _boxMonSize=32, + _partyMonSize=48, + _monNameLength=10, + _speciesNameLength=10, + _playerNameLength=10, + _charmap=gbCharmapEn, + _readBoxMon=readBoxMonGen2, + _readPartyMon=readPartyMonGen2, + _terminator=0x50, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, } local gameCrystalEn = { name="Crystal (USA)", + _party=0xdcdf, + _partyCount=0xdcd7, + _partyNames=0xde41, + _partyOt=0xddff, + _speciesNameTable=0x5337a, + _boxMonSize=32, + _partyMonSize=48, + _monNameLength=10, + _speciesNameLength=10, + _playerNameLength=10, + _charmap=gbCharmapEn, + _readBoxMon=readBoxMonGen2, + _readPartyMon=readPartyMonGen2, + _terminator=0x50, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, } local gameRubyEn = { name="Ruby (USA)", _party=0x3004360, _partyCount=0x3004350, - _speciesNameTable=0x81f716c, + _speciesNameTable=0x1f716c, _boxMonSize=80, _partyMonSize=100, _monNameLength=10, - _playerNameLength=7, + _speciesNameLength=11, + _playerNameLength=10, _charmap=gen3CharmapEn, _readBoxMon=readBoxMonGen3, _readPartyMon=readPartyMonGen3, @@ -210,11 +421,12 @@ local gameSapphireEn = { name="Sapphire (USA)", _party=0x3004360, _partyCount=0x3004350, - _speciesNameTable=0x81f70fc, + _speciesNameTable=0x1f70fc, _boxMonSize=80, _partyMonSize=100, _monNameLength=10, - _playerNameLength=7, + _speciesNameLength=11, + _playerNameLength=10, _charmap=gen3CharmapEn, _readBoxMon=readBoxMonGen3, _readPartyMon=readPartyMonGen3, @@ -224,16 +436,16 @@ local gameSapphireEn = { getSpeciesName=getSpeciesName, } - local gameEmeraldEn = { name="Emerald (USA)", _party=0x20244ec, _partyCount=0x20244e9, - _speciesNameTable=0x8318570, + _speciesNameTable=0x3185c8, _boxMonSize=80, _partyMonSize=100, _monNameLength=10, - _playerNameLength=7, + _speciesNameLength=11, + _playerNameLength=10, _charmap=gen3CharmapEn, _readBoxMon=readBoxMonGen3, _readPartyMon=readPartyMonGen3, @@ -243,14 +455,73 @@ local gameEmeraldEn = { getSpeciesName=getSpeciesName, } -local gameFRLGEn = { - name="FireRed/LeafGreen (USA)", +local gameFireRedEn = { + name="FireRed (USA)", _party=0x2024284, _partyCount=0x2024029, + _speciesNameTable=0x245ee0, _boxMonSize=80, _partyMonSize=100, _monNameLength=10, - _playerNameLength=7, + _speciesNameLength=11, + _playerNameLength=10, + _charmap=gen3CharmapEn, + _readBoxMon=readBoxMonGen3, + _readPartyMon=readPartyMonGen3, + _terminator=0xFF, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, +} + +local gameFireRedEnR1 = { + name="FireRed (USA) (Rev 1)", + _party=0x2024284, + _partyCount=0x2024029, + _speciesNameTable=0x245f50, + _boxMonSize=80, + _partyMonSize=100, + _monNameLength=10, + _speciesNameLength=11, + _playerNameLength=10, + _charmap=gen3CharmapEn, + _readBoxMon=readBoxMonGen3, + _readPartyMon=readPartyMonGen3, + _terminator=0xFF, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, +} + +local gameLeafGreenEn = { + name="LeafGreen (USA)", + _party=0x2024284, + _partyCount=0x2024029, + _speciesNameTable=0x245ebc, + _boxMonSize=80, + _partyMonSize=100, + _monNameLength=10, + _speciesNameLength=11, + _playerNameLength=10, + _charmap=gen3CharmapEn, + _readBoxMon=readBoxMonGen3, + _readPartyMon=readPartyMonGen3, + _terminator=0xFF, + toString=toString, + getParty=getParty, + getSpeciesName=getSpeciesName, +} + +local gameLeafGreenEnR1 = { + name="LeafGreen (USA)", + _party=0x2024284, + _partyCount=0x2024029, + _speciesNameTable=0x245f2c, + _boxMonSize=80, + _partyMonSize=100, + _monNameLength=10, + _speciesNameLength=11, + _playerNameLength=10, _charmap=gen3CharmapEn, _readBoxMon=readBoxMonGen3, _readPartyMon=readPartyMonGen3, @@ -261,14 +532,25 @@ local gameFRLGEn = { } gameCodes = { - ["DMG-AAUE"]=gameGSEn, - ["DMG-AAXE"]=gameGSEn, - ["DMG-BYTE"]=gameCrystalEn, + ["DMG-AAUE"]=gameGSEn, -- Gold + ["DMG-AAXE"]=gameGSEn, -- Silver + ["CGB-BYTE"]=gameCrystalEn, ["AGB-AXVE"]=gameRubyEn, ["AGB-AXPE"]=gameSapphireEn, ["AGB-BPEE"]=gameEmeraldEn, - ["AGB-BPRE"]=gameFRLGEn, - ["AGB-BPGE"]=gameFRLGEn, + ["AGB-BPRE"]=gameFireRedEn, + ["AGB-BPGE"]=gameLeafGreenEn, +} + +-- These versions have slight differences and/or cannot be uniquely +-- identified by their in-header game codes, so fall back on a CRC32 +gameCrc32 = { + [0x9f7fdd53] = gameRBEn, -- Red + [0xd6da8a1a] = gameRBEn, -- Blue + [0x7d527d62] = gameYellowEn, + [0x3358e30a] = gameCrystal, -- Crystal rev 1 + [0x84ee4776] = gameFireRedEnR1, + [0xdaffecec] = gameLeafGreenEnR1, } function printPartyStatus(game, buffer) @@ -284,7 +566,15 @@ function printPartyStatus(game, buffer) end function detectGame() - game = gameCodes[emu:getGameCode()] + local checksum = 0 + for i, v in ipairs({emu:checksum(C.CHECKSUM.CRC32):byte(1, 4)}) do + checksum = checksum * 256 + v + end + game = gameCrc32[checksum] + if not game then + game = gameCodes[emu:getGameCode()] + end + if not game then console:error("Unknown game!") else From 7d7987d2058eea95bd3f407ca7721b24b5e5bfb4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 22 May 2022 21:03:45 -0700 Subject: [PATCH 082/105] Res: Write better Lua --- res/scripts/pokemon.lua | 293 +++++++++++++++------------------------- 1 file changed, 107 insertions(+), 186 deletions(-) diff --git a/res/scripts/pokemon.lua b/res/scripts/pokemon.lua index 606dfe8d0..e93e38270 100644 --- a/res/scripts/pokemon.lua +++ b/res/scripts/pokemon.lua @@ -1,4 +1,92 @@ -gbCharmapEn = { [0]= +local Game = { + new = function (self, game) + self.__index = self + setmetatable(game, self) + return game + end +} + +function Game.getParty(game) + local party = {} + local monStart = game._party + local nameStart = game._partyNames + local otStart = game._partyOt + for i = 1, emu:read8(game._partyCount) do + party[i] = game:_readPartyMon(monStart, nameStart, otStart) + monStart = monStart + game._partyMonSize + if game._partyNames then + nameStart = nameStart + game._monNameLength + 1 + end + if game._partyOt then + otStart = otStart + game._playerNameLength + 1 + end + end + return party +end + +function Game.toString(game, rawstring) + local string = "" + for _, char in ipairs({rawstring:byte(1, #rawstring)}) do + if char == game._terminator then + break + end + string = string..game._charmap[char] + end + return string +end + +function Game.getSpeciesName(game, id) + if game._speciesIndex then + local index = game._index + if not index then + index = {} + for i = 0, 255 do + index[emu.memory.cart0:read8(game._speciesIndex + i)] = i + end + game._index = index + end + id = index[id] + end + local pointer = game._speciesNameTable + (game._speciesNameLength) * id + return game:toString(emu.memory.cart0:readRange(pointer, game._monNameLength)) +end + +local GBGameEn = Game:new{ + _terminator=0x50, + _monNameLength=10, + _speciesNameLength=10, + _playerNameLength=10, +} + +local GBAGameEn = Game:new{ + _terminator=0xFF, + _monNameLength=10, + _speciesNameLength=11, + _playerNameLength=10, +} + +local Generation1En = GBGameEn:new{ + _boxMonSize=33, + _partyMonSize=44, + _readBoxMon=readBoxMonGen1, + _readPartyMon=readPartyMonGen1, +} + +local Generation2En = GBGameEn:new{ + _boxMonSize=32, + _partyMonSize=48, + _readBoxMon=readBoxMonGen2, + _readPartyMon=readPartyMonGen2, +} + +local Generation3En = GBAGameEn:new{ + _boxMonSize=80, + _partyMonSize=100, + _readBoxMon=readBoxMonGen3, + _readPartyMon=readPartyMonGen3, +} + +GBGameEn._charmap = { [0]= "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", "�", @@ -17,7 +105,7 @@ gbCharmapEn = { [0]= "$", "×", ".", "/", ",", "♀", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" } -gen3CharmapEn = { [0]= +GBAGameEn._charmap = { [0]= " ", "À", "Á", "Â", "Ç", "È", "É", "Ê", "Ë", "Ì", "こ", "Î", "Ï", "Ò", "Ó", "Ô", "Œ", "Ù", "Ú", "Û", "Ñ", "ß", "à", "á", "ね", "ç", "è", "é", "ê", "ë", "ì", "ま", "î", "ï", "ò", "ó", "ô", "œ", "ù", "ú", "û", "ñ", "º", "ª", "�", "&", "+", "あ", @@ -40,7 +128,7 @@ function _read16BE(emu, address) return (emu:read8(address) << 8) | emu:read8(address + 1) end -function readBoxMonGen1(game, address, nameAddress, otAddress) +function Generation1En._readBoxMon(game, address, nameAddress, otAddress) local mon = {} mon.species = emu.memory.cart0:read8(game._speciesIndex + emu:read8(address + 0) - 1) mon.hp = _read16BE(emu, address + 1) @@ -80,7 +168,7 @@ function readBoxMonGen1(game, address, nameAddress, otAddress) return mon end -function readPartyMonGen1(game, address, nameAddress, otAddress) +function Generation1En._readPartyMon(game, address, nameAddress, otAddress) local mon = game:_readBoxMon(address, nameAddress, otAddress) mon.level = emu:read8(address + 33) mon.maxHP = _read16BE(emu, address + 34) @@ -92,7 +180,7 @@ function readPartyMonGen1(game, address, nameAddress, otAddress) return mon end -function readBoxMonGen2(game, address, nameAddress, otAddress) +function Generation2En._readBoxMon(game, address, nameAddress, otAddress) local mon = {} mon.species = emu:read8(address + 0) mon.item = emu:read8(address + 1) @@ -133,7 +221,7 @@ function readBoxMonGen2(game, address, nameAddress, otAddress) return mon end -function readPartyMonGen2(game, address, nameAddress, otAddress) +function Generation2En._readPartyMon(game, address, nameAddress, otAddress) local mon = game:_readBoxMon(address, nameAddress, otAddress) mon.status = emu:read8(address + 32) mon.hp = _read16BE(emu, address + 34) @@ -146,7 +234,7 @@ function readPartyMonGen2(game, address, nameAddress, otAddress) return mon end -function readBoxMonGen3(game, address) +function Generation3En._readBoxMon(game, address) local mon = {} mon.personality = emu:read32(address + 0) mon.otId = emu:read32(address + 4) @@ -270,7 +358,7 @@ function readBoxMonGen3(game, address) return mon end -function readPartyMonGen3(game, address) +function Generation3En._readPartyMon(game, address) local mon = game:_readBoxMon(address) mon.status = emu:read32(address + 80) mon.level = emu:read8(address + 84) @@ -285,52 +373,7 @@ function readPartyMonGen3(game, address) return mon end -function getParty(game) - local party = {} - local monStart = game._party - local nameStart = game._partyNames - local otStart = game._partyOt - for i = 1, emu:read8(game._partyCount) do - party[i] = game:_readPartyMon(monStart, nameStart, otStart) - monStart = monStart + game._partyMonSize - if game._partyNames then - nameStart = nameStart + game._monNameLength + 1 - end - if game._partyOt then - otStart = otStart + game._playerNameLength + 1 - end - end - return party -end - -function toString(game, rawstring) - local string = "" - for _, char in ipairs({rawstring:byte(1, #rawstring)}) do - if char == game._terminator then - break - end - string = string..game._charmap[char] - end - return string -end - -function getSpeciesName(game, id) - if game._speciesIndex then - local index = game._index - if not index then - index = {} - for i = 0, 255 do - index[emu.memory.cart0:read8(game._speciesIndex + i)] = i - end - game._index = index - end - id = index[id] - end - local pointer = game._speciesNameTable + (game._speciesNameLength) * id - return game:toString(emu.memory.cart0:readRange(pointer, game._monNameLength)) -end - -local gameRBEn = { +local gameRBEn = Generation1En:new{ name="Red/Blue (USA)", _party=0xd16b, _partyCount=0xd163, @@ -338,197 +381,75 @@ local gameRBEn = { _partyOt=0xd273, _speciesNameTable=0x1c21e, _speciesIndex=0x41024, - _boxMonSize=33, - _partyMonSize=44, - _terminator=0x50, - _monNameLength=10, - _speciesNameLength=10, - _playerNameLength=10, - _charmap=gbCharmapEn, - _readBoxMon=readBoxMonGen1, - _readPartyMon=readPartyMonGen1, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameYellowEn = { +local gameYellowEn = Generation1En:new{ name="Yellow (USA)", } -local gameGSEn = { +local gameGSEn = Generation2En:new{ name="Gold/Silver (USA)", _party=0xda2a, _partyCount=0xda22, _partyNames=0xdb8c, _partyOt=0xdb4a, _speciesNameTable=0x1b0b6a, - _boxMonSize=32, - _partyMonSize=48, - _monNameLength=10, - _speciesNameLength=10, - _playerNameLength=10, - _charmap=gbCharmapEn, - _readBoxMon=readBoxMonGen2, - _readPartyMon=readPartyMonGen2, - _terminator=0x50, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameCrystalEn = { +local gameCrystalEn = Generation2En:new{ name="Crystal (USA)", _party=0xdcdf, _partyCount=0xdcd7, _partyNames=0xde41, _partyOt=0xddff, _speciesNameTable=0x5337a, - _boxMonSize=32, - _partyMonSize=48, - _monNameLength=10, - _speciesNameLength=10, - _playerNameLength=10, - _charmap=gbCharmapEn, - _readBoxMon=readBoxMonGen2, - _readPartyMon=readPartyMonGen2, - _terminator=0x50, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameRubyEn = { +local gameRubyEn = Generation3En:new{ name="Ruby (USA)", _party=0x3004360, _partyCount=0x3004350, _speciesNameTable=0x1f716c, - _boxMonSize=80, - _partyMonSize=100, - _monNameLength=10, - _speciesNameLength=11, - _playerNameLength=10, - _charmap=gen3CharmapEn, - _readBoxMon=readBoxMonGen3, - _readPartyMon=readPartyMonGen3, - _terminator=0xFF, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameSapphireEn = { +local gameSapphireEn = Generation3En:new{ name="Sapphire (USA)", _party=0x3004360, _partyCount=0x3004350, _speciesNameTable=0x1f70fc, - _boxMonSize=80, - _partyMonSize=100, - _monNameLength=10, - _speciesNameLength=11, - _playerNameLength=10, - _charmap=gen3CharmapEn, - _readBoxMon=readBoxMonGen3, - _readPartyMon=readPartyMonGen3, - _terminator=0xFF, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameEmeraldEn = { +local gameEmeraldEn = Generation3En:new{ name="Emerald (USA)", _party=0x20244ec, _partyCount=0x20244e9, _speciesNameTable=0x3185c8, - _boxMonSize=80, - _partyMonSize=100, - _monNameLength=10, - _speciesNameLength=11, - _playerNameLength=10, - _charmap=gen3CharmapEn, - _readBoxMon=readBoxMonGen3, - _readPartyMon=readPartyMonGen3, - _terminator=0xFF, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameFireRedEn = { +local gameFireRedEn = Generation3En:new{ name="FireRed (USA)", _party=0x2024284, _partyCount=0x2024029, _speciesNameTable=0x245ee0, - _boxMonSize=80, - _partyMonSize=100, - _monNameLength=10, - _speciesNameLength=11, - _playerNameLength=10, - _charmap=gen3CharmapEn, - _readBoxMon=readBoxMonGen3, - _readPartyMon=readPartyMonGen3, - _terminator=0xFF, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameFireRedEnR1 = { +local gameFireRedEnR1 = gameFireRedEn:new{ name="FireRed (USA) (Rev 1)", - _party=0x2024284, - _partyCount=0x2024029, _speciesNameTable=0x245f50, - _boxMonSize=80, - _partyMonSize=100, - _monNameLength=10, - _speciesNameLength=11, - _playerNameLength=10, - _charmap=gen3CharmapEn, - _readBoxMon=readBoxMonGen3, - _readPartyMon=readPartyMonGen3, - _terminator=0xFF, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameLeafGreenEn = { +local gameLeafGreenEn = Generation3En:new{ name="LeafGreen (USA)", _party=0x2024284, _partyCount=0x2024029, _speciesNameTable=0x245ebc, - _boxMonSize=80, - _partyMonSize=100, - _monNameLength=10, - _speciesNameLength=11, - _playerNameLength=10, - _charmap=gen3CharmapEn, - _readBoxMon=readBoxMonGen3, - _readPartyMon=readPartyMonGen3, - _terminator=0xFF, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } -local gameLeafGreenEnR1 = { +local gameLeafGreenEnR1 = gameLeafGreenEn:new{ name="LeafGreen (USA)", _party=0x2024284, _partyCount=0x2024029, _speciesNameTable=0x245f2c, - _boxMonSize=80, - _partyMonSize=100, - _monNameLength=10, - _speciesNameLength=11, - _playerNameLength=10, - _charmap=gen3CharmapEn, - _readBoxMon=readBoxMonGen3, - _readPartyMon=readPartyMonGen3, - _terminator=0xFF, - toString=toString, - getParty=getParty, - getSpeciesName=getSpeciesName, } gameCodes = { From 5d7e3bdf1314ae41e65b3b56770cc6e51ed4da72 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 22 May 2022 21:22:14 -0700 Subject: [PATCH 083/105] Res: Support Pokemon Yellow in script --- res/scripts/pokemon.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/res/scripts/pokemon.lua b/res/scripts/pokemon.lua index e93e38270..09cd472ef 100644 --- a/res/scripts/pokemon.lua +++ b/res/scripts/pokemon.lua @@ -385,6 +385,12 @@ local gameRBEn = Generation1En:new{ local gameYellowEn = Generation1En:new{ name="Yellow (USA)", + _party=0xd16a, + _partyCount=0xd162, + _partyNames=0xd2b4, + _partyOt=0xd272, + _speciesNameTable=0xe8000, + _speciesIndex=0x410b1, } local gameGSEn = Generation2En:new{ From b3476a997ae54ee31ed8d252d00cb99d9da4a062 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 22 May 2022 22:29:13 -0700 Subject: [PATCH 084/105] Scripting: Pass filenames down to scripting engines --- include/mgba/script/context.h | 2 +- src/core/test/scripting.c | 2 +- src/platform/qt/ScriptingController.cpp | 9 ++++---- src/platform/qt/ScriptingController.h | 2 +- src/script/context.c | 2 +- src/script/engines/lua.c | 28 ++++++++++++++++++++++--- src/script/test/lua.c | 6 +++--- 7 files changed, 37 insertions(+), 14 deletions(-) diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 74385c291..8633e0ed4 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -49,7 +49,7 @@ struct mScriptEngineContext { bool (*setGlobal)(struct mScriptEngineContext*, const char* name, struct mScriptValue*); struct mScriptValue* (*getGlobal)(struct mScriptEngineContext*, const char* name); - bool (*load)(struct mScriptEngineContext*, struct VFile*); + bool (*load)(struct mScriptEngineContext*, const char* filename, struct VFile*); bool (*run)(struct mScriptEngineContext*); const char* (*getError)(struct mScriptEngineContext*); }; diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index 308f053d3..ddcb448c5 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -69,7 +69,7 @@ static const uint8_t _fakeGBROM[0x4000] = { #define LOAD_PROGRAM(PROG) \ do { \ struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - assert_true(lua->load(lua, vf)); \ + assert_true(lua->load(lua, NULL, vf)); \ vf->close(vf); \ } while(0) diff --git a/src/platform/qt/ScriptingController.cpp b/src/platform/qt/ScriptingController.cpp index 53fed8ec2..8585a33f7 100644 --- a/src/platform/qt/ScriptingController.cpp +++ b/src/platform/qt/ScriptingController.cpp @@ -71,15 +71,16 @@ void ScriptingController::setController(std::shared_ptr controll bool ScriptingController::loadFile(const QString& path) { VFileDevice vf(path, QIODevice::ReadOnly); - return load(vf); + return load(vf, path); } -bool ScriptingController::load(VFileDevice& vf) { +bool ScriptingController::load(VFileDevice& vf, const QString& name) { if (!m_activeEngine) { return false; } + QByteArray utf8 = name.toUtf8(); CoreController::Interrupter interrupter(m_controller); - if (!m_activeEngine->load(m_activeEngine, vf) || !m_activeEngine->run(m_activeEngine)) { + if (!m_activeEngine->load(m_activeEngine, utf8.constData(), vf) || !m_activeEngine->run(m_activeEngine)) { emit error(QString::fromUtf8(m_activeEngine->getError(m_activeEngine))); return false; } @@ -100,7 +101,7 @@ void ScriptingController::clearController() { void ScriptingController::runCode(const QString& code) { VFileDevice vf(code.toUtf8()); - load(vf); + load(vf, "*prompt"); } mScriptTextBuffer* ScriptingController::createTextBuffer(void* context) { diff --git a/src/platform/qt/ScriptingController.h b/src/platform/qt/ScriptingController.h index d220c0377..d7603f4a4 100644 --- a/src/platform/qt/ScriptingController.h +++ b/src/platform/qt/ScriptingController.h @@ -30,7 +30,7 @@ public: void setController(std::shared_ptr controller); bool loadFile(const QString& path); - bool load(VFileDevice& vf); + bool load(VFileDevice& vf, const QString& name); mScriptContext* context() { return &m_scriptContext; } diff --git a/src/script/context.c b/src/script/context.c index 352fbed91..f2437dc5b 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -247,7 +247,7 @@ bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, stru if (!info.context) { return false; } - return info.context->load(info.context, vf); + return info.context->load(info.context, name, vf); } bool mScriptContextLoadFile(struct mScriptContext* context, const char* path) { diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index e5de3a05c..ac26345b4 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -19,7 +19,7 @@ static void _luaDestroy(struct mScriptEngineContext*); static bool _luaIsScript(struct mScriptEngineContext*, const char*, struct VFile*); static struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext*, const char* name); static bool _luaSetGlobal(struct mScriptEngineContext*, const char* name, struct mScriptValue*); -static bool _luaLoad(struct mScriptEngineContext*, struct VFile*); +static bool _luaLoad(struct mScriptEngineContext*, const char*, struct VFile*); static bool _luaRun(struct mScriptEngineContext*); static const char* _luaGetError(struct mScriptEngineContext*); @@ -382,7 +382,7 @@ static const char* _reader(lua_State* lua, void* context, size_t* size) { return reader->block; } -bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf) { +bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFile* vf) { struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx; struct mScriptEngineLuaReader data = { .vf = vf @@ -391,7 +391,29 @@ bool _luaLoad(struct mScriptEngineContext* ctx, struct VFile* vf) { free(luaContext->lastError); luaContext->lastError = NULL; } - int ret = lua_load(luaContext->lua, _reader, &data, NULL, "t"); + char name[80]; + if (filename) { + if (*filename == '*') { + snprintf(name, sizeof(name), "=%s", filename + 1); + } else { + const char* lastSlash = strrchr(filename, '/'); + const char* lastBackslash = strrchr(filename, '\\'); + if (lastSlash && lastBackslash) { + if (lastSlash > lastBackslash) { + filename = lastSlash + 1; + } else { + filename = lastBackslash + 1; + } + } else if (lastSlash) { + filename = lastSlash + 1; + } else if (lastBackslash) { + filename = lastBackslash + 1; + } + snprintf(name, sizeof(name), "@%s", filename); + } + filename = name; + } + int ret = lua_load(luaContext->lua, _reader, &data, filename, "t"); switch (ret) { case LUA_OK: luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX); diff --git a/src/script/test/lua.c b/src/script/test/lua.c index fc91ad742..71b719966 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -16,7 +16,7 @@ #define LOAD_PROGRAM(PROG) \ do { \ struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - assert_true(lua->load(lua, vf)); \ + assert_true(lua->load(lua, NULL, vf)); \ vf->close(vf); \ } while(0) @@ -119,7 +119,7 @@ M_TEST_DEFINE(loadGood) { const char* program = "-- test\n"; struct VFile* vf = VFileFromConstMemory(program, strlen(program)); - assert_true(lua->load(lua, vf)); + assert_true(lua->load(lua, NULL, vf)); lua->destroy(lua); mScriptContextDeinit(&context); @@ -133,7 +133,7 @@ M_TEST_DEFINE(loadBadSyntax) { const char* program = "Invalid syntax! )\n"; struct VFile* vf = VFileFromConstMemory(program, strlen(program)); - assert_false(lua->load(lua, vf)); + assert_false(lua->load(lua, NULL, vf)); lua->destroy(lua); mScriptContextDeinit(&context); From e69be0cc6f669f8da95581da7a3f897ada6f633c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 22 May 2022 22:57:47 -0700 Subject: [PATCH 085/105] Scripting: Add reset menu opiton --- src/core/thread.c | 6 ++-- src/platform/qt/ScriptingController.cpp | 46 +++++++++++++++++-------- src/platform/qt/ScriptingController.h | 3 ++ src/platform/qt/ScriptingView.cpp | 1 + src/platform/qt/ScriptingView.ui | 6 ++++ 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/core/thread.c b/src/core/thread.c index ee46569cd..530ee6f6f 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -273,7 +273,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } #ifdef ENABLE_SCRIPTING // startCallback could add a script context - if (!scriptContext) { + if (scriptContext != threadContext->scriptContext) { scriptContext = threadContext->scriptContext; if (scriptContext) { _mCoreThreadAddCallbacks(threadContext); @@ -294,7 +294,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { #ifdef ENABLE_SCRIPTING // resetCallback could add a script context - if (!scriptContext) { + if (scriptContext != threadContext->scriptContext) { scriptContext = threadContext->scriptContext; if (scriptContext) { _mCoreThreadAddCallbacks(threadContext); @@ -351,7 +351,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } } #ifdef ENABLE_SCRIPTING - if (!scriptContext) { + if (scriptContext != threadContext->scriptContext) { scriptContext = threadContext->scriptContext; if (scriptContext) { _mCoreThreadAddCallbacks(threadContext); diff --git a/src/platform/qt/ScriptingController.cpp b/src/platform/qt/ScriptingController.cpp index 8585a33f7..2113df29f 100644 --- a/src/platform/qt/ScriptingController.cpp +++ b/src/platform/qt/ScriptingController.cpp @@ -13,10 +13,6 @@ using namespace QGBA; ScriptingController::ScriptingController(QObject* parent) : QObject(parent) { - mScriptContextInit(&m_scriptContext); - mScriptContextAttachStdlib(&m_scriptContext); - mScriptContextRegisterEngines(&m_scriptContext); - m_logger.p = this; m_logger.log = [](mLogger* log, int, enum mLogLevel level, const char* format, va_list args) { Logger* logger = static_cast(log); @@ -37,17 +33,7 @@ ScriptingController::ScriptingController(QObject* parent) } }; - mScriptContextAttachLogger(&m_scriptContext, &m_logger); - mScriptContextSetTextBufferFactory(&m_scriptContext, &ScriptingController::createTextBuffer, this); - - HashTableEnumerate(&m_scriptContext.engines, [](const char* key, void* engine, void* context) { - ScriptingController* self = static_cast(context); - self->m_engines[QString::fromUtf8(key)] = static_cast(engine); - }, this); - - if (m_engines.count() == 1) { - m_activeEngine = *m_engines.begin(); - } + init(); } ScriptingController::~ScriptingController() { @@ -99,6 +85,18 @@ void ScriptingController::clearController() { m_controller.reset(); } +void ScriptingController::reset() { + CoreController::Interrupter interrupter(m_controller); + mScriptContextDetachCore(&m_scriptContext); + mScriptContextDeinit(&m_scriptContext); + m_engines.clear(); + m_activeEngine = nullptr; + init(); + if (m_controller && m_controller->hasStarted()) { + mScriptContextAttachCore(&m_scriptContext, m_controller->thread()->core); + } +} + void ScriptingController::runCode(const QString& code) { VFileDevice vf(code.toUtf8()); load(vf, "*prompt"); @@ -110,3 +108,21 @@ mScriptTextBuffer* ScriptingController::createTextBuffer(void* context) { emit self->textBufferCreated(buffer); return buffer->textBuffer(); } + +void ScriptingController::init() { + mScriptContextInit(&m_scriptContext); + mScriptContextAttachStdlib(&m_scriptContext); + mScriptContextRegisterEngines(&m_scriptContext); + + mScriptContextAttachLogger(&m_scriptContext, &m_logger); + mScriptContextSetTextBufferFactory(&m_scriptContext, &ScriptingController::createTextBuffer, this); + + HashTableEnumerate(&m_scriptContext.engines, [](const char* key, void* engine, void* context) { + ScriptingController* self = static_cast(context); + self->m_engines[QString::fromUtf8(key)] = static_cast(engine); + }, this); + + if (m_engines.count() == 1) { + m_activeEngine = *m_engines.begin(); + } +} diff --git a/src/platform/qt/ScriptingController.h b/src/platform/qt/ScriptingController.h index d7603f4a4..0ccd9f67d 100644 --- a/src/platform/qt/ScriptingController.h +++ b/src/platform/qt/ScriptingController.h @@ -42,9 +42,12 @@ signals: public slots: void clearController(); + void reset(); void runCode(const QString& code); private: + void init(); + static mScriptTextBuffer* createTextBuffer(void* context); struct Logger : mLogger { diff --git a/src/platform/qt/ScriptingView.cpp b/src/platform/qt/ScriptingView.cpp index b870d0e61..46857d219 100644 --- a/src/platform/qt/ScriptingView.cpp +++ b/src/platform/qt/ScriptingView.cpp @@ -29,6 +29,7 @@ ScriptingView::ScriptingView(ScriptingController* controller, QWidget* parent) connect(m_ui.buffers, &QListWidget::currentRowChanged, this, &ScriptingView::selectBuffer); connect(m_ui.load, &QAction::triggered, this, &ScriptingView::load); + connect(m_ui.reset, &QAction::triggered, controller, &ScriptingController::reset); } void ScriptingView::submitRepl() { diff --git a/src/platform/qt/ScriptingView.ui b/src/platform/qt/ScriptingView.ui index a0cf7ed1a..3b30ba9b2 100644 --- a/src/platform/qt/ScriptingView.ui +++ b/src/platform/qt/ScriptingView.ui @@ -80,6 +80,7 @@ File + @@ -89,6 +90,11 @@ Load script... + + + &Reset + + From 6b2fa8fe671244ab1411983bcaf9f25966a2b1a0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 May 2022 21:28:10 -0700 Subject: [PATCH 086/105] Scripting: Add more docstrings --- src/core/scripting.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 496242c69..c82cf9c7a 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -368,7 +368,7 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Get internal title of the game from the ROM header") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getGameTitle) - mSCRIPT_DEFINE_DOCSTRING("Get internal product code for the game from the ROM header") + mSCRIPT_DEFINE_DOCSTRING("Get internal product code for the game from the ROM header, if available") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getGameCode) mSCRIPT_DEFINE_DOCSTRING("Run until the next frame") @@ -489,12 +489,13 @@ mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WRAPPER, _get, _mScriptCoreAda mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0); mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) -mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(mScriptCoreAdapter, PS(mCore), _core, core) -mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptCoreAdapter, TABLE, memory) -mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptCoreAdapter) -mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptCoreAdapter) -mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, S(mCore), _core) -mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core) + mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(mScriptCoreAdapter, PS(mCore), _core, core) + mSCRIPT_DEFINE_DOCSTRING("A table containing a platform-specific set of memory adapters") + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptCoreAdapter, TABLE, memory) + mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptCoreAdapter) + mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptCoreAdapter) + mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, S(mCore), _core) + mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core) mSCRIPT_DEFINE_END; void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core) { @@ -564,9 +565,13 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, error, mScriptConsoleError, 1 mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptConsole, S(mScriptTextBuffer), createBuffer, _mScriptConsoleCreateBuffer, 1, CHARP, name); mSCRIPT_DEFINE_STRUCT(mScriptConsole) + mSCRIPT_DEFINE_DOCSTRING("Print a log to the console") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, log) + mSCRIPT_DEFINE_DOCSTRING("Print a warning to the console") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, warn) + mSCRIPT_DEFINE_DOCSTRING("Print an error to the console") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, error) + mSCRIPT_DEFINE_DOCSTRING("Create a text buffer that can be used to display custom information") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, createBuffer) mSCRIPT_DEFINE_END; @@ -608,14 +613,23 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setName, 1, CHARP, name) mSCRIPT_DEFINE_STRUCT(mScriptTextBuffer) mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(mScriptTextBuffer, deinit) + mSCRIPT_DEFINE_DOCSTRING("Get the current x position of the cursor") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getX) + mSCRIPT_DEFINE_DOCSTRING("Get the current y position of the cursor") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getY) + mSCRIPT_DEFINE_DOCSTRING("Get number of columns in the buffer") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, cols) + mSCRIPT_DEFINE_DOCSTRING("Get number of rows in the buffer") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, rows) + mSCRIPT_DEFINE_DOCSTRING("Print a string to the buffer") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, print) + mSCRIPT_DEFINE_DOCSTRING("Clear the buffer") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, clear) + mSCRIPT_DEFINE_DOCSTRING("Set the number of rows and columns") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setSize) + mSCRIPT_DEFINE_DOCSTRING("Set the posiiton of the cursor") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, moveCursor) + mSCRIPT_DEFINE_DOCSTRING("Advance the cursor a number of columns") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, advance) mSCRIPT_DEFINE_DOCSTRING("Set the user-visible name of this buffer") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setName) From 7ab5f3d69031a22d015aa30dde7da21af1e78a7a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 May 2022 21:59:42 -0700 Subject: [PATCH 087/105] Scripting: Allow "opaque" pointers a bit more clarity --- include/mgba/script/macros.h | 6 ++++++ include/mgba/script/types.h | 1 + 2 files changed, 7 insertions(+) diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 1aa6fb87c..293c63f46 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -132,6 +132,9 @@ CXX_GUARD_START }; \ const struct mScriptType mSTStructPtr_ ## STRUCT = { \ .base = mSCRIPT_TYPE_OPAQUE, \ + .details = { \ + .type = &mSTStruct_ ## STRUCT \ + }, \ .size = sizeof(void*), \ .name = "ptr struct::" #STRUCT, \ .alloc = NULL, \ @@ -141,6 +144,9 @@ CXX_GUARD_START }; \ const struct mScriptType mSTStructPtrConst_ ## STRUCT = { \ .base = mSCRIPT_TYPE_OPAQUE, \ + .details = { \ + .type = &mSTStructConst_ ## STRUCT \ + }, \ .isConst = true, \ .size = sizeof(void*), \ .name = "ptr const struct::" #STRUCT, \ diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 7a6bc6513..e0a369c46 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -236,6 +236,7 @@ struct mScriptType { struct mScriptTypeTuple tuple; struct mScriptTypeFunction function; struct mScriptTypeClass* cls; + const struct mScriptType* type; void* opaque; } details; const struct mScriptType* constType; From f739c28b5fe5c29123e3db036466c7ac62393d54 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 May 2022 22:44:30 -0700 Subject: [PATCH 088/105] Scripting: Rename mScriptMemoryAdapter to mScriptMemoryDomain --- src/core/scripting.c | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index c82cf9c7a..da75369c8 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -150,7 +150,7 @@ bool mScriptBridgeLookupSymbol(struct mScriptBridge* sb, const char* name, int32 return info.success; } -struct mScriptMemoryAdapter { +struct mScriptMemoryDomain { struct mCore* core; struct mCoreMemoryBlock block; }; @@ -182,25 +182,25 @@ struct mScriptConsole { segmentAddress += segmentStart; \ } -static uint32_t mScriptMemoryAdapterRead8(struct mScriptMemoryAdapter* adapter, uint32_t address) { +static uint32_t mScriptMemoryDomainRead8(struct mScriptMemoryDomain* adapter, uint32_t address) { CALCULATE_SEGMENT_INFO; CALCULATE_SEGMENT_ADDRESS; return adapter->core->rawRead8(adapter->core, segmentAddress, segment); } -static uint32_t mScriptMemoryAdapterRead16(struct mScriptMemoryAdapter* adapter, uint32_t address) { +static uint32_t mScriptMemoryDomainRead16(struct mScriptMemoryDomain* adapter, uint32_t address) { CALCULATE_SEGMENT_INFO; CALCULATE_SEGMENT_ADDRESS; return adapter->core->rawRead16(adapter->core, segmentAddress, segment); } -static uint32_t mScriptMemoryAdapterRead32(struct mScriptMemoryAdapter* adapter, uint32_t address) { +static uint32_t mScriptMemoryDomainRead32(struct mScriptMemoryDomain* adapter, uint32_t address) { CALCULATE_SEGMENT_INFO; CALCULATE_SEGMENT_ADDRESS; return adapter->core->rawRead32(adapter->core, segmentAddress, segment); } -static struct mScriptValue* mScriptMemoryAdapterReadRange(struct mScriptMemoryAdapter* adapter, uint32_t address, uint32_t length) { +static struct mScriptValue* mScriptMemoryDomainReadRange(struct mScriptMemoryDomain* adapter, uint32_t address, uint32_t length) { CALCULATE_SEGMENT_INFO; struct mScriptValue* value = mScriptStringCreateEmpty(length); char* buffer = value->value.string->buffer; @@ -212,48 +212,48 @@ static struct mScriptValue* mScriptMemoryAdapterReadRange(struct mScriptMemoryAd return value; } -static void mScriptMemoryAdapterWrite8(struct mScriptMemoryAdapter* adapter, uint32_t address, uint8_t value) { +static void mScriptMemoryDomainWrite8(struct mScriptMemoryDomain* adapter, uint32_t address, uint8_t value) { CALCULATE_SEGMENT_INFO; CALCULATE_SEGMENT_ADDRESS; adapter->core->rawWrite8(adapter->core, address, segmentAddress, value); } -static void mScriptMemoryAdapterWrite16(struct mScriptMemoryAdapter* adapter, uint32_t address, uint16_t value) { +static void mScriptMemoryDomainWrite16(struct mScriptMemoryDomain* adapter, uint32_t address, uint16_t value) { CALCULATE_SEGMENT_INFO; CALCULATE_SEGMENT_ADDRESS; adapter->core->rawWrite16(adapter->core, address, segmentAddress, value); } -static void mScriptMemoryAdapterWrite32(struct mScriptMemoryAdapter* adapter, uint32_t address, uint32_t value) { +static void mScriptMemoryDomainWrite32(struct mScriptMemoryDomain* adapter, uint32_t address, uint32_t value) { CALCULATE_SEGMENT_INFO; CALCULATE_SEGMENT_ADDRESS; adapter->core->rawWrite32(adapter->core, address, segmentAddress, value); } -mSCRIPT_DECLARE_STRUCT(mScriptMemoryAdapter); -mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read8, mScriptMemoryAdapterRead8, 1, U32, address); -mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read16, mScriptMemoryAdapterRead16, 1, U32, address); -mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, U32, read32, mScriptMemoryAdapterRead32, 1, U32, address); -mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryAdapter, WRAPPER, readRange, mScriptMemoryAdapterReadRange, 2, U32, address, U32, length); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write8, mScriptMemoryAdapterWrite8, 2, U32, address, U8, value); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write16, mScriptMemoryAdapterWrite16, 2, U32, address, U16, value); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryAdapter, write32, mScriptMemoryAdapterWrite32, 2, U32, address, U32, value); +mSCRIPT_DECLARE_STRUCT(mScriptMemoryDomain); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read8, mScriptMemoryDomainRead8, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read16, mScriptMemoryDomainRead16, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read32, mScriptMemoryDomainRead32, 1, U32, address); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WRAPPER, readRange, mScriptMemoryDomainReadRange, 2, U32, address, U32, length); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write8, mScriptMemoryDomainWrite8, 2, U32, address, U8, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write16, mScriptMemoryDomainWrite16, 2, U32, address, U16, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write32, mScriptMemoryDomainWrite32, 2, U32, address, U32, value); -mSCRIPT_DEFINE_STRUCT(mScriptMemoryAdapter) +mSCRIPT_DEFINE_STRUCT(mScriptMemoryDomain) mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given offset") - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read8) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read8) mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given offset") - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read16) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read16) mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given offset") - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, read32) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read32) mSCRIPT_DEFINE_DOCSTRING("Read byte range from the given offset") - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, readRange) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, readRange) mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given offset") - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write8) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, write8) mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given offset") - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write16) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, write16) mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset") - mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryAdapter, write32) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, write32) mSCRIPT_DEFINE_END; static struct mScriptValue* _mScriptCoreGetGameTitle(const struct mCore* core) { @@ -454,10 +454,10 @@ static void _rebuildMemoryMap(struct mScriptContext* context, struct mScriptCore size_t nBlocks = adapter->core->listMemoryBlocks(adapter->core, &blocks); size_t i; for (i = 0; i < nBlocks; ++i) { - struct mScriptMemoryAdapter* memadapter = calloc(1, sizeof(*memadapter)); + struct mScriptMemoryDomain* memadapter = calloc(1, sizeof(*memadapter)); memadapter->core = adapter->core; memcpy(&memadapter->block, &blocks[i], sizeof(memadapter->block)); - struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptMemoryAdapter)); + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptMemoryDomain)); value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; value->value.opaque = memadapter; struct mScriptValue* key = mScriptStringCreateFromUTF8(blocks[i].internalName); From 98c371b7697980134d639f16b25c2c118dca6250 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 May 2022 23:01:20 -0700 Subject: [PATCH 089/105] Scripting: Rename mScriptCallbackAdapter to mScriptCallbackManager --- src/script/stdlib.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/script/stdlib.c b/src/script/stdlib.c index d1be3e6e1..1a739c160 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -9,11 +9,11 @@ #include #include -struct mScriptCallbackAdapter { +struct mScriptCallbackManager { struct mScriptContext* context; }; -static void _mScriptCallbackAdd(struct mScriptCallbackAdapter* adapter, struct mScriptString* name, struct mScriptValue* fn) { +static void _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct mScriptString* name, struct mScriptValue* fn) { if (fn->type->base == mSCRIPT_TYPE_WRAPPER) { fn = mScriptValueUnwrap(fn); } @@ -21,20 +21,20 @@ static void _mScriptCallbackAdd(struct mScriptCallbackAdapter* adapter, struct m mScriptValueDeref(fn); } -mSCRIPT_DECLARE_STRUCT(mScriptCallbackAdapter); -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackAdapter, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function); +mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function); -mSCRIPT_DEFINE_STRUCT(mScriptCallbackAdapter) +mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackAdapter, add) +mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add) mSCRIPT_DEFINE_END; void mScriptContextAttachStdlib(struct mScriptContext* context) { struct mScriptValue* lib; - lib = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCallbackAdapter)); - lib->value.opaque = calloc(1, sizeof(struct mScriptCallbackAdapter)); - *(struct mScriptCallbackAdapter*) lib->value.opaque = (struct mScriptCallbackAdapter) { + lib = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCallbackManager)); + lib->value.opaque = calloc(1, sizeof(struct mScriptCallbackManager)); + *(struct mScriptCallbackManager*) lib->value.opaque = (struct mScriptCallbackManager) { .context = context }; lib->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; From c6e68f7224ebe07ca64a8831d8c612cef844066a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 24 May 2022 22:28:45 -0700 Subject: [PATCH 090/105] Scripting: Expose information about the memory domains --- src/core/scripting.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/core/scripting.c b/src/core/scripting.c index da75369c8..b0c37d0e0 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -230,6 +230,22 @@ static void mScriptMemoryDomainWrite32(struct mScriptMemoryDomain* adapter, uint adapter->core->rawWrite32(adapter->core, address, segmentAddress, value); } +static uint32_t mScriptMemoryDomainBase(struct mScriptMemoryDomain* adapter) { + return adapter->block.start; +} + +static uint32_t mScriptMemoryDomainEnd(struct mScriptMemoryDomain* adapter) { + return adapter->block.end; +} + +static uint32_t mScriptMemoryDomainSize(struct mScriptMemoryDomain* adapter) { + return adapter->block.size; +} + +static struct mScriptValue* mScriptMemoryDomainName(struct mScriptMemoryDomain* adapter) { + return mScriptStringCreateFromUTF8(adapter->block.shortName); +} + mSCRIPT_DECLARE_STRUCT(mScriptMemoryDomain); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read8, mScriptMemoryDomainRead8, 1, U32, address); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read16, mScriptMemoryDomainRead16, 1, U32, address); @@ -239,6 +255,11 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write8, mScriptMemoryDom mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write16, mScriptMemoryDomainWrite16, 2, U32, address, U16, value); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write32, mScriptMemoryDomainWrite32, 2, U32, address, U32, value); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, base, mScriptMemoryDomainBase, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, bound, mScriptMemoryDomainEnd, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, size, mScriptMemoryDomainSize, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WRAPPER, name, mScriptMemoryDomainName, 0); + mSCRIPT_DEFINE_STRUCT(mScriptMemoryDomain) mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given offset") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read8) @@ -254,6 +275,15 @@ mSCRIPT_DEFINE_STRUCT(mScriptMemoryDomain) mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, write16) mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, write32) + + mSCRIPT_DEFINE_DOCSTRING("Get the address of the base of this memory domain") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, base) + mSCRIPT_DEFINE_DOCSTRING("Get the address of the end bound of this memory domain. Note that this address is not in the domain itself, and is the address of the first byte past it") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, bound) + mSCRIPT_DEFINE_DOCSTRING("Get the size of this memory domain in bytes") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, size) + mSCRIPT_DEFINE_DOCSTRING("Get a short, human-readable name for this memory domain") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, name) mSCRIPT_DEFINE_END; static struct mScriptValue* _mScriptCoreGetGameTitle(const struct mCore* core) { @@ -454,6 +484,9 @@ static void _rebuildMemoryMap(struct mScriptContext* context, struct mScriptCore size_t nBlocks = adapter->core->listMemoryBlocks(adapter->core, &blocks); size_t i; for (i = 0; i < nBlocks; ++i) { + if (blocks[i].flags == mCORE_MEMORY_VIRTUAL) { + continue; + } struct mScriptMemoryDomain* memadapter = calloc(1, sizeof(*memadapter)); memadapter->core = adapter->core; memcpy(&memadapter->block, &blocks[i], sizeof(memadapter->block)); From fc34b727416dca840a72f80857a924b6c6d5a94e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 May 2022 22:01:18 -0700 Subject: [PATCH 091/105] Scripting: Add prototype documentation generator, outputs to YAML --- CMakeLists.txt | 7 + src/script/docgen.c | 348 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 355 insertions(+) create mode 100644 src/script/docgen.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 42aaa896c..48561819c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ if(NOT LIBMGBA_ONLY) set(BUILD_GL ON CACHE BOOL "Build with OpenGL") set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2") set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3") + set(BUILD_DOCGEN OFF CACHE BOOL "Build the scripting API documentation generator") set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") set(DISTBUILD OFF CACHE BOOL "Build distribution packages") @@ -89,6 +90,7 @@ if(NOT LIBMGBA_ONLY) set(WIN32_UNIX_PATHS OFF CACHE BOOL "Use Unix-like paths") mark_as_advanced(WIN32_UNIX_PATHS) endif() + mark_as_advanced(BUILD_DOCGEN) else() set(DISABLE_FRONTENDS ON) set(DISABLE_DEPS ON) @@ -961,6 +963,11 @@ if(BUILD_UPDATER) endif() endif() +if(ENABLE_SCRIPTING AND BUILD_DOCGEN) + add_executable(docgen ${CMAKE_CURRENT_SOURCE_DIR}/src/script/docgen.c) + target_link_libraries(docgen ${OS_LIB} ${PLATFORM_LIBRARY} ${BINARY_NAME}) +endif() + if(BUILD_SDL) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/sdl ${CMAKE_CURRENT_BINARY_DIR}/sdl) endif() diff --git a/src/script/docgen.c b/src/script/docgen.c new file mode 100644 index 000000000..2c832cd67 --- /dev/null +++ b/src/script/docgen.c @@ -0,0 +1,348 @@ +/* Copyright (c) 2013-2022 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 +#include +#include +#include + +struct mScriptContext context; +struct Table types; + +void explainValue(struct mScriptValue* value, int level); +void explainType(struct mScriptType* type, int level); + +void addTypesFromTuple(const struct mScriptTypeTuple*); +void addTypesFromTable(struct Table*); + +void addType(const struct mScriptType* type) { + if (HashTableLookup(&types, type->name) || type->isConst) { + return; + } + HashTableInsert(&types, type->name, (struct mScriptType*) type); + switch (type->base) { + case mSCRIPT_TYPE_FUNCTION: + addTypesFromTuple(&type->details.function.parameters); + addTypesFromTuple(&type->details.function.returnType); + break; + case mSCRIPT_TYPE_OBJECT: + mScriptClassInit(type->details.cls); + if (type->details.cls->parent) { + addType(type->details.cls->parent); + } + addTypesFromTable(&type->details.cls->instanceMembers); + break; + case mSCRIPT_TYPE_OPAQUE: + if (type->details.type) { + addType(type->details.type); + } + } +} + +void addTypesFromTuple(const struct mScriptTypeTuple* tuple) { + size_t i; + for (i = 0; i < tuple->count; ++i) { + addType(tuple->entries[i]); + } +} + +void addTypesFromTable(struct Table* table) { + struct TableIterator iter; + if (!HashTableIteratorStart(table, &iter)) { + return; + } + do { + struct mScriptClassMember* member = HashTableIteratorGetValue(table, &iter); + addType(member->type); + } while(HashTableIteratorNext(table, &iter)); +} + +bool printval(const struct mScriptValue* value, char* buffer, size_t bufferSize) { + struct mScriptValue sval; + switch (value->type->base) { + case mSCRIPT_TYPE_SINT: + if (value->type->size <= 4) { + snprintf(buffer, bufferSize, "%"PRId32, value->value.s32); + return true; + } + if (value->type->size == 8) { + snprintf(buffer, bufferSize, "%"PRId64, value->value.s64); + return true; + } + return false; + case mSCRIPT_TYPE_UINT: + if (value->type->size <= 4) { + snprintf(buffer, bufferSize, "%"PRIu32, value->value.u32); + return true; + } + if (value->type->size == 8) { + snprintf(buffer, bufferSize, "%"PRIu64, value->value.u64); + return true; + } + return false; + case mSCRIPT_TYPE_STRING: + if (!mScriptCast(mSCRIPT_TYPE_MS_CHARP, value, &sval)) { + return false; + } + if (sval.value.opaque) { + snprintf(buffer, bufferSize, "\"%s\"", sval.value.opaque); + } else { + snprintf(buffer, bufferSize, "null"); + } + return true; + } + return false; +} + +void explainTable(struct mScriptValue* value, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + + struct TableIterator iter; + if (mScriptTableIteratorStart(value, &iter)) { + do { + char keyval[1024]; + struct mScriptValue* k = mScriptTableIteratorGetKey(value, &iter); + printval(k, keyval, sizeof(keyval)); + printf("%s- key: %s\n", indent, keyval); + struct mScriptValue* v = mScriptTableIteratorGetValue(value, &iter); + explainValue(v, level + 1); + } while (mScriptTableIteratorNext(value, &iter)); + } +} + +void explainClass(struct mScriptTypeClass* cls, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + + if (cls->parent) { + printf("%sparent: %s\n", indent, cls->parent->name); + } + + printf("%smembers:\n", indent); + const char* docstring = NULL; + const struct mScriptClassInitDetails* details; + size_t i; + for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { + details = &cls->details[i]; + switch (details->type) { + case mSCRIPT_CLASS_INIT_DOCSTRING: + docstring = details->info.comment; + break; + case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER: + printf("%s %s:\n", indent, details->info.member.name); + if (docstring) { + printf("%s comment: \"%s\"\n", indent, docstring); + docstring = NULL; + } + printf("%s type: %s\n", indent, details->info.member.type->name); + break; + } + } +} + +void explainObject(struct mScriptValue* value, int level) { + char indent[(level + 2) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + + struct mScriptTypeClass* cls = value->type->details.cls; + const struct mScriptClassInitDetails* details; + size_t i; + for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { + struct mScriptValue member; + details = &cls->details[i]; + + if (cls->details[i].type != mSCRIPT_CLASS_INIT_INSTANCE_MEMBER) { + continue; + } + printf("%s%s:\n", indent, details->info.member.name); + addType(details->info.member.type); + if (mScriptObjectGet(value, details->info.member.name, &member)) { + struct mScriptValue* unwrappedMember; + if (member.type->base == mSCRIPT_TYPE_WRAPPER) { + unwrappedMember = mScriptValueUnwrap(&member); + explainValue(unwrappedMember, level + 2); + } else { + explainValue(&member, level + 2); + } + } + } +} + +void explainValue(struct mScriptValue* value, int level) { + char valstring[1024]; + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + value = mScriptContextAccessWeakref(&context, value); + addType(value->type); + printf("%stype: %s\n", indent, value->type->name); + switch (value->type->base) { + case mSCRIPT_TYPE_TABLE: + printf("%svalue:\n", indent); + explainTable(value, level); + break; + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_STRING: + printval(value, valstring, sizeof(valstring)); + printf("%svalue: %s\n", indent, valstring); + break; + case mSCRIPT_TYPE_OBJECT: + printf("%svalue:\n", indent); + explainObject(value, level); + break; + default: + break; + } +} + +void explainTypeTuple(struct mScriptTypeTuple* tuple, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + printf("%svariable: %s\n", indent, tuple->variable ? "yes" : "no"); + printf("%slist:\n", indent); + size_t i; + for (i = 0; i < tuple->count; ++i) { + if (tuple->names[i]) { + printf("%s- name: %s\n", indent, tuple->names[i]); + printf("%s type: %s\n", indent, tuple->entries[i]->name); + } else { + printf("%s- type: %s\n", indent, tuple->entries[i]->name); + } + if (tuple->defaults && tuple->defaults[i].type) { + char defaultValue[128]; + printval(&tuple->defaults[i], defaultValue, sizeof(defaultValue)); + printf("%s default: %s\n", indent, defaultValue); + } + } +} + +void explainType(struct mScriptType* type, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + printf("%sbase: ", indent); + switch (type->base) { + case mSCRIPT_TYPE_SINT: + puts("sint"); + break; + case mSCRIPT_TYPE_UINT: + puts("uint"); + break; + case mSCRIPT_TYPE_FLOAT: + puts("float"); + break; + case mSCRIPT_TYPE_STRING: + puts("string"); + break; + case mSCRIPT_TYPE_FUNCTION: + puts("function"); + printf("%sparameters:\n", indent); + explainTypeTuple(&type->details.function.parameters, level + 1); + printf("%sreturn:\n", indent); + explainTypeTuple(&type->details.function.returnType, level + 1); + break; + case mSCRIPT_TYPE_OPAQUE: + puts("opaque"); + break; + case mSCRIPT_TYPE_OBJECT: + puts("object"); + explainClass(type->details.cls, level); + break; + case mSCRIPT_TYPE_LIST: + puts("list"); + break; + case mSCRIPT_TYPE_TABLE: + puts("table"); + break; + case mSCRIPT_TYPE_WRAPPER: + puts("wrapper"); + break; + case mSCRIPT_TYPE_WEAKREF: + puts("weakref"); + break; + case mSCRIPT_TYPE_VOID: + puts("void"); + break; + } +} + +void explainCore(struct mCore* core) { + mScriptContextAttachCore(&context, core); + struct mScriptValue* emu = mScriptContextGetGlobal(&context, "emu"); + explainValue(emu, 1); + mScriptContextDetachCore(&context); +} + +int main(int argc, char* argv[]) { + mScriptContextInit(&context); + mScriptContextAttachStdlib(&context); + mScriptContextSetTextBufferFactory(&context, NULL, NULL); + HashTableInit(&types, 0, NULL); + + addType(mSCRIPT_TYPE_MS_S8); + addType(mSCRIPT_TYPE_MS_U8); + addType(mSCRIPT_TYPE_MS_S16); + addType(mSCRIPT_TYPE_MS_U16); + addType(mSCRIPT_TYPE_MS_S32); + addType(mSCRIPT_TYPE_MS_U32); + addType(mSCRIPT_TYPE_MS_F32); + addType(mSCRIPT_TYPE_MS_S64); + addType(mSCRIPT_TYPE_MS_U64); + addType(mSCRIPT_TYPE_MS_F64); + addType(mSCRIPT_TYPE_MS_STR); + addType(mSCRIPT_TYPE_MS_CHARP); + addType(mSCRIPT_TYPE_MS_LIST); + addType(mSCRIPT_TYPE_MS_TABLE); + addType(mSCRIPT_TYPE_MS_WRAPPER); + + puts("version:"); + printf(" string: \"%s\"\n", projectVersion); + printf(" commit: \"%s\"\n", gitCommit); + puts("root:"); + struct TableIterator iter; + if (HashTableIteratorStart(&context.rootScope, &iter)) { + do { + const char* name = HashTableIteratorGetKey(&context.rootScope, &iter); + printf(" %s:\n", name); + struct mScriptValue* value = HashTableIteratorGetValue(&context.rootScope, &iter); + explainValue(value, 1); + } while (HashTableIteratorNext(&context.rootScope, &iter)); + } + puts("emu:"); + struct mCore* core; + core = mCoreCreate(mPLATFORM_GBA); + if (core) { + puts(" gba:"); + core->init(core); + explainCore(core); + core->deinit(core); + } + core = mCoreCreate(mPLATFORM_GB); + if (core) { + puts(" gb:"); + core->init(core); + explainCore(core); + core->deinit(core); + } + puts("types:"); + if (HashTableIteratorStart(&types, &iter)) { + do { + const char* name = HashTableIteratorGetKey(&types, &iter); + printf(" %s:\n", name); + struct mScriptType* type = HashTableIteratorGetValue(&types, &iter); + explainType(type, 1); + } while (HashTableIteratorNext(&types, &iter)); + } + + HashTableDeinit(&types); + mScriptContextDeinit(&context); + return 0; +} From 42efdc46eba30b0c65284e5ca906f79ae8404f2e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 May 2022 23:07:09 -0700 Subject: [PATCH 092/105] Scripting: Add class-level docstrings --- include/mgba/script/macros.h | 6 +++ include/mgba/script/types.h | 2 + src/core/scripting.c | 21 ++++++++- src/script/docgen.c | 89 ++++++++++++++++++++++++++++++++++-- src/script/stdlib.c | 7 ++- src/script/types.c | 5 ++ 6 files changed, 124 insertions(+), 6 deletions(-) diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 293c63f46..346dbf64f 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -164,6 +164,12 @@ CXX_GUARD_START .comment = DOCSTRING \ } \ }, +#define mSCRIPT_DEFINE_CLASS_DOCSTRING(DOCSTRING) { \ + .type = mSCRIPT_CLASS_INIT_CLASS_DOCSTRING, \ + .info = { \ + .comment = DOCSTRING \ + } \ +}, #define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \ .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index e0a369c46..1914bd1fe 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -125,6 +125,7 @@ enum mScriptTypeBase { enum mScriptClassInitType { mSCRIPT_CLASS_INIT_END = 0, + mSCRIPT_CLASS_INIT_CLASS_DOCSTRING, mSCRIPT_CLASS_INIT_DOCSTRING, mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, mSCRIPT_CLASS_INIT_INHERIT, @@ -218,6 +219,7 @@ struct mScriptTypeClass { bool init; const struct mScriptClassInitDetails* details; const struct mScriptType* parent; + const char* docstring; struct Table instanceMembers; struct Table castToMembers; struct mScriptClassMember* alloc; // TODO diff --git a/src/core/scripting.c b/src/core/scripting.c index b0c37d0e0..2b9ea3190 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -261,6 +261,10 @@ mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, size, mScriptMemoryDomai mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WRAPPER, name, mScriptMemoryDomainName, 0); mSCRIPT_DEFINE_STRUCT(mScriptMemoryDomain) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "An object used for access directly to a memory domain, e.g. the cartridge, " + "instead of through a whole address space, as with the functions directly on struct::mCore." + ) mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given offset") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read8) mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given offset") @@ -385,6 +389,9 @@ mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoad mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, screenshot, mCoreTakeScreenshot, 0); mSCRIPT_DEFINE_STRUCT(mCore) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "An instance of an emulator core." + ) mSCRIPT_DEFINE_DOCSTRING("Get which platform is being emulated") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, platform) mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") @@ -522,8 +529,13 @@ mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WRAPPER, _get, _mScriptCoreAda mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0); mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "A wrapper around a struct::mCore object that exposes more functionality. " + "It can be implicity cast to a Core object, and exposes the same methods. " + "Please see the documentation on struct::mCore for details on those methods." + ) mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(mScriptCoreAdapter, PS(mCore), _core, core) - mSCRIPT_DEFINE_DOCSTRING("A table containing a platform-specific set of memory adapters") + mSCRIPT_DEFINE_DOCSTRING("A table containing a platform-specific set of struct::mScriptMemoryDomain objects") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptCoreAdapter, TABLE, memory) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptCoreAdapter) mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptCoreAdapter) @@ -598,6 +610,9 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, error, mScriptConsoleError, 1 mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptConsole, S(mScriptTextBuffer), createBuffer, _mScriptConsoleCreateBuffer, 1, CHARP, name); mSCRIPT_DEFINE_STRUCT(mScriptConsole) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "A singleton object `console` that can be used for presenting textual information to the user via a console." + ) mSCRIPT_DEFINE_DOCSTRING("Print a log to the console") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, log) mSCRIPT_DEFINE_DOCSTRING("Print a warning to the console") @@ -645,6 +660,10 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, advance, 1, S32, adv); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setName, 1, CHARP, name); mSCRIPT_DEFINE_STRUCT(mScriptTextBuffer) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "An object that can be used to present texual data to the user. It is displayed monospaced, " + "and text can be edited after sending by moving the cursor or clearing the buffer." + ) mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(mScriptTextBuffer, deinit) mSCRIPT_DEFINE_DOCSTRING("Get the current x position of the cursor") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getX) diff --git a/src/script/docgen.c b/src/script/docgen.c index 2c832cd67..31d8363c8 100644 --- a/src/script/docgen.c +++ b/src/script/docgen.c @@ -59,6 +59,34 @@ void addTypesFromTable(struct Table* table) { } while(HashTableIteratorNext(table, &iter)); } +void printchomp(const char* string, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + + const char* start = string; + char lineBuffer[1024]; + while (true) { + const char* end = strchr(start, '\n'); + if (end) { + size_t size = end - start; + if (sizeof(lineBuffer) - 1 < size) { + size = sizeof(lineBuffer) - 1; + } + strncpy(lineBuffer, start, size); + lineBuffer[size] = '\0'; + printf("%s%s\n", indent, lineBuffer); + } else { + printf("%s%s\n", indent, start); + break; + } + start = end + 1; + if (!*end) { + break; + } + } +} + bool printval(const struct mScriptValue* value, char* buffer, size_t bufferSize) { struct mScriptValue sval; switch (value->type->base) { @@ -122,6 +150,14 @@ void explainClass(struct mScriptTypeClass* cls, int level) { if (cls->parent) { printf("%sparent: %s\n", indent, cls->parent->name); } + if (cls->docstring) { + if (strchr(cls->docstring, '\n')) { + printf("%scomment: |-\n", indent); + printchomp(cls->docstring, level + 1); + } else { + printf("%scomment: \"%s\"\n", indent, cls->docstring); + } + } printf("%smembers:\n", indent); const char* docstring = NULL; @@ -137,7 +173,7 @@ void explainClass(struct mScriptTypeClass* cls, int level) { printf("%s %s:\n", indent, details->info.member.name); if (docstring) { printf("%s comment: \"%s\"\n", indent, docstring); - docstring = NULL; + docstring = NULL; } printf("%s type: %s\n", indent, details->info.member.type->name); break; @@ -211,7 +247,7 @@ void explainTypeTuple(struct mScriptTypeTuple* tuple, int level) { size_t i; for (i = 0; i < tuple->count; ++i) { if (tuple->names[i]) { - printf("%s- name: %s\n", indent, tuple->names[i]); + printf("%s- name: %s\n", indent, tuple->names[i]); printf("%s type: %s\n", indent, tuple->entries[i]->name); } else { printf("%s- type: %s\n", indent, tuple->entries[i]->name); @@ -274,10 +310,57 @@ void explainType(struct mScriptType* type, int level) { } } +bool call(struct mScriptValue* obj, const char* method, struct mScriptFrame* frame) { + struct mScriptValue fn; + if (!mScriptObjectGet(obj, method, &fn)) { + return false; + } + struct mScriptValue* this = mScriptListAppend(&frame->arguments); + this->type = mSCRIPT_TYPE_MS_WRAPPER; + this->refs = mSCRIPT_VALUE_UNREF; + this->flags = 0; + this->value.opaque = obj; + return mScriptInvoke(&fn, frame); +} + void explainCore(struct mCore* core) { + struct mScriptValue wrapper; mScriptContextAttachCore(&context, core); struct mScriptValue* emu = mScriptContextGetGlobal(&context, "emu"); - explainValue(emu, 1); + addType(emu->type); + if (mScriptObjectGet(emu, "memory", &wrapper)) { + struct mScriptValue* memory = mScriptValueUnwrap(&wrapper); + struct TableIterator iter; + printf(" memory:\n"); + if (mScriptTableIteratorStart(memory, &iter)) { + do { + struct mScriptValue* name = mScriptTableIteratorGetKey(memory, &iter); + struct mScriptValue* value = mScriptTableIteratorGetValue(memory, &iter); + + printf(" %s:\n", name->value.string->buffer); + value = mScriptContextAccessWeakref(&context, value); + + struct mScriptFrame frame; + uint32_t baseVal; + struct mScriptValue* shortName; + + mScriptFrameInit(&frame); + call(value, "base", &frame); + mScriptPopU32(&frame.returnValues, &baseVal); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + call(value, "name", &frame); + shortName = mScriptValueUnwrap(mScriptListGetPointer(&frame.returnValues, 0)); + mScriptFrameDeinit(&frame); + + printf(" base: 0x%x\n", baseVal); + printf(" name: \"%s\"\n", shortName->value.string->buffer); + + mScriptValueDeref(shortName); + } while (mScriptTableIteratorNext(memory, &iter)); + } + } mScriptContextDetachCore(&context); } diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 1a739c160..cae385735 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -25,8 +25,11 @@ mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function); mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) -mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "A singleton object `callbacks` used for managing callbacks." + ) + mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add) mSCRIPT_DEFINE_END; void mScriptContextAttachStdlib(struct mScriptContext* context) { diff --git a/src/script/types.c b/src/script/types.c index 0c20b9257..b22ee1ad8 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -931,6 +931,11 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript switch (detail->type) { case mSCRIPT_CLASS_INIT_END: break; + case mSCRIPT_CLASS_INIT_CLASS_DOCSTRING: + if (!child) { + cls->docstring = detail->info.comment; + } + break; case mSCRIPT_CLASS_INIT_DOCSTRING: docstring = detail->info.comment; break; From 85b619dc78d255d5bd1ec9048f27ce84092d01ac Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 27 May 2022 15:05:14 -0700 Subject: [PATCH 093/105] Scripting: Add register information to docgen --- src/script/docgen.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/script/docgen.c b/src/script/docgen.c index 31d8363c8..8e1a3221c 100644 --- a/src/script/docgen.c +++ b/src/script/docgen.c @@ -325,13 +325,17 @@ bool call(struct mScriptValue* obj, const char* method, struct mScriptFrame* fra void explainCore(struct mCore* core) { struct mScriptValue wrapper; + size_t size; + size_t i; mScriptContextAttachCore(&context, core); + struct mScriptValue* emu = mScriptContextGetGlobal(&context, "emu"); addType(emu->type); + if (mScriptObjectGet(emu, "memory", &wrapper)) { struct mScriptValue* memory = mScriptValueUnwrap(&wrapper); struct TableIterator iter; - printf(" memory:\n"); + puts(" memory:"); if (mScriptTableIteratorStart(memory, &iter)) { do { struct mScriptValue* name = mScriptTableIteratorGetKey(memory, &iter); @@ -361,6 +365,27 @@ void explainCore(struct mCore* core) { } while (mScriptTableIteratorNext(memory, &iter)); } } + + const struct mCoreRegisterInfo* registers; + size = core->listRegisters(core, ®isters); + if (size) { + puts(" registers:"); + for (i = 0; i < size; ++i) { + if (strncmp(registers[i].name, "spsr", 4) == 0) { + // SPSR access is not implemented yet + continue; + } + printf(" - name: \"%s\"\n", registers[i].name); + if (registers[i].aliases && registers[i].aliases[0]) { + size_t alias; + puts(" aliases:"); + for (alias = 0; registers[i].aliases[alias]; ++alias) { + printf(" - \"%s\"\n", registers[i].aliases[alias]); + } + } + printf(" width: %u\n", registers[i].width); + } + } mScriptContextDetachCore(&context); } From ced8fb516c7bbf25c5206eac6f7fd1287991d749 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 24 May 2022 17:16:57 -0700 Subject: [PATCH 094/105] Scripting: Expose more key functionality --- src/core/scripting.c | 29 +++++++++++++++++++++++++---- src/script/stdlib.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 2b9ea3190..239a00c4a 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -325,6 +325,18 @@ static struct mScriptValue* _mScriptCoreChecksum(const struct mCore* core, int t return ret; } +static void _mScriptCoreAddKey(struct mCore* core, int32_t key) { + core->addKeys(core, 1 << key); +} + +static void _mScriptCoreClearKey(struct mCore* core, int32_t key) { + core->clearKeys(core, 1 << key); +} + +static int32_t _mScriptCoreGetKey(struct mCore* core, int32_t key) { + return (core->getKeys(core) >> key) & 1; +} + static struct mScriptValue* _mScriptCoreReadRange(struct mCore* core, uint32_t address, uint32_t length) { struct mScriptValue* value = mScriptStringCreateEmpty(length); char* buffer = value->value.string->buffer; @@ -366,6 +378,9 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, step, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, setKeys, 1, U32, keys); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, addKeys, 1, U32, keys); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, clearKeys, 1, U32, keys); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, addKey, _mScriptCoreAddKey, 1, S32, key); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, clearKey, _mScriptCoreClearKey, 1, S32, key); +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, S32, getKey, _mScriptCoreGetKey, 1, S32, key); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, getKeys, 0); // Memory functions @@ -413,13 +428,19 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Run a single instruction") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, step) - mSCRIPT_DEFINE_DOCSTRING("Set the currently active keys") + mSCRIPT_DEFINE_DOCSTRING("Set the currently active key list") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, setKeys) - mSCRIPT_DEFINE_DOCSTRING("Add keys to the currently active key list") + mSCRIPT_DEFINE_DOCSTRING("Add a single key to the currently active key list") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, addKey) + mSCRIPT_DEFINE_DOCSTRING("Add a bitmask of keys to the currently active key list") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, addKeys) - mSCRIPT_DEFINE_DOCSTRING("Remove keys from the currently active key list") + mSCRIPT_DEFINE_DOCSTRING("Remove a single key from the currently active key list") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, clearKey) + mSCRIPT_DEFINE_DOCSTRING("Remove a bitmask of keys from the currently active key list") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, clearKeys) - mSCRIPT_DEFINE_DOCSTRING("Get the currently active keys") + mSCRIPT_DEFINE_DOCSTRING("Get the active state of a given key") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getKey) + mSCRIPT_DEFINE_DOCSTRING("Get the currently active keys as a bitmask") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getKeys) mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given bus address") diff --git a/src/script/stdlib.c b/src/script/stdlib.c index cae385735..2af2567f7 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -8,6 +8,12 @@ #include #include #include +#ifdef M_CORE_GBA +#include +#endif +#ifdef M_CORE_GB +#include +#endif struct mScriptCallbackManager { struct mScriptContext* context; @@ -62,5 +68,33 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(mCHECKSUM, CRC32), mSCRIPT_CONSTANT_SENTINEL }); +#ifdef M_CORE_GBA + mScriptContextExportConstants(context, "GBA_KEY", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(GBA_KEY, A), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, B), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, SELECT), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, START), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, RIGHT), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, LEFT), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, UP), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, DOWN), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, R), + mSCRIPT_CONSTANT_PAIR(GBA_KEY, L), + mSCRIPT_CONSTANT_SENTINEL + }); +#endif +#ifdef M_CORE_GB + mScriptContextExportConstants(context, "GB_KEY", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(GB_KEY, A), + mSCRIPT_CONSTANT_PAIR(GB_KEY, B), + mSCRIPT_CONSTANT_PAIR(GB_KEY, SELECT), + mSCRIPT_CONSTANT_PAIR(GB_KEY, START), + mSCRIPT_CONSTANT_PAIR(GB_KEY, RIGHT), + mSCRIPT_CONSTANT_PAIR(GB_KEY, LEFT), + mSCRIPT_CONSTANT_PAIR(GB_KEY, UP), + mSCRIPT_CONSTANT_PAIR(GB_KEY, DOWN), + mSCRIPT_CONSTANT_SENTINEL + }); +#endif mScriptContextSetGlobal(context, "C", context->constants); } From fd202105ef800771b170a4143c93e90b109f74c0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 24 May 2022 18:53:56 -0700 Subject: [PATCH 095/105] Scripting: Improve docs --- src/core/scripting.c | 8 ++++---- src/script/stdlib.c | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 239a00c4a..e0109fb22 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -407,7 +407,7 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_CLASS_DOCSTRING( "An instance of an emulator core." ) - mSCRIPT_DEFINE_DOCSTRING("Get which platform is being emulated") + mSCRIPT_DEFINE_DOCSTRING("Get which platform is being emulated. See C.PLATFORM for possible values") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, platform) mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, currentFrame, frameCounter) @@ -463,9 +463,9 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Write the value of the register with the given name") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, writeRegister) - mSCRIPT_DEFINE_DOCSTRING("Save state to the slot number") + mSCRIPT_DEFINE_DOCSTRING("Save state to the slot number. See C.SAVESTATE for possible values for `flags`") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateSlot) - mSCRIPT_DEFINE_DOCSTRING("Load state from the slot number") + mSCRIPT_DEFINE_DOCSTRING("Load state from the slot number. See C.SAVESTATE for possible values for `flags`") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateSlot) mSCRIPT_DEFINE_DOCSTRING("Save a screenshot") @@ -632,7 +632,7 @@ mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptConsole, S(mScriptTextBuffer) mSCRIPT_DEFINE_STRUCT(mScriptConsole) mSCRIPT_DEFINE_CLASS_DOCSTRING( - "A singleton object `console` that can be used for presenting textual information to the user via a console." + "A global singleton object `console` that can be used for presenting textual information to the user via a console." ) mSCRIPT_DEFINE_DOCSTRING("Print a log to the console") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, log) diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 2af2567f7..35941f6d8 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -32,7 +32,17 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallback mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) mSCRIPT_DEFINE_CLASS_DOCSTRING( - "A singleton object `callbacks` used for managing callbacks." + "A global singleton object `callbacks` used for managing callbacks. The following callbacks are defined:\n\n" + "- **alarm**: An in-game alarm went off\n" + "- **crashed**: The emulation crashed\n" + "- **frame**: The emulation finished a frame\n" + "- **keysRead**: The emulation is about to read the key input\n" + "- **reset**: The emulation has been reset\n" + "- **savedataUpdated**: The emulation has just finished modifying save data\n" + "- **sleep**: The emulation has used the sleep feature to enter a low-power mode\n" + "- **shutdown**: The emulation has been powered off\n" + "- **start**: The emulation has started\n" + "- **stop**: The emulation has voluntarily shut down\n" ) mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add) From ccc7cd4d7f19e8f5a027aee6b4f2d3c2af5a26e2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 24 May 2022 21:42:10 -0700 Subject: [PATCH 096/105] Scripting: Expose more save state functionality --- src/core/scripting.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/core/scripting.c b/src/core/scripting.c index e0109fb22..0372b27fa 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -361,6 +361,25 @@ static void _mScriptCoreWriteRegister(struct mCore* core, const char* regName, i core->writeRegister(core, regName, &in); } +static struct mScriptValue* _mScriptCoreSaveState(struct mCore* core, int32_t flags) { + struct VFile* vf = VFileMemChunk(NULL, 0); + if (!mCoreSaveStateNamed(core, vf, flags)) { + vf->close(vf); + return NULL; + } + void* buffer = vf->map(vf, vf->size(vf), MAP_READ); + struct mScriptValue* value = mScriptStringCreateFromBytes(buffer, vf->size(vf)); + vf->close(vf); + return value; +} + +static int32_t _mScriptCoreLoadState(struct mCore* core, struct mScriptValue* buffer, int32_t flags) { + struct VFile* vf = VFileFromConstMemory(buffer->value.string->buffer, buffer->value.string->size); + int ret = mCoreLoadStateNamed(core, vf, flags); + vf->close(vf); + return ret; +} + // Info functions mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, platform, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); @@ -398,7 +417,9 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegist // Savestate functions mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, saveStateSlot, mCoreSaveState, 2, S32, slot, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, WRAPPER, saveStateBuffer, _mScriptCoreSaveState, 1, S32, flags); mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoadState, 2, S32, slot, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateBuffer, _mScriptCoreLoadState, 2, WRAPPER, buffer, S32, flags); // Miscellaneous functions mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, screenshot, mCoreTakeScreenshot, 0); @@ -465,8 +486,12 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Save state to the slot number. See C.SAVESTATE for possible values for `flags`") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateSlot) + mSCRIPT_DEFINE_DOCSTRING("Save state and return as a buffer. See C.SAVESTATE for possible values for `flags`") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateBuffer) mSCRIPT_DEFINE_DOCSTRING("Load state from the slot number. See C.SAVESTATE for possible values for `flags`") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateSlot) + mSCRIPT_DEFINE_DOCSTRING("Load state a buffer. See C.SAVESTATE for possible values for `flags`") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateBuffer) mSCRIPT_DEFINE_DOCSTRING("Save a screenshot") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshot) @@ -486,6 +511,15 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateSlot) mSCRIPT_MAKE_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA) mSCRIPT_DEFINE_DEFAULTS_END; +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateBuffer) + mSCRIPT_MAKE_S32(SAVESTATE_ALL) +mSCRIPT_DEFINE_DEFAULTS_END; + +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateBuffer) + mSCRIPT_NO_DEFAULT, + mSCRIPT_MAKE_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA) +mSCRIPT_DEFINE_DEFAULTS_END; + static void _clearMemoryMap(struct mScriptContext* context, struct mScriptCoreAdapter* adapter, bool clear) { struct TableIterator iter; if (mScriptTableIteratorStart(&adapter->memory, &iter)) { From df60b17639f7cf9db85536281a2c834f04adc0f4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 24 May 2022 21:55:12 -0700 Subject: [PATCH 097/105] Scripting: Expose core reset --- src/core/scripting.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/scripting.c b/src/core/scripting.c index 0372b27fa..900067edb 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -390,6 +390,7 @@ mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WRAPPER, getGameCode, _mScriptCoreGetGame mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(mCore, WRAPPER, checksum, _mScriptCoreChecksum, 1, S32, type); // Run functions +mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, reset, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, step, 0); @@ -444,6 +445,8 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Get internal product code for the game from the ROM header, if available") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getGameCode) + mSCRIPT_DEFINE_DOCSTRING("Reset the emulation. This does not invoke the **reset** callback") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, reset) mSCRIPT_DEFINE_DOCSTRING("Run until the next frame") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, runFrame) mSCRIPT_DEFINE_DOCSTRING("Run a single instruction") @@ -579,9 +582,15 @@ static struct mScriptValue* _mScriptCoreAdapterGet(struct mScriptCoreAdapter* ad return ret; } +static void _mScriptCoreAdapterReset(struct mScriptCoreAdapter* adapter) { + adapter->core->reset(adapter->core); + mScriptContextTriggerCallback(adapter->context, "reset"); +} + mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WRAPPER, _get, _mScriptCoreAdapterGet, 1, CHARP, name); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, reset, _mScriptCoreAdapterReset, 0); mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) mSCRIPT_DEFINE_CLASS_DOCSTRING( @@ -594,6 +603,8 @@ mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptCoreAdapter, TABLE, memory) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptCoreAdapter) mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptCoreAdapter) + mSCRIPT_DEFINE_DOCSTRING("Reset the emulation. As opposed to struct::mCore.reset, this version calls the **reset** callback") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, reset) mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, S(mCore), _core) mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core) mSCRIPT_DEFINE_END; From 997adecddc9d1fb9c20e170e0868ee4c5efa650b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 May 2022 18:40:33 -0700 Subject: [PATCH 098/105] Scripting: Add MRU --- src/platform/qt/ConfigController.cpp | 19 ++++++++++++---- src/platform/qt/ConfigController.h | 11 ++++++--- src/platform/qt/ScriptingView.cpp | 34 ++++++++++++++++++++++++++-- src/platform/qt/ScriptingView.h | 8 ++++++- src/platform/qt/ScriptingView.ui | 12 ++++++++++ src/platform/qt/Window.cpp | 2 +- src/platform/qt/Window.h | 2 +- 7 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 66c89b4d5..4fc4037e0 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -301,9 +301,9 @@ void ConfigController::setQtOption(const QString& key, const QVariant& value, co } } -QList ConfigController::getMRU() const { - QList mru; - m_settings->beginGroup("mru"); +QStringList ConfigController::getMRU(ConfigController::MRU mruType) const { + QStringList mru; + m_settings->beginGroup(mruName(mruType)); for (int i = 0; i < MRU_LIST_SIZE; ++i) { QString item = m_settings->value(QString::number(i)).toString(); if (item.isNull()) { @@ -315,9 +315,9 @@ QList ConfigController::getMRU() const { return mru; } -void ConfigController::setMRU(const QList& mru) { +void ConfigController::setMRU(const QStringList& mru, ConfigController::MRU mruType) { int i = 0; - m_settings->beginGroup("mru"); + m_settings->beginGroup(mruName(mruType)); for (const QString& item : mru) { m_settings->setValue(QString::number(i), item); ++i; @@ -331,6 +331,15 @@ void ConfigController::setMRU(const QList& mru) { m_settings->endGroup(); } +constexpr const char* ConfigController::mruName(ConfigController::MRU mru) { + switch (mru) { + case MRU::ROM: + return "mru"; + case MRU::Script: + return "recentScripts"; + } +} + void ConfigController::write() { mCoreConfigSave(&m_config); m_settings->sync(); diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 57fbda9c5..4b7f859fb 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -67,6 +67,11 @@ public: constexpr static const char* const PORT = "qt"; static const int MRU_LIST_SIZE = 10; + enum class MRU { + ROM, + Script + }; + ConfigController(QObject* parent = nullptr); ~ConfigController(); @@ -84,8 +89,8 @@ public: QVariant getArgvOption(const QString& key) const; QVariant takeArgvOption(const QString& key); - QList getMRU() const; - void setMRU(const QList& mru); + QStringList getMRU(MRU = MRU::ROM) const; + void setMRU(const QStringList& mru, MRU = MRU::ROM); Configuration* overrides() { return mCoreConfigGetOverrides(&m_config); } void saveOverride(const Override&); @@ -114,7 +119,7 @@ public slots: void write(); private: - void addArgvOption(const QString& key, const QVariant& value); + static constexpr const char* mruName(ConfigController::MRU); Configuration* defaults() { return &m_config.defaultsTable; } diff --git a/src/platform/qt/ScriptingView.cpp b/src/platform/qt/ScriptingView.cpp index 46857d219..834b1abed 100644 --- a/src/platform/qt/ScriptingView.cpp +++ b/src/platform/qt/ScriptingView.cpp @@ -6,13 +6,15 @@ #include "ScriptingView.h" #include "GBAApp.h" +#include "ConfigController.h" #include "ScriptingController.h" #include "ScriptingTextBuffer.h" using namespace QGBA; -ScriptingView::ScriptingView(ScriptingController* controller, QWidget* parent) +ScriptingView::ScriptingView(ScriptingController* controller, ConfigController* config, QWidget* parent) : QMainWindow(parent) + , m_config(config) , m_controller(controller) { m_ui.setupUi(this); @@ -30,6 +32,9 @@ ScriptingView::ScriptingView(ScriptingController* controller, QWidget* parent) connect(m_ui.buffers, &QListWidget::currentRowChanged, this, &ScriptingView::selectBuffer); connect(m_ui.load, &QAction::triggered, this, &ScriptingView::load); connect(m_ui.reset, &QAction::triggered, controller, &ScriptingController::reset); + + m_mruFiles = m_config->getMRU(ConfigController::MRU::Script); + updateMRU(); } void ScriptingView::submitRepl() { @@ -41,7 +46,10 @@ void ScriptingView::submitRepl() { void ScriptingView::load() { QString filename = GBAApp::app()->getOpenFileName(this, tr("Select script to load"), getFilters()); if (!filename.isEmpty()) { - m_controller->loadFile(filename); + if (!m_controller->loadFile(filename)) { + return; + } + appendMRU(filename); } } @@ -72,3 +80,25 @@ QString ScriptingView::getFilters() const { filters.append(tr("All files (*.*)")); return filters.join(";;"); } + +void ScriptingView::appendMRU(const QString& fname) { + int index = m_mruFiles.indexOf(fname); + if (index >= 0) { + m_mruFiles.removeAt(index); + } + m_mruFiles.prepend(fname); + while (m_mruFiles.size() > ConfigController::MRU_LIST_SIZE) { + m_mruFiles.removeLast(); + } + updateMRU(); +} + +void ScriptingView::updateMRU() { + m_config->setMRU(m_mruFiles, ConfigController::MRU::Script); + m_ui.mru->clear(); + for (const auto& fname : m_mruFiles) { + m_ui.mru->addAction(fname, [this, fname]() { + m_controller->loadFile(fname); + }); + } +} diff --git a/src/platform/qt/ScriptingView.h b/src/platform/qt/ScriptingView.h index 5f7668dd2..d6fec7847 100644 --- a/src/platform/qt/ScriptingView.h +++ b/src/platform/qt/ScriptingView.h @@ -9,6 +9,7 @@ namespace QGBA { +class ConfigController; class ScriptingController; class ScriptingTextBuffer; @@ -16,7 +17,7 @@ class ScriptingView : public QMainWindow { Q_OBJECT public: - ScriptingView(ScriptingController* controller, QWidget* parent = nullptr); + ScriptingView(ScriptingController* controller, ConfigController* config, QWidget* parent = nullptr); private slots: void submitRepl(); @@ -28,10 +29,15 @@ private slots: private: QString getFilters() const; + void appendMRU(const QString&); + void updateMRU(); + Ui::ScriptingView m_ui; + ConfigController* m_config; ScriptingController* m_controller; QList m_textBuffers; + QStringList m_mruFiles; }; } diff --git a/src/platform/qt/ScriptingView.ui b/src/platform/qt/ScriptingView.ui index 3b30ba9b2..61afa257f 100644 --- a/src/platform/qt/ScriptingView.ui +++ b/src/platform/qt/ScriptingView.ui @@ -79,7 +79,14 @@ File + + + Load recent script + + + + @@ -95,6 +102,11 @@ &Reset + + + 0 + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 128435831..c32662fb8 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -662,7 +662,7 @@ void Window::scriptingOpen() { m_scripting->setController(m_controller); } } - ScriptingView* view = new ScriptingView(m_scripting.get()); + ScriptingView* view = new ScriptingView(m_scripting.get(), m_config); openView(view); } #endif diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 667cf77ca..f129c44e5 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -222,7 +222,7 @@ private: QTimer m_fpsTimer; QTimer m_mustRestart; QTimer m_mustReset; - QList m_mruFiles; + QStringList m_mruFiles; ShortcutController* m_shortcutController; #if defined(BUILD_GL) || defined(BUILD_GLES2) std::unique_ptr m_shaderView; From edc2e1b7f58ca623cd9e58cd80b5c3619332f04b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 May 2022 22:23:33 -0700 Subject: [PATCH 099/105] Scripting: Pass back old buffers when reopening view --- src/platform/qt/ScriptingController.cpp | 5 +++++ src/platform/qt/ScriptingController.h | 2 ++ src/platform/qt/ScriptingView.cpp | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/src/platform/qt/ScriptingController.cpp b/src/platform/qt/ScriptingController.cpp index 2113df29f..033df225f 100644 --- a/src/platform/qt/ScriptingController.cpp +++ b/src/platform/qt/ScriptingController.cpp @@ -87,6 +87,10 @@ void ScriptingController::clearController() { void ScriptingController::reset() { CoreController::Interrupter interrupter(m_controller); + for (ScriptingTextBuffer* buffer : m_buffers) { + delete buffer; + } + m_buffers.clear(); mScriptContextDetachCore(&m_scriptContext); mScriptContextDeinit(&m_scriptContext); m_engines.clear(); @@ -105,6 +109,7 @@ void ScriptingController::runCode(const QString& code) { mScriptTextBuffer* ScriptingController::createTextBuffer(void* context) { ScriptingController* self = static_cast(context); ScriptingTextBuffer* buffer = new ScriptingTextBuffer(self); + self->m_buffers.append(buffer); emit self->textBufferCreated(buffer); return buffer->textBuffer(); } diff --git a/src/platform/qt/ScriptingController.h b/src/platform/qt/ScriptingController.h index 0ccd9f67d..0981fcab2 100644 --- a/src/platform/qt/ScriptingController.h +++ b/src/platform/qt/ScriptingController.h @@ -33,6 +33,7 @@ public: bool load(VFileDevice& vf, const QString& name); mScriptContext* context() { return &m_scriptContext; } + QList textBuffers() { return m_buffers; } signals: void log(const QString&); @@ -58,6 +59,7 @@ private: mScriptEngineContext* m_activeEngine = nullptr; QHash m_engines; + QList m_buffers; std::shared_ptr m_controller; }; diff --git a/src/platform/qt/ScriptingView.cpp b/src/platform/qt/ScriptingView.cpp index 834b1abed..1252f285c 100644 --- a/src/platform/qt/ScriptingView.cpp +++ b/src/platform/qt/ScriptingView.cpp @@ -35,6 +35,10 @@ ScriptingView::ScriptingView(ScriptingController* controller, ConfigController* m_mruFiles = m_config->getMRU(ConfigController::MRU::Script); updateMRU(); + + for (ScriptingTextBuffer* buffer : controller->textBuffers()) { + addTextBuffer(buffer); + } } void ScriptingView::submitRepl() { From 469ce5d1455583c6af141b552a63f9bb5996d2ef Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 May 2022 21:33:55 -0700 Subject: [PATCH 100/105] Update CHANGES and README --- CHANGES | 1 + README.md | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index faaf2c15d..9a64963b4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,6 @@ 0.10.0: (Future) Features: + - Preliminary Lua scripting support - Presets for Game Boy palettes - Add Super Game Boy palettes for original Game Boy games - Tool for converting scanned pictures of e-Reader cards to raw dotcode data diff --git a/README.md b/README.md index fcf61cae3..f027584a9 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Features - Solar sensor support for Boktai games. - Game Boy Camera and Game Boy Printer support. - A built-in BIOS implementation, and ability to load external BIOS files. +- Scripting support using Lua. - Turbo/fast-forward support by holding Tab. - Rewind by holding Backquote. - Frameskip, configurable up to 10. @@ -154,7 +155,7 @@ This will build and install mGBA into `/usr/bin` and `/usr/lib`. Dependencies th If you are on macOS, the steps are a little different. Assuming you are using the homebrew package manager, the recommended commands to obtain the dependencies and build are: - brew install cmake ffmpeg libzip qt5 sdl2 libedit pkg-config + brew install cmake ffmpeg libzip qt5 sdl2 libedit lua pkg-config mkdir build cd build cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` .. @@ -168,7 +169,7 @@ Note that you should not do a `make install` on macOS, as it will not work prope To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MSYS2 MinGW 32-bit") (or the 64-bit version "MSYS2 MinGW 64-bit" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 1100MiB of packages, so it will take a long time): - pacman -Sy --needed base-devel git ${MINGW_PACKAGE_PREFIX}-{cmake,ffmpeg,gcc,gdb,libelf,libepoxy,libzip,pkgconf,qt5,SDL2,ntldd-git} + pacman -Sy --needed base-devel git ${MINGW_PACKAGE_PREFIX}-{cmake,ffmpeg,gcc,gdb,libelf,libepoxy,libzip,lua,pkgconf,qt5,SDL2,ntldd-git} Check out the source code by running this command: @@ -187,7 +188,7 @@ Please note that this build of mGBA for Windows is not suitable for distribution To build using Visual Studio is a similarly complicated setup. To begin you will need to install [vcpkg](https://github.com/Microsoft/vcpkg). After installing vcpkg you will need to install several additional packages: - vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip sdl2 sqlite3 + vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip lua sdl2 sqlite3 Note that this installation won't support hardware accelerated video encoding on Nvidia hardware. If you care about this, you'll need to install CUDA beforehand, and then substitute `ffmpeg[vpx,x264,nvcodec]` into the previous command. @@ -225,6 +226,7 @@ mGBA has no hard dependencies, however, the following optional dependencies are - libzip or zlib: for loading ROMs stored in zip files. - SQLite3: for game databases. - libelf: for ELF loading. +- Lua: for scripting. SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first. From 6ebe735a7313ccc4911a97cca492286f1128435d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 27 May 2022 14:28:54 -0700 Subject: [PATCH 101/105] Scripting: Add filename parameter to emu:screenshot --- src/core/scripting.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 900067edb..a74beaf08 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -380,6 +380,19 @@ static int32_t _mScriptCoreLoadState(struct mCore* core, struct mScriptValue* bu return ret; } +static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename) { + if (filename) { + struct VFile* vf = VFileOpen(filename, O_WRONLY | O_CREAT | O_TRUNC); + if (!vf) { + return; + } + mCoreTakeScreenshotVF(core, vf); + vf->close(vf); + } else { + mCoreTakeScreenshot(core); + } +} + // Info functions mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, platform, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); @@ -423,7 +436,7 @@ mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoad mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateBuffer, _mScriptCoreLoadState, 2, WRAPPER, buffer, S32, flags); // Miscellaneous functions -mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, screenshot, mCoreTakeScreenshot, 0); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mCore, screenshot, _mScriptCoreTakeScreenshot, 1, CHARP, filename); mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_CLASS_DOCSTRING( @@ -523,6 +536,10 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateBuffer) mSCRIPT_MAKE_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA) mSCRIPT_DEFINE_DEFAULTS_END; +mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, screenshot) + mSCRIPT_MAKE_CHARP(NULL) +mSCRIPT_DEFINE_DEFAULTS_END; + static void _clearMemoryMap(struct mScriptContext* context, struct mScriptCoreAdapter* adapter, bool clear) { struct TableIterator iter; if (mScriptTableIteratorStart(&adapter->memory, &iter)) { From 2471648dd0a50993604c8db09c9ff37773b06798 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 27 May 2022 15:57:11 -0700 Subject: [PATCH 102/105] Scripting: Start bringing up composite wrapper types --- include/mgba/script/macros.h | 24 +++++++++++++++++++++++- include/mgba/script/types.h | 13 +++++++++++-- src/core/scripting.c | 24 ++++++++++++------------ src/script/engines/lua.c | 6 +++--- src/script/types.c | 27 ++++++++++++++++++--------- 5 files changed, 67 insertions(+), 27 deletions(-) diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 346dbf64f..b911e7251 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -16,7 +16,7 @@ CXX_GUARD_START struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ bool deref = true; \ if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ - if (_val->type == mSCRIPT_TYPE_MS_WRAPPER) { \ + if (_val->type->base == mSCRIPT_TYPE_WRAPPER) { \ _val = mScriptValueUnwrap(_val); \ deref = false; \ if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ @@ -73,6 +73,8 @@ CXX_GUARD_START #define mSCRIPT_PREFIX_8(PREFIX, T0, T1, T2, T3, T4, T5, T6, T7) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6, PREFIX ## T7 #define mSCRIPT_PREFIX_N(N) mSCRIPT_PREFIX_ ## N +#define _mSCRIPT_FIELD_NAME(V) (V)->name + #define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) #define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ @@ -154,6 +156,26 @@ CXX_GUARD_START .free = NULL, \ .cast = _mSTStructPtrCast_ ## STRUCT, \ }; \ + const struct mScriptType mSTWrapper_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_WRAPPER, \ + .details = { \ + .type = &mSTStruct_ ## STRUCT \ + }, \ + .size = sizeof(struct mScriptValue), \ + .name = "wrapper struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + }; \ + const struct mScriptType mSTWrapperConst_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_WRAPPER, \ + .details = { \ + .type = &mSTStructConst_ ## STRUCT \ + }, \ + .size = sizeof(struct mScriptValue), \ + .name = "wrapper const struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + }; \ static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ .init = false, \ .details = (const struct mScriptClassInitDetails[]) { diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 1914bd1fe..fd7dbe5d8 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -40,6 +40,9 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME #define mSCRIPT_TYPE_C_PS(X) void #define mSCRIPT_TYPE_C_PCS(X) void +#define mSCRIPT_TYPE_C_WSTR struct mScriptValue* +#define mSCRIPT_TYPE_C_W(X) struct mScriptValue* +#define mSCRIPT_TYPE_C_CW(X) const struct mScriptValue* #define mSCRIPT_TYPE_FIELD_S8 s32 #define mSCRIPT_TYPE_FIELD_U8 s32 @@ -63,6 +66,9 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) copaque #define mSCRIPT_TYPE_FIELD_PS(STRUCT) opaque #define mSCRIPT_TYPE_FIELD_PCS(STRUCT) copaque +#define mSCRIPT_TYPE_FIELD_WSTR opaque +#define mSCRIPT_TYPE_FIELD_W(TYPE) opaque +#define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque #define mSCRIPT_TYPE_MS_S8 (&mSTSInt8) #define mSCRIPT_TYPE_MS_U8 (&mSTUInt8) @@ -85,8 +91,9 @@ CXX_GUARD_START #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_FIELD_NAME(V) (V)->name +#define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper) +#define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE) +#define mSCRIPT_TYPE_MS_CW(TYPE) (&mSTWrapperConst_ ## TYPE) #define mSCRIPT_TYPE_CMP_GENERIC(TYPE0, TYPE1) (TYPE0 == TYPE1) #define mSCRIPT_TYPE_CMP_U8(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U8, TYPE) @@ -107,6 +114,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_CS(STRUCT) mSCRIPT_TYPE_MS_CS(STRUCT)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1) +#define mSCRIPT_TYPE_CMP_WSTR(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) || mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE)) enum mScriptTypeBase { mSCRIPT_TYPE_VOID = 0, @@ -158,6 +166,7 @@ extern const struct mScriptType mSTList; extern const struct mScriptType mSTTable; extern const struct mScriptType mSTWrapper; extern const struct mScriptType mSTWeakref; +extern const struct mScriptType mSTStringWrapper; struct mScriptType; struct mScriptValue { diff --git a/src/core/scripting.c b/src/core/scripting.c index a74beaf08..98a5f6662 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -250,7 +250,7 @@ mSCRIPT_DECLARE_STRUCT(mScriptMemoryDomain); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read8, mScriptMemoryDomainRead8, 1, U32, address); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read16, mScriptMemoryDomainRead16, 1, U32, address); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read32, mScriptMemoryDomainRead32, 1, U32, address); -mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WRAPPER, readRange, mScriptMemoryDomainReadRange, 2, U32, address, U32, length); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WSTR, readRange, mScriptMemoryDomainReadRange, 2, U32, address, U32, length); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write8, mScriptMemoryDomainWrite8, 2, U32, address, U8, value); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write16, mScriptMemoryDomainWrite16, 2, U32, address, U16, value); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write32, mScriptMemoryDomainWrite32, 2, U32, address, U32, value); @@ -258,7 +258,7 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write32, mScriptMemoryDo mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, base, mScriptMemoryDomainBase, 0); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, bound, mScriptMemoryDomainEnd, 0); mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, size, mScriptMemoryDomainSize, 0); -mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WRAPPER, name, mScriptMemoryDomainName, 0); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WSTR, name, mScriptMemoryDomainName, 0); mSCRIPT_DEFINE_STRUCT(mScriptMemoryDomain) mSCRIPT_DEFINE_CLASS_DOCSTRING( @@ -373,8 +373,8 @@ static struct mScriptValue* _mScriptCoreSaveState(struct mCore* core, int32_t fl return value; } -static int32_t _mScriptCoreLoadState(struct mCore* core, struct mScriptValue* buffer, int32_t flags) { - struct VFile* vf = VFileFromConstMemory(buffer->value.string->buffer, buffer->value.string->size); +static int32_t _mScriptCoreLoadState(struct mCore* core, struct mScriptString* buffer, int32_t flags) { + struct VFile* vf = VFileFromConstMemory(buffer->buffer, buffer->size); int ret = mCoreLoadStateNamed(core, vf, flags); vf->close(vf); return ret; @@ -398,9 +398,9 @@ mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, platform, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frameCycles, 0); mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0); -mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WRAPPER, getGameTitle, _mScriptCoreGetGameTitle, 0); -mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WRAPPER, getGameCode, _mScriptCoreGetGameCode, 0); -mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(mCore, WRAPPER, checksum, _mScriptCoreChecksum, 1, S32, type); +mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WSTR, getGameTitle, _mScriptCoreGetGameTitle, 0); +mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WSTR, getGameCode, _mScriptCoreGetGameCode, 0); +mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(mCore, WSTR, checksum, _mScriptCoreChecksum, 1, S32, type); // Run functions mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, reset, 0); @@ -420,20 +420,20 @@ mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, getKeys, 0); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead8, 1, U32, address); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead16, 1, U32, address); mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead32, 1, U32, address); -mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WRAPPER, readRange, _mScriptCoreReadRange, 2, U32, address, U32, length); +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WSTR, readRange, _mScriptCoreReadRange, 2, U32, address, U32, length); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite8, 2, U32, address, U8, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value); // Register functions -mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WRAPPER, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName); +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WSTR, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegister, 2, CHARP, regName, S32, value); // Savestate functions mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, saveStateSlot, mCoreSaveState, 2, S32, slot, S32, flags); -mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, WRAPPER, saveStateBuffer, _mScriptCoreSaveState, 1, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, WSTR, saveStateBuffer, _mScriptCoreSaveState, 1, S32, flags); mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoadState, 2, S32, slot, S32, flags); -mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateBuffer, _mScriptCoreLoadState, 2, WRAPPER, buffer, S32, flags); +mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateBuffer, _mScriptCoreLoadState, 2, STR, buffer, S32, flags); // Miscellaneous functions mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mCore, screenshot, _mScriptCoreTakeScreenshot, 1, CHARP, filename); @@ -605,7 +605,7 @@ static void _mScriptCoreAdapterReset(struct mScriptCoreAdapter* adapter) { } mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter); -mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WRAPPER, _get, _mScriptCoreAdapterGet, 1, CHARP, name); +mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, W(mCore), _get, _mScriptCoreAdapterGet, 1, CHARP, name); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, reset, _mScriptCoreAdapterReset, 0); diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index ac26345b4..5c8906531 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -270,7 +270,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } uint32_t weakref; bool needsWeakref = false; - if (value->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (value->type->base == mSCRIPT_TYPE_WRAPPER) { value = mScriptValueUnwrap(value); if (!value) { lua_pushnil(luaContext->lua); @@ -826,7 +826,7 @@ int _luaGetList(lua_State* lua) { lua_pop(lua, 2); obj = mScriptContextAccessWeakref(luaContext->d.context, obj); - if (obj->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (obj->type->base == mSCRIPT_TYPE_WRAPPER) { obj = mScriptValueUnwrap(obj); } if (!obj || obj->type != mSCRIPT_TYPE_MS_LIST) { @@ -859,7 +859,7 @@ static int _luaLenList(lua_State* lua) { lua_pop(lua, 1); obj = mScriptContextAccessWeakref(luaContext->d.context, obj); - if (obj->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (obj->type->base == mSCRIPT_TYPE_WRAPPER) { obj = mScriptValueUnwrap(obj); } if (!obj || obj->type != mSCRIPT_TYPE_MS_LIST) { diff --git a/src/script/types.c b/src/script/types.c index b22ee1ad8..4ab6653e1 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -212,6 +212,15 @@ const struct mScriptType mSTWrapper = { .hash = NULL, }; +const struct mScriptType mSTStringWrapper = { + .base = mSCRIPT_TYPE_WRAPPER, + .size = sizeof(struct mScriptValue), + .name = "wrapper string", + .alloc = NULL, + .free = NULL, + .hash = NULL, +}; + const struct mScriptType mSTWeakref = { .base = mSCRIPT_TYPE_WEAKREF, .size = sizeof(uint32_t), @@ -745,14 +754,14 @@ void mScriptValueWrap(struct mScriptValue* value, struct mScriptValue* out) { } struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* value) { - if (value->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (value->type->base == mSCRIPT_TYPE_WRAPPER) { return value->value.opaque; } return NULL; } const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* value) { - if (value->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (value->type->base == mSCRIPT_TYPE_WRAPPER) { return value->value.copaque; } return NULL; @@ -833,7 +842,7 @@ bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key) { } struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key) { - if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (table->type->base == mSCRIPT_TYPE_WRAPPER) { table = mScriptValueUnwrap(table); } if (table->type != mSCRIPT_TYPE_MS_TABLE) { @@ -861,7 +870,7 @@ size_t mScriptTableSize(struct mScriptValue* table) { } bool mScriptTableIteratorStart(struct mScriptValue* table, struct TableIterator* iter) { - if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (table->type->base == mSCRIPT_TYPE_WRAPPER) { table = mScriptValueUnwrap(table); } if (table->type != mSCRIPT_TYPE_MS_TABLE) { @@ -871,7 +880,7 @@ bool mScriptTableIteratorStart(struct mScriptValue* table, struct TableIterator* } bool mScriptTableIteratorNext(struct mScriptValue* table, struct TableIterator* iter) { - if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (table->type->base == mSCRIPT_TYPE_WRAPPER) { table = mScriptValueUnwrap(table); } if (table->type != mSCRIPT_TYPE_MS_TABLE) { @@ -881,7 +890,7 @@ bool mScriptTableIteratorNext(struct mScriptValue* table, struct TableIterator* } struct mScriptValue* mScriptTableIteratorGetKey(struct mScriptValue* table, struct TableIterator* iter) { - if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (table->type->base == mSCRIPT_TYPE_WRAPPER) { table = mScriptValueUnwrap(table); } if (table->type != mSCRIPT_TYPE_MS_TABLE) { @@ -891,7 +900,7 @@ struct mScriptValue* mScriptTableIteratorGetKey(struct mScriptValue* table, stru } struct mScriptValue* mScriptTableIteratorGetValue(struct mScriptValue* table, struct TableIterator* iter) { - if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (table->type->base == mSCRIPT_TYPE_WRAPPER) { table = mScriptValueUnwrap(table); } if (table->type != mSCRIPT_TYPE_MS_TABLE) { @@ -901,7 +910,7 @@ struct mScriptValue* mScriptTableIteratorGetValue(struct mScriptValue* table, st } bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator* iter, struct mScriptValue* key) { - if (table->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (table->type->base == mSCRIPT_TYPE_WRAPPER) { table = mScriptValueUnwrap(table); } if (table->type != mSCRIPT_TYPE_MS_TABLE) { @@ -1368,7 +1377,7 @@ bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList continue; } struct mScriptValue* unwrapped = NULL; - if (mScriptListGetPointer(frame, i)->type == mSCRIPT_TYPE_MS_WRAPPER) { + if (mScriptListGetPointer(frame, i)->type->base == mSCRIPT_TYPE_WRAPPER) { unwrapped = mScriptValueUnwrap(mScriptListGetPointer(frame, i)); if (types->entries[i] == unwrapped->type) { continue; From 8e617556e95434f927294309f1d7708fc003ac4d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 27 May 2022 15:57:11 -0700 Subject: [PATCH 103/105] Scripting: Get docgen to print to file, fix some warnings --- src/script/docgen.c | 155 ++++++++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 57 deletions(-) diff --git a/src/script/docgen.c b/src/script/docgen.c index 8e1a3221c..bc16bc9b7 100644 --- a/src/script/docgen.c +++ b/src/script/docgen.c @@ -10,6 +10,7 @@ struct mScriptContext context; struct Table types; +FILE* out; void explainValue(struct mScriptValue* value, int level); void explainType(struct mScriptType* type, int level); @@ -35,9 +36,20 @@ void addType(const struct mScriptType* type) { addTypesFromTable(&type->details.cls->instanceMembers); break; case mSCRIPT_TYPE_OPAQUE: + case mSCRIPT_TYPE_WRAPPER: if (type->details.type) { addType(type->details.type); } + case mSCRIPT_TYPE_VOID: + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + case mSCRIPT_TYPE_STRING: + case mSCRIPT_TYPE_LIST: + case mSCRIPT_TYPE_TABLE: + case mSCRIPT_TYPE_WEAKREF: + // No subtypes + break; } } @@ -75,9 +87,9 @@ void printchomp(const char* string, int level) { } strncpy(lineBuffer, start, size); lineBuffer[size] = '\0'; - printf("%s%s\n", indent, lineBuffer); + fprintf(out, "%s%s\n", indent, lineBuffer); } else { - printf("%s%s\n", indent, start); + fprintf(out, "%s%s\n", indent, start); break; } start = end + 1; @@ -110,16 +122,34 @@ bool printval(const struct mScriptValue* value, char* buffer, size_t bufferSize) return true; } return false; + case mSCRIPT_TYPE_FLOAT: + if (value->type->size <= 4) { + snprintf(buffer, bufferSize, "%g", value->value.f32); + return true; + } + if (value->type->size == 8) { + snprintf(buffer, bufferSize, "%g", value->value.f64); + return true; + } + return false; case mSCRIPT_TYPE_STRING: if (!mScriptCast(mSCRIPT_TYPE_MS_CHARP, value, &sval)) { return false; } - if (sval.value.opaque) { - snprintf(buffer, bufferSize, "\"%s\"", sval.value.opaque); + if (sval.value.copaque) { + snprintf(buffer, bufferSize, "\"%s\"", (const char*) sval.value.copaque); } else { snprintf(buffer, bufferSize, "null"); } return true; + case mSCRIPT_TYPE_VOID: + snprintf(buffer, bufferSize, "null"); + return true; + case mSCRIPT_TYPE_OPAQUE: + case mSCRIPT_TYPE_LIST: + case mSCRIPT_TYPE_TABLE: + // Not scalar or string values + return false; } return false; } @@ -135,7 +165,7 @@ void explainTable(struct mScriptValue* value, int level) { char keyval[1024]; struct mScriptValue* k = mScriptTableIteratorGetKey(value, &iter); printval(k, keyval, sizeof(keyval)); - printf("%s- key: %s\n", indent, keyval); + fprintf(out, "%s- key: %s\n", indent, keyval); struct mScriptValue* v = mScriptTableIteratorGetValue(value, &iter); explainValue(v, level + 1); } while (mScriptTableIteratorNext(value, &iter)); @@ -148,18 +178,18 @@ void explainClass(struct mScriptTypeClass* cls, int level) { indent[sizeof(indent) - 1] = '\0'; if (cls->parent) { - printf("%sparent: %s\n", indent, cls->parent->name); + fprintf(out, "%sparent: %s\n", indent, cls->parent->name); } if (cls->docstring) { if (strchr(cls->docstring, '\n')) { - printf("%scomment: |-\n", indent); + fprintf(out, "%scomment: |-\n", indent); printchomp(cls->docstring, level + 1); } else { - printf("%scomment: \"%s\"\n", indent, cls->docstring); + fprintf(out, "%scomment: \"%s\"\n", indent, cls->docstring); } } - printf("%smembers:\n", indent); + fprintf(out, "%smembers:\n", indent); const char* docstring = NULL; const struct mScriptClassInitDetails* details; size_t i; @@ -170,12 +200,14 @@ void explainClass(struct mScriptTypeClass* cls, int level) { docstring = details->info.comment; break; case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER: - printf("%s %s:\n", indent, details->info.member.name); + fprintf(out, "%s %s:\n", indent, details->info.member.name); if (docstring) { - printf("%s comment: \"%s\"\n", indent, docstring); + fprintf(out, "%s comment: \"%s\"\n", indent, docstring); docstring = NULL; } - printf("%s type: %s\n", indent, details->info.member.type->name); + fprintf(out, "%s type: %s\n", indent, details->info.member.type->name); + break; + case mSCRIPT_CLASS_INIT_END: break; } } @@ -196,7 +228,7 @@ void explainObject(struct mScriptValue* value, int level) { if (cls->details[i].type != mSCRIPT_CLASS_INIT_INSTANCE_MEMBER) { continue; } - printf("%s%s:\n", indent, details->info.member.name); + fprintf(out, "%s%s:\n", indent, details->info.member.name); addType(details->info.member.type); if (mScriptObjectGet(value, details->info.member.name, &member)) { struct mScriptValue* unwrappedMember; @@ -217,20 +249,20 @@ void explainValue(struct mScriptValue* value, int level) { indent[sizeof(indent) - 1] = '\0'; value = mScriptContextAccessWeakref(&context, value); addType(value->type); - printf("%stype: %s\n", indent, value->type->name); + fprintf(out, "%stype: %s\n", indent, value->type->name); switch (value->type->base) { case mSCRIPT_TYPE_TABLE: - printf("%svalue:\n", indent); + fprintf(out, "%svalue:\n", indent); explainTable(value, level); break; case mSCRIPT_TYPE_SINT: case mSCRIPT_TYPE_UINT: case mSCRIPT_TYPE_STRING: printval(value, valstring, sizeof(valstring)); - printf("%svalue: %s\n", indent, valstring); + fprintf(out, "%svalue: %s\n", indent, valstring); break; case mSCRIPT_TYPE_OBJECT: - printf("%svalue:\n", indent); + fprintf(out, "%svalue:\n", indent); explainObject(value, level); break; default: @@ -242,20 +274,20 @@ void explainTypeTuple(struct mScriptTypeTuple* tuple, int level) { char indent[(level + 1) * 2 + 1]; memset(indent, ' ', sizeof(indent) - 1); indent[sizeof(indent) - 1] = '\0'; - printf("%svariable: %s\n", indent, tuple->variable ? "yes" : "no"); - printf("%slist:\n", indent); + fprintf(out, "%svariable: %s\n", indent, tuple->variable ? "yes" : "no"); + fprintf(out, "%slist:\n", indent); size_t i; for (i = 0; i < tuple->count; ++i) { if (tuple->names[i]) { - printf("%s- name: %s\n", indent, tuple->names[i]); - printf("%s type: %s\n", indent, tuple->entries[i]->name); + fprintf(out, "%s- name: %s\n", indent, tuple->names[i]); + fprintf(out, "%s type: %s\n", indent, tuple->entries[i]->name); } else { - printf("%s- type: %s\n", indent, tuple->entries[i]->name); + fprintf(out, "%s- type: %s\n", indent, tuple->entries[i]->name); } if (tuple->defaults && tuple->defaults[i].type) { char defaultValue[128]; printval(&tuple->defaults[i], defaultValue, sizeof(defaultValue)); - printf("%s default: %s\n", indent, defaultValue); + fprintf(out, "%s default: %s\n", indent, defaultValue); } } } @@ -264,48 +296,48 @@ void explainType(struct mScriptType* type, int level) { char indent[(level + 1) * 2 + 1]; memset(indent, ' ', sizeof(indent) - 1); indent[sizeof(indent) - 1] = '\0'; - printf("%sbase: ", indent); + fprintf(out, "%sbase: ", indent); switch (type->base) { case mSCRIPT_TYPE_SINT: - puts("sint"); + fputs("sint\n", out); break; case mSCRIPT_TYPE_UINT: - puts("uint"); + fputs("uint\n", out); break; case mSCRIPT_TYPE_FLOAT: - puts("float"); + fputs("float\n", out); break; case mSCRIPT_TYPE_STRING: - puts("string"); + fputs("string\n", out); break; case mSCRIPT_TYPE_FUNCTION: - puts("function"); - printf("%sparameters:\n", indent); + fputs("function\n", out); + fprintf(out, "%sparameters:\n", indent); explainTypeTuple(&type->details.function.parameters, level + 1); - printf("%sreturn:\n", indent); + fprintf(out, "%sreturn:\n", indent); explainTypeTuple(&type->details.function.returnType, level + 1); break; case mSCRIPT_TYPE_OPAQUE: - puts("opaque"); + fputs("opaque\n", out); break; case mSCRIPT_TYPE_OBJECT: - puts("object"); + fputs("object\n", out); explainClass(type->details.cls, level); break; case mSCRIPT_TYPE_LIST: - puts("list"); + fputs("list\n", out); break; case mSCRIPT_TYPE_TABLE: - puts("table"); + fputs("table\n", out); break; case mSCRIPT_TYPE_WRAPPER: - puts("wrapper"); + fputs("wrapper\n", out); break; case mSCRIPT_TYPE_WEAKREF: - puts("weakref"); + fputs("weakref\n", out); break; case mSCRIPT_TYPE_VOID: - puts("void"); + fputs("void\n", out); break; } } @@ -335,13 +367,13 @@ void explainCore(struct mCore* core) { if (mScriptObjectGet(emu, "memory", &wrapper)) { struct mScriptValue* memory = mScriptValueUnwrap(&wrapper); struct TableIterator iter; - puts(" memory:"); + fputs(" memory:\n", out); if (mScriptTableIteratorStart(memory, &iter)) { do { struct mScriptValue* name = mScriptTableIteratorGetKey(memory, &iter); struct mScriptValue* value = mScriptTableIteratorGetValue(memory, &iter); - printf(" %s:\n", name->value.string->buffer); + fprintf(out, " %s:\n", name->value.string->buffer); value = mScriptContextAccessWeakref(&context, value); struct mScriptFrame frame; @@ -358,8 +390,8 @@ void explainCore(struct mCore* core) { shortName = mScriptValueUnwrap(mScriptListGetPointer(&frame.returnValues, 0)); mScriptFrameDeinit(&frame); - printf(" base: 0x%x\n", baseVal); - printf(" name: \"%s\"\n", shortName->value.string->buffer); + fprintf(out, " base: 0x%x\n", baseVal); + fprintf(out, " name: \"%s\"\n", shortName->value.string->buffer); mScriptValueDeref(shortName); } while (mScriptTableIteratorNext(memory, &iter)); @@ -369,27 +401,36 @@ void explainCore(struct mCore* core) { const struct mCoreRegisterInfo* registers; size = core->listRegisters(core, ®isters); if (size) { - puts(" registers:"); + fputs(" registers:\n", out); for (i = 0; i < size; ++i) { if (strncmp(registers[i].name, "spsr", 4) == 0) { // SPSR access is not implemented yet continue; } - printf(" - name: \"%s\"\n", registers[i].name); + fprintf(out, " - name: \"%s\"\n", registers[i].name); if (registers[i].aliases && registers[i].aliases[0]) { size_t alias; - puts(" aliases:"); + fputs(" aliases:\n", out); for (alias = 0; registers[i].aliases[alias]; ++alias) { - printf(" - \"%s\"\n", registers[i].aliases[alias]); + fprintf(out, " - \"%s\"\n", registers[i].aliases[alias]); } } - printf(" width: %u\n", registers[i].width); + fprintf(out, " width: %u\n", registers[i].width); } } mScriptContextDetachCore(&context); } int main(int argc, char* argv[]) { + out = stdout; + if (argc > 1) { + out = fopen(argv[1], "w"); + if (!out) { + perror("Couldn't open output"); + return 1; + } + } + mScriptContextInit(&context); mScriptContextAttachStdlib(&context); mScriptContextSetTextBufferFactory(&context, NULL, NULL); @@ -411,40 +452,40 @@ int main(int argc, char* argv[]) { addType(mSCRIPT_TYPE_MS_TABLE); addType(mSCRIPT_TYPE_MS_WRAPPER); - puts("version:"); - printf(" string: \"%s\"\n", projectVersion); - printf(" commit: \"%s\"\n", gitCommit); - puts("root:"); + fputs("version:\n", out); + fprintf(out, " string: \"%s\"\n", projectVersion); + fprintf(out, " commit: \"%s\"\n", gitCommit); + fputs("root:\n", out); struct TableIterator iter; if (HashTableIteratorStart(&context.rootScope, &iter)) { do { const char* name = HashTableIteratorGetKey(&context.rootScope, &iter); - printf(" %s:\n", name); + fprintf(out, " %s:\n", name); struct mScriptValue* value = HashTableIteratorGetValue(&context.rootScope, &iter); explainValue(value, 1); } while (HashTableIteratorNext(&context.rootScope, &iter)); } - puts("emu:"); + fputs("emu:\n", out); struct mCore* core; core = mCoreCreate(mPLATFORM_GBA); if (core) { - puts(" gba:"); + fputs(" gba:\n", out); core->init(core); explainCore(core); core->deinit(core); } core = mCoreCreate(mPLATFORM_GB); if (core) { - puts(" gb:"); + fputs(" gb:\n", out); core->init(core); explainCore(core); core->deinit(core); } - puts("types:"); + fputs("types:\n", out); if (HashTableIteratorStart(&types, &iter)) { do { const char* name = HashTableIteratorGetKey(&types, &iter); - printf(" %s:\n", name); + fprintf(out, " %s:\n", name); struct mScriptType* type = HashTableIteratorGetValue(&types, &iter); explainType(type, 1); } while (HashTableIteratorNext(&types, &iter)); From e93d780db75476b550c92323321532ff7703cdef Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 May 2022 02:25:56 -0700 Subject: [PATCH 104/105] Scripting: Fix indentation --- include/mgba/script/macros.h | 608 +++++++++++++++++------------------ 1 file changed, 304 insertions(+), 304 deletions(-) diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index b911e7251..1a82d85b4 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -11,27 +11,27 @@ CXX_GUARD_START #define mSCRIPT_POP(STACK, TYPE, NAME) \ - mSCRIPT_TYPE_C_ ## TYPE NAME; \ - do { \ - struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ - bool deref = true; \ - if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ - if (_val->type->base == mSCRIPT_TYPE_WRAPPER) { \ - _val = mScriptValueUnwrap(_val); \ - deref = false; \ - if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ - return false; \ - } \ - } else { \ - return false; \ - } \ - } \ - NAME = _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE; \ - if (deref) { \ - mScriptValueDeref(_val); \ - } \ - mScriptListResize(STACK, -1); \ - } while (0) + mSCRIPT_TYPE_C_ ## TYPE NAME; \ + do { \ + struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \ + bool deref = true; \ + if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ + if (_val->type->base == mSCRIPT_TYPE_WRAPPER) { \ + _val = mScriptValueUnwrap(_val); \ + deref = false; \ + if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \ + return false; \ + } \ + } else { \ + return false; \ + } \ + } \ + NAME = _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE; \ + if (deref) { \ + mScriptValueDeref(_val); \ + } \ + mScriptListResize(STACK, -1); \ + } while (0) #define mSCRIPT_POP_0(...) #define mSCRIPT_POP_1(FRAME, T0) mSCRIPT_POP(FRAME, T0, p0) @@ -44,13 +44,13 @@ CXX_GUARD_START #define mSCRIPT_POP_8(FRAME, T0, T1, T2, T3, T4, T5, T6, T7) mSCRIPT_POP(FRAME, T7, p7); mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) #define mSCRIPT_PUSH(STACK, TYPE, NAME) \ - do { \ - struct mScriptValue* _val = mScriptListAppend(STACK); \ - _val->type = mSCRIPT_TYPE_MS_ ## TYPE; \ - _val->refs = mSCRIPT_VALUE_UNREF; \ - _val->flags = 0; \ - _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE = NAME; \ - } while (0) + do { \ + struct mScriptValue* _val = mScriptListAppend(STACK); \ + _val->type = mSCRIPT_TYPE_MS_ ## TYPE; \ + _val->refs = mSCRIPT_VALUE_UNREF; \ + _val->flags = 0; \ + _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE = NAME; \ + } while (0) #define mSCRIPT_ARG_NAMES_0 #define mSCRIPT_ARG_NAMES_1 p0 @@ -77,283 +77,283 @@ CXX_GUARD_START #define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) #define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ - mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ - mSCRIPT_PUSH(&frame->returnValues, RETURN, out) + mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ + mSCRIPT_PUSH(&frame->returnValues, RETURN, out) #define mSCRIPT_DECLARE_STRUCT(STRUCT) \ - extern const struct mScriptType mSTStruct_ ## STRUCT; \ - extern const struct mScriptType mSTStructConst_ ## STRUCT; \ - extern const struct mScriptType mSTStructPtr_ ## STRUCT; \ - extern const struct mScriptType mSTStructPtrConst_ ## STRUCT; + extern const struct mScriptType mSTStruct_ ## STRUCT; \ + extern const struct mScriptType mSTStructConst_ ## STRUCT; \ + extern const struct mScriptType mSTStructPtr_ ## STRUCT; \ + extern const struct mScriptType mSTStructPtrConst_ ## STRUCT; #define mSCRIPT_DEFINE_STRUCT(STRUCT) \ - const struct mScriptType mSTStruct_ ## STRUCT; \ - const struct mScriptType mSTStructConst_ ## STRUCT; \ - const struct mScriptType mSTStructPtr_ ## STRUCT; \ - const struct mScriptType mSTStructPtrConst_ ## STRUCT; \ - static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT; \ - static bool _mSTStructPtrCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \ - if (input->type == type || (input->type->constType == type)) { \ - output->type = type; \ - output->value.opaque = input->value.opaque; \ - return true; \ - } \ - if (input->type != &mSTStructPtr_ ## STRUCT && input->type != &mSTStructPtrConst_ ## STRUCT) { \ - return false; \ - } \ - if (type == &mSTStructConst_ ## STRUCT || (!input->type->isConst && type == &mSTStruct_ ## STRUCT)) { \ - output->type = type; \ - output->value.opaque = *(void**) input->value.opaque; \ - return true; \ - } \ - return false; \ - } \ - const struct mScriptType mSTStruct_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_OBJECT, \ - .details = { \ - .cls = &_mSTStructDetails_ ## STRUCT \ - }, \ - .size = sizeof(struct STRUCT), \ - .name = "struct::" #STRUCT, \ - .alloc = NULL, \ - .free = mScriptObjectFree, \ - .cast = mScriptObjectCast, \ - .constType = &mSTStructConst_ ## STRUCT, \ - }; \ - const struct mScriptType mSTStructConst_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_OBJECT, \ - .isConst = true, \ - .details = { \ - .cls = &_mSTStructDetails_ ## STRUCT \ - }, \ - .size = sizeof(struct STRUCT), \ - .name = "const struct::" #STRUCT, \ - .alloc = NULL, \ - .free = NULL, \ - .cast = mScriptObjectCast, \ - }; \ - const struct mScriptType mSTStructPtr_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_OPAQUE, \ - .details = { \ - .type = &mSTStruct_ ## STRUCT \ - }, \ - .size = sizeof(void*), \ - .name = "ptr struct::" #STRUCT, \ - .alloc = NULL, \ - .free = NULL, \ - .cast = _mSTStructPtrCast_ ## STRUCT, \ - .constType = &mSTStructPtrConst_ ## STRUCT, \ - }; \ - const struct mScriptType mSTStructPtrConst_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_OPAQUE, \ - .details = { \ - .type = &mSTStructConst_ ## STRUCT \ - }, \ - .isConst = true, \ - .size = sizeof(void*), \ - .name = "ptr const struct::" #STRUCT, \ - .alloc = NULL, \ - .free = NULL, \ - .cast = _mSTStructPtrCast_ ## STRUCT, \ - }; \ - const struct mScriptType mSTWrapper_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_WRAPPER, \ - .details = { \ - .type = &mSTStruct_ ## STRUCT \ - }, \ - .size = sizeof(struct mScriptValue), \ - .name = "wrapper struct::" #STRUCT, \ - .alloc = NULL, \ - .free = NULL, \ - }; \ - const struct mScriptType mSTWrapperConst_ ## STRUCT = { \ - .base = mSCRIPT_TYPE_WRAPPER, \ - .details = { \ - .type = &mSTStructConst_ ## STRUCT \ - }, \ - .size = sizeof(struct mScriptValue), \ - .name = "wrapper const struct::" #STRUCT, \ - .alloc = NULL, \ - .free = NULL, \ - }; \ - static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ - .init = false, \ - .details = (const struct mScriptClassInitDetails[]) { + const struct mScriptType mSTStruct_ ## STRUCT; \ + const struct mScriptType mSTStructConst_ ## STRUCT; \ + const struct mScriptType mSTStructPtr_ ## STRUCT; \ + const struct mScriptType mSTStructPtrConst_ ## STRUCT; \ + static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT; \ + static bool _mSTStructPtrCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \ + if (input->type == type || (input->type->constType == type)) { \ + output->type = type; \ + output->value.opaque = input->value.opaque; \ + return true; \ + } \ + if (input->type != &mSTStructPtr_ ## STRUCT && input->type != &mSTStructPtrConst_ ## STRUCT) { \ + return false; \ + } \ + if (type == &mSTStructConst_ ## STRUCT || (!input->type->isConst && type == &mSTStruct_ ## STRUCT)) { \ + output->type = type; \ + output->value.opaque = *(void**) input->value.opaque; \ + return true; \ + } \ + return false; \ + } \ + const struct mScriptType mSTStruct_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OBJECT, \ + .details = { \ + .cls = &_mSTStructDetails_ ## STRUCT \ + }, \ + .size = sizeof(struct STRUCT), \ + .name = "struct::" #STRUCT, \ + .alloc = NULL, \ + .free = mScriptObjectFree, \ + .cast = mScriptObjectCast, \ + .constType = &mSTStructConst_ ## STRUCT, \ + }; \ + const struct mScriptType mSTStructConst_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OBJECT, \ + .isConst = true, \ + .details = { \ + .cls = &_mSTStructDetails_ ## STRUCT \ + }, \ + .size = sizeof(struct STRUCT), \ + .name = "const struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = mScriptObjectCast, \ + }; \ + const struct mScriptType mSTStructPtr_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OPAQUE, \ + .details = { \ + .type = &mSTStruct_ ## STRUCT \ + }, \ + .size = sizeof(void*), \ + .name = "ptr struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = _mSTStructPtrCast_ ## STRUCT, \ + .constType = &mSTStructPtrConst_ ## STRUCT, \ + }; \ + const struct mScriptType mSTStructPtrConst_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_OPAQUE, \ + .details = { \ + .type = &mSTStructConst_ ## STRUCT \ + }, \ + .isConst = true, \ + .size = sizeof(void*), \ + .name = "ptr const struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + .cast = _mSTStructPtrCast_ ## STRUCT, \ + }; \ + const struct mScriptType mSTWrapper_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_WRAPPER, \ + .details = { \ + .type = &mSTStruct_ ## STRUCT \ + }, \ + .size = sizeof(struct mScriptValue), \ + .name = "wrapper struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + }; \ + const struct mScriptType mSTWrapperConst_ ## STRUCT = { \ + .base = mSCRIPT_TYPE_WRAPPER, \ + .details = { \ + .type = &mSTStructConst_ ## STRUCT \ + }, \ + .size = sizeof(struct mScriptValue), \ + .name = "wrapper const struct::" #STRUCT, \ + .alloc = NULL, \ + .free = NULL, \ + }; \ + static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \ + .init = false, \ + .details = (const struct mScriptClassInitDetails[]) { #define mSCRIPT_DEFINE_DOCSTRING(DOCSTRING) { \ - .type = mSCRIPT_CLASS_INIT_DOCSTRING, \ - .info = { \ - .comment = DOCSTRING \ - } \ + .type = mSCRIPT_CLASS_INIT_DOCSTRING, \ + .info = { \ + .comment = DOCSTRING \ + } \ }, #define mSCRIPT_DEFINE_CLASS_DOCSTRING(DOCSTRING) { \ - .type = mSCRIPT_CLASS_INIT_CLASS_DOCSTRING, \ - .info = { \ - .comment = DOCSTRING \ - } \ + .type = mSCRIPT_CLASS_INIT_CLASS_DOCSTRING, \ + .info = { \ + .comment = DOCSTRING \ + } \ }, #define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \ - .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ - .info = { \ - .member = { \ - .name = #EXPORTED_NAME, \ - .type = mSCRIPT_TYPE_MS_ ## TYPE, \ - .offset = offsetof(struct STRUCT, NAME) \ - } \ - } \ + .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ + .info = { \ + .member = { \ + .name = #EXPORTED_NAME, \ + .type = mSCRIPT_TYPE_MS_ ## TYPE, \ + .offset = offsetof(struct STRUCT, NAME) \ + } \ + } \ }, #define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \ - mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME) + mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME) #define mSCRIPT_DEFINE_INHERIT(PARENT) { \ - .type = mSCRIPT_CLASS_INIT_INHERIT, \ - .info = { \ - .parent = mSCRIPT_TYPE_MS_S(PARENT) \ - } \ + .type = mSCRIPT_CLASS_INIT_INHERIT, \ + .info = { \ + .parent = mSCRIPT_TYPE_MS_S(PARENT) \ + } \ }, #define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \ - _mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ - return false; \ - } + _mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } #define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, DEFAULTS, ...) \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \ - static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \ - .call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \ - }; \ - \ - static void _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME(struct mScriptValue* val) { \ - val->value.copaque = &_mSTStructBindingFunction_ ## TYPE ## _ ## NAME; \ - }\ - static const struct mScriptType _mSTStructBindingType_ ## TYPE ## _ ## NAME = { \ - .base = mSCRIPT_TYPE_FUNCTION, \ - .name = "struct::" #TYPE "." #NAME, \ - .alloc = _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME, \ - .details = { \ - .function = { \ - .parameters = { \ - .count = _mSUCC_ ## NPARAMS, \ - .entries = { mSCRIPT_TYPE_MS_ ## S(TYPE), _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ - .names = { "this", _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ - .defaults = DEFAULTS, \ - }, \ - .returnType = { \ - .count = NRET, \ - .entries = { RETURN } \ - }, \ - }, \ - } \ - }; + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \ + static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \ + .call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \ + }; \ + \ + static void _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME(struct mScriptValue* val) { \ + val->value.copaque = &_mSTStructBindingFunction_ ## TYPE ## _ ## NAME; \ + }\ + static const struct mScriptType _mSTStructBindingType_ ## TYPE ## _ ## NAME = { \ + .base = mSCRIPT_TYPE_FUNCTION, \ + .name = "struct::" #TYPE "." #NAME, \ + .alloc = _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME, \ + .details = { \ + .function = { \ + .parameters = { \ + .count = _mSUCC_ ## NPARAMS, \ + .entries = { mSCRIPT_TYPE_MS_ ## S(TYPE), _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ + .names = { "this", _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ + .defaults = DEFAULTS, \ + }, \ + .returnType = { \ + .count = NRET, \ + .entries = { RETURN } \ + }, \ + }, \ + } \ + }; #define _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, RETURN, NAME, CONST, NPARAMS, ...) \ - typedef RETURN (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mCOMMA_ ## NPARAMS(CONST struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__))) + typedef RETURN (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mCOMMA_ ## NPARAMS(CONST struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__))) #define _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, T, NPARAMS, ...) \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \ - return true; \ - } + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \ + return true; \ + } #define _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, T, NPARAMS, ...) \ - static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ - return true; \ - } \ + static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \ + return true; \ + } \ #define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, NULL, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, NULL, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ - _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ - _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_CD_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_D_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_CD_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \ - mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAU(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) + mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAU(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX] = { \ - mSCRIPT_NO_DEFAULT, + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX] = { \ + mSCRIPT_NO_DEFAULT, #define mSCRIPT_DEFINE_DEFAULTS_END } #define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME) { \ - .type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \ - .info = { \ - .member = { \ - .name = #EXPORTED_NAME, \ - .type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME \ - } \ - }, \ + .type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \ + .info = { \ + .member = { \ + .name = #EXPORTED_NAME, \ + .type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME \ + } \ + }, \ }, #define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) \ - _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME) + _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME) #define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) @@ -365,81 +365,81 @@ CXX_GUARD_START #define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set) #define mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(TYPE, CAST_TYPE, MEMBER) { \ - .type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \ - .info = { \ - .castMember = { \ - .type = mSCRIPT_TYPE_MS_ ## CAST_TYPE, \ - .member = #MEMBER \ - } \ - }, \ + .type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \ + .info = { \ + .castMember = { \ + .type = mSCRIPT_TYPE_MS_ ## CAST_TYPE, \ + .member = #MEMBER \ + } \ + }, \ }, #define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } #define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ - static struct mScriptFunction _function_ ## NAME = { \ - .call = _binding_ ## NAME \ - }; \ - static void _alloc_ ## NAME(struct mScriptValue* val) { \ - val->value.copaque = &_function_ ## NAME; \ - } \ - static const struct mScriptType _type_ ## NAME = { \ - .base = mSCRIPT_TYPE_FUNCTION, \ - .name = "function::" #NAME, \ - .alloc = _alloc_ ## NAME, \ - .details = { \ - .function = { \ - .parameters = { \ - .count = NPARAMS, \ - .entries = { _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ - .names = { _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ - }, \ - .returnType = { \ - .count = NRET, \ - .entries = { RETURN } \ - }, \ - }, \ - } \ - }; \ - const struct mScriptValue NAME = { \ - .type = &_type_ ## NAME, \ - .refs = mSCRIPT_VALUE_UNREF, \ - .value = { \ - .copaque = &_function_ ## NAME \ - } \ - } + static struct mScriptFunction _function_ ## NAME = { \ + .call = _binding_ ## NAME \ + }; \ + static void _alloc_ ## NAME(struct mScriptValue* val) { \ + val->value.copaque = &_function_ ## NAME; \ + } \ + static const struct mScriptType _type_ ## NAME = { \ + .base = mSCRIPT_TYPE_FUNCTION, \ + .name = "function::" #NAME, \ + .alloc = _alloc_ ## NAME, \ + .details = { \ + .function = { \ + .parameters = { \ + .count = NPARAMS, \ + .entries = { _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \ + .names = { _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \ + }, \ + .returnType = { \ + .count = NRET, \ + .entries = { RETURN } \ + }, \ + }, \ + } \ + }; \ + const struct mScriptValue NAME = { \ + .type = &_type_ ## NAME, \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = { \ + .copaque = &_function_ ## NAME \ + } \ + } #define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \ - static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ - return false; \ - } \ - _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ - return true; \ - } \ - _mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) + static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } \ + _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ + return true; \ + } \ + _mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__) #define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \ - static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ - UNUSED(ctx); \ - _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ - return false; \ - } \ - _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ - return true; \ - } \ - _mSCRIPT_BIND_FUNCTION(NAME, 0, , NPARAMS, __VA_ARGS__) + static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ + UNUSED(ctx); \ + _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + if (mScriptListSize(&frame->arguments)) { \ + return false; \ + } \ + _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ + return true; \ + } \ + _mSCRIPT_BIND_FUNCTION(NAME, 0, , NPARAMS, __VA_ARGS__) #define mSCRIPT_MAKE(TYPE, VALUE) (struct mScriptValue) { \ - .type = (mSCRIPT_TYPE_MS_ ## TYPE), \ - .refs = mSCRIPT_VALUE_UNREF, \ - .value = { \ - .mSCRIPT_TYPE_FIELD_ ## TYPE = (VALUE) \ - }, \ - } \ + .type = (mSCRIPT_TYPE_MS_ ## TYPE), \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = { \ + .mSCRIPT_TYPE_FIELD_ ## TYPE = (VALUE) \ + }, \ + } \ #define mSCRIPT_MAKE_S8(VALUE) mSCRIPT_MAKE(S8, VALUE) #define mSCRIPT_MAKE_U8(VALUE) mSCRIPT_MAKE(U8, VALUE) @@ -456,9 +456,9 @@ CXX_GUARD_START #define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(CS(STRUCT), VALUE) #define mSCRIPT_NO_DEFAULT { \ - .type = NULL, \ - .refs = mSCRIPT_VALUE_UNREF, \ - .value = {0} \ + .type = NULL, \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = {0} \ } CXX_GUARD_END From bd205402768067911356f63b9a0e6affa3db3667 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 May 2022 02:19:57 -0700 Subject: [PATCH 105/105] Scripting: MSVC fixes --- include/mgba-util/macros.h | 35 ++++++++++++++-------------- include/mgba/script/macros.h | 44 +++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/include/mgba-util/macros.h b/include/mgba-util/macros.h index d9f208df9..1ab494709 100644 --- a/include/mgba-util/macros.h +++ b/include/mgba-util/macros.h @@ -8,7 +8,8 @@ #define _mCPP_CAT(A, B) A ## B -#define _mCALL(FN, ...) FN(__VA_ARGS__) +#define _mIDENT(...) __VA_ARGS__ +#define _mCALL(FN, ...) _mIDENT(FN(__VA_ARGS__)) #define _mCAT(A, B) _mCPP_CAT(A, B) #define _mSTRINGIFY(X, ...) #X @@ -36,25 +37,25 @@ #define _mEVEN_0(...) #define _mEVEN_1(A, B, ...) A -#define _mEVEN_2(A, B, ...) A, _mEVEN_1(__VA_ARGS__) -#define _mEVEN_3(A, B, ...) A, _mEVEN_2(__VA_ARGS__) -#define _mEVEN_4(A, B, ...) A, _mEVEN_3(__VA_ARGS__) -#define _mEVEN_5(A, B, ...) A, _mEVEN_4(__VA_ARGS__) -#define _mEVEN_6(A, B, ...) A, _mEVEN_5(__VA_ARGS__) -#define _mEVEN_7(A, B, ...) A, _mEVEN_6(__VA_ARGS__) -#define _mEVEN_8(A, B, ...) A, _mEVEN_7(__VA_ARGS__) -#define _mEVEN_9(A, B, ...) A, _mEVEN_7(__VA_ARGS__) +#define _mEVEN_2(A, B, ...) A, _mIDENT(_mEVEN_1(__VA_ARGS__)) +#define _mEVEN_3(A, B, ...) A, _mIDENT(_mEVEN_2(__VA_ARGS__)) +#define _mEVEN_4(A, B, ...) A, _mIDENT(_mEVEN_3(__VA_ARGS__)) +#define _mEVEN_5(A, B, ...) A, _mIDENT(_mEVEN_4(__VA_ARGS__)) +#define _mEVEN_6(A, B, ...) A, _mIDENT(_mEVEN_5(__VA_ARGS__)) +#define _mEVEN_7(A, B, ...) A, _mIDENT(_mEVEN_6(__VA_ARGS__)) +#define _mEVEN_8(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__)) +#define _mEVEN_9(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__)) #define _mODD_0(...) #define _mODD_1(A, B, ...) B -#define _mODD_2(A, B, ...) B, _mODD_1(__VA_ARGS__) -#define _mODD_3(A, B, ...) B, _mODD_2(__VA_ARGS__) -#define _mODD_4(A, B, ...) B, _mODD_3(__VA_ARGS__) -#define _mODD_5(A, B, ...) B, _mODD_4(__VA_ARGS__) -#define _mODD_6(A, B, ...) B, _mODD_5(__VA_ARGS__) -#define _mODD_7(A, B, ...) B, _mODD_6(__VA_ARGS__) -#define _mODD_8(A, B, ...) B, _mODD_7(__VA_ARGS__) -#define _mODD_9(A, B, ...) B, _mODD_7(__VA_ARGS__) +#define _mODD_2(A, B, ...) B, _mIDENT(_mODD_1(__VA_ARGS__)) +#define _mODD_3(A, B, ...) B, _mIDENT(_mODD_2(__VA_ARGS__)) +#define _mODD_4(A, B, ...) B, _mIDENT(_mODD_3(__VA_ARGS__)) +#define _mODD_5(A, B, ...) B, _mIDENT(_mODD_4(__VA_ARGS__)) +#define _mODD_6(A, B, ...) B, _mIDENT(_mODD_5(__VA_ARGS__)) +#define _mODD_7(A, B, ...) B, _mIDENT(_mODD_6(__VA_ARGS__)) +#define _mODD_8(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__)) +#define _mODD_9(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__)) #define _mSUCC_0 1 #define _mSUCC_1 2 diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 1a82d85b4..79b9ed611 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -275,7 +275,7 @@ CXX_GUARD_START #define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, 0, NPARAMS, NULL, __VA_ARGS__) \ _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ @@ -285,31 +285,31 @@ CXX_GUARD_START #define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, NULL, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, 0, NPARAMS, NULL, __VA_ARGS__) \ _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME), __VA_ARGS__) \ _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, 0, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME), __VA_ARGS__) \ _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_##RETURN, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME), __VA_ARGS__) \ _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX]; \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \ _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, , NPARAMS, _mSTStructBindingDefaults_ ## TYPE ## NAME, __VA_ARGS__) \ + _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, 0, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME, __VA_ARGS__) \ _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__) #define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ @@ -337,7 +337,7 @@ CXX_GUARD_START mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAU(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) #define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \ - static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## NAME[mSCRIPT_PARAMS_MAX] = { \ + static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX] = { \ mSCRIPT_NO_DEFAULT, #define mSCRIPT_DEFINE_DEFAULTS_END } @@ -441,6 +441,14 @@ CXX_GUARD_START }, \ } \ +#define mSCRIPT_VAL(TYPE, VALUE) { \ + .type = (mSCRIPT_TYPE_MS_ ## TYPE), \ + .refs = mSCRIPT_VALUE_UNREF, \ + .value = { \ + .mSCRIPT_TYPE_FIELD_ ## TYPE = (VALUE) \ + }, \ + } \ + #define mSCRIPT_MAKE_S8(VALUE) mSCRIPT_MAKE(S8, VALUE) #define mSCRIPT_MAKE_U8(VALUE) mSCRIPT_MAKE(U8, VALUE) #define mSCRIPT_MAKE_S16(VALUE) mSCRIPT_MAKE(S16, VALUE) @@ -455,6 +463,20 @@ CXX_GUARD_START #define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(S(STRUCT), VALUE) #define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(CS(STRUCT), VALUE) +#define mSCRIPT_S8(VALUE) mSCRIPT_VAL(S8, VALUE) +#define mSCRIPT_U8(VALUE) mSCRIPT_VAL(U8, VALUE) +#define mSCRIPT_S16(VALUE) mSCRIPT_VAL(S16, VALUE) +#define mSCRIPT_U16(VALUE) mSCRIPT_VAL(U16, VALUE) +#define mSCRIPT_S32(VALUE) mSCRIPT_VAL(S32, VALUE) +#define mSCRIPT_U32(VALUE) mSCRIPT_VAL(U32, VALUE) +#define mSCRIPT_F32(VALUE) mSCRIPT_VAL(F32, VALUE) +#define mSCRIPT_S64(VALUE) mSCRIPT_VAL(S64, VALUE) +#define mSCRIPT_U64(VALUE) mSCRIPT_VAL(U64, VALUE) +#define mSCRIPT_F64(VALUE) mSCRIPT_VAL(F64, VALUE) +#define mSCRIPT_CHARP(VALUE) mSCRIPT_VAL(CHARP, VALUE) +#define mSCRIPT_S(STRUCT, VALUE) mSCRIPT_VAL(S(STRUCT), VALUE) +#define mSCRIPT_CS(STRUCT, VALUE) mSCRIPT_VAL(CS(STRUCT), VALUE) + #define mSCRIPT_NO_DEFAULT { \ .type = NULL, \ .refs = mSCRIPT_VALUE_UNREF, \