From 459da8ab5bac7ef84c99f5e5b5e5ba68f1e9fbc7 Mon Sep 17 00:00:00 2001 From: pikastech Date: Fri, 23 Sep 2022 14:10:37 +0800 Subject: [PATCH] support '__getattribute__()' support `__getattr__()` disable proxy for PIKA_NANO_ENABLE fix typo support `__setattr__()` proxy for cmodule is ok --- port/linux/.vscode/launch.json | 2 +- port/linux/.vscode/settings.json | 3 +- port/linux/package/pikascript/GTestTask.pyi | 5 + .../pikascript-lib/GTestTask/GTestTask.c | 14 +- port/linux/test/VM-test.cpp | 100 +++++++++ port/linux/test/compile-test.cpp | 18 ++ src/PikaObj.c | 43 +++- src/PikaObj.h | 9 +- src/PikaVM.c | 203 +++++++++++++++--- 9 files changed, 353 insertions(+), 44 deletions(-) diff --git a/port/linux/.vscode/launch.json b/port/linux/.vscode/launch.json index 0e4e33556..d7a4a0118 100644 --- a/port/linux/.vscode/launch.json +++ b/port/linux/.vscode/launch.json @@ -11,7 +11,7 @@ "program": "${workspaceFolder}/build/test/pikascript_test", // "program": "${workspaceFolder}/build/boot/demo06-pikamain/pikascript_demo06-pikamain", "args": [ - // "--gtest_filter=stddata.list_pop_" + // "--gtest_filter=vm.getattribute" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/port/linux/.vscode/settings.json b/port/linux/.vscode/settings.json index 1445cea93..60404c2c6 100644 --- a/port/linux/.vscode/settings.json +++ b/port/linux/.vscode/settings.json @@ -65,6 +65,7 @@ "agile_modbus.h": "c", "_modbus__modbusrtu.h": "c", "_modbus__modbustcp.h": "c", - "pikastddata_string.h": "c" + "pikastddata_string.h": "c", + "gtesttask_proxytest.h": "c" } } \ No newline at end of file diff --git a/port/linux/package/pikascript/GTestTask.pyi b/port/linux/package/pikascript/GTestTask.pyi index 03d9dc3d1..dcd520234 100644 --- a/port/linux/package/pikascript/GTestTask.pyi +++ b/port/linux/package/pikascript/GTestTask.pyi @@ -14,3 +14,8 @@ class Task(PikaStdTask.Task): def test(a: int, b: object): ... def test_dict() -> dict: ... def test64(a: int64, b: int64) -> int64: ... + + +class ProxyTest: + def __getattribute__(self, __name: str) -> any: ... + def __setattr__(self, __name: str, __value: any): ... diff --git a/port/linux/package/pikascript/pikascript-lib/GTestTask/GTestTask.c b/port/linux/package/pikascript/pikascript-lib/GTestTask/GTestTask.c index 3e4386d15..572367271 100644 --- a/port/linux/package/pikascript/pikascript-lib/GTestTask/GTestTask.c +++ b/port/linux/package/pikascript/pikascript-lib/GTestTask/GTestTask.c @@ -1,4 +1,5 @@ #include "GTestTask.h" +#include "GTestTask_ProxyTest.h" #include "GTestTask_Task.h" #include "PikaStdData_Dict.h" @@ -38,6 +39,17 @@ PikaObj* GTestTask_test_dict(PikaObj* self) { return dict; } -int64_t GTestTask_test64(PikaObj *self, int64_t a, int64_t b){ +int64_t GTestTask_test64(PikaObj* self, int64_t a, int64_t b) { return a * b; } + +Arg* GTestTask_ProxyTest___getattribute__(PikaObj* self, char* __name) { + return arg_newStr(__name); +} + +void GTestTask_ProxyTest___setattr__(PikaObj* self, + char* __name, + Arg* __value) { + __platform_printf("GTestTask_ProxyTest___setattr__: %s, %s\r\n", __name, + arg_getStr(__value)); +} diff --git a/port/linux/test/VM-test.cpp b/port/linux/test/VM-test.cpp index 0045b2ee3..42bf21440 100644 --- a/port/linux/test/VM-test.cpp +++ b/port/linux/test/VM-test.cpp @@ -1781,4 +1781,104 @@ TEST(vm, call_dict_err) { obj_deinit(pikaMain); EXPECT_EQ(pikaMemNow(), 0); } + +TEST(vm, getattribute) { + /* init */ + pikaMemInfo.heapUsedMax = 0; + PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain); + extern unsigned char pikaModules_py_a[]; + obj_linkLibrary(pikaMain, pikaModules_py_a); + /* run */ + __platform_printf("BEGIN\r\n"); + obj_run(pikaMain, + "class test:\n" + " def __getattribute__(self, name):\n" + " return name\n" + "t = test()\n" + "t.a\n"); + /* collect */ + /* assert */ + EXPECT_STREQ(log_buff[0], "'a'\r\n"); + /* deinit */ + obj_deinit(pikaMain); + EXPECT_EQ(pikaMemNow(), 0); +} + +TEST(vm, getattr) { + /* init */ + pikaMemInfo.heapUsedMax = 0; + PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain); + extern unsigned char pikaModules_py_a[]; + obj_linkLibrary(pikaMain, pikaModules_py_a); + /* run */ + __platform_printf("BEGIN\r\n"); + obj_run(pikaMain, + "class test:\n" + " a = 1\n" + " def __getattr__(self, name):\n" + " return name\n" + "t = test()\n" + "t.a\n" + "t.b\n"); + /* collect */ + /* assert */ + EXPECT_STREQ(log_buff[0], "'b'\r\n"); + EXPECT_STREQ(log_buff[1], "1\r\n"); + EXPECT_STREQ(log_buff[2], "BEGIN\r\n"); + /* deinit */ + obj_deinit(pikaMain); + EXPECT_EQ(pikaMemNow(), 0); +} + +TEST(vm, setattr) { + /* init */ + pikaMemInfo.heapUsedMax = 0; + PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain); + extern unsigned char pikaModules_py_a[]; + obj_linkLibrary(pikaMain, pikaModules_py_a); + /* run */ + __platform_printf("BEGIN\r\n"); + obj_run(pikaMain, + "class test:\n" + " a = 1\n" + " def __setattr__(self, name, val):\n" + " print((name, val))\n" + "t = test()\n" + "t.a = 1\n" + "t.b = 'test'\n"); + /* collect */ + /* assert */ + EXPECT_STREQ(log_buff[2], "BEGIN\r\n"); + EXPECT_STREQ(log_buff[1], "('a', 1)\r\n"); + EXPECT_STREQ(log_buff[0], "('b', 'test')\r\n"); + /* deinit */ + obj_deinit(pikaMain); + EXPECT_EQ(pikaMemNow(), 0); +} + +TEST(vm, c_module_get_set_attr) { + /* init */ + pikaMemInfo.heapUsedMax = 0; + PikaObj* pikaMain = newRootObj("pikaMain", New_PikaMain); + extern unsigned char pikaModules_py_a[]; + obj_linkLibrary(pikaMain, pikaModules_py_a); + /* run */ + __platform_printf("BEGIN\r\n"); + obj_run(pikaMain, + "t = GTestTask.ProxyTest()\n" + "t.a\n" + "t.b\n" + "t.a = 'test1'\n" + "t.b = 'test2'\n"); + /* collect */ + /* assert */ + EXPECT_STREQ(log_buff[3], "'a'\r\n"); + EXPECT_STREQ(log_buff[2], "'b'\r\n"); + EXPECT_STREQ(log_buff[1], "GTestTask_ProxyTest___setattr__: a, test1\r\n"); + EXPECT_STREQ(log_buff[0], "GTestTask_ProxyTest___setattr__: b, test2\r\n"); + /* deinit */ + obj_deinit(pikaMain); + EXPECT_EQ(pikaMemNow(), 0); +} + #endif diff --git a/port/linux/test/compile-test.cpp b/port/linux/test/compile-test.cpp index 176f932a9..becd734f3 100644 --- a/port/linux/test/compile-test.cpp +++ b/port/linux/test/compile-test.cpp @@ -622,3 +622,21 @@ TEST(compiler, __list) { Parser_linesToArray(lines); EXPECT_EQ(pikaMemNow(), 0); } + +TEST(compiler, getattr) { + char* lines = "@res = __getattribute__(@name)"; + Parser_linesToArray(lines); + EXPECT_EQ(pikaMemNow(), 0); +} + +TEST(compiler, getattr2) { + char* lines = "@res = __getattr__(@name)"; + Parser_linesToArray(lines); + EXPECT_EQ(pikaMemNow(), 0); +} + +TEST(compiler, setattr) { + char* lines = "__setattr__(@name, @value)"; + Parser_linesToArray(lines); + EXPECT_EQ(pikaMemNow(), 0); +} diff --git a/src/PikaObj.c b/src/PikaObj.c index 57ca859eb..ca02fd7c9 100644 --- a/src/PikaObj.c +++ b/src/PikaObj.c @@ -197,14 +197,15 @@ int64_t obj_getInt(PikaObj* self, char* argPath) { } Arg* obj_getArg(PikaObj* self, char* argPath) { - PIKA_BOOL isClass = PIKA_FALSE; - PikaObj* obj = obj_getHostObjWithIsTemp(self, argPath, &isClass); + PIKA_BOOL is_temp = PIKA_FALSE; + PikaObj* obj = obj_getHostObjWithIsTemp(self, argPath, &is_temp); if (NULL == obj) { return NULL; } + Arg* res = NULL; char* argName = strPointToLastToken(argPath, '.'); - Arg* res = args_getArg(obj->list, argName); - if (isClass) { + res = args_getArg(obj->list, argName); + if (is_temp) { obj_setArg(self, "_buf", res); res = obj_getArg(self, "_buf"); obj_deinit(obj); @@ -520,13 +521,13 @@ exit: } PikaObj* obj_getObj(PikaObj* self, char* objPath) { - PIKA_BOOL isClass = PIKA_FALSE; - return __obj_getObjWithKeepDeepth(self, objPath, &isClass, 0); + PIKA_BOOL is_temp = PIKA_FALSE; + return __obj_getObjWithKeepDeepth(self, objPath, &is_temp, 0); } PikaObj* obj_getHostObj(PikaObj* self, char* objPath) { - PIKA_BOOL isClass = PIKA_FALSE; - return __obj_getObjWithKeepDeepth(self, objPath, &isClass, 1); + PIKA_BOOL is_temp = PIKA_FALSE; + return __obj_getObjWithKeepDeepth(self, objPath, &is_temp, 1); } PikaObj* obj_getHostObjWithIsTemp(PikaObj* self, @@ -594,6 +595,30 @@ PikaObj* methodArg_getDefContext(Arg* method_arg) { return context; } +void _update_proxy(PikaObj* self, char* name) { +#if PIKA_NANO_ENABLE + return; +#endif + if (!(self->proxy & PIKA_PROXY_GETATTRIBUTE)) { + if (strEqu(name, "__getattribute__")) { + self->proxy |= PIKA_PROXY_GETATTRIBUTE; + return; + } + } + if (!(self->proxy & PIKA_PROXY_GETATTR)) { + if (strEqu(name, "__getattr__")) { + self->proxy |= PIKA_PROXY_GETATTR; + return; + } + } + if (!(self->proxy & PIKA_PROXY_SETATTR)) { + if (strEqu(name, "__setattr__")) { + self->proxy |= PIKA_PROXY_SETATTR; + return; + } + } +} + static void obj_saveMethodInfo(PikaObj* self, MethodInfo* method_info) { Args buffs = {0}; method_info->pars = method_info->dec; @@ -612,6 +637,7 @@ static void obj_saveMethodInfo(PikaObj* self, MethodInfo* method_info) { sizeof(method_info_def_context)); arg = arg_append(arg, method_info->pars, size_pars + 1); + _update_proxy(self, method_info->name); args_setArg(self->list, arg); strsDeinit(&buffs); } @@ -1001,6 +1027,7 @@ PikaObj* New_PikaObj(void) { self->list = New_args(NULL); self->refcnt = 0; self->constructor = NULL; + self->proxy = 0; return self; } diff --git a/src/PikaObj.h b/src/PikaObj.h index a852f55a3..a1d8609d2 100644 --- a/src/PikaObj.h +++ b/src/PikaObj.h @@ -71,8 +71,13 @@ struct PikaObj { Args* list; uint8_t refcnt; void* constructor; + uint8_t proxy; }; +#define PIKA_PROXY_GETATTRIBUTE 0x01 +#define PIKA_PROXY_GETATTR 0x02 +#define PIKA_PROXY_SETATTR 0x04 + typedef PikaObj* (*NewFun)(Args* args); typedef PikaObj* (*InitFun)(PikaObj* self, Args* args); typedef PikaObj VMParameters; @@ -128,8 +133,8 @@ int32_t obj_addOther(PikaObj* self, char* subObjectName, void* new_projcetFun); PikaObj* obj_getObj(PikaObj* self, char* objPath); PikaObj* obj_getHostObj(PikaObj* self, char* objPath); PikaObj* obj_getHostObjWithIsTemp(PikaObj* self, - char* objPath, - PIKA_BOOL* pIsClass); + char* objPath, + PIKA_BOOL* pIsClass); // subProcess int32_t obj_freeObj(PikaObj* self, char* subObjectName); diff --git a/src/PikaVM.c b/src/PikaVM.c index 22e8a5fe7..53ce86fa9 100644 --- a/src/PikaVM.c +++ b/src/PikaVM.c @@ -508,48 +508,147 @@ static Arg* VM_instruction_handler_NEW(PikaObj* self, return new_arg; } +static Arg* _proxy_getattribute(PikaObj* host, char* name) { +#if PIKA_NANO_ENABLE + return NULL; +#endif + if ('@' != name[0] && (host->proxy & PIKA_PROXY_GETATTRIBUTE)) { + args_setStr(host->list, "@name", name); + /* clang-format off */ + PIKA_PYTHON( + @res = __getattribute__(@name) + ) + /* clang-format on */ + const uint8_t bytes[] = { + 0x0c, 0x00, /* instruct array size */ + 0x10, 0x81, 0x01, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x04, 0x18, + 0x00, + /* instruct array */ + 0x1d, 0x00, /* const pool size */ + 0x00, 0x40, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x5f, 0x5f, 0x67, 0x65, + 0x74, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, + 0x5f, 0x00, 0x40, 0x72, 0x65, 0x73, 0x00, /* const pool */ + }; + pikaVM_runByteCode(host, (uint8_t*)bytes); + return args_getArg(host->list, "@res"); + } + return NULL; +} + +static Arg* _proxy_getattr(PikaObj* host, char* name) { +#if PIKA_NANO_ENABLE + return NULL; +#endif + if ('@' != name[0] && (host->proxy & PIKA_PROXY_GETATTR)) { + args_setStr(host->list, "@name", name); + /* clang-format off */ + PIKA_PYTHON( + @res = __getattr__(@name) + ) + /* clang-format on */ + const uint8_t bytes[] = { + 0x0c, 0x00, /* instruct array size */ + 0x10, 0x81, 0x01, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x04, 0x13, + 0x00, + /* instruct array */ + 0x18, 0x00, /* const pool size */ + 0x00, 0x40, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x5f, 0x5f, 0x67, 0x65, + 0x74, 0x61, 0x74, 0x74, 0x72, 0x5f, 0x5f, 0x00, 0x40, 0x72, 0x65, + 0x73, 0x00, /* const pool */ + }; + pikaVM_runByteCode(host, (uint8_t*)bytes); + return args_getArg(host->list, "@res"); + } + return NULL; +} + static Arg* VM_instruction_handler_REF(PikaObj* self, VMState* vm, char* data, Arg* arg_ret_reg) { - if (strEqu(data, (char*)"True")) { + PikaObj* host_object = NULL; + char* arg_path = data; + char* arg_name = strPointToLastToken(arg_path, '.'); + PIKA_BOOL is_temp = PIKA_FALSE; + if (strEqu(arg_path, (char*)"True")) { return arg_setInt(arg_ret_reg, "", 1); } - if (strEqu(data, (char*)"False")) { + if (strEqu(arg_path, (char*)"False")) { return arg_setInt(arg_ret_reg, "", 0); } - if (strEqu(data, (char*)"None")) { + if (strEqu(arg_path, (char*)"None")) { return arg_setNull(arg_ret_reg); } - if (strEqu(data, (char*)"RuntimeError")) { + if (strEqu(arg_path, (char*)"RuntimeError")) { return arg_setInt(arg_ret_reg, "", PIKA_RES_ERR_RUNTIME_ERROR); } - Arg* arg = NULL; - if (data[0] == '.') { + Arg* res = NULL; + if (arg_path[0] == '.') { /* find host from stack */ Arg* host_obj = stack_popArg_alloc(&(vm->stack)); if (argType_isObject(arg_getType(host_obj))) { - arg = arg_copy_noalloc(obj_getArg(arg_getPtr(host_obj), data + 1), + host_object = arg_getPtr(host_obj); + res = arg_copy_noalloc(obj_getArg(host_object, arg_path + 1), arg_ret_reg); } arg_deinit(host_obj); goto exit; } + /* host_object is self */ + if (NULL == host_object) { + if (!strIsContain(arg_path, '.')) { + host_object = vm->locals; + } + } + /* find in local list first */ - arg = arg_copy_noalloc(obj_getArg(vm->locals, data), arg_ret_reg); - if (NULL == arg) { - /* find in global list second */ - arg = arg_copy_noalloc(obj_getArg(vm->globals, data), arg_ret_reg); + if (NULL == host_object) { + host_object = obj_getHostObjWithIsTemp(vm->locals, arg_path, &is_temp); + } + + /* find in global list */ + if (NULL == host_object) { + host_object = obj_getHostObjWithIsTemp(vm->globals, arg_path, &is_temp); + } + + /* error cannot found host_object */ + if (NULL == host_object) { + goto exit; + } + + /* proxy */ + if (NULL == res) { + res = _proxy_getattribute(host_object, arg_name); + } + + /* find res in host */ + if (NULL == res) { + res = args_getArg(host_object->list, arg_name); + } + + /* find res in globlas */ + if (NULL == res) { + res = args_getArg(vm->globals->list, arg_name); + } + + /* proxy */ + if (NULL == res) { + res = _proxy_getattr(host_object, arg_name); } exit: - if (NULL == arg) { + if (NULL == res) { VMState_setErrorCode(vm, PIKA_RES_ERR_ARG_NO_FOUND); - __platform_printf("NameError: name '%s' is not defined\r\n", data); + __platform_printf("NameError: name '%s' is not defined\r\n", arg_path); + } else { + res = arg_copy_noalloc(res, arg_ret_reg); } - return arg; + if (is_temp) { + obj_deinit(host_object); + } + return res; } static Arg* VM_instruction_handler_GER(PikaObj* self, @@ -1303,33 +1402,65 @@ static Arg* VM_instruction_handler_BYT(PikaObj* self, return arg_newBytes((uint8_t*)data, strGetSize(data)); } +static PIKA_BOOL _proxy_setattr(PikaObj* self, char* name, Arg* arg) { +#if PIKA_NANO_ENABLE + return PIKA_FALSE; +#endif + if (self->proxy & PIKA_PROXY_SETATTR) { + obj_setStr(self, "@name", name); + obj_setArg(self, "@value", arg); + /* clang-format off */ + PIKA_PYTHON( + __setattr__(@name, @value) + ) + /* clang-format on */ + const uint8_t bytes[] = { + 0x0c, 0x00, /* instruct array size */ + 0x10, 0x81, 0x01, 0x00, 0x10, 0x01, 0x07, 0x00, 0x00, 0x02, 0x0e, + 0x00, + /* instruct array */ + 0x1a, 0x00, /* const pool size */ + 0x00, 0x40, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x40, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x00, 0x5f, 0x5f, 0x73, 0x65, 0x74, 0x61, 0x74, 0x74, + 0x72, 0x5f, 0x5f, 0x00, /* const pool */ + }; + pikaVM_runByteCode(self, (uint8_t*)bytes); + return PIKA_TRUE; + } + return PIKA_FALSE; +} + static Arg* VM_instruction_handler_OUT(PikaObj* self, VMState* vm, char* data, Arg* arg_ret_reg) { + char* arg_path = data; + char* arg_name = strPointToLastToken(arg_path, '.'); + PikaObj* host_obj = NULL; + PIKA_BOOL is_temp = PIKA_FALSE; arg_newReg(outArg_reg, PIKA_ARG_BUFF_SIZE); - Arg* outArg = stack_popArg(&vm->stack, &outArg_reg); + Arg* out_arg = stack_popArg(&vm->stack, &outArg_reg); // Arg* outArg = stack_popArg_alloc(&vm->stack); - ArgType outArg_type = arg_getType(outArg); + ArgType outArg_type = arg_getType(out_arg); if (VMState_getInvokeDeepthNow(vm) > 0) { /* in block, is a keyword arg */ - arg_setIsKeyword(outArg, PIKA_TRUE); - arg_setName(outArg, data); - return arg_copy_noalloc(outArg, arg_ret_reg); + arg_setIsKeyword(out_arg, PIKA_TRUE); + arg_setName(out_arg, arg_path); + return arg_copy_noalloc(out_arg, arg_ret_reg); } - if (_checkLReg(data)) { - uint8_t index = _getLRegIndex(data); + if (_checkLReg(arg_path)) { + uint8_t index = _getLRegIndex(arg_path); if (argType_isObject(outArg_type)) { - PikaObj* obj = arg_getPtr(outArg); + PikaObj* obj = arg_getPtr(out_arg); VMState_setLReg(vm, index, obj); - arg_deinit(outArg); + arg_deinit(out_arg); } return NULL; } - PikaObj* hostObj = vm->locals; + PikaObj* context = vm->locals; /* match global_list */ if (args_isArgExist(vm->locals->list, "__gl")) { char* global_list = args_getStr(vm->locals->list, "__gl"); @@ -1340,24 +1471,34 @@ static Arg* VM_instruction_handler_OUT(PikaObj* self, char token_buff[32] = {0}; for (int i = 0; i < strCountSign(global_list, ',') + 1; i++) { char* global_arg = strPopToken(token_buff, global_list_buff, ','); - /* matched global arg, hostObj set to global */ - if (strEqu(global_arg, data)) { - hostObj = vm->globals; + /* matched global arg, context set to global */ + if (strEqu(global_arg, arg_path)) { + context = vm->globals; } } arg_deinit(global_list_arg); } /* use RunAs object */ if (args_isArgExist(vm->locals->list, "__runAs")) { - hostObj = args_getPtr(vm->locals->list, "__runAs"); + context = args_getPtr(vm->locals->list, "__runAs"); } /* set free object to nomal object */ if (ARG_TYPE_OBJECT_NEW == outArg_type) { - arg_setType(outArg, ARG_TYPE_OBJECT); + arg_setType(out_arg, ARG_TYPE_OBJECT); } - /* ouput arg to locals */ - obj_setArg_noCopy(hostObj, data, outArg); + /* ouput arg to context */ + if (arg_path == arg_name) { + obj_setArg_noCopy(context, arg_path, out_arg); + return NULL; + } + + host_obj = obj_getHostObjWithIsTemp(context, arg_path, &is_temp); + if (_proxy_setattr(host_obj, arg_name, out_arg)) { + return NULL; + } + + obj_setArg_noCopy(context, arg_path, out_arg); return NULL; }