MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include "ant.h"
2#include "runtime.h"
3#include "descriptors.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <uthash.h>
9#include <argtable3.h>
10
11#ifdef _WIN32
12#include <process.h>
13#define ant_getpid _getpid
14#else
15#include <unistd.h>
16#define ant_getpid getpid
17#endif
18
19typedef struct {
20 const char *ptr;
21 size_t len;
22 UT_hash_handle hh;
23} intern_entry_t;
24
25typedef struct code_block {
26 struct code_block *next;
27 size_t used;
28 size_t capacity;
29 char data[];
30} code_block_t;
31
32static struct ant_runtime runtime = {0};
33struct ant_runtime *const rt = &runtime;
34static intern_entry_t *code_interns = NULL;
35
36static code_block_t *code_arena_head = NULL;
37static code_block_t *code_arena_current = NULL;
38static code_block_t *parse_arena_head = NULL;
39static code_block_t *parse_arena_current = NULL;
40
41static void code_interns_prune_for_block_range(
42 const code_block_t *block,
43 size_t start_offset
44) {
45 if (!block) return;
46
47 const char *start = block->data + start_offset;
48 const char *end = block->data + block->capacity;
49 intern_entry_t *entry = NULL;
50 intern_entry_t *tmp = NULL;
51
52 HASH_ITER(hh, code_interns, entry, tmp)
53 if (entry->ptr >= start && entry->ptr < end) {
54 HASH_DEL(code_interns, entry);
55 free(entry);
56 }
57}
58
59static void code_interns_prune_for_blocks(code_block_t *first) {
60 for (
61 code_block_t *block = first;
62 block; block = block->next
63 ) code_interns_prune_for_block_range(block, 0);
64}
65
66static code_block_t *code_arena_new_block(size_t min_size) {
67 size_t capacity = CODE_ARENA_BLOCK_SIZE;
68 if (min_size > capacity) capacity = min_size;
69
70 code_block_t *block = malloc(sizeof(code_block_t) + capacity);
71 if (!block) return NULL;
72
73 block->next = NULL;
74 block->used = 0;
75 block->capacity = capacity;
76 return block;
77}
78
79static void *arena_bump(
80 code_block_t **head,
81 code_block_t **current,
82 size_t size
83) {
84 size = (size + 7) & ~(size_t)7;
85 if (!*current || (*current)->used + size > (*current)->capacity) {
86 code_block_t *new_block = code_arena_new_block(size);
87 if (!new_block) return NULL;
88 if (!*head) *head = new_block;
89 else if (*current) (*current)->next = new_block;
90 *current = new_block;
91 }
92
93 void *ptr = &(*current)->data[(*current)->used];
94 (*current)->used += size;
95 return ptr;
96}
97
98static size_t arena_get_memory(code_block_t *head) {
99 size_t total = 0;
100 for (code_block_t *b = head; b; b = b->next)
101 total += sizeof(code_block_t) + b->capacity;
102 return total;
103}
104
105static code_arena_mark_t arena_mark(code_block_t *current) {
106 code_arena_mark_t mark = {0};
107 mark.block = current;
108 mark.used = current ? current->used : 0;
109 return mark;
110}
111
112static void arena_rewind_plain(
113 code_block_t **head,
114 code_block_t **current,
115 code_arena_mark_t mark
116) {
117 code_block_t *target = (code_block_t *)mark.block;
118
119 if (!target) {
120 code_block_t *block = *head;
121 while (block) {
122 code_block_t *next = block->next;
123 free(block);
124 block = next;
125 }
126 *head = NULL;
127 *current = NULL;
128 return;
129 }
130
131 size_t clamped_used = mark.used <= target->capacity ? mark.used : target->capacity;
132 target->used = clamped_used;
133
134 code_block_t *b = target->next;
135 while (b) {
136 code_block_t *next = b->next;
137 free(b);
138 b = next;
139 }
140
141 target->next = NULL;
142 *current = target;
143}
144
145const char *code_arena_alloc(const char *code, size_t len) {
146 if (!code || len == 0) return NULL;
147
148 intern_entry_t *found = NULL;
149 HASH_FIND(hh, code_interns, code, len, found);
150 if (found) return found->ptr;
151
152 size_t alloc_size = len + 1;
153 if (!code_arena_current || code_arena_current->used + alloc_size > code_arena_current->capacity) {
154 code_block_t *new_block = code_arena_new_block(alloc_size);
155 if (!new_block) return NULL;
156 if (!code_arena_head) code_arena_head = new_block;
157 else if (code_arena_current) code_arena_current->next = new_block;
158 code_arena_current = new_block;
159 }
160
161 char *dest = &code_arena_current->data[code_arena_current->used];
162 memcpy(dest, code, len);
163 dest[len] = '\0';
164 code_arena_current->used += alloc_size;
165
166 intern_entry_t *entry = malloc(sizeof(*entry));
167 if (entry) {
168 entry->ptr = dest;
169 entry->len = len;
170 HASH_ADD_KEYPTR(hh, code_interns, entry->ptr, entry->len, entry);
171 }
172
173 return dest;
174}
175
176void *code_arena_bump(size_t size) {
177 return arena_bump(&code_arena_head, &code_arena_current, size);
178}
179
180size_t code_arena_get_memory(void) {
181 return arena_get_memory(code_arena_head);
182}
183
184code_arena_mark_t code_arena_mark(void) {
185 return arena_mark(code_arena_current);
186}
187
188void code_arena_rewind(code_arena_mark_t mark) {
189 code_block_t *target = (code_block_t *)mark.block;
190
191 if (!target) {
192 code_interns_prune_for_blocks(code_arena_head);
193 code_block_t *block = code_arena_head;
194
195 while (block) {
196 code_block_t *next = block->next;
197 free(block); block = next;
198 }
199
200 code_arena_head = NULL;
201 code_arena_current = NULL;
202
203 return;
204 }
205
206 size_t clamped_used = mark.used <= target->capacity ? mark.used : target->capacity;
207 code_interns_prune_for_block_range(target, clamped_used);
208 code_interns_prune_for_blocks(target->next);
209
210 if (mark.used <= target->capacity) target->used = mark.used;
211 code_block_t *b = target->next;
212
213 while (b) {
214 code_block_t *next = b->next;
215 free(b); b = next;
216 }
217
218 target->next = NULL;
219 code_arena_current = target;
220}
221
222void *parse_arena_bump(size_t size) {
223 return arena_bump(&parse_arena_head, &parse_arena_current, size);
224}
225
226size_t parse_arena_get_memory(void) {
227 return arena_get_memory(parse_arena_head);
228}
229
230code_arena_mark_t parse_arena_mark(void) {
231 return arena_mark(parse_arena_current);
232}
233
234void parse_arena_rewind(code_arena_mark_t mark) {
235 arena_rewind_plain(&parse_arena_head, &parse_arena_current, mark);
236}
237
238void parse_arena_reset(void) {
239 parse_arena_rewind((code_arena_mark_t){0});
240}
241
242void code_arena_reset(void) {
243 intern_entry_t *entry, *tmp;
244 HASH_ITER(hh, code_interns, entry, tmp) {
245 HASH_DEL(code_interns, entry);
246 free(entry);
247 }
248 code_interns = NULL;
249
250 code_block_t *block = code_arena_head;
251 while (block) {
252 code_block_t *next = block->next;
253 free(block);
254 block = next;
255 }
256
257 code_arena_head = NULL;
258 code_arena_current = NULL;
259 parse_arena_reset();
260}
261
262void destroy_runtime(ant_t *js) {
263 if (rt->js == js) memset(&runtime, 0, sizeof(runtime));
264}
265
266struct ant_runtime *ant_runtime_init(ant_t *js, int argc, char **argv, struct arg_file *ls_p) {
267 ant_value_t global = js_glob(js);
268
269 runtime = (struct ant_runtime){
270 .js = js,
271 .ant_obj = js_newobj(js),
272 .flags = 0, .argc = argc, .argv = argv,
273 .pid = (int)ant_getpid(),
274 .ls_fp = (ls_p && ls_p->count > 0) ? ls_p->filename[0] : NULL,
275 };
276
277 js_set(js, global, "onerror", js_mknull());
278 js_set_descriptor(js, global, "onerror", 7, JS_DESC_W | JS_DESC_C);
279
280 js_set(js, global, "onunhandledrejection", js_mknull());
281 js_set_descriptor(js, global, "onunhandledrejection", 20, JS_DESC_W | JS_DESC_C);
282
283 js_set(js, global, "onrejectionhandled", js_mknull());
284 js_set_descriptor(js, global, "onrejectionhandled", 18, JS_DESC_W | JS_DESC_C);
285
286 js_set(js, global, "self", global);
287 js_set_descriptor(js, global, "self", 4, JS_DESC_W | JS_DESC_C);
288
289 js_set(js, global, "window", global);
290 js_set_descriptor(js, global, "window", 6, JS_DESC_W | JS_DESC_C);
291
292 js_set(js, global, "global", global);
293 js_set_descriptor(js, global, "global", 6, JS_DESC_W | JS_DESC_C);
294
295 js_set(js, global, "globalThis", global);
296 js_set_descriptor(js, global, "globalThis", 10, JS_DESC_W | JS_DESC_C);
297
298 js_set(js, global, "Ant", runtime.ant_obj);
299 js_set_descriptor(js, global, "Ant", 3, JS_DESC_E);
300
301 return &runtime;
302}