names buffer merged with constant pool (#202)

a buffer of classes for primitive types added to PKVM, but they'll
not be initialized in this commit.

names buffer and constant pool of a module are now merged (just like
java's constant pool).

VM's core libraries and scirpt modules are merged into a single
map name modules.

creating a new module doesn't register it automatically anymore,
you need to call pkRegisterModule(...) each time.

newModule() function refactored with a simpler interface, we're not
setting path or registering globals anymore, the caller is
responsible for that.
This commit is contained in:
Thakee Nathees 2022-04-17 06:47:27 +05:30 committed by GitHub
parent f980e91b60
commit c67572d552
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 280 additions and 218 deletions

View File

@ -23,6 +23,7 @@
/*****************************************************************************/
// Library : cwalk
// License : MIT
// Source : https://github.com/likle/cwalk/
// Doc : https://likle.github.io/cwalk/
// About : Path library for C/C++. Cross-Platform for Windows, MacOS and
@ -30,6 +31,19 @@
#include "modules/thirdparty/cwalk/cwalk.c"
// Library : argparse
// License : MIT
// Source : https://github.com/cofyc/argparse/
// About : Command-line arguments parsing library.
#include "thirdparty/argparse/argparse.c"
// Library : dlfcn-win32
// License : MIT
// Source : https://github.com/dlfcn-win32/dlfcn-win32/
// About : An implementation of dlfcn for Windows.
#ifdef _WIN32
// FIXME:
// This library redefine the malloc family macro, which cause a compile
// time warning.
//
// #include "modules/thirdparty/dlfcn-win32/dlfcn.c"
#endif

View File

@ -141,5 +141,6 @@ void registerModuleFile(PKVM* vm) {
pkModuleAddFunction(vm, file, "write", _fileWrite, 2);
pkModuleAddFunction(vm, file, "close", _fileClose, 1);
pkRegisterModule(vm, file);
pkReleaseHandle(vm, file);
}

View File

@ -233,5 +233,6 @@ void registerModulePath(PKVM* vm) {
pkModuleAddFunction(vm, path, "isfile", _pathIsFile, 1);
pkModuleAddFunction(vm, path, "isdir", _pathIsDir, 1);
pkRegisterModule(vm, path);
pkReleaseHandle(vm, path);
}

View File

@ -18,6 +18,7 @@ PK_API = "pk_api"
PK_API_TYPE = "PkNativeApi"
API_DEF = f'''\
static {PK_API_TYPE} {PK_API};
void pkInitApi({PK_API_TYPE}* api) {{%s
}}
'''

View File

@ -102,7 +102,8 @@ argparse_getvalue(struct argparse *self, const struct argparse_option *opt,
errno = 0;
if (self->optvalue) {
// -- pocketlang start --
// Not sure why but tcc cause an error "tcc: error: undefined symbol 'strtof'".
// tcc cause an error "tcc: error: undefined symbol 'strtof'" on Windows since
// it depend on the libm. Maybe I should add _WIN32 marcro along with it.
#if defined(__TINYC__)
*(float*)opt->value = (float)strtod(self->optvalue, (char**)&s);
#else

View File

@ -426,6 +426,10 @@ PK_PUBLIC PkHandle* pkNewMap(PKVM* vm);
// already existed, otherwise an assertion will fail to indicate that.
PK_PUBLIC PkHandle* pkNewModule(PKVM* vm, const char* name);
// Register the module to the PKVM's modules map, once after it can be
// imported in other modules.
PK_PUBLIC void pkRegisterModule(PKVM* vm, PkHandle* module);
// Create and return a new fiber around the function [fn].
PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn);

View File

@ -1931,8 +1931,9 @@ static void exprAttrib(Compiler* compiler) {
int length = compiler->parser.previous.length;
// Store the name in module's names buffer.
int index = moduleAddName(compiler->module, compiler->parser.vm,
name, length);
int index = 0;
moduleAddString(compiler->module, compiler->parser.vm,
name, length, &index);
if (compiler->l_value && matchAssignment(compiler)) {
TokenType assignment = compiler->parser.previous.type;
@ -2319,12 +2320,14 @@ static void compileClass(Compiler* compiler) {
const char* f_name = compiler->parser.previous.start;
int f_len = compiler->parser.previous.length;
uint32_t f_index = moduleAddName(compiler->module, compiler->parser.vm,
f_name, f_len);
int f_index = 0;
String* new_name = moduleAddString(compiler->module, compiler->parser.vm,
f_name, f_len, &f_index);
String* new_name = compiler->module->names.data[f_index];
for (uint32_t i = 0; i < cls->field_names.count; i++) {
String* prev = compiler->module->names.data[cls->field_names.data[i]];
String* prev = moduleGetStringAt(compiler->module,
cls->field_names.data[i]);
ASSERT(prev != NULL, OOPS);
if (IS_STR_EQ(new_name, prev)) {
parseError(compiler, "Class field with name '%s' already exists.",
new_name->data);
@ -2519,13 +2522,15 @@ static Module* importFile(Compiler* compiler, const char* path) {
}
// Create new string for the resolved path. And free the resolved path.
int index = (int)moduleAddName(compiler->module, compiler->parser.vm,
resolved.string, (uint32_t)strlen(resolved.string));
String* path_name = compiler->module->names.data[index];
int index = 0;
String* path_ = moduleAddString(compiler->module, compiler->parser.vm,
resolved.string,
(uint32_t)strlen(resolved.string),
&index);
if (resolved.on_done != NULL) resolved.on_done(vm, resolved);
// Check if the script already compiled and cached in the PKVM.
Var entry = mapGet(vm->modules, VAR_OBJ(path_name));
Var entry = mapGet(vm->modules, VAR_OBJ(path_));
if (!IS_UNDEF(entry)) {
ASSERT(IS_OBJ_TYPE(entry, OBJ_MODULE), OOPS);
@ -2544,17 +2549,29 @@ static Module* importFile(Compiler* compiler, const char* path) {
}
// Load the script at the path.
PkStringPtr source = vm->config.load_script_fn(vm, path_name->data);
PkStringPtr source = vm->config.load_script_fn(vm, path_->data);
if (source.string == NULL) {
parseError(compiler, "Error loading script at \"%s\"", path_name->data);
parseError(compiler, "Error loading script at \"%s\"", path_->data);
return NULL;
}
// Make a new module and to compile it.
Module* module = newModule(vm, path_name, false);
vmPushTempRef(vm, &module->_super); // scr.
mapSet(vm, vm->modules, VAR_OBJ(path_name), VAR_OBJ(module));
vmPopTempRef(vm); // scr.
Module* module = newModule(vm);
module->path = path_;
vmPushTempRef(vm, &module->_super); // module.
{
moduleAddMain(vm, module);
// Add '__file__' variable with it's path as value. If the path starts with
// '@' It's a special file (@(REPL) or @(TRY)) and don't define __file__.
if (module->path->data[0] != SPECIAL_NAME_CHAR) {
moduleAddGlobal(vm, module, "__file__", 8, VAR_OBJ(module->path));
}
// TODO: Add ARGV to the module's globals.
vmRegisterModule(vm, module, path_);
}
vmPopTempRef(vm); // module.
// Push the compiled script on the stack.
emitOpcode(compiler, OP_IMPORT);
@ -2572,7 +2589,7 @@ static Module* importFile(Compiler* compiler, const char* path) {
if (result != PK_RESULT_SUCCESS) {
parseError(compiler, "Compilation of imported script '%s' failed",
path_name->data);
path_->data);
}
return module;
@ -2586,12 +2603,12 @@ static Module* importCoreLib(Compiler* compiler, const char* name_start,
// Add the name to the module's name buffer, we need it as a key to the
// PKVM's module cache.
int index = (int)moduleAddName(compiler->module, compiler->parser.vm,
name_start, name_length);
String* module_name = compiler->module->names.data[index];
int index = 0;
String* module_name = moduleAddString(compiler->module, compiler->parser.vm,
name_start, name_length, &index);
Var entry = mapGet(compiler->parser.vm->core_libs, VAR_OBJ(module_name));
if (IS_UNDEF(entry)) {
Module* imported = vmGetModule(compiler->parser.vm, module_name);
if (imported == NULL) {
parseError(compiler, "No module named '%s' exists.", module_name->data);
return NULL;
}
@ -2600,8 +2617,7 @@ static Module* importCoreLib(Compiler* compiler, const char* name_start,
emitOpcode(compiler, OP_IMPORT);
emitShort(compiler, index);
ASSERT(IS_OBJ_TYPE(entry, OBJ_MODULE), OOPS);
return (Module*)AS_OBJ(entry);
return imported;
}
// Push the imported module on the stack and return the pointer. It could be
@ -2672,8 +2688,9 @@ static void compilerImportSingleEntry(Compiler* compiler,
int line = compiler->parser.previous.line;
// Add the name to the **current** module's name buffer.
int name_index = (int)moduleAddName(compiler->module, compiler->parser.vm,
name, length);
int name_index = 0;
moduleAddString(compiler->module, compiler->parser.vm,
name, length, &name_index);
// Get the global/function/class from the module.
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
@ -2694,9 +2711,10 @@ static void compilerImportAll(Compiler* compiler, Module* module) {
// Import all globals.
ASSERT(module->global_names.count == module->globals.count, OOPS);
for (uint32_t i = 0; i < module->globals.count; i++) {
ASSERT(module->global_names.data[i] < module->names.count, OOPS);
const String* name = module->names.data[module->global_names.data[i]];
String* name = moduleGetStringAt(module, module->global_names.data[i]);
ASSERT(name != NULL, OOPS);
// If a name starts with '_' we treat it as private and not importing.
if (name->length >= 1 && name->data[0] == '_') continue;
compilerImportSingleEntry(compiler, name->data, name->length);
}
}
@ -2726,9 +2744,9 @@ static void compileFromImport(Compiler* compiler) {
int line = compiler->parser.previous.line;
// Add the name of the symbol to the names buffer.
int name_index = (int)moduleAddName(compiler->module,
compiler->parser.vm,
name, length);
int name_index = 0;
moduleAddString(compiler->module, compiler->parser.vm,
name, length, &name_index);
// Don't pop the lib since it'll be used for the next entry.
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
@ -3122,7 +3140,6 @@ PkResult compile(PKVM* vm, Module* module, const char* source,
// Remember the count of constants, names, and globals, If the compilation
// failed discard all of them and roll back.
uint32_t constants_count = module->constants.count;
uint32_t names_count = module->names.count;
uint32_t globals_count = module->globals.count;
Func curr_fn;
@ -3184,7 +3201,6 @@ PkResult compile(PKVM* vm, Module* module, const char* source,
// If compilation failed, discard all the invalid functions and globals.
if (compiler->parser.has_errors) {
module->constants.count = constants_count;
module->names.count = names_count;
module->globals.count = module->global_names.count = globals_count;
}

View File

@ -51,6 +51,14 @@ PkHandle* pkNewModule(PKVM* vm, const char* name) {
return vmNewHandle(vm, VAR_OBJ(module));
}
void pkRegisterModule(PKVM* vm, PkHandle* module) {
ASSERT(module != NULL, "Argument module was NULL.");
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
Module* module_ = (Module*)AS_OBJ(module->value);
vmRegisterModule(vm, module_, module_->name);
}
void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
const char* name, PkHandle* value) {
ASSERT(module != NULL, "Argument module was NULL.");
@ -413,17 +421,12 @@ static inline bool validateCond(PKVM* vm, bool condition, const char* err) {
static void initializeBuiltinFunctions(PKVM* vm);
static void initializeCoreModules(PKVM* vm);
static void initializePrimitiveClasses(PKVM* vm);
void initializeCore(PKVM* vm) {
initializeBuiltinFunctions(vm);
initializeCoreModules(vm);
}
Module* getCoreLib(const PKVM* vm, String* name) {
Var lib = mapGet(vm->core_libs, VAR_OBJ(name));
if (IS_UNDEF(lib)) return NULL;
ASSERT(IS_OBJ_TYPE(lib, OBJ_MODULE), OOPS);
return (Module*)AS_OBJ(lib);
initializePrimitiveClasses(vm);
}
/*****************************************************************************/
@ -805,19 +808,16 @@ static Module* newModuleInternal(PKVM* vm, const char* name) {
// Check if any module with the same name already exists and assert to the
// hosting application.
if (!IS_UNDEF(mapGet(vm->core_libs, VAR_OBJ(_name)))) {
if (vmGetModule(vm, _name) != NULL) {
ASSERT(false, stringFormat(vm,
"A module named '$' already exists", name)->data);
}
Module* module = newModule(vm, _name, true);
Module* module = newModule(vm);
module->name = _name;
module->initialized = true;
vmPopTempRef(vm); // _name
// Add the module to core_libs.
vmPushTempRef(vm, &module->_super); // module.
mapSet(vm, vm->core_libs, VAR_OBJ(_name), VAR_OBJ(module));
vmPopTempRef(vm); // module.
return module;
}
@ -905,6 +905,8 @@ DEF(stdLangWrite,
}
}
// TODO: Move math to cli as it's not part of the pocketlang core.
//
// 'math' library methods.
// -----------------------
@ -1155,7 +1157,13 @@ static void initializeCoreModules(PKVM* vm) {
#define MODULE_ADD_FN(module, name, fn, argc) \
moduleAddFunctionInternal(vm, module, name, fn, argc, DOCSTRING(fn))
Module* lang = newModuleInternal(vm, "lang");
#define NEW_MODULE(module, name_string) \
Module* module = newModuleInternal(vm, name_string); \
vmPushTempRef(vm, &module->_super); /* module */ \
vmRegisterModule(vm, module, module->name); \
vmPopTempRef(vm) /* module */
NEW_MODULE(lang, "lang");
MODULE_ADD_FN(lang, "clock", stdLangClock, 0);
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
MODULE_ADD_FN(lang, "disas", stdLangDisas, 1);
@ -1164,7 +1172,7 @@ static void initializeCoreModules(PKVM* vm) {
MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0);
#endif
Module* math = newModuleInternal(vm, "math");
NEW_MODULE(math, "math");
MODULE_ADD_FN(math, "floor", stdMathFloor, 1);
MODULE_ADD_FN(math, "ceil", stdMathCeil, 1);
MODULE_ADD_FN(math, "pow", stdMathPow, 2);
@ -1189,18 +1197,27 @@ static void initializeCoreModules(PKVM* vm) {
// modify the PI, like in python.
moduleAddGlobal(vm, math, "PI", 2, VAR_NUM(M_PI));
Module* fiber = newModuleInternal(vm, "Fiber");
NEW_MODULE(fiber, "Fiber");
MODULE_ADD_FN(fiber, "new", stdFiberNew, 1);
MODULE_ADD_FN(fiber, "run", stdFiberRun, -1);
MODULE_ADD_FN(fiber, "resume", stdFiberResume, -1);
#undef MODULE_ADD_FN
#undef NEW_MODULE
}
#undef IS_NUM_BYTE
#undef DOCSTRING
#undef DEF
/*****************************************************************************/
/* PRIMITIVE TYPES CLASS */
/*****************************************************************************/
static void initializePrimitiveClasses(PKVM* vm) {
// TODO
}
/*****************************************************************************/
/* OPERATORS */
/*****************************************************************************/

View File

@ -13,10 +13,6 @@
// Initialize core language, builtin function and core libs.
void initializeCore(PKVM* vm);
// Return the core library with the [name] if exists in the core libs,
// otherwise returns NULL.
Module* getCoreLib(const PKVM* vm, String* name);
/*****************************************************************************/
/* OPERATORS */
/*****************************************************************************/

View File

@ -133,14 +133,11 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
ASSERT_INDEX((uint32_t)cls_index, func->owner->constants.count);
Var constant = func->owner->constants.data[cls_index];
ASSERT(IS_OBJ_TYPE(constant, OBJ_CLASS), OOPS);
uint32_t name_ind = ((Class*)(AS_OBJ(constant)))->name;
ASSERT_INDEX(name_ind, func->owner->names.count);
String* cls_name = func->owner->names.data[name_ind];
// Prints: %5d [Class:%s]\n
PRINT_INT(cls_index);
PRINT(" [Class:");
PRINT(cls_name->data);
PRINT(func->owner->name->data);
PRINT("]\n");
break;
}
@ -218,12 +215,10 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
case OP_STORE_GLOBAL:
{
int index = READ_BYTE();
int name_index = func->owner->global_names.data[index];
String* name = func->owner->names.data[name_index];
// Prints: %5d '%s'\n
PRINT_INT(index);
PRINT(" '");
PRINT(name->data);
PRINT(func->owner->name->data);
PRINT("'\n");
break;
}
@ -273,7 +268,8 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
case OP_IMPORT:
{
int index = READ_SHORT();
String* name = func->owner->names.data[index];
String* name = moduleGetStringAt(func->owner, index);
ASSERT(name != NULL, OOPS);
// Prints: %5d '%s'\n
PRINT_INT(index);
PRINT(" '");
@ -330,7 +326,9 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
case OP_SET_ATTRIB:
{
int index = READ_SHORT();
String* name = func->owner->names.data[index];
String* name = moduleGetStringAt(func->owner, index);
ASSERT(name != NULL, OOPS);
// Prints: %5d '%s'\n
PRINT_INT(index);
PRINT(" '");
@ -397,7 +395,8 @@ void dumpGlobalValues(PKVM* vm) {
Module* module = frame->closure->fn->owner;
for (uint32_t i = 0; i < module->global_names.count; i++) {
String* name = module->names.data[module->global_names.data[i]];
String* name = moduleGetStringAt(module, module->global_names.data[i]);
ASSERT(name != NULL, OOPS);
Var value = module->globals.data[i];
printf("%10s = ", name->data);
dumpValue(vm, value);

View File

@ -213,9 +213,6 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) {
markVarBuffer(vm, &module->constants);
vm->bytes_allocated += sizeof(Var) * module->constants.capacity;
markStringBuffer(vm, &module->names);
vm->bytes_allocated += sizeof(String*) * module->names.capacity;
markObject(vm, &module->body->_super);
} break;
@ -287,11 +284,12 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) {
case OBJ_CLASS:
{
Class* type = (Class*)obj;
Class* cls = (Class*)obj;
vm->bytes_allocated += sizeof(Class);
markObject(vm, &type->owner->_super);
markObject(vm, &type->ctor->_super);
vm->bytes_allocated += sizeof(uint32_t) * type->field_names.capacity;
markObject(vm, &cls->owner->_super);
markObject(vm, &cls->ctor->_super);
markObject(vm, &cls->name->_super);
vm->bytes_allocated += sizeof(uint32_t) * cls->field_names.capacity;
} break;
case OBJ_INST:
@ -380,47 +378,24 @@ Range* newRange(PKVM* vm, double from, double to) {
return range;
}
Module* newModule(PKVM* vm, String* name, bool is_native) {
Module* newModule(PKVM* vm) {
Module* module = ALLOCATE(vm, Module);
varInitObject(&module->_super, vm, OBJ_MODULE);
ASSERT(name != NULL && name->length > 0, OOPS);
module->path = name;
module->path = NULL;
module->name = NULL;
module->initialized = is_native;
module->initialized = false;
module->body = NULL;
// Core modules has its name as the module name.
if (is_native) module->name = name;
pkVarBufferInit(&module->globals);
pkUintBufferInit(&module->global_names);
pkVarBufferInit(&module->constants);
pkStringBufferInit(&module->names);
// Add a implicit main function and the '__file__' global to the module, only
// if it's not a core module.
if (!is_native) {
vmPushTempRef(vm, &module->_super); // module.
moduleAddMain(vm, module);
// Add '__file__' variable with it's path as value. If the path starts with
// '@' It's a special file (@(REPL) or @(TRY)) and don't define __file__.
if (module->path->data[0] != SPECIAL_NAME_CHAR) {
moduleAddGlobal(vm, module, "__file__", 8, VAR_OBJ(module->path));
}
// TODO: Add ARGV as a global.
vmPopTempRef(vm); // module.
}
return module;
}
Function* newFunction(PKVM* vm, const char* name, int length, Module* owner,
Function* newFunction(PKVM* vm, const char* name, int length,
Module* owner,
bool is_native, const char* docstring,
int* fn_index) {
@ -429,38 +404,36 @@ Function* newFunction(PKVM* vm, const char* name, int length, Module* owner,
vmPushTempRef(vm, &func->_super); // func
if (owner == NULL) {
ASSERT(is_native, OOPS);
func->owner = owner;
func->is_native = is_native;
func->upvalue_count = 0;
func->arity = -2; // -2 means un-initialized (TODO: make it as a macro).
func->docstring = docstring;
ASSERT(is_native || owner != NULL, OOPS);
// Only builtin function does't have an owner module.
if (is_native && owner == NULL) {
func->name = name;
func->owner = NULL;
func->native = NULL;
} else {
uint32_t _fn_index = moduleAddConstant(vm, owner, VAR_OBJ(func));
if (fn_index) *fn_index = _fn_index;
func->name = moduleAddString(owner, vm, name, length, NULL)->data;
uint32_t name_index = moduleAddName(owner, vm, name, length);
if (is_native) {
func->native = NULL;
func->name = owner->names.data[name_index]->data;
func->owner = owner;
func->arity = -2; // -2 means un-initialized (TODO: make it as a macro).
} else {
Fn* fn = ALLOCATE(vm, Fn);
pkByteBufferInit(&fn->opcodes);
pkUintBufferInit(&fn->oplines);
fn->stack_size = 0;
func->fn = fn;
}
}
func->is_native = is_native;
func->upvalue_count = 0;
if (is_native) {
func->native = NULL;
} else {
Fn* fn = ALLOCATE(vm, Fn);
pkByteBufferInit(&fn->opcodes);
pkUintBufferInit(&fn->oplines);
fn->stack_size = 0;
func->fn = fn;
}
func->docstring = docstring;
vmPopTempRef(vm); // func
return func;
}
@ -552,14 +525,13 @@ Class* newClass(PKVM* vm, Module* module, const char* name, uint32_t length,
pkUintBufferInit(&cls->field_names);
cls->owner = module;
cls->name = moduleAddName(module, vm, name, length);
cls->name = moduleAddString(module, vm, name, length, NULL);
// Since characters '@' and '$' are special in stringFormat, and they
// currently cannot be escaped (TODO), a string (char array) created
// for that character and passed as C string format.
char special[2] = { SPECIAL_NAME_CHAR, '\0' };
String* cls_name = module->names.data[cls->name];
String* ctor_name = stringFormat(vm, "$(Ctor:@)", special, cls_name);
String* ctor_name = stringFormat(vm, "$(Ctor:@)", special, cls->name);
// Constructor.
vmPushTempRef(vm, &ctor_name->_super); // ctor_name.
@ -583,8 +555,7 @@ Instance* newInstance(PKVM* vm, Class* cls, bool initialize) {
vmPushTempRef(vm, &inst->_super); // inst.
ASSERT(cls->name < cls->owner->names.count, OOPS);
inst->ty_name = cls->owner->names.data[cls->name]->data;
inst->ty_name = cls->name->data;
inst->is_native = false;
Inst* ins = ALLOCATE(vm, Inst);
@ -1083,7 +1054,6 @@ void freeObject(PKVM* vm, Object* self) {
pkVarBufferClear(&module->globals, vm);
pkUintBufferClear(&module->global_names, vm);
pkVarBufferClear(&module->constants, vm);
pkStringBufferClear(&module->names, vm);
} break;
case OBJ_FUNC: {
@ -1143,24 +1113,37 @@ uint32_t moduleAddConstant(PKVM* vm, Module* module, Var value) {
return (int)module->constants.count - 1;
}
uint32_t moduleAddName(Module* module, PKVM* vm, const char* name,
uint32_t length) {
String* moduleAddString(Module* module, PKVM* vm, const char* name,
uint32_t length, int* index) {
for (uint32_t i = 0; i < module->names.count; i++) {
String* _name = module->names.data[i];
for (uint32_t i = 0; i < module->constants.count; i++) {
if (!IS_OBJ_TYPE(module->constants.data[i], OBJ_STRING)) continue;
String* _name = (String*)AS_OBJ(module->constants.data[i]);
if (_name->length == length && strncmp(_name->data, name, length) == 0) {
// Name already exists in the buffer.
return i;
if (index) *index = i;
return _name;
}
}
// If we reach here the name doesn't exists in the buffer, so add it and
// return the index.
String* new_name = newStringLength(vm, name, length);
vmPushTempRef(vm, &new_name->_super);
pkStringBufferWrite(&module->names, vm, new_name);
vmPopTempRef(vm);
return module->names.count - 1;
vmPushTempRef(vm, &new_name->_super); // new_name
pkVarBufferWrite(&module->constants, vm, VAR_OBJ(new_name));
vmPopTempRef(vm); // new_name
if (index) *index = module->constants.count - 1;
return new_name;
}
String* moduleGetStringAt(Module* module, int index) {
ASSERT(index >= 0, OOPS);
if (index >= (int)module->constants.count) return NULL;
Var constant = module->constants.data[index];
if (IS_OBJ_TYPE(constant, OBJ_STRING)) {
return (String*)AS_OBJ(constant);
}
return NULL;
}
uint32_t moduleAddGlobal(PKVM* vm, Module* module,
@ -1177,8 +1160,9 @@ uint32_t moduleAddGlobal(PKVM* vm, Module* module,
// If we're reached here that means we don't already have a variable with
// that name, create new one and set the value.
uint32_t name_ind = moduleAddName(module, vm, name, length);
pkUintBufferWrite(&module->global_names, vm, name_ind);
int name_index = 0;
moduleAddString(module, vm, name, length, &name_index);
pkUintBufferWrite(&module->global_names, vm, name_index);
pkVarBufferWrite(&module->globals, vm, value);
return module->globals.count - 1;
}
@ -1186,7 +1170,8 @@ uint32_t moduleAddGlobal(PKVM* vm, Module* module,
int moduleGetGlobalIndex(Module* module, const char* name, uint32_t length) {
for (uint32_t i = 0; i < module->global_names.count; i++) {
uint32_t name_index = module->global_names.data[i];
String* g_name = module->names.data[name_index];
String* g_name = moduleGetStringAt(module, name_index);
ASSERT(g_name != NULL, OOPS);
if (g_name->length == length && strncmp(g_name->data, name, length) == 0) {
return (int)i;
}
@ -1271,8 +1256,8 @@ bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) {
Class* cls = inst->ins->type;
for (uint32_t i = 0; i < cls->field_names.count; i++) {
ASSERT_INDEX(i, cls->field_names.count);
ASSERT_INDEX(cls->field_names.data[i], cls->owner->names.count);
String* f_name = cls->owner->names.data[cls->field_names.data[i]];
String* f_name = moduleGetStringAt(cls->owner, cls->field_names.data[i]);
ASSERT(f_name != NULL, OOPS);
if (IS_STR_EQ(f_name, attrib)) {
*value = inst->ins->fields.data[i];
return true;
@ -1326,8 +1311,8 @@ bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) {
Class* ty = inst->ins->type;
for (uint32_t i = 0; i < ty->field_names.count; i++) {
ASSERT_INDEX(i, ty->field_names.count);
ASSERT_INDEX(ty->field_names.data[i], ty->owner->names.count);
String* f_name = ty->owner->names.data[ty->field_names.data[i]];
String* f_name = moduleGetStringAt(ty->owner, ty->field_names.data[i]);
ASSERT(f_name != NULL, OOPS);
if (f_name->hash == attrib->hash &&
f_name->length == attrib->length &&
memcmp(f_name->data, attrib->data, attrib->length) == 0) {
@ -1686,8 +1671,7 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
case OBJ_CLASS: {
const Class* cls = (const Class*)obj;
pkByteBufferAddString(buff, vm, "[Class:", 7);
String* ty_name = cls->owner->names.data[cls->name];
pkByteBufferAddString(buff, vm, ty_name->data, ty_name->length);
pkByteBufferAddString(buff, vm, cls->name->data, cls->name->length);
pkByteBufferWrite(buff, vm, ']');
return;
}
@ -1709,7 +1693,8 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
if (i != 0) pkByteBufferWrite(buff, vm, ',');
pkByteBufferWrite(buff, vm, ' ');
String* f_name = cls->owner->names.data[cls->field_names.data[i]];
String* f_name = moduleGetStringAt(cls->owner,
cls->field_names.data[i]);
pkByteBufferAddString(buff, vm, f_name->data, f_name->length);
pkByteBufferWrite(buff, vm, '=');
_toStringInternal(vm, ins->fields.data[i], buff, outer, repr);

View File

@ -209,7 +209,7 @@ void pkByteBufferAddString(pkByteBuffer* self, PKVM* vm, const char* str,
// Type enums of the pocketlang heap allocated types.
typedef enum {
OBJ_STRING,
OBJ_STRING = 0,
OBJ_LIST,
OBJ_MAP,
OBJ_RANGE,
@ -219,7 +219,7 @@ typedef enum {
OBJ_UPVALUE,
OBJ_FIBER,
OBJ_CLASS,
OBJ_INST,
OBJ_INST, // OBJ_INST should be the last element of this enums (don't move).
} ObjectType;
// Base struct for all heap allocated objects.
@ -288,20 +288,10 @@ struct Module {
// a moduel as well as classes.
pkVarBuffer constants;
// All the variable names, globals name, attribute name etc, are stored in
// the [names] buffer. They can be stored in the constants but to make it
// more clear different between string literal and names they're stored in
// a different location.
pkStringBuffer names;
// TODO:
// Consider merging names and constants. Java's class file doesn't have
// a seperation between string literals and names in it's constant pool.
// Globals is an array of global variables of the module. All the names
// (including global variables) are stored in the names buffer of the module
// (defined bellow). The (i)th global variables names is located at index (j)
// in the names buffer where j = global_names[i].
// (including global variables) are stored in the constant pool of the
// module. The (i)th global variable's names is located at index (j)
// in the constant pool where j = global_names[i].
pkVarBuffer globals;
pkUintBuffer global_names;
@ -493,9 +483,8 @@ struct Class {
// The module that owns this class.
Module* owner;
// The index of the name of this class in the owner module's names
// buffer.
uint32_t name;
// Name of the class.
String* name;
Closure* ctor; //< The constructor function.
pkUintBuffer field_names; //< Buffer of field names.
@ -525,11 +514,8 @@ struct Instance {
/* "CONSTRUCTORS" */
/*****************************************************************************/
// Initialize the object with it's default value.
void varInitObject(Object* self, PKVM* vm, ObjectType type);
// Allocate new String object with from [text] with a given [length] and return
// String*.
String* newStringLength(PKVM* vm, const char* text, uint32_t length);
// An inline function/macro implementation of newString(). Set below 0 to 1, to
@ -547,32 +533,29 @@ String* newStringLength(PKVM* vm, const char* text, uint32_t length);
(newStringLength(vm, text, (!(text)) ? 0 : (uint32_t)strlen(text)))
#endif
// Allocate new List and return List*.
List* newList(PKVM* vm, uint32_t size);
// Allocate new Map and return Map*.
Map* newMap(PKVM* vm);
// Allocate new Range object and return Range*.
Range* newRange(PKVM* vm, double from, double to);
// FIXME:
// We may need 2 different constructor for native and script modules.
Module* newModule(PKVM* vm, String* name, bool is_native);
Module* newModule(PKVM* vm);
// FIXME:
// We may need 2 different constuctor for native and script functions.
Function* newFunction(PKVM* vm, const char* name, int length, Module* owner,
// The docstring should be allocated and stored in the module's constants
// as a string if it's not a native function. (native function's docs are
// C string liteals).
//
// Allocate a new function and return it.
Function* newFunction(PKVM* vm, const char* name, int length,
Module* owner,
bool is_native, const char* docstring,
int* fn_index);
// Allocate a new closure object and return it.
Closure* newClosure(PKVM* vm, Function* fn);
// Allocate a new upvalue object for the [value] and return it.
Upvalue* newUpvalue(PKVM* vm, Var* value);
// Allocate new Fiber object for the [closure] and return Fiber*.
Fiber* newFiber(PKVM* vm, Closure* closure);
// FIXME:
@ -689,10 +672,16 @@ bool fiberHasError(Fiber* fiber);
// constant buffer and return it's index.
uint32_t moduleAddConstant(PKVM* vm, Module* module, Var value);
// Add the name (string literal) to the string buffer if not already exists and
// return it's index in the buffer.
uint32_t moduleAddName(Module* module, PKVM* vm, const char* name,
uint32_t length);
// Add a string literal to the module's constant buffer if not already exists
// and return it. If the [index] isn't NULL, the index of the string will be
// written on it.
String* moduleAddString(Module* module, PKVM* vm, const char* name,
uint32_t length, int* index);
// Returns a string at the index of the module, if the index is invalid or the
// constant at the index is not a string, it'll return NULL. (however if the
// index is negative i'll fail an assertion).
String* moduleGetStringAt(Module* module, int index);
// Add a global [value] to the [module] and return its index.
uint32_t moduleAddGlobal(PKVM* vm, Module* module,

View File

@ -69,9 +69,14 @@ PKVM* pkNewVM(PkConfiguration* config) {
vm->heap_fill_percent = HEAP_FILL_PERCENT;
vm->modules = newMap(vm);
vm->core_libs = newMap(vm);
vm->builtins_count = 0;
// This is necessary to prevent garbage collection skip the entry in this
// array while we're building it.
for (int i = 0; i < OBJ_INST; i++) {
vm->primitives[i] = NULL;
}
initializeCore(vm);
return vm;
}
@ -132,21 +137,23 @@ void pkReleaseHandle(PKVM* vm, PkHandle* handle) {
PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path,
const PkCompileOptions* options) {
String* path_name = newString(vm, path.string);
String* path_ = newString(vm, path.string);
if (path.on_done) path.on_done(vm, path);
vmPushTempRef(vm, &path_name->_super); // path_name.
vmPushTempRef(vm, &path_->_super); // path_
// TODO: Should I clean the module if it already exists before compiling it?
// FIXME:
// Should I clean the module if it already exists before compiling it?
// Load a new module to the vm's modules cache.
Module* module = vmGetModule(vm, path_name);
Module* module = vmGetModule(vm, path_);
if (module == NULL) {
module = newModule(vm, path_name, false);
module = newModule(vm);
module->path = path_;
vmPushTempRef(vm, &module->_super); // module.
mapSet(vm, vm->modules, VAR_OBJ(path_name), VAR_OBJ(module));
vmRegisterModule(vm, module, path_);
vmPopTempRef(vm); // module.
}
vmPopTempRef(vm); // path_name.
vmPopTempRef(vm); // path_
// Compile the source.
PkResult result = compile(vm, module, source.string, options);
@ -241,8 +248,17 @@ void vmPopTempRef(PKVM* vm) {
vm->temp_reference_count--;
}
Module* vmGetModule(PKVM* vm, String* path) {
Var module = mapGet(vm->modules, VAR_OBJ(path));
void vmRegisterModule(PKVM* vm, Module* module, String* key) {
ASSERT((((module->name != NULL) && IS_STR_EQ(module->name, key)) ||
IS_STR_EQ(module->path, key)), OOPS);
// FIXME:
// Not sure what to do, if a module the the same key already exists. Should
// I override or assert.
mapSet(vm, vm->modules, VAR_OBJ(key), VAR_OBJ(module));
}
Module* vmGetModule(PKVM* vm, String* key) {
Var module = mapGet(vm->modules, VAR_OBJ(key));
if (IS_UNDEF(module)) return NULL;
ASSERT(AS_OBJ(module)->type == OBJ_MODULE, OOPS);
return (Module*)AS_OBJ(module);
@ -254,13 +270,25 @@ void vmCollectGarbage(PKVM* vm) {
// required to know the size of each object that'll be freeing.
vm->bytes_allocated = 0;
// Mark the core libs and builtin functions.
markObject(vm, &vm->core_libs->_super);
// Mark builtin functions.
for (int i = 0; i < vm->builtins_count; i++) {
markObject(vm, &vm->builtins[i]->_super);
}
// Mark the modules cache.
// Mark primitive types' classes.
for (int i = 0; i < (int)OBJ_INST; i++) {
// Upvalue and functions aren't first class objects and they doesn't
// require classes.
if (i == OBJ_UPVALUE || i == OBJ_FUNC) continue;
// It's possible that a garbage collection could be triggered while we're
// building the primitives and the class could be NULL.
if (vm->primitives[i] == NULL) continue;
markObject(vm, &vm->primitives[i]->_super);
}
// Mark the modules.
markObject(vm, &vm->modules->_super);
// Mark temp references.
@ -450,14 +478,9 @@ static void* defaultRealloc(void* memory, size_t new_size, void* user_data) {
//
// Import and return the Module object with the [name] (if it's a scirpt
// doesn't have a module name, the name would be it's resolved path).
static inline Var importModule(PKVM* vm, String* name) {
static inline Var importModule(PKVM* vm, String* key) {
// Check in the core libs.
Module* module = getCoreLib(vm, name);
if (module != NULL) return VAR_OBJ(module);
// Check in the modules cache.
Var entry = mapGet(vm->modules, VAR_OBJ(name));
Var entry = mapGet(vm->modules, VAR_OBJ(key));
if (!IS_UNDEF(entry)) {
ASSERT(AS_OBJ(entry)->type == OBJ_MODULE, OOPS);
return entry;
@ -1005,7 +1028,9 @@ L_vm_main_loop:
OPCODE(IMPORT):
{
String* name = module->names.data[READ_SHORT()];
uint16_t index = READ_SHORT();
String* name = moduleGetStringAt(module, (int)index);
ASSERT(name != NULL, OOPS);
Var _imported = importModule(vm, name);
ASSERT(IS_OBJ_TYPE(_imported, OBJ_MODULE), OOPS);
@ -1332,7 +1357,8 @@ L_vm_main_loop:
OPCODE(GET_ATTRIB):
{
Var on = PEEK(-1); // Don't pop yet, we need the reference for gc.
String* name = module->names.data[READ_SHORT()];
String* name = moduleGetStringAt(module, READ_SHORT());
ASSERT(name != NULL, OOPS);
Var value = varGetAttrib(vm, on, name);
DROP(); // on
PUSH(value);
@ -1344,7 +1370,8 @@ L_vm_main_loop:
OPCODE(GET_ATTRIB_KEEP):
{
Var on = PEEK(-1);
String* name = module->names.data[READ_SHORT()];
String* name = moduleGetStringAt(module, READ_SHORT());
ASSERT(name != NULL, OOPS);
PUSH(varGetAttrib(vm, on, name));
CHECK_ERROR();
DISPATCH();
@ -1354,7 +1381,8 @@ L_vm_main_loop:
{
Var value = PEEK(-1); // Don't pop yet, we need the reference for gc.
Var on = PEEK(-2); // Don't pop yet, we need the reference for gc.
String* name = module->names.data[READ_SHORT()];
String* name = moduleGetStringAt(module, READ_SHORT());
ASSERT(name != NULL, OOPS);
varSetAttrib(vm, on, name, value);
DROP(); // value

View File

@ -105,18 +105,23 @@ struct PKVM {
// a new module is being imported and compiled at compiletime.
Compiler* compiler;
// A cache of the compiled modules with their path as key and the Scrpit
// object as the value.
// A map of all the modules which are compiled or natively registered.
// The key of the modules will be:
// 1. Native modules : name of the module.
// 2. Compiled script :
// - module name if one defined with the module keyword
// - otherwise path of the module.
Map* modules;
// A map of core libraries with their name as the key and the Module object
// as the value.
Map* core_libs;
// Array of all builtin functions.
Closure* builtins[BUILTIN_FN_CAPACITY];
int builtins_count;
// An array of all the primitive types' class except for OBJ_INST. Since the
// type of the objects are enums starting from 0 we can directly get the
// class by using their enum (ex: primitives[OBJ_LIST]).
Class* primitives[(int)OBJ_INST];
// Current fiber.
Fiber* fiber;
};
@ -183,9 +188,14 @@ void vmPushTempRef(PKVM* vm, Object* obj);
// Pop the top most object from temporary reference stack.
void vmPopTempRef(PKVM* vm);
// Returns the module with the resolved [path] (also the key) in the VM's
// modules cache. If not found itll return NULL.
Module* vmGetModule(PKVM* vm, String* path);
// Regsiter a module to the VM's modules map, the key could be either it's
// name (for core module) or it's path (if it's a compiled script). If the
// key doesn't match either of it's name or path an assertion will fail.
void vmRegisterModule(PKVM* vm, Module* module, String* key);
// Returns the module, where the [key] could be either it's name or path that
// was used to register the module. If it doesn't exists, returns NULL.
Module* vmGetModule(PKVM* vm, String* key);
// ((Context switching - start))
// Prepare a new fiber for execution with the given arguments. That can be used