mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 15:16:41 +08:00
early root marking implemented
This commit is contained in:
parent
34716b4f6b
commit
bd4642be70
@ -113,7 +113,7 @@ typedef struct {
|
|||||||
|
|
||||||
// Initialize the configuration and set ALL of it's values to the defaults.
|
// Initialize the configuration and set ALL of it's values to the defaults.
|
||||||
// Call this before setting any particular field of it.
|
// Call this before setting any particular field of it.
|
||||||
void MSInitConfiguration(MSConfiguration* config);
|
void msInitConfiguration(MSConfiguration* config);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RESULT_SUCCESS = 0,
|
RESULT_SUCCESS = 0,
|
||||||
@ -124,6 +124,9 @@ typedef enum {
|
|||||||
// Allocate initialize and returns a new VM
|
// Allocate initialize and returns a new VM
|
||||||
MSVM* msNewVM(MSConfiguration* config);
|
MSVM* msNewVM(MSConfiguration* config);
|
||||||
|
|
||||||
|
// Clean the VM and dispose all the resources allocated by the VM.
|
||||||
|
void msFreeVM(MSVM* vm);
|
||||||
|
|
||||||
// Compile and execut file at given path.
|
// Compile and execut file at given path.
|
||||||
MSInterpretResult msInterpret(MSVM* vm, const char* file);
|
MSInterpretResult msInterpret(MSVM* vm, const char* file);
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ static _Keyword _keywords[] = {
|
|||||||
{ "continue", 8, TK_CONTINUE },
|
{ "continue", 8, TK_CONTINUE },
|
||||||
{ "return", 6, TK_RETURN },
|
{ "return", 6, TK_RETURN },
|
||||||
|
|
||||||
{ NULL, (TokenType)(0) }, // Sentinal to mark the end of the array
|
{ NULL, 0, (TokenType)(0) }, // Sentinal to mark the end of the array
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -195,7 +195,6 @@ typedef struct {
|
|||||||
|
|
||||||
// Precedence parsing references:
|
// Precedence parsing references:
|
||||||
// https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
// https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
||||||
// TODO: I should explicitly state wren-lang as a reference "globaly".
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PREC_NONE,
|
PREC_NONE,
|
||||||
@ -299,9 +298,7 @@ struct Compiler {
|
|||||||
int var_count; //< Number of locals in [variables].
|
int var_count; //< Number of locals in [variables].
|
||||||
int global_count; //< Number of globals in [variables].
|
int global_count; //< Number of globals in [variables].
|
||||||
|
|
||||||
int stack_size; //< Current size including locals ind temps.
|
int stack_size; //< Current size including locals ind temps.=
|
||||||
|
|
||||||
// TODO: compiler should mark Script* below not to be garbage collected.
|
|
||||||
|
|
||||||
Script* script; //< Current script.
|
Script* script; //< Current script.
|
||||||
Loop* loop; //< Current loop.
|
Loop* loop; //< Current loop.
|
||||||
@ -1035,7 +1032,8 @@ static void exprName(Compiler* compiler, bool can_assign) {
|
|||||||
if (can_assign && matchAssignment(parser)) {
|
if (can_assign && matchAssignment(parser)) {
|
||||||
TokenType assignment = parser->previous.type;
|
TokenType assignment = parser->previous.type;
|
||||||
if (assignment != TK_EQ) {
|
if (assignment != TK_EQ) {
|
||||||
emitPushVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
emitPushVariable(compiler, result.index,
|
||||||
|
result.type == NAME_GLOBAL_VAR);
|
||||||
compileExpression(compiler);
|
compileExpression(compiler);
|
||||||
|
|
||||||
switch (assignment) {
|
switch (assignment) {
|
||||||
@ -1800,3 +1798,17 @@ Script* compileSource(MSVM* vm, const char* path) {
|
|||||||
if (compiler.parser.has_errors) return NULL;
|
if (compiler.parser.has_errors) return NULL;
|
||||||
return script;
|
return script;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void compilerMarkObjects(Compiler* compiler, MSVM* vm) {
|
||||||
|
|
||||||
|
// Mark the script which is currently being compiled.
|
||||||
|
markObject(&compiler->script->_super, vm);
|
||||||
|
|
||||||
|
// Mark the string literals (they haven't added to the script's literal
|
||||||
|
// buffer yet).
|
||||||
|
markValue(compiler->parser.current.value, vm);
|
||||||
|
markValue(compiler->parser.previous.value, vm);
|
||||||
|
markValue(compiler->parser.next.value, vm);
|
||||||
|
}
|
||||||
|
@ -13,4 +13,6 @@ typedef struct Compiler Compiler;
|
|||||||
|
|
||||||
Script* compileSource(MSVM* vm, const char* path);
|
Script* compileSource(MSVM* vm, const char* path);
|
||||||
|
|
||||||
|
void compilerMarkObjects(Compiler* compiler, MSVM* vm);
|
||||||
|
|
||||||
#endif // COMPILER_H
|
#endif // COMPILER_H
|
||||||
|
69
src/core.c
69
src/core.c
@ -14,7 +14,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
const char* name; //< Name of the function.
|
const char* name; //< Name of the function.
|
||||||
int length; //< Length of the name.
|
int length; //< Length of the name.
|
||||||
Function fn; //< Native function pointer.
|
Function* fn; //< Native function pointer.
|
||||||
} _BuiltinFn;
|
} _BuiltinFn;
|
||||||
|
|
||||||
// Count of builtin function +1 for termination.
|
// Count of builtin function +1 for termination.
|
||||||
@ -24,26 +24,23 @@ typedef struct {
|
|||||||
#define _AS_INTEGER(var) (int32_t)trunc(AS_NUM(var))
|
#define _AS_INTEGER(var) (int32_t)trunc(AS_NUM(var))
|
||||||
|
|
||||||
// Array of all builtin functions.
|
// Array of all builtin functions.
|
||||||
_BuiltinFn builtins[BUILTIN_COUNT];
|
static _BuiltinFn builtins[BUILTIN_COUNT];
|
||||||
|
static int builtins_count = 0;
|
||||||
|
|
||||||
static void initializeBuiltinFN(MSVM* vm, _BuiltinFn* bfn, const char* name,
|
static void initializeBuiltinFN(MSVM* vm, _BuiltinFn* bfn, const char* name,
|
||||||
int length, int arity, MiniScriptNativeFn ptr) {
|
int length, int arity, MiniScriptNativeFn ptr) {
|
||||||
bfn->name = name;
|
bfn->name = name;
|
||||||
bfn->length = length;
|
bfn->length = length;
|
||||||
|
|
||||||
varInitObject(&bfn->fn._super, vm, OBJ_FUNC);
|
bfn->fn = newFunction(vm, name, length, NULL, true);
|
||||||
bfn->fn.name = name;
|
bfn->fn->arity = arity;
|
||||||
bfn->fn.arity = arity;
|
bfn->fn->native = ptr;
|
||||||
bfn->fn.owner = NULL;
|
|
||||||
bfn->fn.is_native = true;
|
|
||||||
bfn->fn.native = ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int findBuiltinFunction(const char* name, int length) {
|
int findBuiltinFunction(const char* name, int length) {
|
||||||
for (int i = 0; i < BUILTIN_COUNT; i++) {
|
for (int i = 0; i < builtins_count; i++) {
|
||||||
if (builtins[i].name == NULL) return -1;
|
if (length == builtins[i].length &&
|
||||||
|
strncmp(name, builtins[i].name, length) == 0) {
|
||||||
if (length == builtins[i].length && strncmp(name, builtins[i].name, length) == 0) {
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,12 +115,12 @@ static inline bool validateIndex(MSVM* vm, int32_t index, int32_t size,
|
|||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
Function* getBuiltinFunction(int index) {
|
Function* getBuiltinFunction(int index) {
|
||||||
ASSERT(index < BUILTIN_COUNT, "Index out of bound.");
|
ASSERT_INDEX(index, builtins_count);
|
||||||
return &builtins[index].fn;
|
return builtins[index].fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* getBuiltinFunctionName(int index) {
|
const char* getBuiltinFunctionName(int index) {
|
||||||
ASSERT(index < BUILTIN_COUNT, "Index out of bound.");
|
ASSERT_INDEX(index, builtins_count);
|
||||||
return builtins[index].name;
|
return builtins[index].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,10 +216,11 @@ void stdOsClock(MSVM* vm) {
|
|||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
void initializeCore(MSVM* vm) {
|
void initializeCore(MSVM* vm) {
|
||||||
|
|
||||||
int i = 0; //< Iterate through builtins.
|
ASSERT(builtins_count == 0, "Initialize core only once.");
|
||||||
|
|
||||||
#define INITALIZE_BUILTIN_FN(name, fn, argc) \
|
#define INITALIZE_BUILTIN_FN(name, fn, argc) \
|
||||||
initializeBuiltinFN(vm, &builtins[i++], name, (int)strlen(name), argc, fn);
|
initializeBuiltinFN(vm, &builtins[builtins_count++], name, \
|
||||||
|
(int)strlen(name), argc, fn);
|
||||||
|
|
||||||
// Initialize builtin functions.
|
// Initialize builtin functions.
|
||||||
INITALIZE_BUILTIN_FN("is_null", coreIsNull, 1);
|
INITALIZE_BUILTIN_FN("is_null", coreIsNull, 1);
|
||||||
@ -242,7 +240,7 @@ void initializeCore(MSVM* vm) {
|
|||||||
INITALIZE_BUILTIN_FN("import", coreImport, 1);
|
INITALIZE_BUILTIN_FN("import", coreImport, 1);
|
||||||
|
|
||||||
// Sentinal to mark the end of the array.
|
// Sentinal to mark the end of the array.
|
||||||
initializeBuiltinFN(vm, &builtins[i], NULL, 0, 0, NULL);
|
//initializeBuiltinFN(vm, &builtins[i], NULL, 0, 0, NULL);
|
||||||
|
|
||||||
// Make STD scripts.
|
// Make STD scripts.
|
||||||
Script* std; // A temporary pointer to the current std script.
|
Script* std; // A temporary pointer to the current std script.
|
||||||
@ -273,6 +271,12 @@ void initializeCore(MSVM* vm) {
|
|||||||
STD_ADD_FUNCTION("clock", stdOsClock, 0);
|
STD_ADD_FUNCTION("clock", stdOsClock, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void markCoreObjects(MSVM* vm) {
|
||||||
|
for (int i = 0; i < builtins_count; i++) {
|
||||||
|
markObject(&builtins[i].fn->_super, vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* OPERATORS */
|
/* OPERATORS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -287,7 +291,32 @@ Var varAdd(MSVM* vm, Var v1, Var v2) {
|
|||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
TODO; //string addition/ array addition etc.
|
if (IS_OBJ(v1) && IS_OBJ(v2)) {
|
||||||
|
Object *o1 = AS_OBJ(v1), *o2 = AS_OBJ(v2);
|
||||||
|
switch (o1->type) {
|
||||||
|
|
||||||
|
case OBJ_STRING:
|
||||||
|
{
|
||||||
|
if (o2->type == OBJ_STRING) {
|
||||||
|
TODO; // Implement String.format('@@', s1, s2);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OBJ_LIST:
|
||||||
|
case OBJ_MAP:
|
||||||
|
case OBJ_RANGE:
|
||||||
|
case OBJ_SCRIPT:
|
||||||
|
case OBJ_FUNC:
|
||||||
|
case OBJ_FIBER:
|
||||||
|
case OBJ_USER:
|
||||||
|
TODO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
msSetRuntimeError(vm, "Unsupported operand types for operator '-' "
|
||||||
|
"%s and %s", varTypeName(v1), varTypeName(v2));
|
||||||
|
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,6 +329,8 @@ Var varSubtract(MSVM* vm, Var v1, Var v2) {
|
|||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TODO; // for user objects call vm.config.sub_userobj_sub(handles).
|
||||||
|
|
||||||
msSetRuntimeError(vm, "Unsupported operand types for operator '-' "
|
msSetRuntimeError(vm, "Unsupported operand types for operator '-' "
|
||||||
"%s and %s", varTypeName(v1), varTypeName(v2));
|
"%s and %s", varTypeName(v1), varTypeName(v2));
|
||||||
|
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
// 2. Destroy scripts buffer only when the last VM die.
|
// 2. Destroy scripts buffer only when the last VM die.
|
||||||
void initializeCore(MSVM* vm);
|
void initializeCore(MSVM* vm);
|
||||||
|
|
||||||
|
// Mark the heap allocated core object at the mark phase.
|
||||||
|
void markCoreObjects(MSVM* vm);
|
||||||
|
|
||||||
// Find the builtin function name and returns it's index in the builtins array
|
// Find the builtin function name and returns it's index in the builtins array
|
||||||
// if not found returns -1.
|
// if not found returns -1.
|
||||||
int findBuiltinFunction(const char* name, int length);
|
int findBuiltinFunction(const char* name, int length);
|
||||||
|
98
src/var.c
98
src/var.c
@ -40,9 +40,31 @@ const char* msAsString(MSVM* vm, Var value) {
|
|||||||
|
|
||||||
void varInitObject(Object* self, MSVM* vm, ObjectType type) {
|
void varInitObject(Object* self, MSVM* vm, ObjectType type) {
|
||||||
self->type = type;
|
self->type = type;
|
||||||
|
self->is_marked = false;
|
||||||
self->next = vm->first;
|
self->next = vm->first;
|
||||||
vm->first = self;
|
vm->first = self;
|
||||||
// TODO: set isGray = false;
|
}
|
||||||
|
|
||||||
|
void markObject(Object* self, MSVM* vm) {
|
||||||
|
if (self == NULL || self->is_marked) return;
|
||||||
|
self->is_marked = true;
|
||||||
|
|
||||||
|
// Add the object to the VM's gray_list so that we can recursively mark
|
||||||
|
// it's referenced objects later.
|
||||||
|
if (vm->marked_list_count >= vm->marked_list_capacity) {
|
||||||
|
vm->marked_list_capacity *= 2;
|
||||||
|
vm->marked_list = (Object**)vm->config.realloc_fn(
|
||||||
|
vm->marked_list,
|
||||||
|
vm->marked_list_capacity * sizeof(Object*),
|
||||||
|
vm->config.user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
vm->marked_list[vm->marked_list_count++] = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void markValue(Var self, MSVM* vm) {
|
||||||
|
if (!IS_OBJ(self)) return;
|
||||||
|
markObject(AS_OBJ(self), vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if VAR_NAN_TAGGING
|
#if VAR_NAN_TAGGING
|
||||||
@ -134,6 +156,13 @@ Function* newFunction(MSVM* vm, const char* name, int length, Script* owner,
|
|||||||
Function* func = ALLOCATE(vm, Function);
|
Function* func = ALLOCATE(vm, Function);
|
||||||
varInitObject(&func->_super, vm, OBJ_FUNC);
|
varInitObject(&func->_super, vm, OBJ_FUNC);
|
||||||
|
|
||||||
|
if (owner == NULL) {
|
||||||
|
ASSERT(is_native, OOPS);
|
||||||
|
func->name = name;
|
||||||
|
func->owner = NULL;
|
||||||
|
func->is_native = is_native;
|
||||||
|
|
||||||
|
} else {
|
||||||
// Add the name in the script's function buffer.
|
// Add the name in the script's function buffer.
|
||||||
String* name_ptr;
|
String* name_ptr;
|
||||||
vmPushTempRef(vm, &func->_super);
|
vmPushTempRef(vm, &func->_super);
|
||||||
@ -145,6 +174,8 @@ Function* newFunction(MSVM* vm, const char* name, int length, Script* owner,
|
|||||||
func->owner = owner;
|
func->owner = owner;
|
||||||
func->arity = -2; // -1 means variadic args.
|
func->arity = -2; // -1 means variadic args.
|
||||||
func->is_native = is_native;
|
func->is_native = is_native;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (is_native) {
|
if (is_native) {
|
||||||
func->native = NULL;
|
func->native = NULL;
|
||||||
@ -159,6 +190,71 @@ Function* newFunction(MSVM* vm, const char* name, int length, Script* owner,
|
|||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Fiber* newFiber(MSVM* vm) {
|
||||||
|
Fiber* fiber = ALLOCATE(vm, Fiber);
|
||||||
|
memset(fiber, 0, sizeof(Fiber));
|
||||||
|
varInitObject(&fiber->_super, vm, OBJ_FIBER);
|
||||||
|
return fiber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeObject(MSVM* vm, Object* obj) {
|
||||||
|
// TODO: Debug trace memory here.
|
||||||
|
|
||||||
|
// First clean the object's referencs, but we're not recursively doallocating
|
||||||
|
// them because they're not marked and will be cleaned later.
|
||||||
|
// Example: List's `elements` is VarBuffer that contain a heap allocated
|
||||||
|
// array of `var*` which will be cleaned below but the actual `var` elements
|
||||||
|
// will won't be freed here instead they havent marked at all, and will be
|
||||||
|
// removed at the sweeping phase of the garbage collection.
|
||||||
|
switch (obj->type) {
|
||||||
|
case OBJ_STRING:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBJ_LIST:
|
||||||
|
varBufferClear(&(((List*)obj)->elements), vm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBJ_MAP:
|
||||||
|
TODO;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBJ_RANGE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBJ_SCRIPT: {
|
||||||
|
Script* scr = (Script*)obj;
|
||||||
|
varBufferClear(&scr->globals, vm);
|
||||||
|
nameTableClear(&scr->global_names, vm);
|
||||||
|
varBufferClear(&scr->literals, vm);
|
||||||
|
functionBufferClear(&scr->functions, vm);
|
||||||
|
nameTableClear(&scr->function_names, vm);
|
||||||
|
stringBufferClear(&scr->names, vm);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OBJ_FUNC:
|
||||||
|
{
|
||||||
|
Function* func = (Function*)obj;
|
||||||
|
if (!func->is_native) {
|
||||||
|
byteBufferClear(&func->fn->opcodes, vm);
|
||||||
|
intBufferClear(&func->fn->oplines, vm);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OBJ_FIBER:
|
||||||
|
{
|
||||||
|
Fiber* fiber = (Fiber*)obj;
|
||||||
|
DEALLOCATE(vm, fiber->stack);
|
||||||
|
DEALLOCATE(vm, fiber->frames);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OBJ_USER:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEALLOCATE(vm, obj);
|
||||||
|
}
|
||||||
|
|
||||||
// Utility functions //////////////////////////////////////////////////////////
|
// Utility functions //////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const char* varTypeName(Var v) {
|
const char* varTypeName(Var v) {
|
||||||
|
62
src/var.h
62
src/var.h
@ -136,7 +136,7 @@
|
|||||||
// Check types.
|
// Check types.
|
||||||
#define IS_CONST(value) ((value & _MASK_CONST) == _MASK_CONST)
|
#define IS_CONST(value) ((value & _MASK_CONST) == _MASK_CONST)
|
||||||
#define IS_NULL(value) ((value) == VAR_NULL)
|
#define IS_NULL(value) ((value) == VAR_NULL)
|
||||||
#define IS_UNDEF(value) ((value) == VAR_UNDEF)
|
#define IS_UNDEF(value) ((value) == VAR_UNDEFINED)
|
||||||
#define IS_FALSE(value) ((value) == VAR_FALSE)
|
#define IS_FALSE(value) ((value) == VAR_FALSE)
|
||||||
#define IS_TRUE(value) ((value) == VAR_TRUE)
|
#define IS_TRUE(value) ((value) == VAR_TRUE)
|
||||||
#define IS_BOOL(value) (IS_TRUE(value) || IS_FALSE(value))
|
#define IS_BOOL(value) (IS_TRUE(value) || IS_FALSE(value))
|
||||||
@ -210,6 +210,7 @@ typedef enum /* ObjectType */ {
|
|||||||
// Base struct for all heap allocated objects.
|
// Base struct for all heap allocated objects.
|
||||||
struct Object {
|
struct Object {
|
||||||
ObjectType type; //< Type of the object in \ref var_Object_Type.
|
ObjectType type; //< Type of the object in \ref var_Object_Type.
|
||||||
|
bool is_marked; //< Marked when garbage collection's marking phase.
|
||||||
//Class* is; //< The class the object IS. // No OOP in MS.
|
//Class* is; //< The class the object IS. // No OOP in MS.
|
||||||
|
|
||||||
Object* next; //< Next object in the heap allocated link list.
|
Object* next; //< Next object in the heap allocated link list.
|
||||||
@ -280,10 +281,59 @@ struct Function {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t* ip; //< Pointer to the next instruction byte code.
|
||||||
|
Function* fn; //< Function of the frame.
|
||||||
|
Var* rbp; //< Stack base pointer. (%rbp)
|
||||||
|
} CallFrame;
|
||||||
|
|
||||||
|
struct Fiber {
|
||||||
|
Object _super;
|
||||||
|
|
||||||
|
// The root function of the fiber. (For script it'll be the script's implicit
|
||||||
|
// body function).
|
||||||
|
Function* func;
|
||||||
|
|
||||||
|
// The stack of the execution holding locals and temps. A heap will be
|
||||||
|
// allocated and grow as needed.
|
||||||
|
Var* stack;
|
||||||
|
|
||||||
|
// The stack pointer (%rsp) pointing to the stack top.
|
||||||
|
Var* sp;
|
||||||
|
|
||||||
|
// The stack base pointer of the current frame. It'll be updated before
|
||||||
|
// calling a native function. (`fiber->ret` === `curr_call_frame->rbp`).
|
||||||
|
Var* ret;
|
||||||
|
|
||||||
|
// Size of the allocated stack.
|
||||||
|
int stack_size;
|
||||||
|
|
||||||
|
// Heap allocated array of call frames will grow as needed.
|
||||||
|
CallFrame* frames;
|
||||||
|
|
||||||
|
// Capacity of the frames array.
|
||||||
|
int frame_capacity;
|
||||||
|
|
||||||
|
// Number of frame entry in frames.
|
||||||
|
int frame_count;
|
||||||
|
|
||||||
|
// Runtime error initially NULL, heap allocated.
|
||||||
|
String* error;
|
||||||
|
};
|
||||||
|
|
||||||
// Methods ////////////////////////////////////////////////////////////////////
|
// Methods ////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Initialize the object with it's default value.
|
||||||
void varInitObject(Object* self, MSVM* vm, ObjectType type);
|
void varInitObject(Object* self, MSVM* vm, ObjectType type);
|
||||||
|
|
||||||
|
// Mark the reachable objects at the mark-and-sweep phase of the garbage
|
||||||
|
// collection.
|
||||||
|
void markObject(Object* self, MSVM* vm);
|
||||||
|
|
||||||
|
// Mark the reachable values at the mark-and-sweep phase of the garbage
|
||||||
|
// collection.
|
||||||
|
void markValue(Var self, MSVM* vm);
|
||||||
|
|
||||||
// Instead use VAR_NUM(value) and AS_NUM(value)
|
// Instead use VAR_NUM(value) and AS_NUM(value)
|
||||||
Var doubleToVar(double value);
|
Var doubleToVar(double value);
|
||||||
double varToDouble(Var value);
|
double varToDouble(Var value);
|
||||||
@ -301,10 +351,18 @@ Range* newRange(MSVM* vm, double from, double to);
|
|||||||
Script* newScript(MSVM* vm);
|
Script* newScript(MSVM* vm);
|
||||||
|
|
||||||
// Allocate new Function object and return Function*. Parameter [name] should
|
// Allocate new Function object and return Function*. Parameter [name] should
|
||||||
// be the name in the Script's nametable.
|
// be the name in the Script's nametable. If the [owner] is NULL the function
|
||||||
|
// would be builtin function. For builtin function arity and the native
|
||||||
|
// function pointer would be initialized after calling this function.
|
||||||
Function* newFunction(MSVM* vm, const char* name, int length, Script* owner,
|
Function* newFunction(MSVM* vm, const char* name, int length, Script* owner,
|
||||||
bool is_native);
|
bool is_native);
|
||||||
|
|
||||||
|
// Allocate new Fiber object and return Fiber*.
|
||||||
|
Fiber* newFiber(MSVM* vm);
|
||||||
|
|
||||||
|
// Release all the object owned by the [obj] including itself.
|
||||||
|
void freeObject(MSVM* vm, Object* obj);
|
||||||
|
|
||||||
// Utility functions //////////////////////////////////////////////////////////
|
// Utility functions //////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Returns the type name of the var [v].
|
// Returns the type name of the var [v].
|
||||||
|
105
src/vm.c
105
src/vm.c
@ -25,13 +25,6 @@ static void* defaultRealloc(void* memory, size_t new_size, void* user_data) {
|
|||||||
return realloc(memory, new_size);
|
return realloc(memory, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Fiber* newFiber(MSVM* vm) {
|
|
||||||
Fiber* fiber = ALLOCATE(vm, Fiber);
|
|
||||||
memset(fiber, 0, sizeof(Fiber));
|
|
||||||
varInitObject(&fiber->_super, vm, OBJ_FIBER);
|
|
||||||
return fiber;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
||||||
|
|
||||||
// TODO: Debug trace allocations here.
|
// TODO: Debug trace allocations here.
|
||||||
@ -44,7 +37,7 @@ void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
|||||||
if (new_size > 0 && self->bytes_allocated > self->next_gc) {
|
if (new_size > 0 && self->bytes_allocated > self->next_gc) {
|
||||||
vmCollectGarbage(self);
|
vmCollectGarbage(self);
|
||||||
}
|
}
|
||||||
|
Function* f = (Function*)memory;
|
||||||
if (new_size == 0) {
|
if (new_size == 0) {
|
||||||
free(memory);
|
free(memory);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -53,14 +46,47 @@ void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
|||||||
return self->config.realloc_fn(memory, new_size, self->config.user_data);
|
return self->config.realloc_fn(memory, new_size, self->config.user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void msInitConfiguration(MSConfiguration* config) {
|
||||||
|
config->realloc_fn = defaultRealloc;
|
||||||
|
|
||||||
|
// TODO: Handle Null functions before calling them.
|
||||||
|
config->error_fn = NULL;
|
||||||
|
config->write_fn = NULL;
|
||||||
|
|
||||||
|
config->load_script_fn = NULL;
|
||||||
|
config->load_script_done_fn = NULL;
|
||||||
|
config->user_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MSVM* msNewVM(MSConfiguration* config) {
|
||||||
|
MSVM* vm = (MSVM*)malloc(sizeof(MSVM));
|
||||||
|
vmInit(vm, config);
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void msFreeVM(MSVM* self) {
|
||||||
|
// TODO: Check if vm already freed.
|
||||||
|
|
||||||
|
Object* obj = self->first;
|
||||||
|
while (obj != NULL) {
|
||||||
|
Object* next = obj->next;
|
||||||
|
freeObject(self, obj);
|
||||||
|
obj = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->marked_list = (Object**)self->config.realloc_fn(
|
||||||
|
self->marked_list, 0, self->config.user_data);
|
||||||
|
self->config.realloc_fn(self, 0, self->config.user_data);
|
||||||
|
}
|
||||||
|
|
||||||
void vmInit(MSVM* self, MSConfiguration* config) {
|
void vmInit(MSVM* self, MSConfiguration* config) {
|
||||||
memset(self, 0, sizeof(MSVM));
|
memset(self, 0, sizeof(MSVM));
|
||||||
self->config = *config;
|
self->config = *config;
|
||||||
|
|
||||||
self->gray_list_count = 0;
|
self->marked_list_count = 0;
|
||||||
self->gray_list_capacity = 8; // TODO: refactor the magic '8' here.
|
self->marked_list_capacity = 8; // TODO: refactor the magic '8' here.
|
||||||
self->gray_list = (Object**)self->config.realloc_fn(
|
self->marked_list = (Object**)self->config.realloc_fn(
|
||||||
NULL, sizeof(Object*) * self->gray_list_capacity, NULL);
|
NULL, sizeof(Object*) * self->marked_list_capacity, NULL);
|
||||||
self->next_gc = 1024 * 1024 * 10; // TODO:
|
self->next_gc = 1024 * 1024 * 10; // TODO:
|
||||||
|
|
||||||
// TODO: no need to initialize if already done by another vm.
|
// TODO: no need to initialize if already done by another vm.
|
||||||
@ -85,6 +111,33 @@ void vmCollectGarbage(MSVM* self) {
|
|||||||
// required to know the size of each object that'll be freeing.
|
// required to know the size of each object that'll be freeing.
|
||||||
self->bytes_allocated = 0;
|
self->bytes_allocated = 0;
|
||||||
|
|
||||||
|
// Mark core objects (mostlikely builtin functions).
|
||||||
|
markCoreObjects(self);
|
||||||
|
|
||||||
|
// Mark all the 'std' scripts.
|
||||||
|
for (int i = 0; i < self->std_count; i++) {
|
||||||
|
markObject(&(self->std_scripts[i]->_super), self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark temp references.
|
||||||
|
for (int i = 0; i < self->temp_reference_count; i++) {
|
||||||
|
markObject(self->temp_reference[i], self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Garbage collection triggered at the middle of a compilation.
|
||||||
|
if (self->compiler != NULL) {
|
||||||
|
compilerMarkObjects(self->compiler, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Garbage collection triggered at the middle of runtime.
|
||||||
|
if (self->script != NULL) {
|
||||||
|
markObject(&self->script->_super, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->fiber != NULL) {
|
||||||
|
markObject(&self->fiber->_super, self);
|
||||||
|
}
|
||||||
|
|
||||||
TODO;
|
TODO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,31 +207,11 @@ void vmReportError(MSVM* vm) {
|
|||||||
ASSERT(false, "TODO: create debug.h");
|
ASSERT(false, "TODO: create debug.h");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MSInitConfiguration(MSConfiguration* config) {
|
|
||||||
config->realloc_fn = defaultRealloc;
|
|
||||||
|
|
||||||
// TODO: Handle Null functions before calling them.
|
|
||||||
config->error_fn = NULL;
|
|
||||||
config->write_fn = NULL;
|
|
||||||
|
|
||||||
config->load_script_fn = NULL;
|
|
||||||
config->load_script_done_fn = NULL;
|
|
||||||
config->user_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSVM* msNewVM(MSConfiguration* config) {
|
|
||||||
MSVM* vm = (MSVM*)malloc(sizeof(MSVM));
|
|
||||||
vmInit(vm, config);
|
|
||||||
return vm;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSInterpretResult msInterpret(MSVM* vm, const char* file) {
|
MSInterpretResult msInterpret(MSVM* vm, const char* file) {
|
||||||
Script* script = compileSource(vm, file);
|
Script* script = compileSource(vm, file);
|
||||||
if (script == NULL) return RESULT_COMPILE_ERROR;
|
if (script == NULL) return RESULT_COMPILE_ERROR;
|
||||||
|
|
||||||
// TODO: The below assertion should be an error report.
|
vm->script = script;
|
||||||
ASSERT(vm->script_count + 1 < MAX_SCRIPT_CACHE, "Scripts cache out of bound.");
|
|
||||||
vm->scripts[vm->script_count++] = script;
|
|
||||||
return vmRunScript(vm, script);
|
return vmRunScript(vm, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,13 +502,15 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
OPCODE(JUMP): {
|
OPCODE(JUMP):
|
||||||
|
{
|
||||||
int offset = READ_SHORT();
|
int offset = READ_SHORT();
|
||||||
ip += offset;
|
ip += offset;
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
OPCODE(LOOP): {
|
OPCODE(LOOP):
|
||||||
|
{
|
||||||
int offset = READ_SHORT();
|
int offset = READ_SHORT();
|
||||||
ip -= offset;
|
ip -= offset;
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
61
src/vm.h
61
src/vm.h
@ -23,46 +23,6 @@ typedef enum {
|
|||||||
#undef OPCODE
|
#undef OPCODE
|
||||||
} Opcode;
|
} Opcode;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t* ip; //< Pointer to the next instruction byte code.
|
|
||||||
Function* fn; //< Function of the frame.
|
|
||||||
Var* rbp; //< Stack base pointer. (%rbp)
|
|
||||||
} CallFrame;
|
|
||||||
|
|
||||||
struct Fiber {
|
|
||||||
Object _super;
|
|
||||||
|
|
||||||
// The root function of the fiber. (For script it'll be the script's implicit
|
|
||||||
// body function).
|
|
||||||
Function* func;
|
|
||||||
|
|
||||||
// The stack of the execution holding locals and temps. A heap will be
|
|
||||||
// allocated and grow as needed.
|
|
||||||
Var* stack;
|
|
||||||
|
|
||||||
// The stack pointer (%rsp) pointing to the stack top.
|
|
||||||
Var* sp;
|
|
||||||
|
|
||||||
// The stack base pointer of the current frame. It'll be updated before
|
|
||||||
// calling a native function. (`fiber->ret` === `curr_call_frame->rbp`).
|
|
||||||
Var* ret;
|
|
||||||
|
|
||||||
// Size of the allocated stack.
|
|
||||||
int stack_size;
|
|
||||||
|
|
||||||
// Heap allocated array of call frames will grow as needed.
|
|
||||||
CallFrame* frames;
|
|
||||||
|
|
||||||
// Capacity of the frames array.
|
|
||||||
int frame_capacity;
|
|
||||||
|
|
||||||
// Number of frame entry in frames.
|
|
||||||
int frame_count;
|
|
||||||
|
|
||||||
// Runtime error initially NULL, heap allocated.
|
|
||||||
String* error;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MSVM {
|
struct MSVM {
|
||||||
|
|
||||||
// The first object in the link list of all heap allocated objects.
|
// The first object in the link list of all heap allocated objects.
|
||||||
@ -76,9 +36,9 @@ struct MSVM {
|
|||||||
|
|
||||||
// In the tri coloring scheme gray is the working list. We recursively pop
|
// In the tri coloring scheme gray is the working list. We recursively pop
|
||||||
// from the list color it balck and add it's referenced objects to gray_list.
|
// from the list color it balck and add it's referenced objects to gray_list.
|
||||||
Object** gray_list;
|
Object** marked_list;
|
||||||
int gray_list_count;
|
int marked_list_count;
|
||||||
int gray_list_capacity;
|
int marked_list_capacity;
|
||||||
|
|
||||||
// A stack of temporary object references to ensure that the object
|
// A stack of temporary object references to ensure that the object
|
||||||
// doesn't garbage collected.
|
// doesn't garbage collected.
|
||||||
@ -88,29 +48,24 @@ struct MSVM {
|
|||||||
// VM's configurations.
|
// VM's configurations.
|
||||||
MSConfiguration config;
|
MSConfiguration config;
|
||||||
|
|
||||||
// Current compiler reference to mark it's heap allocated objects.
|
// Current compiler reference to mark it's heap allocated objects. Note that
|
||||||
|
// The compiler isn't heap allocated.
|
||||||
Compiler* compiler;
|
Compiler* compiler;
|
||||||
|
|
||||||
// Std scripts array. (TODO: assert "std" scripts doesn't have global vars).
|
// Std scripts array. (TODO: assert "std" scripts doesn't have global vars).
|
||||||
Script* std_scripts[MAX_SCRIPT_CACHE];
|
Script* std_scripts[MAX_SCRIPT_CACHE];
|
||||||
|
|
||||||
// Std scripts count.
|
|
||||||
int std_count;
|
int std_count;
|
||||||
|
|
||||||
// Execution variables ////////////////////////////////////////////////////
|
// Execution variables ////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Compiled script cache.
|
// The root script of the runtime and it's one of the VM's reference root.
|
||||||
Script* scripts[MAX_SCRIPT_CACHE];
|
// VM is responsible to manage the memory (TODO: implement handlers).
|
||||||
|
Script* script;
|
||||||
// Number of script cache.
|
|
||||||
int script_count;
|
|
||||||
|
|
||||||
// Current fiber.
|
// Current fiber.
|
||||||
Fiber* fiber;
|
Fiber* fiber;
|
||||||
};
|
};
|
||||||
|
|
||||||
Fiber* newFiber(MSVM* vm);
|
|
||||||
|
|
||||||
// A realloc wrapper which handles memory allocations of the VM.
|
// A realloc wrapper which handles memory allocations of the VM.
|
||||||
// - To allocate new memory pass NULL to parameter [memory] and 0 to
|
// - To allocate new memory pass NULL to parameter [memory] and 0 to
|
||||||
// parameter [old_size] on failure it'll return NULL.
|
// parameter [old_size] on failure it'll return NULL.
|
||||||
|
@ -68,7 +68,7 @@ int main(int argc, char** argv) {
|
|||||||
const char* source_path = argv[1];
|
const char* source_path = argv[1];
|
||||||
|
|
||||||
MSConfiguration config;
|
MSConfiguration config;
|
||||||
MSInitConfiguration(&config);
|
msInitConfiguration(&config);
|
||||||
config.error_fn = errorPrint;
|
config.error_fn = errorPrint;
|
||||||
config.write_fn = writeFunction;
|
config.write_fn = writeFunction;
|
||||||
config.load_script_fn = loadScript;
|
config.load_script_fn = loadScript;
|
||||||
@ -76,6 +76,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
MSVM* vm = msNewVM(&config);
|
MSVM* vm = msNewVM(&config);
|
||||||
MSInterpretResult result = msInterpret(vm, source_path);
|
MSInterpretResult result = msInterpret(vm, source_path);
|
||||||
|
msFreeVM(vm);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user