mirror of https://github.com/mgba-emu/mgba.git
Scripting: Start working on mCore bridge
This commit is contained in:
parent
7226b7ee31
commit
ce3710323b
|
@ -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
|
* 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
|
* 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
|
#ifdef USE_DEBUGGERS
|
||||||
#include <mgba/debugger/debugger.h>
|
#include <mgba/debugger/debugger.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <mgba/script/types.h>
|
||||||
|
|
||||||
|
struct mCore;
|
||||||
|
mSCRIPT_DECLARE_STRUCT(mCore);
|
||||||
|
|
||||||
struct mScriptBridge;
|
struct mScriptBridge;
|
||||||
struct VFile;
|
struct VFile;
|
||||||
|
@ -47,6 +51,11 @@ bool mScriptBridgeLoadScript(struct mScriptBridge*, const char* name);
|
||||||
|
|
||||||
bool mScriptBridgeLookupSymbol(struct mScriptBridge*, const char* name, int32_t* out);
|
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
|
CXX_GUARD_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct mScriptFunction;
|
||||||
struct mScriptEngineContext;
|
struct mScriptEngineContext;
|
||||||
|
|
||||||
struct mScriptContext {
|
struct mScriptContext {
|
||||||
struct mScriptValue rootScope;
|
struct Table rootScope;
|
||||||
struct Table engines;
|
struct Table engines;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ struct mScriptEngineContext {
|
||||||
void mScriptContextInit(struct mScriptContext*);
|
void mScriptContextInit(struct mScriptContext*);
|
||||||
void mScriptContextDeinit(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);
|
void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key);
|
||||||
|
|
||||||
bool mScriptInvoke(const struct mScriptValue* fn, struct mScriptFrame* frame);
|
bool mScriptInvoke(const struct mScriptValue* fn, struct mScriptFrame* frame);
|
||||||
|
|
|
@ -580,6 +580,7 @@ void mScriptValueDeref(struct mScriptValue* val);
|
||||||
|
|
||||||
void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out);
|
void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out);
|
||||||
struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val);
|
struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val);
|
||||||
|
const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val);
|
||||||
|
|
||||||
struct mScriptValue* mScriptStringCreateFromUTF8(const char* string);
|
struct mScriptValue* mScriptStringCreateFromUTF8(const char* string);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ set(SOURCE_FILES
|
||||||
map-cache.c
|
map-cache.c
|
||||||
mem-search.c
|
mem-search.c
|
||||||
rewind.c
|
rewind.c
|
||||||
scripting.c
|
|
||||||
serialize.c
|
serialize.c
|
||||||
sync.c
|
sync.c
|
||||||
thread.c
|
thread.c
|
||||||
|
@ -24,6 +23,16 @@ set(SOURCE_FILES
|
||||||
set(TEST_FILES
|
set(TEST_FILES
|
||||||
test/core.c)
|
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" FILES ${SOURCE_FILES})
|
||||||
source_group("mCore tests" FILES ${TEST_FILES})
|
source_group("mCore tests" FILES ${TEST_FILES})
|
||||||
|
|
||||||
|
|
|
@ -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
|
* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <mgba/core/scripting.h>
|
#include <mgba/core/scripting.h>
|
||||||
|
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/script/context.h>
|
||||||
#include <mgba-util/table.h>
|
#include <mgba-util/table.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
|
@ -144,3 +146,33 @@ bool mScriptBridgeLookupSymbol(struct mScriptBridge* sb, const char* name, int32
|
||||||
HashTableEnumerate(&sb->engines, _seLookupSymbol, &info);
|
HashTableEnumerate(&sb->engines, _seLookupSymbol, &info);
|
||||||
return info.success;
|
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");
|
||||||
|
}
|
||||||
|
|
|
@ -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 <mgba/core/core.h>
|
||||||
|
#include <mgba/core/scripting.h>
|
||||||
|
#include <mgba/internal/script/lua.h>
|
||||||
|
#include <mgba/script/context.h>
|
||||||
|
#include <mgba/script/types.h>
|
||||||
|
|
||||||
|
#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),
|
||||||
|
)
|
|
@ -5,13 +5,64 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script/context.h>
|
||||||
|
|
||||||
|
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) {
|
void mScriptContextInit(struct mScriptContext* context) {
|
||||||
// TODO: rootScope
|
HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref);
|
||||||
HashTableInit(&context->engines, 0, NULL);
|
HashTableInit(&context->engines, 0, _engineContextDestroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mScriptContextDeinit(struct mScriptContext* context) {
|
void mScriptContextDeinit(struct mScriptContext* context) {
|
||||||
HashTableDeinit(&context->engines);
|
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) {
|
bool mScriptInvoke(const struct mScriptValue* val, struct mScriptFrame* frame) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ static void _luaDeref(struct mScriptValue*);
|
||||||
static int _luaThunk(lua_State* lua);
|
static int _luaThunk(lua_State* lua);
|
||||||
static int _luaGetObject(lua_State* lua);
|
static int _luaGetObject(lua_State* lua);
|
||||||
static int _luaSetObject(lua_State* lua);
|
static int _luaSetObject(lua_State* lua);
|
||||||
|
static int _luaGcObject(lua_State* lua);
|
||||||
|
|
||||||
#if LUA_VERSION_NUM < 503
|
#if LUA_VERSION_NUM < 503
|
||||||
#define lua_pushinteger lua_pushnumber
|
#define lua_pushinteger lua_pushnumber
|
||||||
|
@ -83,6 +84,7 @@ struct mScriptEngine2* const mSCRIPT_ENGINE_LUA = &_engineLua.d;
|
||||||
static const luaL_Reg _mSTStruct[] = {
|
static const luaL_Reg _mSTStruct[] = {
|
||||||
{ "__index", _luaGetObject },
|
{ "__index", _luaGetObject },
|
||||||
{ "__newindex", _luaSetObject },
|
{ "__newindex", _luaSetObject },
|
||||||
|
{ "__gc", _luaGcObject },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -483,3 +485,13 @@ int _luaSetObject(lua_State* lua) {
|
||||||
mScriptValueDeref(val);
|
mScriptValueDeref(val);
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -687,6 +687,13 @@ struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* value) {
|
||||||
return NULL;
|
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* mScriptStringCreateFromUTF8(const char* string) {
|
||||||
struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR);
|
struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR);
|
||||||
struct mScriptString* internal = val->value.opaque;
|
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) {
|
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) {
|
if (obj->type->base != mSCRIPT_TYPE_OBJECT) {
|
||||||
return false;
|
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) {
|
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)) {
|
if (type->cast && type->cast(input, type, output)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue