instance and class type cleaned

this is a part of the class implementation. In this commit classes
and instances type were completely cleaned and prepared for a new
implementation. Native class registering mechanism were fully refactored
and made it much simpler.
This commit is contained in:
Thakee Nathees 2022-04-20 14:40:08 +05:30
parent 29be68fc86
commit 4d46930d1b
12 changed files with 279 additions and 649 deletions

View File

@ -13,9 +13,7 @@
/* STD MODULE SOURCES */
/*****************************************************************************/
#include "modules/modules.c"
#include "modules/std_file.c"
#include "modules/std_io.c"
#include "modules/std_path.c"
/*****************************************************************************/

View File

@ -129,11 +129,6 @@ static PKVM* intializePocketVM() {
config.write_fn = writeFunction;
config.read_fn = readFunction;
config.inst_free_fn = freeObj;
config.inst_name_fn = getObjName;
config.inst_get_attrib_fn = objGetAttrib;
config.inst_set_attrib_fn = objSetAttrib;
config.load_script_fn = loadScript;
config.resolve_path_fn = resolvePath;
@ -193,7 +188,8 @@ int main(int argc, const char** argv) {
user_data.repl_mode = false;
pkSetUserData(vm, &user_data);
registerModules(vm);
registerModulePath(vm);
//REGISTER_ALL_MODULES(vm);
PkCompileOptions options = pkNewCompilerOptions();
options.debug = debug;

View File

@ -1,91 +0,0 @@
/*
* Copyright (c) 2020-2022 Thakee Nathees
* Copyright (c) 2021-2022 Pocketlang Contributors
* Distributed Under The MIT License
*/
#include "modules.h"
// Note: Everything here is for testing the native API, and will have to
// refactor everything.
// Allocate a new module object of type [Ty].
#define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty))
// Dellocate module object, allocated by NEW_OBJ(). Called by the freeObj
// callback.
#define FREE_OBJ(ptr) free(ptr)
/*****************************************************************************/
/* MODULE FUNCTIONS DECLARATION */
/*****************************************************************************/
void fileGetAttrib(PKVM* vm, File* file, const char* attrib);
bool fileSetAttrib(PKVM* vm, File* file, const char* attrib);
void fileClean(PKVM* vm, File* file);
void registerModuleFile(PKVM* vm);
void registerModulePath(PKVM* vm);
/*****************************************************************************/
/* MODULE PUBLIC FUNCTIONS */
/*****************************************************************************/
void initObj(Obj* obj, ObjType type) {
obj->type = type;
}
void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) {
Obj* obj = (Obj*)instance;
ASSERT(obj->type == (ObjType)id, OOPS);
switch (obj->type) {
case OBJ_FILE:
fileGetAttrib(vm, (File*)obj, attrib.string);
return;
}
STATIC_ASSERT(_OBJ_MAX_ == 2);
}
bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) {
Obj* obj = (Obj*)instance;
ASSERT(obj->type == (ObjType)id, OOPS);
switch (obj->type) {
case OBJ_FILE:
return fileSetAttrib(vm, (File*)obj, attrib.string);
}
STATIC_ASSERT(_OBJ_MAX_ == 2);
return false;
}
void freeObj(PKVM* vm, void* instance, uint32_t id) {
Obj* obj = (Obj*)instance;
ASSERT(obj->type == (ObjType)id, OOPS);
switch (obj->type) {
case OBJ_FILE:
fileClean(vm, (File*)obj);
}
STATIC_ASSERT(_OBJ_MAX_ == 2);
FREE_OBJ(obj);
}
const char* getObjName(uint32_t id) {
switch ((ObjType)id) {
case OBJ_FILE: return "File";
}
STATIC_ASSERT(_OBJ_MAX_ == 2);
return NULL;
}
/*****************************************************************************/
/* REGISTER MODULES */
/*****************************************************************************/
void registerModules(PKVM* vm) {
registerModuleFile(vm);
registerModulePath(vm);
}

View File

@ -14,73 +14,26 @@
#include <stdio.h>
#include <string.h>
/*****************************************************************************/
/* MODULE OBJECTS */
/*****************************************************************************/
// Str | If already exists | If does not exist |
// -----+-------------------+-------------------|
// 'r' | read from start | failure to open |
// 'w' | destroy contents | create new |
// 'a' | write to end | create new |
// 'r+' | read from start | error |
// 'w+' | destroy contents | create new |
// 'a+' | write to end | create new |
typedef enum {
FMODE_READ = (1 << 0),
FMODE_WRITE = (1 << 1),
FMODE_APPEND = (1 << 2),
_FMODE_EXT = (1 << 3),
FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ),
FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE),
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
} FileAccessMode;
typedef enum {
OBJ_FILE = 1,
_OBJ_MAX_
} ObjType;
typedef struct {
ObjType type;
} Obj;
typedef struct {
Obj _super;
FILE* fp; // C file poinnter.
FileAccessMode mode; // Access mode of the file.
bool closed; // True if the file isn't closed yet.
} File;
/*****************************************************************************/
/* MODULE PUBLIC FUNCTIONS */
/*****************************************************************************/
// Initialize the native module object with it's default values.
void initObj(Obj* obj, ObjType type);
// A function callback called by pocket VM to get attribute of a native
// instance. The value of the attributes will be returned with pkReturn...()
// functions and if the attribute doesn't exists on the instance we're
// shouldn't return anything, PKVM will know it and set error (or use some
// common attributes like "as_string", "as_repr", etc).
void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib);
// A function callback called by pocket VM to set attribute of a native
// instance.
bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib);
// The free callback of the object, that'll called by pocketlang when a
// pocketlang native instance garbage collected.
void freeObj(PKVM* vm, void* instance, uint32_t id);
// The native instance get_name callback used to get the name of a native
// instance from pocketlang. Here the id we're using is the ObjType enum.
const char* getObjName(uint32_t id);
void registerModuleIO(PKVM* vm);
void registerModulePath(PKVM* vm);
// Registers all the cli modules.
void registerModules(PKVM* vm);
#define REGISTER_ALL_MODULES(vm) \
do { \
registerModuleIO(vm); \
registerModulePath(vm); \
} while (false)
/*****************************************************************************/
/* MODULES INTERNAL */
/*****************************************************************************/
// Allocate a new module object of type [Ty].
#define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty))
// Dellocate module object, allocated by NEW_OBJ(). Called by the freeObj
// callback.
#define FREE_OBJ(ptr) free(ptr)
/*****************************************************************************/
/* SHARED FUNCTIONS */

View File

@ -6,6 +6,31 @@
#include "modules.h"
// Str | If already exists | If does not exist |
// -----+-------------------+-------------------|
// 'r' | read from start | failure to open |
// 'w' | destroy contents | create new |
// 'a' | write to end | create new |
// 'r+' | read from start | error |
// 'w+' | destroy contents | create new |
// 'a+' | write to end | create new |
typedef enum {
FMODE_NONE = 0,
FMODE_READ = (1 << 0),
FMODE_WRITE = (1 << 1),
FMODE_APPEND = (1 << 2),
_FMODE_EXT = (1 << 3),
FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ),
FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE),
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
} FileAccessMode;
typedef struct {
FILE* fp; // C file poinnter.
FileAccessMode mode; // Access mode of the file.
bool closed; // True if the file isn't closed yet.
} File;
/*****************************************************************************/
/* FILE OBJECT OPERATORS */
/*****************************************************************************/
@ -62,25 +87,29 @@ static void _fileOpen(PKVM* vm) {
} while (false);
}
// This TODO is just a blockade from running the bellow code, complete the
// native interface and test before removing it.
TODO;
FILE* fp = fopen(path, mode_str);
if (fp != NULL) {
File* file = NEW_OBJ(File);
initObj(&file->_super, OBJ_FILE);
file->fp = fp;
file->mode = mode;
file->closed = false;
pkReturnInstNative(vm, (void*)file, OBJ_FILE);
File* self = (File*)pkGetSelf(vm);
self->fp = fp;
self->mode = mode;
self->closed = false;
} else {
pkReturnNull(vm);
pkSetRuntimeError(vm, "Error opening the file.");
}
}
static void _fileRead(PKVM* vm) {
File* file;
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
// This TODO is just a blockade from running the bellow code, complete the
// native interface and test before removing it.
TODO;
File* file = (File*)pkGetSelf(vm);
if (file->closed) {
pkSetRuntimeError(vm, "Cannot read from a closed file.");
@ -99,10 +128,13 @@ static void _fileRead(PKVM* vm) {
}
static void _fileWrite(PKVM* vm) {
File* file;
// This TODO is just a blockade from running the bellow code, complete the
// native interface and test before removing it.
TODO;
File* file = (File*)pkGetSelf(vm);
const char* text; uint32_t length;
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
if (!pkGetArgString(vm, 2, &text, &length)) return;
if (!pkGetArgString(vm, 1, &text, &length)) return;
if (file->closed) {
pkSetRuntimeError(vm, "Cannot write to a closed file.");
@ -118,8 +150,11 @@ static void _fileWrite(PKVM* vm) {
}
static void _fileClose(PKVM* vm) {
File* file;
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
// This TODO is just a blockade from running the bellow code, complete the
// native interface and test before removing it.
TODO;
File* file = (File*)pkGetSelf(vm);
if (file->closed) {
pkSetRuntimeError(vm, "File already closed.");
@ -133,14 +168,16 @@ static void _fileClose(PKVM* vm) {
file->closed = true;
}
void registerModuleFile(PKVM* vm) {
PkHandle* file = pkNewModule(vm, "File");
void registerModuleIO(PKVM* vm) {
PkHandle* io = pkNewModule(vm, "io");
pkModuleAddFunction(vm, file, "open", _fileOpen, -1);
pkModuleAddFunction(vm, file, "read", _fileRead, 1);
pkModuleAddFunction(vm, file, "write", _fileWrite, 2);
pkModuleAddFunction(vm, file, "close", _fileClose, 1);
PkHandle* cls_file = pkNewClass(vm, io, "File");
pkClassAddMethod(vm, cls_file, "open", _fileOpen, -1);
pkClassAddMethod(vm, cls_file, "read", _fileRead, 0);
pkClassAddMethod(vm, cls_file, "write", _fileWrite, 1);
pkClassAddMethod(vm, cls_file, "close", _fileClose, 0);
pkReleaseHandle(vm, cls_file);
pkRegisterModule(vm, file);
pkReleaseHandle(vm, file);
pkRegisterModule(vm, io);
pkReleaseHandle(vm, io);
}

View File

@ -55,8 +55,9 @@ extern "C" {
#define PK_PUBLIC
#endif
/*****************************************************************************/
/* POCKETLANG TYPES */
/* POCKETLANG TYPEDEFS & CALLBACKS */
/*****************************************************************************/
// PocketLang Virtual Machine. It'll contain the state of the execution, stack,
@ -74,57 +75,13 @@ typedef struct PkHandle PkHandle;
// alive use `pkNewHandle()`.
typedef void* PkVar;
// Type enum of the pocketlang's first class types. Note that Object isn't
// instanciable (as of now) but they're considered first calss.
typedef enum {
PK_OBJECT = 0,
PK_NULL,
PK_BOOL,
PK_NUMBER,
PK_STRING,
PK_LIST,
PK_MAP,
PK_RANGE,
PK_MODULE,
PK_CLOSURE,
PK_FIBER,
PK_CLASS,
PK_INSTANCE,
} PkVarType;
typedef enum PkVarType PkVarType;
typedef enum PkErrorType PkErrorType;
typedef enum PkResult PkResult;
typedef struct PkStringPtr PkStringPtr;
typedef struct PkConfiguration PkConfiguration;
typedef struct PkCompileOptions PkCompileOptions;
// Type of the error message that pocketlang will provide with the pkErrorFn
// callback.
typedef enum {
PK_ERROR_COMPILE = 0, // Compile time errors.
PK_ERROR_RUNTIME, // Runtime error message.
PK_ERROR_STACKTRACE, // One entry of a runtime error stack.
} PkErrorType;
// Result that pocketlang will return after a compilation or running a script
// or a function or evaluating an expression.
typedef enum {
PK_RESULT_SUCCESS = 0, // Successfully finished the execution.
// Unexpected EOF while compiling the source. This is another compile time
// error that will ONLY be returned if we're compiling with the REPL mode set
// in the compile options. We need this specific error to indicate the host
// application to add another line to the last input. If REPL is not enabled,
// this will be PK_RESULT_COMPILE_ERROR.
PK_RESULT_UNEXPECTED_EOF,
PK_RESULT_COMPILE_ERROR, // Compilation failed.
PK_RESULT_RUNTIME_ERROR, // An error occurred at runtime.
} PkResult;
/*****************************************************************************/
/* POCKETLANG FUNCTION POINTERS & CALLBACKS */
/*****************************************************************************/
// C function pointer which is callable from pocketLang by native module
// functions.
typedef void (*pkNativeFn)(PKVM* vm);
@ -155,35 +112,6 @@ typedef void (*pkWriteFn) (PKVM* vm, const char* text);
// contain a line ending (\n or \r\n).
typedef PkStringPtr (*pkReadFn) (PKVM* vm);
// A function callback, that'll be called when a native instance (wrapper) is
// freed by by the garbage collector, to indicate that pocketlang is done with
// the native instance.
typedef void (*pkInstFreeFn) (PKVM* vm, void* instance, uint32_t id);
// A function callback to get the type name of the native instance from
// pocketlang, using it's [id]. The returned string won't be copied by
// pocketlang so it's expected to be alived since the instance is alive and
// recomended to return a C literal string.
typedef const char* (*pkInstNameFn) (uint32_t id);
// A get arribute callback, called by pocket VM when trying to get an attribute
// from a native type. to return the value of the attribute use 'pkReturn...()'
// functions. DON'T set an error to the VM if the attribute not exists. Example
// if the '.as_string' attribute doesn't exists, pocket VM will use a default
// to string value.
typedef void (*pkInstGetAttribFn) (PKVM* vm, void* instance, uint32_t id,
PkStringPtr attrib);
// Use pkGetArg...(vm, 0, ptr) function to get the value of the attribute
// and use 0 as the argument index, using any other arg index value cause UB.
//
// If the attribute dones't exists DON'T set an error, instead return false.
// Pocket VM will handle it, On success update the native instance and return
// true. And DON'T ever use 'pkReturn...()' in the attribute setter It's is a
// void return function.
typedef bool (*pkInstSetAttribFn) (PKVM* vm, void* instance, uint32_t id,
PkStringPtr attrib);
// A function callback symbol for clean/free the pkStringResult.
typedef void (*pkResultDoneFn) (PKVM* vm, PkStringPtr result);
@ -199,6 +127,96 @@ typedef PkStringPtr (*pkResolvePathFn) (PKVM* vm, const char* from,
// to indicate if it's failed to load the script.
typedef PkStringPtr (*pkLoadScriptFn) (PKVM* vm, const char* path);
/*****************************************************************************/
/* POCKETLANG TYPES */
/*****************************************************************************/
// Type enum of the pocketlang's first class types. Note that Object isn't
// instanciable (as of now) but they're considered first calss.
enum PkVarType {
PK_OBJECT = 0,
PK_NULL,
PK_BOOL,
PK_NUMBER,
PK_STRING,
PK_LIST,
PK_MAP,
PK_RANGE,
PK_MODULE,
PK_CLOSURE,
PK_FIBER,
PK_CLASS,
PK_INSTANCE,
};
// Type of the error message that pocketlang will provide with the pkErrorFn
// callback.
enum PkErrorType {
PK_ERROR_COMPILE = 0, // Compile time errors.
PK_ERROR_RUNTIME, // Runtime error message.
PK_ERROR_STACKTRACE, // One entry of a runtime error stack.
};
// Result that pocketlang will return after a compilation or running a script
// or a function or evaluating an expression.
enum PkResult {
PK_RESULT_SUCCESS = 0, // Successfully finished the execution.
// Unexpected EOF while compiling the source. This is another compile time
// error that will ONLY be returned if we're compiling with the REPL mode set
// in the compile options. We need this specific error to indicate the host
// application to add another line to the last input. If REPL is not enabled,
// this will be PK_RESULT_COMPILE_ERROR.
PK_RESULT_UNEXPECTED_EOF,
PK_RESULT_COMPILE_ERROR, // Compilation failed.
PK_RESULT_RUNTIME_ERROR, // An error occurred at runtime.
};
// A string pointer wrapper to pass c string between host application and
// pocket VM. With a on_done() callback to clean it when the pocket VM is done
// with the string.
struct PkStringPtr {
const char* string; //< The string result.
pkResultDoneFn on_done; //< Called once vm done with the string.
void* user_data; //< User related data.
// These values are provided by the pocket VM to the host application, you're
// not expected to set this when provideing string to the pocket VM.
uint32_t length; //< Length of the string.
uint32_t hash; //< Its 32 bit FNV-1a hash.
};
struct PkConfiguration {
// The callback used to allocate, reallocate, and free. If the function
// pointer is NULL it defaults to the VM's realloc(), free() wrappers.
pkReallocFn realloc_fn;
pkErrorFn error_fn;
pkWriteFn write_fn;
pkReadFn read_fn;
pkResolvePathFn resolve_path_fn;
pkLoadScriptFn load_script_fn;
// User defined data associated with VM.
void* user_data;
};
// The options to configure the compilation provided by the command line
// arguments (or other ways the host application provides).
struct PkCompileOptions {
// Compile debug version of the source.
bool debug;
// Set to true if compiling in REPL mode, This will print repr version of
// each evaluated non-null values.
bool repl_mode;
};
/*****************************************************************************/
/* POCKETLANG PUBLIC API */
/*****************************************************************************/
@ -250,7 +268,7 @@ PK_PUBLIC PkHandle* pkModuleGetGlobal(PKVM* vm, PkHandle* module,
const char* name);
// Add a native function to the given module. If [arity] is -1 that means
// The function has variadic parameters and use pkGetArgc() to get the argc.
// the function has variadic parameters and use pkGetArgc() to get the argc.
// Note that the function will be added as a global variable of the module,
// to retrieve the function use pkModuleGetGlobal().
PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module,
@ -261,6 +279,26 @@ PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module,
// it's statements are wrapped around an implicit main function.
PK_PUBLIC PkHandle* pkModuleGetMainFunction(PKVM* vm, PkHandle* module);
// Add a new module named [name] to the [vm]. Note that the module shouldn't
// 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 a new class on the [module] with the [name] and return it.
PK_PUBLIC PkHandle* pkNewClass(PKVM* vm, PkHandle* module, const char* name);
// Add a native method to the given class. If the [arity] is -1 that means
// the method has variadic parameters and use pkGetArgc() to get the argc.
PK_PUBLIC void pkClassAddMethod(PKVM* vm, PkHandle* cls,
const char* name,
pkNativeFn fptr, int arity);
// Create and return a new fiber around the function [fn].
PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn);
// Compile the [module] with the provided [source]. Set the compiler options
// with the the [options] argument or set to NULL for default options.
PK_PUBLIC PkResult pkCompileModule(PKVM* vm, PkHandle* module,
@ -289,59 +327,6 @@ PK_PUBLIC PkResult pkRunFiber(PKVM* vm, PkHandle* fiber,
// yielded or returned value use the pkFiberGetReturnValue() function.
PK_PUBLIC PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber, PkVar value);
/*****************************************************************************/
/* POCKETLANG PUBLIC TYPE DEFINES */
/*****************************************************************************/
// A string pointer wrapper to pass c string between host application and
// pocket VM. With a on_done() callback to clean it when the pocket VM is done
// with the string.
struct PkStringPtr {
const char* string; //< The string result.
pkResultDoneFn on_done; //< Called once vm done with the string.
void* user_data; //< User related data.
// These values are provided by the pocket VM to the host application, you're
// not expected to set this when provideing string to the pocket VM.
uint32_t length; //< Length of the string.
uint32_t hash; //< Its 32 bit FNV-1a hash.
};
struct PkConfiguration {
// The callback used to allocate, reallocate, and free. If the function
// pointer is NULL it defaults to the VM's realloc(), free() wrappers.
pkReallocFn realloc_fn;
pkErrorFn error_fn;
pkWriteFn write_fn;
pkReadFn read_fn;
pkInstFreeFn inst_free_fn;
pkInstNameFn inst_name_fn;
pkInstGetAttribFn inst_get_attrib_fn;
pkInstSetAttribFn inst_set_attrib_fn;
pkResolvePathFn resolve_path_fn;
pkLoadScriptFn load_script_fn;
// User defined data associated with VM.
void* user_data;
};
// The options to configure the compilation provided by the command line
// arguments (or other ways the host application provides).
struct PkCompileOptions {
// Compile debug version of the source.
bool debug;
// Set to true if compiling in REPL mode, This will print repr version of
// each evaluated non-null values.
bool repl_mode;
};
/*****************************************************************************/
/* NATIVE FUNCTION API */
/*****************************************************************************/
@ -352,6 +337,9 @@ PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* message);
// TODO: Set a runtime error to VM, with the formated string.
//PK_PUBLIC void pkSetRuntimeErrorFmt(PKVM* vm, const char* fmt, ...);
// Returns native [self] of the current method as a void*.
PK_PUBLIC void* pkGetSelf(const PKVM* vm);
// Return the type of the [value] this will help to get the type of the
// variable that was extracted from pkGetArg() earlier.
PK_PUBLIC PkVarType pkGetValueType(const PkVar value);
@ -383,7 +371,6 @@ PK_PUBLIC bool pkGetArgBool(PKVM* vm, int arg, bool* value);
PK_PUBLIC bool pkGetArgNumber(PKVM* vm, int arg, double* value);
PK_PUBLIC bool pkGetArgString(PKVM* vm, int arg,
const char** value, uint32_t* length);
PK_PUBLIC bool pkGetArgInst(PKVM* vm, int arg, uint32_t id, void** value);
PK_PUBLIC bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value);
// The functions follow are used to set the return value of the current native
@ -397,8 +384,6 @@ PK_PUBLIC void pkReturnStringLength(PKVM* vm, const char* value, size_t len);
PK_PUBLIC void pkReturnValue(PKVM* vm, PkVar value);
PK_PUBLIC void pkReturnHandle(PKVM* vm, PkHandle* handle);
PK_PUBLIC void pkReturnInstNative(PKVM* vm, void* data, uint32_t id);
// Returns the cstring pointer of the given string. Make sure if the [value] is
// a string before calling this function, otherwise it'll fail an assertion.
PK_PUBLIC const char* pkStringGetData(const PkVar value);
@ -424,23 +409,6 @@ PK_PUBLIC PkHandle* pkNewStringLength(PKVM* vm, const char* value, size_t len);
PK_PUBLIC PkHandle* pkNewList(PKVM* vm);
PK_PUBLIC PkHandle* pkNewMap(PKVM* vm);
// Add a new module named [name] to the [vm]. Note that the module shouldn't
// 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);
// Create and return a native instance around the [data]. The [id] is the
// unique id of the instance, this would be used to check if two instances are
// equal and used to get the name of the instance using NativeTypeNameFn
// callback.
PK_PUBLIC PkHandle* pkNewInstNative(PKVM* vm, void* data, uint32_t id);
// TODO: Create a primitive (non garbage collected) variable buffer (or a
// fixed size array) to store them and make the handle points to the variable
// in that buffer, this will prevent us from invoking an allocation call for

View File

@ -69,6 +69,34 @@ void pkRegisterModule(PKVM* vm, PkHandle* module) {
vmRegisterModule(vm, module_, module_->name);
}
PkHandle* pkNewClass(PKVM* vm, PkHandle* module, const char* name) {
CHECK_NULL(module);
CHECK_NULL(name);
CHECK_TYPE(module, OBJ_MODULE);
Class* class_ = newClass(vm, name, (int)strlen(name),
(Module*)AS_OBJ(module->value), NULL, NULL);
return vmNewHandle(vm, VAR_OBJ(class_));
}
void pkClassAddMethod(PKVM* vm, PkHandle* cls,
const char* name,
pkNativeFn fptr, int arity) {
CHECK_NULL(cls);
CHECK_NULL(fptr);
CHECK_TYPE(cls, OBJ_MODULE);
TODO;
}
void* pkGetSelf(const PKVM* vm) {
Var self = vm->fiber->frames[vm->fiber->frame_count - 1].self;
ASSERT(IS_OBJ_TYPE(self, OBJ_INST), OOPS);
Instance* inst = (Instance*)AS_OBJ(self);
ASSERT(inst->native != NULL, OOPS);
return inst->native;
}
void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
const char* name, PkHandle* value) {
CHECK_TYPE(module, OBJ_MODULE);
@ -230,33 +258,6 @@ bool pkGetArgString(PKVM* vm, int arg, const char** value, uint32_t* length) {
return true;
}
bool pkGetArgInst(PKVM* vm, int arg, uint32_t id, void** value) {
CHECK_GET_ARG_API_ERRORS();
Var val = ARG(arg);
bool is_native_instance = false;
if (IS_OBJ_TYPE(val, OBJ_INST)) {
Instance* inst = ((Instance*)AS_OBJ(val));
if (inst->is_native && inst->native_id == id) {
*value = inst->native;
is_native_instance = true;
}
}
if (!is_native_instance) {
const char* ty_name = "$(?)";
if (vm->config.inst_name_fn != NULL) {
ty_name = vm->config.inst_name_fn(id);
}
ERR_INVALID_ARG_TYPE(ty_name);
return false;
}
return true;
}
bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
CHECK_GET_ARG_API_ERRORS();
@ -300,10 +301,6 @@ void pkReturnHandle(PKVM* vm, PkHandle* handle) {
RET(handle->value);
}
void pkReturnInstNative(PKVM* vm, void* data, uint32_t id) {
RET(VAR_OBJ(newInstanceNative(vm, data, id)));
}
const char* pkStringGetData(const PkVar value) {
const Var str = (*(const Var*)value);
ASSERT(IS_OBJ_TYPE(str, OBJ_STRING), "Value should be of type string.");

View File

@ -126,26 +126,14 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
NO_ARGS();
break;
case OP_PUSH_LIST: SHORT_ARG(); break;
case OP_PUSH_INSTANCE:
{
int cls_index = READ_SHORT();
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);
// Prints: %5d [Class:%s]\n
PRINT_INT(cls_index);
PRINT(" [Class:");
PRINT(func->owner->name->data);
PRINT("]\n");
case OP_PUSH_LIST:
SHORT_ARG();
break;
}
case OP_PUSH_MAP:
case OP_PUSH_SELF:
case OP_LIST_APPEND:
case OP_MAP_INSERT:
case OP_INST_APPEND:
NO_ARGS();
break;

View File

@ -43,10 +43,6 @@ OPCODE(PUSH_MAP, 0, 1)
// Push the self of the current method on the stack.
OPCODE(PUSH_SELF, 0, 1)
// Push a new instance to the stack.
// param: 1 byte index.
OPCODE(PUSH_INSTANCE, 1, 1)
// Pop the value on the stack the next stack top would be a list. Append the
// value to the list. Used in literal array construction.
OPCODE(LIST_APPEND, 0, -1)
@ -55,10 +51,6 @@ OPCODE(LIST_APPEND, 0, -1)
// Insert the key value pairs to the map. Used in literal map construction.
OPCODE(MAP_INSERT, 0, -2)
// Pop the value on the stack, the next stack top would be an instance. Append
// the value to the instance. Used in instance construction.
OPCODE(INST_APPEND, 0, -1)
// Push stack local on top of the stack. Locals at 0 to 8 marked explicitly
// since it's performance critical.
// params: PUSH_LOCAL_N -> 1 byte count value.

View File

@ -75,14 +75,6 @@ PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) {
return handle;
}
PkHandle* pkNewInstNative(PKVM* vm, void* data, uint32_t id) {
Instance* inst = newInstanceNative(vm, data, id);
vmPushTempRef(vm, &inst->_super); // inst
PkHandle* handle = vmNewHandle(vm, VAR_OBJ(inst));
vmPopTempRef(vm); // inst
return handle;
}
/*****************************************************************************/
/* VAR INTERNALS */
/*****************************************************************************/
@ -279,17 +271,14 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) {
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:
{
Instance* inst = (Instance*)obj;
if (!inst->is_native) {
Inst* ins = inst->ins;
vm->bytes_allocated += sizeof(Inst);
vm->bytes_allocated += sizeof(Var*) * ins->fields.capacity;
}
markObject(vm, &inst->attribs._super);
markObject(vm, &inst->cls->_super);
vm->bytes_allocated += sizeof(Instance);
} break;
}
}
@ -529,43 +518,11 @@ Class* newClass(PKVM* vm, const char* name, int length,
return cls;
}
Instance* newInstance(PKVM* vm, Class* cls, bool initialize) {
Instance* newInstance(PKVM* vm, Class* cls) {
Instance* inst = ALLOCATE(vm, Instance);
varInitObject(&inst->_super, vm, OBJ_INST);
vmPushTempRef(vm, &inst->_super); // inst.
inst->ty_name = cls->name->data;
inst->is_native = false;
Inst* ins = ALLOCATE(vm, Inst);
inst->ins = ins;
ins->type = cls;
pkVarBufferInit(&ins->fields);
if (initialize && cls->field_names.count != 0) {
pkVarBufferFill(&ins->fields, vm, VAR_NULL, cls->field_names.count);
}
vmPopTempRef(vm); // inst.
return inst;
}
Instance* newInstanceNative(PKVM* vm, void* data, uint32_t id) {
Instance* inst = ALLOCATE(vm, Instance);
varInitObject(&inst->_super, vm, OBJ_INST);
inst->is_native = true;
inst->native_id = id;
if (vm->config.inst_name_fn != NULL) {
inst->ty_name = vm->config.inst_name_fn(id);
} else {
inst->ty_name = "$(?)";
}
inst->native = data;
inst->cls = cls;
inst->native = NULL;
return inst;
}
@ -1058,27 +1015,14 @@ void freeObject(PKVM* vm, Object* self) {
case OBJ_CLASS: {
Class* cls = (Class*)self;
pkUintBufferClear(&cls->field_names, vm);
} break;
case OBJ_INST:
{
case OBJ_INST: {
Instance* inst = (Instance*)self;
if (inst->is_native) {
if (vm->config.inst_free_fn != NULL) {
// TODO: Allow user to set error when freeing the object.
vm->config.inst_free_fn(vm, inst->native, inst->native_id);
}
} else {
Inst* ins = inst->ins;
pkVarBufferClear(&ins->fields, vm);
DEALLOCATE(vm, ins);
if (inst->native) {
TODO; // Call native clean function.
}
break;
}
} break;
}
DEALLOCATE(vm, self);
@ -1185,129 +1129,35 @@ void moduleAddMain(PKVM* vm, Module* module) {
}
bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) {
ASSERT(inst != NULL, OOPS);
ASSERT(attrib != NULL, OOPS);
ASSERT(value != NULL, OOPS);
ASSERT((inst != NULL) && (attrib != NULL) && (value != NULL), OOPS);
// This function should only be called at runtime.
ASSERT(vm->fiber != NULL, OOPS);
if (inst->is_native) {
if (vm->config.inst_get_attrib_fn) {
// Temproarly change the fiber's "return address" to points to the
// below var 'val' so that the users can use 'pkReturn...()' function
// to return the attribute as well.
Var* temp = vm->fiber->ret;
Var val = VAR_UNDEFINED;
vm->fiber->ret = &val;
PkStringPtr attr = { attrib->data, NULL, NULL,
attrib->length, attrib->hash };
vm->config.inst_get_attrib_fn(vm, inst->native, inst->native_id, attr);
vm->fiber->ret = temp;
if (IS_UNDEF(val)) {
// FIXME: add a list of attribute overrides.
if ((CHECK_HASH("as_string", 0xbdef4147) == attrib->hash) &&
IS_CSTR_EQ(attrib, "as_string", 9)) {
*value = VAR_OBJ(toRepr(vm, VAR_OBJ(inst)));
return true;
}
// If we reached here, the native instance don't have the attribute
// and no overriden attributes found, return false to indicate that the
// attribute doesn't exists.
return false;
}
// Attribute [val] updated by the hosting application.
*value = val;
return true;
}
// If the hosting application doesn't provided a getter function, we treat
// it as if the instance don't has the attribute.
return false;
} else {
// TODO: Optimize this with binary search.
Class* cls = inst->ins->type;
for (uint32_t i = 0; i < cls->field_names.count; i++) {
ASSERT_INDEX(i, cls->field_names.count);
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;
}
}
// Couldn't find the attribute in it's type class, return false.
return false;
if (inst->native != NULL) {
}
TODO;
UNREACHABLE();
return false;
Var value_ = mapGet(&inst->attribs, VAR_OBJ(attrib));
if (IS_UNDEF(value_)) return false;
*value = value_;
return true;
}
bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) {
ASSERT((inst != NULL) && (attrib != NULL), OOPS);
if (inst->is_native) {
if (inst->native != NULL) {
// Try setting the attribute from the native interface, and if success, we
// should return. otherwise the code will "fall through" and set on it's
// dynamic attributes map.
TODO;
if (vm->config.inst_set_attrib_fn) {
// Temproarly change the fiber's "return address" to points to the
// below var 'attrib_ptr' so that the users can use 'pkGetArg...()'
// function to validate and get the attribute (users should use 0 as the
// index of the argument since it's at the return address and we cannot
// ensure fiber->ret[1] will be in bounds).
Var* temp = vm->fiber->ret;
Var attrib_ptr = value;
vm->fiber->ret = &attrib_ptr;
PkStringPtr attr = { attrib->data, NULL, NULL,
attrib->length, attrib->hash };
bool exists = vm->config.inst_set_attrib_fn(vm, inst->native,
inst->native_id, attr);
vm->fiber->ret = temp;
// If the type is incompatible there'll be an error by now, return false
// and the user of this function has to check VM_HAS_ERROR() as well.
if (VM_HAS_ERROR(vm)) return false;
// If the attribute exists on the native type, the host application would
// returned true by now, return it.
return exists;
}
// If the host application doesn't provided a setter we treat it as it
// doesn't has the attribute.
return false;
} else {
// TODO: Optimize this with binary search.
Class* ty = inst->ins->type;
for (uint32_t i = 0; i < ty->field_names.count; i++) {
ASSERT_INDEX(i, ty->field_names.count);
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) {
inst->ins->fields.data[i] = value;
return true;
}
}
// Couldn't find the attribute in it's type class, return false.
return false;
// FIXME:
// Only return true if attribute have been set.
return true;
}
UNREACHABLE();
return false;
mapSet(vm, &inst->attribs, VAR_OBJ(attrib), value);
return true;
}
/*****************************************************************************/
@ -1693,35 +1543,16 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
{
const Instance* inst = (const Instance*)obj;
pkByteBufferWrite(buff, vm, '[');
pkByteBufferAddString(buff, vm, inst->ty_name,
(uint32_t)strlen(inst->ty_name));
pkByteBufferAddString(buff, vm, inst->cls->name->data,
inst->cls->name->length);
pkByteBufferWrite(buff, vm, ':');
if (!inst->is_native) {
const Class* cls = inst->ins->type;
const Inst* ins = inst->ins;
ASSERT(ins->fields.count == cls->field_names.count, OOPS);
for (uint32_t i = 0; i < cls->field_names.count; i++) {
if (i != 0) pkByteBufferWrite(buff, vm, ',');
pkByteBufferWrite(buff, vm, ' ');
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);
}
} else {
char buff_addr[STR_HEX_BUFF_SIZE];
char* ptr = (char*)buff_addr;
(*ptr++) = '0'; (*ptr++) = 'x';
const int len = snprintf(ptr, sizeof(buff_addr) - 2,
"%08x", (unsigned int)(uintptr_t)inst->native);
pkByteBufferAddString(buff, vm, buff_addr, (uint32_t)len);
}
char buff_addr[STR_HEX_BUFF_SIZE];
char* ptr = (char*)buff_addr;
(*ptr++) = '0'; (*ptr++) = 'x';
const int len = snprintf(ptr, sizeof(buff_addr) - 2,
"%08x", (unsigned int)(uintptr_t)inst);
pkByteBufferAddString(buff, vm, buff_addr, (uint32_t)len);
pkByteBufferWrite(buff, vm, ']');
return;
}

View File

@ -491,8 +491,6 @@ struct Class {
const char* docstring;
Closure* ctor; //< The constructor function.
pkUintBuffer field_names; //< Buffer of field names.
// TODO: ordered names buffer for binary search.
};
typedef struct {
@ -503,15 +501,17 @@ typedef struct {
struct Instance {
Object _super;
const char* ty_name; //< Name of the type it belongs to.
Class* cls; //< Class of the instance.
bool is_native; //< True if it's a native type instance.
uint32_t native_id; //< Unique ID of this native instance.
// If the instance is native, the [native] pointer points to the user data
// (generally a heap allocated struct of that type) that contains it's
// attributes. We'll use it to access an attribute first with setters and
// getters and if the attribute not exists we'll continue search in the
// bellow attribs map.
void* native;
union {
void* native; //< C struct pointer. // TODO:
Inst* ins; //< Module instance pointer.
};
// Dynamic attributes of an instance.
Map attribs;
};
/*****************************************************************************/
@ -569,16 +569,8 @@ Class* newClass(PKVM* vm, const char* name, int length,
Module* module, const char* docstring,
int* cls_index);
// Allocate new instance with of the base [type]. Note that if [initialize] is
// false, the field value buffer of the instance would be un initialized (ie.
// the buffer count = 0). Otherwise they'll be set to VAR_NULL.
Instance* newInstance(PKVM* vm, Class* cls, bool initialize);
// Allocate new native instance and with [data] as the native type handle and
// return Instance*. The [id] is the unique id of the instance, this would be
// used to check if two instances are equal and used to get the name of the
// instance using NativeTypeNameFn callback.
Instance* newInstanceNative(PKVM* vm, void* data, uint32_t id);
// Allocate new instance with of the base [type].
Instance* newInstance(PKVM* vm, Class* cls);
/*****************************************************************************/
/* METHODS */

View File

@ -31,11 +31,6 @@ PkConfiguration pkNewConfiguration(void) {
config.write_fn = NULL;
config.read_fn = NULL;
config.inst_free_fn = NULL;
config.inst_name_fn = NULL;
config.inst_get_attrib_fn = NULL;
config.inst_set_attrib_fn = NULL;
config.load_script_fn = NULL;
config.resolve_path_fn = NULL;
config.user_data = NULL;
@ -857,17 +852,6 @@ L_vm_main_loop:
DISPATCH();
}
OPCODE(PUSH_INSTANCE):
{
uint8_t index = READ_SHORT();
ASSERT_INDEX(index, module->constants.count);
ASSERT(IS_OBJ_TYPE(module->constants.data[index], OBJ_CLASS), OOPS);
Instance* inst = newInstance(vm,
(Class*)AS_OBJ(module->constants.data[index]), false);
PUSH(VAR_OBJ(inst));
DISPATCH();
}
OPCODE(LIST_APPEND):
{
Var elem = PEEK(-1); // Don't pop yet, we need the reference for gc.
@ -898,21 +882,6 @@ L_vm_main_loop:
DISPATCH();
}
OPCODE(INST_APPEND):
{
Var value = PEEK(-1); // Don't pop yet, we need the reference for gc.
Var inst = PEEK(-2);
ASSERT(IS_OBJ_TYPE(inst, OBJ_INST), OOPS);
Instance* inst_p = (Instance*)AS_OBJ(inst);
ASSERT(!inst_p->is_native, OOPS);
Inst* ins = inst_p->ins;
pkVarBufferWrite(&ins->fields, vm, value);
DROP(); // value
DISPATCH();
}
OPCODE(PUSH_LOCAL_0):
OPCODE(PUSH_LOCAL_1):
OPCODE(PUSH_LOCAL_2):