From 191b2bb49bf6221d359e2282eb1455502d735963 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Sun, 29 May 2022 18:10:49 +0530 Subject: [PATCH] path library api changes - relpath parameter order change for cosistency with python - normalize change to normpath --- src/core/core.c | 131 +++++++++++++++++++++++++++++++++++++------- src/core/core.h | 12 ++-- src/core/public.c | 5 +- src/core/value.c | 17 ++++++ src/core/value.h | 11 +++- src/core/vm.c | 14 ++++- src/libs/std_path.c | 16 +++--- 7 files changed, 168 insertions(+), 38 deletions(-) diff --git a/src/core/core.c b/src/core/core.c index cffaedf..8b92a8d 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -140,14 +140,32 @@ void initializeCore(PKVM* vm) { initializePrimitiveClasses(vm); } -void initializeScript(PKVM* vm, Module* module) { - ASSERT(module->path != NULL, OOPS); - ASSERT(module->path->data[0] != SPECIAL_NAME_CHAR, OOPS); +void initializeModule(PKVM* vm, Module* module, bool is_main) { + + String *path = module->path, *name = NULL; + + if (is_main) { + // TODO: consider static string "__main__" stored in PKVM. to reduce + // allocations everytime here. + name = newString(vm, "__main__"); + vmPushTempRef(vm, &name->_super); // _main. + } else { + ASSERT(module->name != NULL, OOPS); + name = module->name; + } + + ASSERT(name != NULL, OOPS); // A script's path will always the absolute normalized path (the path // resolving function would do take care of it) which is something that // was added after python 3.9. - moduleSetGlobal(vm, module, "__file__", 8, VAR_OBJ(module->path)); + if (path != NULL) { + moduleSetGlobal(vm, module, "__file__", 8, VAR_OBJ(path)); + } + + moduleSetGlobal(vm, module, "__name__", 8, VAR_OBJ(name)); + + if (is_main) vmPopTempRef(vm); // _main. } /*****************************************************************************/ @@ -233,6 +251,22 @@ static inline bool _callBinaryOpMethod(PKVM* vm, Var self, Var other, return true; } +/*****************************************************************************/ +/* REFLECTION AND HELPER FUNCTIONS */ +/*****************************************************************************/ + +// Add all the methods recursively to the lits used for generating a list of +// attributes for the 'dir()' function. +static void _collectMethods(PKVM* vm, List* list, Class* cls) { + if (cls == NULL) return; + + for (uint32_t i = 0; i < cls->methods.count; i++) { + listAppend(vm, list, + VAR_OBJ(newString(vm, cls->methods.data[i]->fn->name))); + } + _collectMethods(vm, list, cls->super_class); +} + /*****************************************************************************/ /* CORE BUILTIN FUNCTIONS */ /*****************************************************************************/ @@ -273,18 +307,6 @@ DEF(coreHelp, } } -// Add all the methods recursively to the lits used for generating a list of -// attributes for the 'dir()' function. -static void _collectMethods(PKVM* vm, List* list, Class* cls) { - if (cls == NULL) return; - - for (uint32_t i = 0; i < cls->methods.count; i++) { - listAppend(vm, list, - VAR_OBJ(newString(vm, cls->methods.data[i]->fn->name))); - } - _collectMethods(vm, list, cls->super_class); -} - DEF(coreDir, "dir(v:var) -> List[String]\n" "It'll return all the elements of the variable [v]. If [v] is a module " @@ -661,6 +683,7 @@ Module* newModuleInternal(PKVM* vm, const char* name) { module->initialized = true; vmPopTempRef(vm); // _name + initializeModule(vm, module, false); return module; } @@ -683,7 +706,7 @@ void moduleAddFunctionInternal(PKVM* vm, Module* module, // 'lang' library methods. DEF(stdLangGC, - "gc() -> num\n" + "lang.gc() -> num\n" "Trigger garbage collection and return the amount of bytes cleaned.") { size_t bytes_before = vm->bytes_allocated; @@ -693,7 +716,7 @@ DEF(stdLangGC, } DEF(stdLangDisas, - "disas(fn:Closure) -> String\n" + "lang.disas(fn:Closure) -> String\n" "Returns the disassembled opcode of the function [fn].") { // TODO: support dissasemble class constructors and module main body. @@ -707,9 +730,73 @@ DEF(stdLangDisas, dumpFunctionCode(vm, closure->fn); } +DEF(stdLangBackTrace, + "lang.backtrace() -> String\n" + "Returns the backtrace as a string, each line is formated as " + "';;\n'.") { + + // FIXME: + // All of the bellow code were copied from "debug.c" file, consider + // refactor the functionality in a way that it's possible to re use them. + + pkByteBuffer bb; + pkByteBufferInit(&bb); + + Fiber* fiber = vm->fiber; + ASSERT(fiber != NULL, OOPS); + + while (fiber) { + + for (int i = fiber->frame_count - 1; i >= 0; i--) { + CallFrame* frame = &fiber->frames[i]; + const Function* fn = frame->closure->fn; + + // After fetching the instruction the ip will be inceased so we're + // reducing it by 1. But stack overflows are occure before executing + // any instruction of that function, so the instruction_index possibly + // be -1 (set it to zero in that case). + int instruction_index = (int)(frame->ip - fn->fn->opcodes.data) - 1; + if (instruction_index == -1) instruction_index = 0; + int line = fn->fn->oplines.data[instruction_index]; + + // Note that path can be null. + const char* path = (fn->owner->path) ? fn->owner->path->data : ""; + const char* fn_name = (fn->name) ? fn->name : ""; + + pkByteBufferAddStringFmt(&bb, vm, "%s;%s;%i\n", fn_name, path, line); + } + + if (fiber->caller) fiber = fiber->caller; + else fiber = fiber->native; + } + + // bb.count not including the null byte and which is the length. + String* bt = newStringLength(vm, bb.data, bb.count); + vmPushTempRef(vm, &bt->_super); // bt. + pkByteBufferClear(&bb, vm); + vmPopTempRef(vm); // bt. + + RET(VAR_OBJ(bt)); +} + +DEF(stdLangModules, + "lang.modules() -> List\n" + "Returns the list of all registered modules.") { + + List* list = newList(vm, 8); + vmPushTempRef(vm, &list->_super); // list. + for (uint32_t i = 0; i < vm->modules->capacity; i++) { + if (!IS_UNDEF(vm->modules->entries[i].key)) { + listAppend(vm, list, vm->modules->entries[i].value); + } + } + vmPopTempRef(vm); // list. + RET(VAR_OBJ(list)); +} + #ifdef DEBUG DEF(stdLangDebugBreak, - "debug_break() -> null\n" + "lang.debug_break() -> null\n" "A debug function for development (will be removed).") { DEBUG_BREAK(); @@ -727,8 +814,10 @@ static void initializeCoreModules(PKVM* vm) { vmPopTempRef(vm) /* module */ NEW_MODULE(lang, "lang"); - MODULE_ADD_FN(lang, "gc", stdLangGC, 0); - MODULE_ADD_FN(lang, "disas", stdLangDisas, 1); + MODULE_ADD_FN(lang, "gc", stdLangGC, 0); + MODULE_ADD_FN(lang, "disas", stdLangDisas, 1); + MODULE_ADD_FN(lang, "backtrace", stdLangBackTrace, 0); + MODULE_ADD_FN(lang, "modules", stdLangModules, 0); #ifdef DEBUG MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0); #endif diff --git a/src/core/core.h b/src/core/core.h index 61eb876..ec89b08 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -45,10 +45,14 @@ // Initialize core language, builtin function and core libs. void initializeCore(PKVM* vm); -// Initialize a script module. At the moment it'll define __file__ global -// as an absolute path of the script. [path] should be the normalized absolute -// path of the script. -void initializeScript(PKVM* vm, Module* module); +// Initialize a module. If the script has path, it'll define __file__ global +// as an absolute path of the module. [path] will be the normalized absolute +// path of the module. If the module's path is NULL, it's name is used. +// +// Also define __name__ as the name of the module, assuming all the modules +// have name excpet for main which. for main the name will be defined as +// '__main__' just like python. +void initializeModule(PKVM* vm, Module* module, bool is_main); // Create a new module with the given [name] and returns as a Module*. // This is function is a wrapper around `newModule()` function to create diff --git a/src/core/public.c b/src/core/public.c index 51dbb9e..c969c10 100644 --- a/src/core/public.c +++ b/src/core/public.c @@ -378,7 +378,7 @@ PkResult pkRunFile(PKVM* vm, const char* path) { module->path = script_path; vmPopTempRef(vm); // script_path. - initializeScript(vm, module); + initializeModule(vm, module, true); const char* _path = module->path->data; char* source = vm->config.load_script_fn(vm, _path); @@ -460,7 +460,8 @@ PkResult pkRunREPL(PKVM* vm) { // The main module that'll be used to compile and execute the input source. PkHandle* module = pkNewModule(vm, "@(REPL)"); ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE), OOPS); - Module* _module = (Module*)AS_OBJ(module->value); + Module* _module = (Module*) AS_OBJ(module->value); + initializeModule(vm, _module, true); // A buffer to store multiple lines read from stdin. pkByteBuffer lines; diff --git a/src/core/value.c b/src/core/value.c index 0b5041d..372c76c 100644 --- a/src/core/value.c +++ b/src/core/value.c @@ -41,6 +41,22 @@ void pkByteBufferAddString(pkByteBuffer* self, PKVM* vm, const char* str, } } +void pkByteBufferAddStringFmt(pkByteBuffer* self, PKVM* vm, + const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + va_list copy; + va_copy(copy, args); + int length = vsnprintf(NULL, 0, fmt, copy); + va_end(copy); + + pkByteBufferReserve(self, vm, self->count + (size_t) length + 1); + vsnprintf(self->data + self->count, self->capacity - self->count, fmt, args); + self->count += length; + va_end(args); +} + void varInitObject(Object* self, PKVM* vm, ObjectType type) { self->type = type; self->is_marked = false; @@ -207,6 +223,7 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) { vm->bytes_allocated += sizeof(CallFrame) * fiber->frame_capacity; markObject(vm, &fiber->caller->_super); + markObject(vm, &fiber->native->_super); markObject(vm, &fiber->error->_super); markValue(vm, fiber->self); diff --git a/src/core/value.h b/src/core/value.h index 144e1ad..30f6540 100644 --- a/src/core/value.h +++ b/src/core/value.h @@ -210,6 +210,10 @@ DECLARE_BUFFER(Closure, Closure*) void pkByteBufferAddString(pkByteBuffer* self, PKVM* vm, const char* str, uint32_t length); +// Add formated string to the byte buffer. +void pkByteBufferAddStringFmt(pkByteBuffer* self, PKVM* vm, + const char* fmt, ...); + // Type enums of the pocketlang heap allocated types. typedef enum { OBJ_STRING = 0, @@ -484,8 +488,11 @@ struct Fiber { // and reset to VAR_UNDEFINED. Var self; - // Caller of this fiber if it has one, NULL otherwise. - Fiber* caller; + // [caller] is the caller of the fiber that was created by invoking the + // pocket concurency model. Where [native] is the native fiber which + // started this fiber. If a native function wants to call pocket function + // it needs to create a new fiber, so we keep track of both. + Fiber *caller, *native; // Runtime error initially NULL, heap allocated. String* error; diff --git a/src/core/vm.c b/src/core/vm.c index ee13975..7ec8e6c 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -319,6 +319,7 @@ PkResult vmCallMethod(PKVM* vm, Var self, Closure* fn, Fiber* fiber = newFiber(vm, fn); fiber->self = self; + fiber->native = vm->fiber; vmPushTempRef(vm, &fiber->_super); // fiber. bool success = vmPrepareFiber(vm, fiber, argc, argv); @@ -436,9 +437,20 @@ Var vmImportModule(PKVM* vm, String* from, String* path) { // Make a new module, compile and cache it. module = newModule(vm); module->path = resolved; + + // FIXME: + // __name__ will contain '/' instead of '.', To fix this I should use + // stringReplace with old = '/', new = '.' and the parameters should be + // string objects which should be allocated in VM statically for single + // char strings. + // + // path here is the imported symbol not the actual path, + // Example: "foo/bar" which was imported as "foo.bar" + module->name = path; + vmPushTempRef(vm, &module->_super); // module. { - initializeScript(vm, module); + initializeModule(vm, module, false); PkResult result = compile(vm, module, source, NULL); pkRealloc(vm, source, 0); if (result == PK_RESULT_SUCCESS) { diff --git a/src/libs/std_path.c b/src/libs/std_path.c index 2b47aaa..b9adc83 100644 --- a/src/libs/std_path.c +++ b/src/libs/std_path.c @@ -241,16 +241,16 @@ DEF(_pathAbspath, "") { } DEF(_pathRelpath, "") { - const char* from, * path; - if (!pkValidateSlotString(vm, 1, &from, NULL)) return; - if (!pkValidateSlotString(vm, 2, &path, NULL)) return; - - char abs_from[MAX_PATH_LEN]; - pathAbs(from, abs_from, sizeof(abs_from)); + const char* path, * from; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + if (!pkValidateSlotString(vm, 2, &from, NULL)) return; char abs_path[MAX_PATH_LEN]; pathAbs(path, abs_path, sizeof(abs_path)); + char abs_from[MAX_PATH_LEN]; + pathAbs(from, abs_from, sizeof(abs_from)); + char result[MAX_PATH_LEN]; uint32_t len = (uint32_t) cwk_path_get_relative(abs_from, abs_path, result, sizeof(result)); @@ -278,7 +278,7 @@ DEF(_pathJoin, "") { pkSetSlotStringLength(vm, 0, result, len); } -DEF(_pathNormalize, "") { +DEF(_pathNormpath, "") { const char* path; if (!pkValidateSlotString(vm, 1, &path, NULL)) return; @@ -386,7 +386,7 @@ void registerModulePath(PKVM* vm) { pkModuleAddFunction(vm, path, "abspath", _pathAbspath, 1); pkModuleAddFunction(vm, path, "relpath", _pathRelpath, 2); pkModuleAddFunction(vm, path, "join", _pathJoin, -1); - pkModuleAddFunction(vm, path, "normalize", _pathNormalize, 1); + pkModuleAddFunction(vm, path, "normpath", _pathNormpath, 1); pkModuleAddFunction(vm, path, "basename", _pathBaseName, 1); pkModuleAddFunction(vm, path, "dirname", _pathDirName, 1); pkModuleAddFunction(vm, path, "isabspath", _pathIsPathAbs, 1);