work
This commit is contained in:
3
runtime/Makefile
Normal file
3
runtime/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
SOURCES += $(wildcard )
|
||||
70
runtime/include/gc.h
Normal file
70
runtime/include/gc.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// typedef struct {
|
||||
// size_t cap, n;
|
||||
// js_gc_task_node_t *tasks;
|
||||
// } js_gc_task_stack_t;
|
||||
|
||||
typedef struct js_gc *js_gc_t;
|
||||
|
||||
typedef void (*js_gc_task_consumer_t)(void *data, js_gc_task_searcher_t searcher);
|
||||
typedef void (*js_gc_task_searcher_t)(void *data, js_gc_task_consumer_t consumer);
|
||||
typedef size_t js_gc_data_t;
|
||||
|
||||
void js_gc_run(size_t iterations);
|
||||
|
||||
// void js_gc_task_add(js_gc_task_stack_t *tasks, void *data, int type);
|
||||
|
||||
// typedef enum {
|
||||
// GC_BLACK,
|
||||
// GC_GREY,
|
||||
// GC_WHITE,
|
||||
// } js_gc_state_t;
|
||||
|
||||
// typedef struct {
|
||||
// // Whether the node is a root
|
||||
// bool root;
|
||||
|
||||
// } js_gc_data_t;
|
||||
|
||||
// typedef enum {
|
||||
// JS_GC_OBJECT,
|
||||
// JS_GC_STRING,
|
||||
// JS_GC_CAPTURE,
|
||||
// JS_GC_FRAME,
|
||||
// } js_gc_node_type_t;
|
||||
|
||||
// typedef struct {
|
||||
// js_gc_data_t *data;
|
||||
// void *ptr;
|
||||
// js_gc_node_type_t type;
|
||||
// } js_gc_node_t;
|
||||
|
||||
// typedef struct js_gc_table_node {
|
||||
// struct js_gc_table_node *next;
|
||||
// js_gc_node_t node;
|
||||
// } js_gc_table_node_t;
|
||||
|
||||
// typedef struct {
|
||||
// size_t cap, n;
|
||||
// js_gc_table_node_t **buckets;
|
||||
// } js_gc_table_t;
|
||||
|
||||
// typedef struct {
|
||||
// void **prev_ref;
|
||||
// js_gc_node_t node;
|
||||
// } js_gc_task_node_t;
|
||||
|
||||
// typedef struct {
|
||||
// size_t cap, n;
|
||||
// js_gc_task_node_t *tasks;
|
||||
// } js_gc_task_stack_t;
|
||||
|
||||
// void js_gc_stack_push(js_gc_task_stack_t *stack, js_gc_task_node_t task);
|
||||
// bool js_gc_stack_pop(js_gc_task_stack_t *stack, js_gc_task_node_t *pout);
|
||||
|
||||
// static void js_gc_dryrun_table(js_gc_task_stack_t *stack, void **ref, js_ctx_t obj);
|
||||
// static void js_gc_reclaim_table(js_gc_task_stack_t *stack, void **ref, js_ctx_t obj);
|
||||
55
runtime/include/gc_impl.h
Normal file
55
runtime/include/gc_impl.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum {
|
||||
GC_BLACK,
|
||||
GC_GREY,
|
||||
GC_WHITE,
|
||||
} js_gc_state_t;
|
||||
|
||||
typedef struct {
|
||||
// Whether the node is a root
|
||||
bool root;
|
||||
|
||||
} js_gc_data_t;
|
||||
|
||||
typedef enum {
|
||||
JS_GC_OBJECT,
|
||||
JS_GC_STRING,
|
||||
JS_GC_CAPTURE,
|
||||
JS_GC_FRAME,
|
||||
} js_gc_node_type_t;
|
||||
|
||||
typedef struct {
|
||||
js_gc_data_t *data;
|
||||
void *ptr;
|
||||
js_gc_node_type_t type;
|
||||
} js_gc_node_t;
|
||||
|
||||
typedef struct js_gc_table_node {
|
||||
struct js_gc_table_node *next;
|
||||
js_gc_node_t node;
|
||||
} js_gc_table_node_t;
|
||||
|
||||
typedef struct {
|
||||
size_t cap, n;
|
||||
js_gc_table_node_t **buckets;
|
||||
} js_gc_table_t;
|
||||
|
||||
typedef struct {
|
||||
void **prev_ref;
|
||||
js_gc_node_t node;
|
||||
} js_gc_task_node_t;
|
||||
|
||||
typedef struct {
|
||||
size_t cap, n;
|
||||
js_gc_task_node_t *tasks;
|
||||
} js_gc_task_stack_t;
|
||||
|
||||
void js_gc_stack_push(js_gc_task_stack_t *stack, js_gc_task_node_t task);
|
||||
bool js_gc_stack_pop(js_gc_task_stack_t *stack, js_gc_task_node_t *pout);
|
||||
|
||||
static void js_gc_dryrun_table(js_gc_task_stack_t *stack, void **ref, js_ctx_t obj);
|
||||
static void js_gc_reclaim_table(js_gc_task_stack_t *stack, void **ref, js_ctx_t obj);
|
||||
25
runtime/include/impl.h
Normal file
25
runtime/include/impl.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "./runtime.h"
|
||||
#include "./gc.h"
|
||||
|
||||
struct js_ctx {
|
||||
// js_gc_table_t gc;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
js_string_t str;
|
||||
js_gc_data_t gc;
|
||||
} *js_gc_str_t;
|
||||
|
||||
typedef struct {
|
||||
js_gc_str_t str;
|
||||
js_gc_data_t gc;
|
||||
} *js_symbol_t;
|
||||
|
||||
|
||||
struct js_capture {
|
||||
js_value_t *ref;
|
||||
js_gc_data_t gc;
|
||||
};
|
||||
|
||||
// void js_gc_reclaim_str(js_gc_task_stack_t *stack, void **ref, js_obj_t obj);
|
||||
26
runtime/include/obj.h
Normal file
26
runtime/include/obj.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "./runtime.h"
|
||||
#include "./impl.h"
|
||||
|
||||
typedef struct {
|
||||
bool is_symbol;
|
||||
js_gc_str_t name;
|
||||
} js_obj_member_t;
|
||||
|
||||
typedef struct js_obj_node {
|
||||
size_t hash;
|
||||
js_obj_member_t *member;
|
||||
struct js_obj_node *next;
|
||||
} js_obj_node_t;
|
||||
|
||||
typedef struct {
|
||||
js_gc_data_t gc;
|
||||
|
||||
size_t buckets_n;
|
||||
js_obj_node_t *buckets;
|
||||
js_obj_node_t *first;
|
||||
|
||||
} *js_obj_t;
|
||||
|
||||
void js_gc_dryrun_obj(js_gc_task_stack_t *stack, void **ref, js_obj_t obj);
|
||||
void js_gc_reclaim_obj(js_gc_task_stack_t *stack, void **ref, js_obj_t obj);
|
||||
115
runtime/include/runtime.h
Normal file
115
runtime/include/runtime.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct js_value {
|
||||
void *data;
|
||||
enum {
|
||||
JS_T_UNDEFINED,
|
||||
JS_T_NULL,
|
||||
JS_T_BOOL,
|
||||
JS_T_STR,
|
||||
JS_T_NUM,
|
||||
JS_T_FUNC,
|
||||
JS_T_OBJ,
|
||||
JS_T_ARR,
|
||||
} type;
|
||||
} js_value_t;
|
||||
|
||||
typedef struct js_capture *js_capture_t;
|
||||
typedef struct js_ctx *js_ctx_t;
|
||||
|
||||
typedef js_value_t (*js_func_body_t)(js_ctx_t ctx, js_value_t self, size_t argc, js_value_t argv);
|
||||
|
||||
typedef struct {
|
||||
uint16_t *data;
|
||||
size_t size: sizeof(size_t) - 1;
|
||||
size_t dynamic: 1;
|
||||
} js_string_t;
|
||||
|
||||
const js_string_t EMPTY = { .data = &(uint16_t[]){'t'}, .size = -1, .dynamic = false };
|
||||
|
||||
#define JS_STRING_LITERAL(literal) ((js_string_t) { .data = (literal), .size = sizeof (literal) / sizeof (literal)[0] - 1, .dynamic = false })
|
||||
|
||||
js_value_t JS_UNDEFINED = { .data = NULL, .type = JS_T_UNDEFINED };
|
||||
js_value_t JS_NULL = { .data = NULL, .type = JS_T_NULL };
|
||||
js_value_t JS_TRUE = { .data = (void*)1, .type = JS_T_BOOL };
|
||||
js_value_t JS_FALSE = { .data = (void*)0, .type = JS_T_BOOL };
|
||||
|
||||
const js_string_t JS_TYPES[] = {
|
||||
JS_STRING_LITERAL(u"undefined"),
|
||||
JS_STRING_LITERAL(u"object"),
|
||||
JS_STRING_LITERAL(u"boolean"),
|
||||
JS_STRING_LITERAL(u"string"),
|
||||
JS_STRING_LITERAL(u"number"),
|
||||
JS_STRING_LITERAL(u"function"),
|
||||
JS_STRING_LITERAL(u"object"),
|
||||
JS_STRING_LITERAL(u"object"),
|
||||
};
|
||||
|
||||
extern bool JS_ERR_FLAG;
|
||||
|
||||
js_value_t js_mk_num(double val);
|
||||
js_value_t js_mk_bool(bool val);
|
||||
js_value_t js_mk_str(js_ctx_t ctx, js_string_t str);
|
||||
js_value_t js_mk_sym(js_ctx_t ctx, js_string_t name);
|
||||
js_value_t js_mk_obj(js_ctx_t ctx);
|
||||
js_value_t js_mk_arr(js_ctx_t ctx, size_t cap);
|
||||
js_value_t js_mk_args(js_ctx_t ctx, size_t argc, js_value_t *argv);
|
||||
js_value_t js_mk_func(js_ctx_t ctx, bool apply, bool construct, js_string_t name, js_func_body_t body, size_t capn, js_capture_t *capv);
|
||||
|
||||
int32_t js_to_int32(js_ctx_t ctx, js_value_t val);
|
||||
int64_t js_to_int64(js_ctx_t ctx, js_value_t val);
|
||||
bool js_to_bool(js_ctx_t ctx, js_value_t val);
|
||||
js_string_t js_to_string(js_ctx_t ctx, js_value_t val);
|
||||
|
||||
js_value_t js_op_add(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_sub(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_mod(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_div(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_neg(js_ctx_t ctx, js_value_t val);
|
||||
|
||||
js_value_t js_op_b_and(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_or(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_xor(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_rsh(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_lsh(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_ursh(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
bool js_op_b_not(js_ctx_t ctx, js_value_t val);
|
||||
|
||||
bool js_op_less(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
bool js_op_gr(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
bool js_op_leq(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
bool js_op_geq(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
|
||||
bool js_op_eq_loose(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
bool js_op_neq_loose(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
bool js_op_eq(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
bool js_op_neq(js_ctx_t ctx, js_value_t a, js_value_t b);
|
||||
|
||||
js_value_t js_obj_set_proto(js_ctx_t ctx, js_value_t obj, js_value_t proto);
|
||||
js_value_t js_obj_get_proto(js_ctx_t ctx, js_value_t obj);
|
||||
|
||||
js_value_t js_obj_get_own(js_ctx_t ctx, js_value_t obj, js_value_t key);
|
||||
js_value_t js_obj_set_own(js_ctx_t ctx, js_value_t obj, js_value_t key, js_value_t value);
|
||||
js_value_t js_obj_has_own(js_ctx_t ctx, js_value_t obj, js_value_t key);
|
||||
js_value_t js_obj_del_own(js_ctx_t ctx, js_value_t obj, js_value_t key);
|
||||
|
||||
js_value_t js_obj_get(js_ctx_t ctx, js_value_t obj, js_value_t key);
|
||||
bool js_obj_set(js_ctx_t ctx, js_value_t obj, js_value_t key, js_value_t value);
|
||||
bool js_obj_has(js_ctx_t ctx, js_value_t obj, js_value_t key);
|
||||
|
||||
js_value_t js_obj_def_field(js_ctx_t ctx, js_value_t obj, js_value_t key, bool w, bool c, bool e);
|
||||
js_value_t js_obj_def_prop(js_ctx_t ctx, js_value_t obj, js_value_t key, js_value_t get, js_value_t set, bool c, bool e);
|
||||
|
||||
js_value_t js_func_apply(js_ctx_t ctx, js_value_t func, js_value_t self, size_t argc, js_value_t *argv);
|
||||
js_value_t js_func_construct(js_ctx_t ctx, js_value_t func, js_value_t self, size_t argc, js_value_t *argv);
|
||||
|
||||
js_value_t js_get_error(js_ctx_t ctx);
|
||||
js_value_t js_return(js_ctx_t ctx, js_value_t val);
|
||||
|
||||
void js_gc_();
|
||||
|
||||
int js_runtime_bootstrap(int argc, const char **argv, void (*callee)(js_ctx_t ctx));
|
||||
@@ -1,71 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
const int8_t JS_UNDEFINED = 0x00;
|
||||
const int8_t JS_NULL = 0x01;
|
||||
const int8_t JS_TRUE = 0x02;
|
||||
const int8_t JS_FALSE = 0x03;
|
||||
const int8_t JS_STRING = 0x05;
|
||||
// const int8_t JS_INT = 0x06;
|
||||
const int8_t JS_FLOAT = 0x07;
|
||||
const int8_t JS_SYMBOL = 0x09;
|
||||
const int8_t JS_OBJECT = 0x0A;
|
||||
const int8_t JS_ARRAY = 0x0B;
|
||||
const int8_t JS_FUNCTION = 0x0C;
|
||||
|
||||
typedef struct js_string {
|
||||
int8_t mark;
|
||||
size_t n;
|
||||
const uint16_t *val;
|
||||
} js_string_t;
|
||||
typedef struct js_symbol {
|
||||
int8_t mark;
|
||||
js_string_t *description;
|
||||
} js_symbol_t;
|
||||
typedef struct js_object {
|
||||
int8_t mark;
|
||||
} js_object_t;
|
||||
typedef struct js_function {
|
||||
js_object_t object;
|
||||
js_function_body_t body;
|
||||
} js_function_t;
|
||||
|
||||
typedef struct js_val {
|
||||
int8_t type;
|
||||
|
||||
union {
|
||||
double number;
|
||||
js_string_t *string;
|
||||
js_symbol_t *symbol;
|
||||
js_object_t *object;
|
||||
js_function_t *function;
|
||||
} value;
|
||||
} js_val_t;
|
||||
|
||||
typedef struct js_weak_ref {
|
||||
int8_t type;
|
||||
|
||||
union {
|
||||
js_string_t *string;
|
||||
js_symbol_t *symbol;
|
||||
js_object_t *object;
|
||||
js_function_t *function;
|
||||
} value;
|
||||
} js_weak_ref_t;
|
||||
|
||||
typedef js_val (*js_function_body_t)();
|
||||
|
||||
bool js_is_undefined(js_val_t);
|
||||
|
||||
js_val_t js_undefined();
|
||||
js_val_t js_null();
|
||||
|
||||
js_val_t js_number_new(double val);
|
||||
js_val_t js_string_new_c(const char *val);
|
||||
js_val_t js_string_new_cn(size_t n, const char *val);
|
||||
js_val_t js_string_new_cp(size_t n, const uint16_t *val);
|
||||
js_val_t js_string_new_br(size_t n, const uint16_t *val);
|
||||
js_val_t js_boolean_new(bool val);
|
||||
js_val_t js_object_new(size_t alloc);
|
||||
js_val_t js_array_new(size_t alloc);
|
||||
js_val_t js_function_new(js_function_body_t val);
|
||||
@@ -1,6 +0,0 @@
|
||||
#include "runtime.h"
|
||||
|
||||
struct js_val {
|
||||
|
||||
};
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct js_context *js_context_t;
|
||||
typedef struct js_value *js_value_t;
|
||||
|
||||
extern js_value_t JS_UNDEFINED, JS_NULL, JS_TRUE, JS_FALSE;
|
||||
|
||||
typedef struct {
|
||||
js_value_t *value;
|
||||
int ref_count;
|
||||
} js_capture_t;
|
||||
|
||||
// typedef struct {
|
||||
// js_value_t
|
||||
// } js_member_t;
|
||||
|
||||
js_value_t js_op_add(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_sub(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_mod(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_div(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_neg(js_context_t ctx, js_value_t val);
|
||||
|
||||
js_value_t js_op_b_and(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_or(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_xor(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_rsh(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_lsh(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_ursh(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_b_not(js_context_t ctx, js_value_t val);
|
||||
|
||||
js_value_t js_op_less(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_gr(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_leq(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_geq(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
|
||||
js_value_t js_op_eq_loose(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_neq_loose(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_eq(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
js_value_t js_op_neq(js_context_t ctx, js_value_t a, js_value_t b);
|
||||
|
||||
js_value_t js_obj_set_proto(js_context_t ctx, js_value_t obj, js_value_t key);
|
||||
js_value_t js_obj_get_proto(js_context_t ctx, js_value_t obj, js_value_t key);
|
||||
|
||||
js_value_t js_obj_get_own(js_context_t ctx, js_value_t obj, js_value_t key);
|
||||
js_value_t js_obj_set_own(js_context_t ctx, js_value_t obj, js_value_t key, js_value_t value);
|
||||
js_value_t js_obj_has_own(js_context_t ctx, js_value_t obj, js_value_t key);
|
||||
js_value_t js_obj_del_own(js_context_t ctx, js_value_t obj, js_value_t key);
|
||||
|
||||
js_value_t js_obj_get(js_context_t ctx, js_value_t obj, js_value_t key);
|
||||
js_value_t js_obj_set(js_context_t ctx, js_value_t obj, js_value_t key, js_value_t value);
|
||||
js_value_t js_obj_has(js_context_t ctx, js_value_t obj, js_value_t key);
|
||||
|
||||
js_value_t js_obj_def_field(js_context_t ctx, js_value_t obj, js_value_t key, bool writable, bool configurable, bool enumerable);
|
||||
js_value_t js_obj_def_prop(js_context_t ctx, js_value_t obj, js_value_t key, js_value_t get, js_value_t set, bool configurable, bool enumerable);
|
||||
|
||||
js_value_t js_func_create(js_context_t ctx, bool apply, bool construct, js_string_t name, js_func_body_t body, size_t capture_n, js_capture_t *captures);
|
||||
|
||||
// void *ref = malloc(1);
|
||||
|
||||
typedef struct js_string {
|
||||
int8_t mark;
|
||||
size_t n;
|
||||
const uint16_t *val;
|
||||
} js_string_t;
|
||||
typedef struct js_symbol {
|
||||
int8_t mark;
|
||||
js_string_t *description;
|
||||
} js_symbol_t;
|
||||
typedef struct js_object {
|
||||
int8_t mark;
|
||||
} js_object_t;
|
||||
typedef struct js_function {
|
||||
js_object_t object;
|
||||
js_function_body_t body;
|
||||
} js_function_t;
|
||||
|
||||
typedef struct js_val {
|
||||
int8_t type;
|
||||
|
||||
union {
|
||||
double number;
|
||||
js_string_t *string;
|
||||
js_symbol_t *symbol;
|
||||
js_object_t *object;
|
||||
js_function_t *function;
|
||||
} value;
|
||||
} js_val_t;
|
||||
|
||||
typedef struct js_weak_ref {
|
||||
int8_t type;
|
||||
|
||||
union {
|
||||
js_string_t *string;
|
||||
js_symbol_t *symbol;
|
||||
js_object_t *object;
|
||||
js_function_t *function;
|
||||
} value;
|
||||
} js_weak_ref_t;
|
||||
|
||||
typedef js_val (*js_function_body_t)();
|
||||
|
||||
bool js_is_undefined(js_val_t);
|
||||
|
||||
js_val_t js_undefined();
|
||||
js_val_t js_null();
|
||||
|
||||
js_val_t js_number_new(double val);
|
||||
js_val_t js_string_new_c(const char *val);
|
||||
js_val_t js_string_new_cn(size_t n, const char *val);
|
||||
js_val_t js_string_new_cp(size_t n, const uint16_t *val);
|
||||
js_val_t js_string_new_br(size_t n, const uint16_t *val);
|
||||
js_val_t js_boolean_new(bool val);
|
||||
js_val_t js_object_new(size_t alloc);
|
||||
js_val_t js_array_new(size_t alloc);
|
||||
js_val_t js_function_new(js_function_body_t val);
|
||||
208
runtime/src/gc.c
Normal file
208
runtime/src/gc.c
Normal file
@@ -0,0 +1,208 @@
|
||||
#include <stdlib.h>
|
||||
#include "./include/gc.h"
|
||||
// #include "./internal/obj.h"
|
||||
|
||||
static uint64_t bucket_i(size_t cap, void *ptr) {
|
||||
return (((size_t)ptr / 256) * 4567) % cap;
|
||||
}
|
||||
|
||||
static void table_enlarge(js_gc_table_t *table) {
|
||||
js_gc_table_t new_table = {
|
||||
.cap = table->cap * 2,
|
||||
.n = table->n,
|
||||
.buckets = malloc(sizeof table->buckets * table->cap * 2),
|
||||
};
|
||||
|
||||
js_gc_table_node_t **tasks[new_table.cap];
|
||||
|
||||
for (size_t i = 0; i < sizeof table->buckets * new_table.cap; i++) {
|
||||
new_table.buckets[i] = NULL;
|
||||
tasks[i] = &new_table.buckets[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < table->cap; i++) {
|
||||
for (js_gc_table_node_t *node = table->buckets[i]; node != NULL; ) {
|
||||
js_gc_table_node_t *curr = node;
|
||||
|
||||
node = curr->next;
|
||||
curr->next = NULL;
|
||||
|
||||
size_t index = bucket_i(new_table.cap, curr->node.ptr);
|
||||
|
||||
*tasks[index] = curr;
|
||||
tasks[index] = &curr->next;
|
||||
}
|
||||
}
|
||||
|
||||
free(table->buckets);
|
||||
|
||||
*table = new_table;
|
||||
}
|
||||
|
||||
static bool table_add_stat(js_gc_table_t *table, js_gc_node_t node, bool enlarge) {
|
||||
if (enlarge && table->n >= table->cap) table_enlarge(table);
|
||||
|
||||
js_gc_table_node_t **target = &table->buckets[bucket_i(table, &node)];
|
||||
while (*target) {
|
||||
if (
|
||||
(*target)->node.type == node.type &&
|
||||
(*target)->node.ptr == node.ptr
|
||||
) return false;
|
||||
target = (*target)->next;
|
||||
}
|
||||
|
||||
*target = malloc(sizeof *target);
|
||||
(*target)->next = NULL;
|
||||
(*target)->node = node;
|
||||
table->n++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool table_del(js_gc_table_t *table, js_gc_node_t node) {
|
||||
js_gc_table_node_t **target = &table->buckets[bucket_i(table, &node)];
|
||||
|
||||
while (*target) {
|
||||
if (
|
||||
(*target)->node.type == node.type &&
|
||||
(*target)->node.ptr == node.ptr
|
||||
) {
|
||||
js_gc_table_node_t *curr = *target;
|
||||
*target = (*target)->next;
|
||||
free(curr);
|
||||
|
||||
table->n--;
|
||||
return true;
|
||||
}
|
||||
|
||||
target = (*target)->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void table_free(js_gc_table_t table) {
|
||||
for (size_t i = 0; i < table.cap; i++) {
|
||||
for (js_gc_table_node_t *node = table.buckets[i]; node != NULL; ) {
|
||||
js_gc_table_node_t *curr = node;
|
||||
|
||||
node = curr->next;
|
||||
free(curr);
|
||||
}
|
||||
}
|
||||
|
||||
free(table.buckets);
|
||||
}
|
||||
|
||||
static void pin_obj(js_gc_table_t *table, js_obj_t obj) {
|
||||
bool added = table_add_stat(table, (js_gc_node_t){ .ptr = obj, .type = JS_GC_OBJECT }, true);
|
||||
if (added) obj->gc.refs++;
|
||||
}
|
||||
static void pin_str(js_gc_table_t *table, js_gc_str_t str) {
|
||||
bool added = table_add_stat(table, (js_gc_node_t){ .ptr = str, .type = JS_GC_STRING }, true);
|
||||
if (added) str->gc.refs++;
|
||||
}
|
||||
|
||||
static bool unpin_obj(js_gc_table_t *table, js_obj_t obj) {
|
||||
if (obj->gc.refs == 0) return true;
|
||||
|
||||
bool removed = table_del(table, (js_gc_node_t){ .ptr = obj, .type = JS_GC_OBJECT });
|
||||
if (removed) obj->gc.refs--;
|
||||
|
||||
return obj->gc.refs == 0;
|
||||
}
|
||||
static bool unpin_str(js_gc_table_t *table, js_gc_str_t str) {
|
||||
if (str->gc.refs == 0) return true;
|
||||
|
||||
bool removed = table_del(table, (js_gc_node_t){ .ptr = str, .type = JS_GC_STRING });
|
||||
if (removed) str->gc.refs--;
|
||||
|
||||
return str->gc.refs == 0;
|
||||
}
|
||||
|
||||
static js_gc_task_stack_t stack_new(const js_gc_table_t *table) {
|
||||
size_t n = table->cap > 16 ? table->cap : 16;
|
||||
size_t j = 0;
|
||||
js_gc_task_stack_t stack;
|
||||
stack.cap = n;
|
||||
stack.n = table->n;
|
||||
stack.tasks = malloc(sizeof(js_gc_task_node_t) * n);
|
||||
|
||||
for (size_t i = 0; i < table->cap; i++) {
|
||||
for (js_gc_table_node_t *node = table->buckets[i]; node; node = node->next) {
|
||||
stack.tasks[j++] = (js_gc_task_node_t){
|
||||
.node = node->node,
|
||||
.prev_ref = NULL,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
static void stack_free(const js_gc_task_stack_t *stack) {
|
||||
free(stack->tasks);
|
||||
}
|
||||
|
||||
static void table_dryrun(const js_gc_table_t *table) {
|
||||
if (table->n == 0) return;
|
||||
|
||||
js_gc_task_stack_t stack = stack_new(table);
|
||||
|
||||
while(stack.n > 0) {
|
||||
js_gc_task_node_t task = stack.tasks[--stack.n];
|
||||
switch (task.node.type) {
|
||||
case JS_GC_OBJECT:
|
||||
js_gc_dryrun_obj(&stack, task.prev_ref, task.node.ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stack_free(&stack);
|
||||
}
|
||||
static void table_reclaim(const js_gc_table_t *table) {
|
||||
js_gc_task_stack_t stack = stack_new(table);
|
||||
|
||||
while(stack.n > 0) {
|
||||
js_gc_task_node_t task = stack.tasks[--stack.n];
|
||||
switch (task.node.type) {
|
||||
case JS_GC_OBJECT:
|
||||
js_gc_reclaim_obj(&stack, task.prev_ref, task.node.ptr);
|
||||
break;
|
||||
case JS_GC_STRING:
|
||||
js_gc_reclaim_str(&stack, task.prev_ref, task.node.ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stack_free(&stack);
|
||||
}
|
||||
|
||||
void js_gc_stack_push(js_gc_task_stack_t *stack, js_gc_task_node_t task) {
|
||||
while (stack->n >= stack->cap) {
|
||||
stack->cap *= 2;
|
||||
stack->tasks = realloc(stack->tasks, sizeof *stack->tasks * stack->cap);
|
||||
}
|
||||
|
||||
stack->tasks[stack->n++] = task;
|
||||
}
|
||||
bool js_gc_stack_pop(js_gc_task_stack_t *stack, js_gc_task_node_t *pout) {
|
||||
if (stack->n == 0) return false;
|
||||
|
||||
*pout = stack->tasks[--stack->n];
|
||||
return true;
|
||||
}
|
||||
|
||||
void js_gc_pin_val(js_gc_table_t *table, js_value_t val) {
|
||||
if (val.type == JS_T_OBJ) pin_obj(table, val.data);
|
||||
if (val.type == JS_T_STR && ((js_gc_str_t)val.data)->str.dynamic) pin_str(table, val.data);
|
||||
}
|
||||
void js_gc_unpin_val(js_gc_table_t *table, js_value_t val) {
|
||||
if (val.type == JS_T_OBJ) unpin_obj(table, val.data);
|
||||
if (val.type == JS_T_STR && ((js_gc_str_t)val.data)->str.dynamic) unpin_str(table, val.data);
|
||||
}
|
||||
|
||||
void js_gc_reclaim_frame(js_ctx_t ctx) {
|
||||
table_dryrun(&ctx->gc);
|
||||
table_reclaim(&ctx->gc);
|
||||
table_free(ctx->gc);
|
||||
}
|
||||
64
runtime/src/runtime.c
Normal file
64
runtime/src/runtime.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "./include/runtime.h"
|
||||
#include "./include/obj.h"
|
||||
|
||||
extern bool JS_ERR_FLAG = false;
|
||||
|
||||
js_value_t js_mk_num(double val) {
|
||||
return (js_value_t){ .type = JS_T_NUM, .data = *(void**)&val };
|
||||
}
|
||||
js_value_t js_mk_bool(bool val) {
|
||||
return val ? JS_TRUE : JS_FALSE;
|
||||
}
|
||||
|
||||
js_value_t js_mk_str(js_string_t str) {
|
||||
js_gc_str_t res = malloc(sizeof *res);
|
||||
res->str = str;
|
||||
res->gc = 0;
|
||||
|
||||
return (js_value_t){ .data = res, .type = JS_T_STR };
|
||||
}
|
||||
void js_str_free(js_gc_str_t str) {
|
||||
if (!str->str.dynamic || str->gc.refs == 0 || str == NULL) return;
|
||||
|
||||
str->gc.refs--;
|
||||
if (str->gc.refs == 0) {
|
||||
free(str->str.data);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
|
||||
js_value_t js_mk_symbol(js_string_t str) {
|
||||
js_gc_str_t res = malloc(sizeof *res);
|
||||
res->str = str;
|
||||
res->gc.refs = 0;
|
||||
|
||||
return (js_value_t){ .data = res, .type = JS_T_STR };
|
||||
}
|
||||
|
||||
js_value_t js_mk_obj(js_ctx_t ctx) {
|
||||
js_obj_t obj = malloc(sizeof *obj);
|
||||
|
||||
obj->buckets_n = 16;
|
||||
obj->buckets = malloc(sizeof *obj->buckets * 16);
|
||||
obj->first = NULL;
|
||||
obj->gc.refs = 0;
|
||||
|
||||
return (js_value_t){ .type = JS_T_OBJ, .data = obj };
|
||||
}
|
||||
void js_obj_free(js_gc_str_t str) {
|
||||
if (!str->str.dynamic || str->gc.refs == 0 || str == NULL) return;
|
||||
|
||||
str->gc.refs--;
|
||||
if (str->gc.refs == 0) {
|
||||
free(str->str.data);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
|
||||
void js_bootstrap(int argc, const char **argv) {
|
||||
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
js_gc_table_node_t next;
|
||||
} js_gc_table_node_t;
|
||||
Reference in New Issue
Block a user