From 1a6fa25a3395c0024f3ada8d2b6d937dcfe9b058 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 28 Apr 2022 15:44:50 -0700 Subject: [PATCH] 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;