mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
some (minor) code cleanups
This commit is contained in:
parent
4bf045bcac
commit
749456e215
@ -36,9 +36,10 @@ void errorFunction(PKVM* vm, PkErrorType type, const char* file, int line,
|
||||
bool repl = (ud) ? ud->repl_mode : false;
|
||||
|
||||
if (type == PK_ERROR_COMPILE) {
|
||||
|
||||
|
||||
if (repl) fprintf(stderr, "Error: %s\n", message);
|
||||
else fprintf(stderr, "Error: %s\n at \"%s\":%i\n", message, file, line);
|
||||
else fprintf(stderr, "Error: %s\n at \"%s\":%i\n",
|
||||
message, file, line);
|
||||
|
||||
} else if (type == PK_ERROR_RUNTIME) {
|
||||
fprintf(stderr, "Error: %s\n", message);
|
||||
|
@ -22,9 +22,9 @@ static inline int powerOf2Ceil(int n) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* BYTE BUFFER IMPLEMENTATION */
|
||||
/*****************************************************************************/
|
||||
/*****************************************************************************/
|
||||
/* BYTE BUFFER IMPLEMENTATION */
|
||||
/*****************************************************************************/
|
||||
|
||||
void byteBufferInit(ByteBuffer* buffer) {
|
||||
buffer->data = NULL;
|
||||
|
@ -21,10 +21,10 @@ void byteBufferInit(ByteBuffer* buffer);
|
||||
// Clears the allocated elements from the VM's realloc function.
|
||||
void byteBufferClear(ByteBuffer* buffer);
|
||||
|
||||
// Ensure the capacity is greater than [size], if not resize.
|
||||
// Ensure the capacity is greater than [size], if not resize.
|
||||
void byteBufferReserve(ByteBuffer* buffer, size_t size);
|
||||
|
||||
// Fill the buffer at the end of it with provided data if the capacity
|
||||
// Fill the buffer at the end of it with provided data if the capacity
|
||||
// isn't enough using VM's realloc function.
|
||||
void byteBufferFill(ByteBuffer* buffer, uint8_t data, int count);
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
|
||||
// To implement.
|
||||
|
||||
- Refactor fiber into a module and is_done as an attribute.
|
||||
|
||||
- move moduleAddGlobalInternal() to var.h (also other var functions).
|
||||
|
||||
- refactor build.bat batch script to powershell
|
||||
refactor makefile and setup a github action.
|
||||
|
||||
- check for tabs and trailing white space in source.
|
||||
they're not allowed.
|
||||
|
||||
- Hex, binary literals and floats like ".5".
|
||||
|
||||
|
@ -222,7 +222,11 @@ PK_PUBLIC PkVar pkGetHandleValue(const PkHandle* handle);
|
||||
// this for every handles before freeing the VM.
|
||||
PK_PUBLIC void pkReleaseHandle(PKVM* vm, PkHandle* handle);
|
||||
|
||||
// Add a native function to the given script. If [arity] is -1 that means
|
||||
// Add a global [value] to the given module.
|
||||
PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
|
||||
const char* name, PkHandle* value);
|
||||
|
||||
// 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.
|
||||
PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module,
|
||||
const char* name,
|
||||
|
226
src/pk_core.c
226
src/pk_core.c
@ -13,6 +13,13 @@
|
||||
#include "pk_var.h"
|
||||
#include "pk_vm.h"
|
||||
|
||||
// M_PI is non standard. The macro _USE_MATH_DEFINES defining before importing
|
||||
// <math.h> will define the constants for MSVC. But for a portable solution,
|
||||
// we're defining it ourselves if it isn't already.
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* CORE PUBLIC API */
|
||||
/*****************************************************************************/
|
||||
@ -21,6 +28,10 @@
|
||||
// internal. Which will be wrapped by pkNewModule to return a pkHandle*.
|
||||
static Script* newModuleInternal(PKVM* vm, const char* name);
|
||||
|
||||
// The internal function to add global value to a module.
|
||||
static void moduleAddGlobalInternal(PKVM* vm, Script* script,
|
||||
const char* name, Var value);
|
||||
|
||||
// The internal function to add functions to a module.
|
||||
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
const char* name, pkNativeFn fptr,
|
||||
@ -32,6 +43,18 @@ PkHandle* pkNewModule(PKVM* vm, const char* name) {
|
||||
return vmNewHandle(vm, VAR_OBJ(module));
|
||||
}
|
||||
|
||||
// pkModuleAddGlobal implementation (see pocketlang.h for description).
|
||||
PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
|
||||
const char* name, PkHandle* value) {
|
||||
__ASSERT(module != NULL, "Argument module was NULL.");
|
||||
__ASSERT(value != NULL, "Argument value was NULL.");
|
||||
Var scr = module->value;
|
||||
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
|
||||
|
||||
moduleAddGlobalInternal(vm, (Script*)AS_OBJ(scr), name, value->value);
|
||||
|
||||
}
|
||||
|
||||
// pkModuleAddFunction implementation (see pocketlang.h for description).
|
||||
void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
||||
pkNativeFn fptr, int arity) {
|
||||
@ -88,7 +111,7 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
|
||||
|
||||
#define RET_ERR(err) \
|
||||
do { \
|
||||
vm->fiber->error = err; \
|
||||
VM_SET_ERROR(vm, err); \
|
||||
RET(VAR_NULL); \
|
||||
} while(false)
|
||||
|
||||
@ -102,12 +125,12 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
|
||||
} while (false)
|
||||
|
||||
// Set error for incompatible type provided as an argument.
|
||||
#define ERR_INVALID_ARG_TYPE(m_type) \
|
||||
do { \
|
||||
char buff[STR_INT_BUFF_SIZE]; \
|
||||
sprintf(buff, "%d", arg); \
|
||||
vm->fiber->error = stringFormat(vm, "Expected a " m_type \
|
||||
" at argument $.", buff); \
|
||||
#define ERR_INVALID_ARG_TYPE(m_type) \
|
||||
do { \
|
||||
char buff[STR_INT_BUFF_SIZE]; \
|
||||
sprintf(buff, "%d", arg); \
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Expected a " m_type \
|
||||
" at argument $.", buff)); \
|
||||
} while (false)
|
||||
|
||||
// pkGetArgc implementation (see pocketlang.h for description).
|
||||
@ -175,8 +198,8 @@ bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
|
||||
Var val = ARG(arg);
|
||||
if (pkGetValueType((PkVar)&val) != type) {
|
||||
char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", arg);
|
||||
vm->fiber->error = stringFormat(vm,
|
||||
"Expected a $ at argument $.", getPkVarTypeName(type), buff);
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Expected a $ at argument $.",
|
||||
getPkVarTypeName(type), buff));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -240,7 +263,7 @@ bool pkFiberIsDone(const PkHandle* fiber) {
|
||||
/* VALIDATORS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Check if a numeric value bool/number and set [value].
|
||||
// Check if [var] is a numeric value (bool/number) and set [value].
|
||||
static inline bool isNumeric(Var var, double* value) {
|
||||
if (IS_NUM(var)) {
|
||||
*value = AS_NUM(var);
|
||||
@ -253,17 +276,8 @@ static inline bool isNumeric(Var var, double* value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if [var] is bool/number. If not set error and return false.
|
||||
static inline bool validateNumeric(PKVM* vm, Var var, double* value,
|
||||
const char* name) {
|
||||
if (isNumeric(var, value)) return true;
|
||||
vm->fiber->error = stringFormat(vm, "$ must be a numeric value.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if [var] is 32 bit integer. If not set error and return false.
|
||||
static inline bool validateInteger(PKVM* vm, Var var, int64_t* value,
|
||||
const char* name) {
|
||||
// Check if [var] is a numeric value (bool/number) and set [value].
|
||||
static inline bool isInteger(Var var, int64_t* value) {
|
||||
double number;
|
||||
if (isNumeric(var, &number)) {
|
||||
// TODO: check if the number is larger for a 64 bit integer.
|
||||
@ -273,8 +287,22 @@ static inline bool validateInteger(PKVM* vm, Var var, int64_t* value,
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vm->fiber->error = stringFormat(vm, "$ must be a whole number.", name);
|
||||
// Check if [var] is bool/number. If not set error and return false.
|
||||
static inline bool validateNumeric(PKVM* vm, Var var, double* value,
|
||||
const char* name) {
|
||||
if (isNumeric(var, value)) return true;
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "$ must be a numeric value.", name));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if [var] is 32 bit integer. If not set error and return false.
|
||||
static inline bool validateInteger(PKVM* vm, Var var, int64_t* value,
|
||||
const char* name) {
|
||||
if (isInteger(var, value)) return true;
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "$ must be a whole number.", name));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -283,7 +311,7 @@ static inline bool validateInteger(PKVM* vm, Var var, int64_t* value,
|
||||
static inline bool validateIndex(PKVM* vm, int64_t index, uint32_t size,
|
||||
const char* container) {
|
||||
if (index < 0 || size <= index) {
|
||||
vm->fiber->error = stringFormat(vm, "$ index out of bound.", container);
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "$ index out of bound.", container));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -297,8 +325,8 @@ static inline bool validateIndex(PKVM* vm, int64_t index, uint32_t size,
|
||||
ASSERT(arg > 0 && arg <= ARGC, OOPS); \
|
||||
if (!IS_OBJ(var) || AS_OBJ(var)->type != m_type) { \
|
||||
char buff[12]; sprintf(buff, "%d", arg); \
|
||||
vm->fiber->error = stringFormat(vm, "Expected a " m_name \
|
||||
" at argument $.", buff, false); \
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Expected a " m_name \
|
||||
" at argument $.", buff, false)); \
|
||||
} \
|
||||
*value = (m_class*)AS_OBJ(var); \
|
||||
return true; \
|
||||
@ -413,7 +441,7 @@ static void coreHex(PKVM* vm)) {
|
||||
*ptr++ = '0'; *ptr++ = 'x';
|
||||
|
||||
if (value > UINT32_MAX || value < -(int64_t)(UINT32_MAX)) {
|
||||
vm->fiber->error = newString(vm, "Integer is too large.");
|
||||
VM_SET_ERROR(vm, newString(vm, "Integer is too large."));
|
||||
RET(VAR_NULL);
|
||||
}
|
||||
|
||||
@ -444,10 +472,10 @@ static void coreAssert(PKVM* vm)) {
|
||||
msg = (String*)AS_OBJ(ARG(2));
|
||||
}
|
||||
vmPushTempRef(vm, &msg->_super);
|
||||
vm->fiber->error = stringFormat(vm, "Assertion failed: '@'.", msg);
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Assertion failed: '@'.", msg));
|
||||
vmPopTempRef(vm);
|
||||
} else {
|
||||
vm->fiber->error = newString(vm, "Assertion failed.");
|
||||
VM_SET_ERROR(vm, newString(vm, "Assertion failed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -558,7 +586,7 @@ static void coreListAppend(PKVM* vm)) {
|
||||
if (!validateArgList(vm, 1, &list)) return;
|
||||
Var elem = ARG(2);
|
||||
|
||||
pkVarBufferWrite(&list->elements, vm, elem);
|
||||
listAppend(vm, list, elem);
|
||||
RET(VAR_OBJ(list));
|
||||
}
|
||||
|
||||
@ -691,11 +719,10 @@ static Script* newModuleInternal(PKVM* vm, const char* name) {
|
||||
return scr;
|
||||
}
|
||||
|
||||
// An internal function to add a function to the given [script].
|
||||
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
const char* name, pkNativeFn fptr,
|
||||
int arity) {
|
||||
|
||||
// This will fail an assertion if a function or a global with the [name]
|
||||
// already exists in the module.
|
||||
static inline void assertModuleNameDef(PKVM* vm, Script* script,
|
||||
const char* name) {
|
||||
// Check if function with the same name already exists.
|
||||
if (scriptGetFunc(script, name, (uint32_t)strlen(name)) != -1) {
|
||||
__ASSERT(false, stringFormat(vm, "A function named '$' already esists "
|
||||
@ -707,6 +734,30 @@ static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
__ASSERT(false, stringFormat(vm, "A global variable named '$' already "
|
||||
"esists on module '@'", name, script->moudle)->data);
|
||||
}
|
||||
}
|
||||
|
||||
// The internal function to add global value to a module.
|
||||
static void moduleAddGlobalInternal(PKVM* vm, Script* script,
|
||||
const char* name, Var value) {
|
||||
|
||||
// Ensure the name isn't predefined.
|
||||
assertModuleNameDef(vm, script, name);
|
||||
|
||||
// TODO: move this to pk_var.h and use it in the compilerAddVariable
|
||||
// function.
|
||||
uint32_t name_index = scriptAddName(script, vm, name,
|
||||
(uint32_t)strlen(name));
|
||||
pkUintBufferWrite(&script->global_names, vm, name_index);
|
||||
pkVarBufferWrite(&script->globals, vm, value);
|
||||
}
|
||||
|
||||
// An internal function to add a function to the given [script].
|
||||
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
const char* name, pkNativeFn fptr,
|
||||
int arity) {
|
||||
|
||||
// Ensure the name isn't predefined.
|
||||
assertModuleNameDef(vm, script, name);
|
||||
|
||||
Function* fn = newFunction(vm, name, (int)strlen(name), script, true);
|
||||
fn->native = fptr;
|
||||
@ -810,8 +861,7 @@ PK_DOC(
|
||||
"hash(value:var) -> num\n"
|
||||
"Return the hash value of the variable, if it's not hashable it'll "
|
||||
"return null.",
|
||||
static void stdMathHash(PKVM* vm));
|
||||
void stdMathHash(PKVM* vm) {
|
||||
static void stdMathHash(PKVM* vm)) {
|
||||
if (IS_OBJ(ARG(1))) {
|
||||
if (!isObjectHashable(AS_OBJ(ARG(1))->type)) {
|
||||
RET(VAR_NULL);
|
||||
@ -820,6 +870,16 @@ void stdMathHash(PKVM* vm) {
|
||||
RET(VAR_NUM((double)varHashValue(ARG(1))));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
"sin(rad:num) -> num\n"
|
||||
"Return the sine value of the argument [rad] which is an angle expressed "
|
||||
"in radians.",
|
||||
static void stdMathSine(PKVM* vm)) {
|
||||
double rad;
|
||||
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
|
||||
RET(VAR_NUM(sin(rad)));
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* CORE INITIALIZATION */
|
||||
/*****************************************************************************/
|
||||
@ -903,6 +963,17 @@ void initializeCore(PKVM* vm) {
|
||||
moduleAddFunctionInternal(vm, math, "abs", stdMathAbs, 1);
|
||||
moduleAddFunctionInternal(vm, math, "sign", stdMathSign, 1);
|
||||
moduleAddFunctionInternal(vm, math, "hash", stdMathHash, 1);
|
||||
moduleAddFunctionInternal(vm, math, "sin", stdMathSine, 1);
|
||||
// TODO: add - cos, tan.
|
||||
// low priority: sinh, cosh, tanh, asin, acos, atan.
|
||||
|
||||
// Note that currently it's mutable (since it's a global variable, not
|
||||
// constant and pocketlang doesn't support constant) so the user shouldn't
|
||||
// modify the PI, like in python.
|
||||
// TODO: at varSetAttrib() we can detect if the user try to change an
|
||||
// attribute of a core module and we can throw an error.
|
||||
moduleAddGlobalInternal(vm, math, "PI", VAR_NUM(M_PI));
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -910,8 +981,8 @@ void initializeCore(PKVM* vm) {
|
||||
/*****************************************************************************/
|
||||
|
||||
#define UNSUPPORT_OPERAND_TYPES(op) \
|
||||
vm->fiber->error = stringFormat(vm, "Unsupported operand types for " \
|
||||
"operator '" op "' $ and $", varTypeName(v1), varTypeName(v2))
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand types for " \
|
||||
"operator '" op "' $ and $", varTypeName(v1), varTypeName(v2)))
|
||||
|
||||
Var varAdd(PKVM* vm, Var v1, Var v2) {
|
||||
|
||||
@ -937,10 +1008,10 @@ Var varAdd(PKVM* vm, Var v1, Var v2) {
|
||||
case OBJ_LIST:
|
||||
{
|
||||
if (o2->type == OBJ_LIST) {
|
||||
List* l1 = (List*)o1, * l2 = (List*)o2;
|
||||
TODO;
|
||||
}
|
||||
}
|
||||
TODO;
|
||||
} break;
|
||||
|
||||
case OBJ_MAP:
|
||||
case OBJ_RANGE:
|
||||
@ -1015,6 +1086,20 @@ Var varModulo(PKVM* vm, Var v1, Var v2) {
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
Var varBitAnd(PKVM* vm, Var v1, Var v2) {
|
||||
|
||||
int64_t i1, i2;
|
||||
if (isInteger(v1, &i1)) {
|
||||
if (validateInteger(vm, v2, &i2, "Right operand")) {
|
||||
return VAR_NUM(i1 & i2);
|
||||
}
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
UNSUPPORT_OPERAND_TYPES("&");
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
bool varGreater(Var v1, Var v2) {
|
||||
double d1, d2;
|
||||
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
|
||||
@ -1058,16 +1143,15 @@ bool varLesser(Var v1, Var v2) {
|
||||
#define CASE_ATTRIB(name, hash) case hash
|
||||
|
||||
// Set error for accessing non-existed attribute.
|
||||
#define ERR_NO_ATTRIB(on) \
|
||||
vm->fiber->error = stringFormat(vm, "'$' object has no attribute " \
|
||||
"named '$'", \
|
||||
varTypeName(on), attrib->data);
|
||||
#define ERR_NO_ATTRIB(vm, on, attrib) \
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no attribute named '$'", \
|
||||
varTypeName(on), attrib->data))
|
||||
|
||||
Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
|
||||
if (!IS_OBJ(on)) {
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on));
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on)));
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
@ -1091,7 +1175,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
return VAR_OBJ(stringStrip(vm, str));
|
||||
|
||||
default:
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
@ -1107,7 +1191,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
return VAR_NUM((double)(list->elements.count));
|
||||
|
||||
default:
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
@ -1132,7 +1216,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
return VAR_OBJ(rangeAsList(vm, range));
|
||||
|
||||
default:
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
@ -1156,7 +1240,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
return scr->globals.data[index];
|
||||
}
|
||||
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
@ -1172,7 +1256,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
return VAR_OBJ(newString(vm, fn->name));
|
||||
|
||||
default:
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return VAR_NULL;
|
||||
}
|
||||
UNREACHABLE();
|
||||
@ -1195,14 +1279,14 @@ void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) {
|
||||
#define ATTRIB_IMMUTABLE(name) \
|
||||
do { \
|
||||
if ((attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)) { \
|
||||
vm->fiber->error = stringFormat(vm, "'$' attribute is immutable.", name); \
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "'$' attribute is immutable.", name)); \
|
||||
return; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
if (!IS_OBJ(on)) {
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on));
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on)));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1213,12 +1297,12 @@ do { \
|
||||
ATTRIB_IMMUTABLE("lower");
|
||||
ATTRIB_IMMUTABLE("upper");
|
||||
ATTRIB_IMMUTABLE("strip");
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return;
|
||||
|
||||
case OBJ_LIST:
|
||||
ATTRIB_IMMUTABLE("length");
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return;
|
||||
|
||||
case OBJ_MAP:
|
||||
@ -1228,12 +1312,12 @@ do { \
|
||||
// map.foo = 'bar'
|
||||
TODO;
|
||||
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return;
|
||||
|
||||
case OBJ_RANGE:
|
||||
ATTRIB_IMMUTABLE("as_list");
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return;
|
||||
|
||||
case OBJ_SCRIPT: {
|
||||
@ -1255,22 +1339,22 @@ do { \
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return;
|
||||
}
|
||||
|
||||
case OBJ_FUNC:
|
||||
ATTRIB_IMMUTABLE("arity");
|
||||
ATTRIB_IMMUTABLE("name");
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return;
|
||||
|
||||
case OBJ_FIBER:
|
||||
ERR_NO_ATTRIB(on);
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return;
|
||||
|
||||
case OBJ_USER:
|
||||
TODO; //ERR_NO_ATTRIB(on);
|
||||
TODO; //ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return;
|
||||
|
||||
default:
|
||||
@ -1284,8 +1368,8 @@ do { \
|
||||
|
||||
Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||
if (!IS_OBJ(on)) {
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on));
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on)));
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
@ -1326,9 +1410,9 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||
String* key_str = toString(vm, key);
|
||||
vmPushTempRef(vm, &key_str->_super);
|
||||
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
||||
vm->fiber->error = stringFormat(vm, "Invalid key '@'.", key_str);
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Invalid key '@'.", key_str));
|
||||
} else {
|
||||
vm->fiber->error = stringFormat(vm, "Key '@' not exists", key_str);
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Key '@' not exists", key_str));
|
||||
}
|
||||
vmPopTempRef(vm);
|
||||
return VAR_NULL;
|
||||
@ -1352,15 +1436,15 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||
|
||||
void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
||||
if (!IS_OBJ(on)) {
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on));
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
|
||||
varTypeName(on)));
|
||||
return;
|
||||
}
|
||||
|
||||
Object* obj = AS_OBJ(on);
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING:
|
||||
vm->fiber->error = newString(vm, "String objects are immutable.");
|
||||
VM_SET_ERROR(vm, newString(vm, "String objects are immutable."));
|
||||
return;
|
||||
|
||||
case OBJ_LIST:
|
||||
@ -1376,8 +1460,8 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
||||
case OBJ_MAP:
|
||||
{
|
||||
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
||||
vm->fiber->error = stringFormat(vm, "$ type is not hashable.",
|
||||
varTypeName(key));
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "$ type is not hashable.",
|
||||
varTypeName(key)));
|
||||
} else {
|
||||
mapSet(vm, (Map*)obj, key, value);
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ Var varMultiply(PKVM* vm, Var v1, Var v2); // Returns v1 * v2.
|
||||
Var varDivide(PKVM* vm, Var v1, Var v2); // Returns v1 / v2.
|
||||
Var varModulo(PKVM* vm, Var v1, Var v2); // Returns v1 % v2.
|
||||
|
||||
Var varBitAnd(PKVM* vm, Var v1, Var v2); // Returns v1 & v2.
|
||||
|
||||
bool varGreater(Var v1, Var v2); // Returns v1 > v2.
|
||||
bool varLesser(Var v1, Var v2); // Returns v1 < v2.
|
||||
|
||||
|
@ -146,10 +146,10 @@ int utf8_encodeValue(int value, uint8_t* bytes) {
|
||||
// to 4th byte, next 6 bits to 3rd byte, next 6 bits to 2nd byte, 3 bits
|
||||
// first byte.
|
||||
if (value <= 0x10ffff) {
|
||||
*(bytes++) = (uint8_t)(0b11110000 | ((value & 0b111000000000000000000) >> 18));
|
||||
*(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000000000) >> 12));
|
||||
*(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000) >> 6));
|
||||
*(bytes) = (uint8_t)(0b10000000 | ((value & 0b111111)));
|
||||
*(bytes++) = (uint8_t)(0b11110000 | ((value & (0b111 << 18)) >> 18));
|
||||
*(bytes++) = (uint8_t)(0b10000000 | ((value & (0b111111 << 12)) >> 12));
|
||||
*(bytes++) = (uint8_t)(0b10000000 | ((value & (0b111111 << 6)) >> 6));
|
||||
*(bytes) = (uint8_t)(0b10000000 | ((value & 0b111111)));
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
18
src/pk_var.c
18
src/pk_var.c
@ -58,7 +58,7 @@ PkHandle* pkNewMap(PKVM* vm) {
|
||||
|
||||
PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) {
|
||||
__ASSERT(IS_OBJ_TYPE(fn->value, OBJ_FUNC), "Fn should be of type function.");
|
||||
|
||||
|
||||
Fiber* fiber = newFiber(vm, (Function*)AS_OBJ(fn->value));
|
||||
return vmNewHandle(vm, VAR_OBJ(fiber));
|
||||
}
|
||||
@ -362,7 +362,7 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
|
||||
uint32_t name_index = scriptAddName(owner, vm, name, length);
|
||||
pkUintBufferWrite(&owner->function_names, vm, name_index);
|
||||
vmPopTempRef(vm);
|
||||
|
||||
|
||||
func->name = owner->names.data[name_index]->data;
|
||||
func->owner = owner;
|
||||
func->arity = -2; // -1 means variadic args.
|
||||
@ -488,10 +488,10 @@ String* stringUpper(PKVM* vm, String* self) {
|
||||
String* stringStrip(PKVM* vm, String* self) {
|
||||
|
||||
// Implementation:
|
||||
//
|
||||
//
|
||||
// " a string with leading and trailing white space "
|
||||
// ^start >> << end^
|
||||
//
|
||||
//
|
||||
// These 'start' and 'end' pointers will move respectively right and left
|
||||
// while it's a white space and return an allocated string from 'start' with
|
||||
// length of (end - start + 1). For already trimed string it'll not allocate
|
||||
@ -558,7 +558,7 @@ Var listRemoveAt(PKVM* vm, List* self, uint32_t index) {
|
||||
|
||||
// Return a has value for the object.
|
||||
static uint32_t _hashObject(Object* obj) {
|
||||
|
||||
|
||||
ASSERT(isObjectHashable(obj->type),
|
||||
"Check if it's hashable before calling this method.");
|
||||
|
||||
@ -646,7 +646,7 @@ static bool _mapFindEntry(Map* self, Var key, MapEntry** result) {
|
||||
*result = entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
index = (index + 1) % self->capacity;
|
||||
|
||||
} while (index != start_index);
|
||||
@ -658,7 +658,7 @@ static bool _mapFindEntry(Map* self, Var key, MapEntry** result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the key, value pair to the entries array of the map. Returns true if
|
||||
// Add the key, value pair to the entries array of the map. Returns true if
|
||||
// the entry added for the first time and false for replaced vlaue.
|
||||
static bool _mapInsertEntry(Map* self, Var key, Var value) {
|
||||
|
||||
@ -1198,7 +1198,7 @@ bool toBool(Var v) {
|
||||
String* stringFormat(PKVM* vm, const char* fmt, ...) {
|
||||
va_list arg_list;
|
||||
|
||||
// Calculate the total length of the resulting string. This is required to
|
||||
// Calculate the total length of the resulting string. This is required to
|
||||
// determine the final string size to allocate.
|
||||
va_start(arg_list, fmt);
|
||||
size_t total_length = 0;
|
||||
@ -1269,7 +1269,7 @@ String* stringJoin(PKVM* vm, String* str1, String* str2) {
|
||||
}
|
||||
|
||||
uint32_t scriptAddName(Script* self, PKVM* vm, const char* name,
|
||||
uint32_t length) {
|
||||
uint32_t length) {
|
||||
|
||||
for (uint32_t i = 0; i < self->names.count; i++) {
|
||||
String* _name = self->names.data[i];
|
||||
|
22
src/pk_var.h
22
src/pk_var.h
@ -17,7 +17,7 @@
|
||||
* Reference:
|
||||
* https://github.com/wren-lang/wren/blob/main/src/vm/wren_value.h
|
||||
* https://leonardschuetz.ch/blog/nan-boxing/
|
||||
*
|
||||
*
|
||||
* The previous implementation was to add a type field to every var and use
|
||||
* smart pointers(C++17) to object with custom destructors, which makes the
|
||||
* programme inefficient for small types such null, bool, int and float.
|
||||
@ -56,7 +56,7 @@
|
||||
*
|
||||
* v~~~~~~~~~~ NaN value
|
||||
* -11111111111----------------------------------------------------
|
||||
*
|
||||
*
|
||||
* We define a our variant \ref var as an unsigned 64 bit integer (we treat it
|
||||
* like a bit array) if the exponent bits were not set, just reinterprit it as
|
||||
* a IEEE 754 double precision 64 bit number. Other wise we there are a lot of
|
||||
@ -69,14 +69,14 @@
|
||||
*
|
||||
* v~Highest mestissa bit
|
||||
* -[NaN ]1---------------------------------------------------
|
||||
*
|
||||
*
|
||||
* if sign bit set, it's a heap allocated pointer.
|
||||
* | these 2 bits are type tags representing 8 different types
|
||||
* | vv
|
||||
* S[NaN ]1cXX------------------------------------------------
|
||||
* | ^~~~~~~~ 48 bits to represent the value (51 for pointer)
|
||||
* '- if this (const) bit set, it's a constant.
|
||||
*
|
||||
*
|
||||
* On a 32-bit machine a pointer size is 32 and on a 64-bit machine actually 48
|
||||
* bits are used for pointers. Ta-da, now we have double precision number,
|
||||
* primitives, pointers all inside a 64 bit sequence and for numbers it doesn't
|
||||
@ -153,7 +153,7 @@
|
||||
|
||||
#else
|
||||
|
||||
// TODO: Union tagging implementation of all the above macros ignore macros
|
||||
// TODO: Union tagging implementation of all the above macros ignore macros
|
||||
// starts with an underscore.
|
||||
|
||||
typedef enum {
|
||||
@ -466,6 +466,18 @@ String* stringUpper(PKVM* vm, String* self);
|
||||
// If the string is already trimed it'll return the same string.
|
||||
String* stringStrip(PKVM* vm, String* self);
|
||||
|
||||
// An inline function/macro implementation of listAppend(). Set below 0 to 1,
|
||||
// to make the implementation a static inline function, it's totally okey to
|
||||
// define a function inside a header as long as it's static (but not a fan).
|
||||
#if 0 // Function implementation.
|
||||
static inline void listAppend(PKVM* vm, List* self, Var value) {
|
||||
pkVarBufferWrite(&self->elements, vm, value);
|
||||
}
|
||||
#else // Macro implementation.
|
||||
#define listAppend(vm, self, value) \
|
||||
pkVarBufferWrite(&self->elements, vm, value)
|
||||
#endif
|
||||
|
||||
// Insert [value] to the list at [index] and shift down the rest of the
|
||||
// elements.
|
||||
void listInsert(PKVM* vm, List* self, uint32_t index, Var value);
|
||||
|
46
src/pk_vm.c
46
src/pk_vm.c
@ -10,9 +10,6 @@
|
||||
#include "pk_utils.h"
|
||||
#include "pk_debug.h"
|
||||
|
||||
// Evaluated to "true" if a runtime error set on the current fiber.
|
||||
#define HAS_ERROR() (vm->fiber->error != NULL)
|
||||
|
||||
/*****************************************************************************/
|
||||
/* VM PUBLIC API */
|
||||
/*****************************************************************************/
|
||||
@ -127,7 +124,7 @@ void pkReleaseHandle(PKVM* vm, PkHandle* handle) {
|
||||
DEALLOCATE(vm, handle);
|
||||
}
|
||||
|
||||
// This function is responsible to call on_done function if it's done with the
|
||||
// This function is responsible to call on_done function if it's done with the
|
||||
// provided string pointers.
|
||||
PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path,
|
||||
const PkCompileOptions* options) {
|
||||
@ -195,7 +192,7 @@ PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber, PkVar value) {
|
||||
|
||||
void pkSetRuntimeError(PKVM* vm, const char* message) {
|
||||
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
||||
vm->fiber->error = newString(vm, message);
|
||||
VM_SET_ERROR(vm, newString(vm, message));
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -217,7 +214,7 @@ void* vmRealloc(PKVM* vm, void* memory, size_t old_size, size_t new_size) {
|
||||
// TODO: Debug trace allocations here.
|
||||
|
||||
// Track the total allocated memory of the VM to trigger the GC.
|
||||
// if vmRealloc is called for freeing, the old_size would be 0 since
|
||||
// if vmRealloc is called for freeing, the old_size would be 0 since
|
||||
// deallocated bytes are traced by garbage collector.
|
||||
vm->bytes_allocated += new_size - old_size;
|
||||
|
||||
@ -315,7 +312,7 @@ void vmCollectGarbage(PKVM* vm) {
|
||||
|
||||
#define _ERR_FAIL(msg) \
|
||||
do { \
|
||||
if (vm->fiber != NULL) vm->fiber->error = msg; \
|
||||
if (vm->fiber != NULL) VM_SET_ERROR(vm, msg); \
|
||||
return false; \
|
||||
} while (false)
|
||||
|
||||
@ -491,7 +488,7 @@ static inline void growStack(PKVM* vm, int size) {
|
||||
sizeof(Var) * fiber->stack_size,
|
||||
sizeof(Var) * new_size);
|
||||
fiber->stack_size = new_size;
|
||||
|
||||
|
||||
// If the old stack base pointer is the same as the current, that means the
|
||||
// stack hasn't been moved by the reallocation. In that case we're done.
|
||||
if (old_rbp == fiber->stack) return;
|
||||
@ -549,7 +546,7 @@ static inline void pushCallFrame(PKVM* vm, const Function* fn) {
|
||||
}
|
||||
|
||||
static void reportError(PKVM* vm) {
|
||||
ASSERT(HAS_ERROR(), "runtimeError() should be called after an error.");
|
||||
ASSERT(VM_HAS_ERROR(vm), "runtimeError() should be called after an error.");
|
||||
// TODO: pass the error to the caller of the fiber.
|
||||
|
||||
// Print the Error message and stack trace.
|
||||
@ -607,7 +604,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
|
||||
#define CHECK_ERROR() \
|
||||
do { \
|
||||
if (HAS_ERROR()) { \
|
||||
if (VM_HAS_ERROR(vm)) { \
|
||||
UPDATE_FRAME(); \
|
||||
reportError(vm); \
|
||||
FIBER_SWITCH_BACK(); \
|
||||
@ -618,7 +615,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
// [err_msg] must be of type String.
|
||||
#define RUNTIME_ERROR(err_msg) \
|
||||
do { \
|
||||
vm->fiber->error = err_msg; \
|
||||
VM_SET_ERROR(vm, err_msg); \
|
||||
UPDATE_FRAME(); \
|
||||
reportError(vm); \
|
||||
FIBER_SWITCH_BACK(); \
|
||||
@ -728,7 +725,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
OPCODE(MAP_INSERT):
|
||||
{
|
||||
Var value = PEEK(-1); // Don't pop yet, we need the reference for gc.
|
||||
Var key = PEEK(-2); // Don't pop yet, we need the reference for gc.
|
||||
Var key = PEEK(-2); // Don't pop yet, we need the reference for gc.
|
||||
Var on = PEEK(-3);
|
||||
|
||||
ASSERT(IS_OBJ_TYPE(on, OBJ_MAP), OOPS);
|
||||
@ -962,7 +959,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
if (iter >= elems->count) JUMP_ITER_EXIT();
|
||||
*value = elems->data[iter];
|
||||
*iterator = VAR_NUM((double)iter + 1);
|
||||
|
||||
|
||||
} DISPATCH();
|
||||
|
||||
case OBJ_MAP: {
|
||||
@ -978,7 +975,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
|
||||
*value = map->entries[iter].key;
|
||||
*iterator = VAR_NUM((double)iter + 1);
|
||||
|
||||
|
||||
} DISPATCH();
|
||||
|
||||
case OBJ_RANGE: {
|
||||
@ -995,7 +992,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
if (current == to) JUMP_ITER_EXIT();
|
||||
*value = VAR_NUM(current);
|
||||
*iterator = VAR_NUM(it + 1);
|
||||
|
||||
|
||||
} DISPATCH();
|
||||
|
||||
case OBJ_SCRIPT:
|
||||
@ -1079,7 +1076,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
|
||||
OPCODE(GET_ATTRIB):
|
||||
{
|
||||
Var on = PEEK(-1); // Don't pop yet, we need the reference for gc.
|
||||
Var on = PEEK(-1); // Don't pop yet, we need the reference for gc.
|
||||
String* name = script->names.data[READ_SHORT()];
|
||||
Var value = varGetAttrib(vm, on, name);
|
||||
DROP(); // on
|
||||
@ -1100,8 +1097,8 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
|
||||
OPCODE(SET_ATTRIB):
|
||||
{
|
||||
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.
|
||||
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 = script->names.data[READ_SHORT()];
|
||||
varSetAttrib(vm, on, name, value);
|
||||
|
||||
@ -1233,7 +1230,18 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(BIT_AND):
|
||||
OPCODE(BIT_AND) :
|
||||
{
|
||||
// Don't pop yet, we need the reference for gc.
|
||||
Var r = PEEK(-1), l = PEEK(-2);
|
||||
Var value = varBitAnd(vm, l, r);
|
||||
DROP(); DROP(); // r, l
|
||||
PUSH(value);
|
||||
|
||||
CHECK_ERROR();
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(BIT_OR):
|
||||
OPCODE(BIT_XOR):
|
||||
OPCODE(BIT_LSHIFT):
|
||||
|
22
src/pk_vm.h
22
src/pk_vm.h
@ -27,7 +27,7 @@
|
||||
// The allocated size the'll trigger the first GC. (~10MB).
|
||||
#define INITIAL_GC_SIZE (1024 * 1024 * 10)
|
||||
|
||||
// The heap size might shrink if the remaining allocated bytes after a GC
|
||||
// The heap size might shrink if the remaining allocated bytes after a GC
|
||||
// is less than the one before the last GC. So we need a minimum size.
|
||||
#define MIN_HEAP_SIZE (1024 * 1024)
|
||||
|
||||
@ -35,6 +35,12 @@
|
||||
// allocated so far plus the fill factor of it.
|
||||
#define HEAP_FILL_PERCENT 75
|
||||
|
||||
// Evaluated to "true" if a runtime error set on the current fiber.
|
||||
#define VM_HAS_ERROR(vm) (vm->fiber->error != NULL)
|
||||
|
||||
// Set the error message [err] to the [vm]'s current fiber.
|
||||
#define VM_SET_ERROR(vm, err) (vm->fiber->error = err)
|
||||
|
||||
// Builtin functions are stored in an array in the VM (unlike script functions
|
||||
// they're member of function buffer of the script) and this struct is a single
|
||||
// entry of the array.
|
||||
@ -130,16 +136,16 @@ PkHandle* vmNewHandle(PKVM* vm, Var value);
|
||||
|
||||
// Trigger garbage collection. This is an implementation of mark and sweep
|
||||
// garbage collection (https://en.wikipedia.org/wiki/Tracing_garbage_collection).
|
||||
//
|
||||
//
|
||||
// 1. MARKING PHASE
|
||||
//
|
||||
//
|
||||
// | |
|
||||
// | [obj0] -+---> [obj2] -> [obj6] .------- Garbage --------.
|
||||
// | [obj3] | | | |
|
||||
// | [obj8] | '-----> [obj1] | [obj7] ---> [obj5] |
|
||||
// '----------' | [obj4] |
|
||||
// working set '------------------------'
|
||||
//
|
||||
//
|
||||
// First we preform a tree traversel from all the vm's root objects. such as
|
||||
// stack values, temp references, handles, vm's running fiber, current
|
||||
// compiler (if it has any) etc. Mark them (ie. is_marked = true) and add
|
||||
@ -147,23 +153,23 @@ PkHandle* vmNewHandle(PKVM* vm, Var value);
|
||||
// working set add all of it's referenced objects to the working set and mark
|
||||
// it black (try-color marking) We'll keep doing this till the working set
|
||||
// become empty, at this point any object which isn't marked is a garbage.
|
||||
//
|
||||
//
|
||||
// Every single heap allocated objects will be in the VM's link list. Those
|
||||
// objects which are reachable have marked (ie. is_marked = true) once the
|
||||
// marking phase is completed.
|
||||
// .----------------.
|
||||
// .----------------.
|
||||
// | VM |
|
||||
// | Object* first -+--------> [obj8] -> [obj7] -> [obj6] ... [obj0] -> NULL
|
||||
// '----------------' marked = true false true true
|
||||
//
|
||||
// 2. SWEEPING PHASE
|
||||
//
|
||||
//
|
||||
// .----------------. .-------------.
|
||||
// | VM | | V
|
||||
// | Object* first -+--------> [obj8] [obj7] [obj6] ... [obj0] -> NULL
|
||||
// '----------------' marked = true false true true
|
||||
// '--free()--'
|
||||
//
|
||||
//
|
||||
// Once the marking phase is done, we iterate through the objects and remove
|
||||
// the objects which are not marked from the linked list and deallocate them.
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user