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