MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

esm import system

+791 -138
+1 -1
meson.build
··· 41 41 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 42 42 43 43 version_conf = configuration_data() 44 - version_conf.set('ANT_VERSION', '0.0.5.56') 44 + version_conf.set('ANT_VERSION', '0.0.5.57') 45 45 version_conf.set('ANT_GIT_HASH', git_hash) 46 46 version_conf.set('ANT_BUILD_DATE', build_date) 47 47
+738 -11
src/ant.c
··· 11 11 #include <string.h> 12 12 #include <sys/time.h> 13 13 #include <time.h> 14 + #include <libgen.h> 15 + #include <sys/stat.h> 14 16 15 17 #include "ant.h" 16 18 #include "config.h" ··· 70 72 static call_stack_t global_call_stack = {NULL, 0, 0}; 71 73 static coroutine_queue_t pending_coroutines = {NULL, NULL}; 72 74 75 + typedef struct esm_module { 76 + char *path; 77 + char *resolved_path; 78 + jsval_t namespace_obj; 79 + jsval_t default_export; 80 + bool is_loaded; 81 + bool is_loading; 82 + bool is_json; 83 + bool is_text; 84 + bool is_image; 85 + struct esm_module *next; 86 + } esm_module_t; 87 + 88 + typedef struct { 89 + esm_module_t *head; 90 + int count; 91 + } esm_module_cache_t; 92 + 93 + static esm_module_cache_t global_module_cache = {NULL, 0}; 94 + 73 95 struct js { 74 96 jsoff_t css; // max observed C stack size 75 97 jsoff_t lwm; // JS ram low watermark: min free ram observed ··· 111 133 TOK_ERR, TOK_EOF, TOK_IDENTIFIER, TOK_NUMBER, TOK_STRING, TOK_SEMICOLON, 112 134 TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE, TOK_LBRACKET, TOK_RBRACKET, 113 135 TOK_ASYNC = 50, TOK_AWAIT, TOK_BREAK, TOK_CASE, TOK_CATCH, TOK_CLASS, TOK_CONST, TOK_CONTINUE, 114 - TOK_DEFAULT, TOK_DELETE, TOK_DO, TOK_ELSE, TOK_FINALLY, TOK_FOR, TOK_FUNC, 115 - TOK_IF, TOK_IN, TOK_INSTANCEOF, TOK_LET, TOK_NEW, TOK_RETURN, TOK_SWITCH, 136 + TOK_DEFAULT, TOK_DELETE, TOK_DO, TOK_ELSE, TOK_EXPORT, TOK_FINALLY, TOK_FOR, TOK_FROM, TOK_FUNC, 137 + TOK_IF, TOK_IMPORT, TOK_IN, TOK_INSTANCEOF, TOK_LET, TOK_NEW, TOK_RETURN, TOK_SWITCH, 116 138 TOK_THIS, TOK_THROW, TOK_TRY, TOK_VAR, TOK_VOID, TOK_WHILE, TOK_WITH, 117 - TOK_YIELD, TOK_UNDEF, TOK_NULL, TOK_TRUE, TOK_FALSE, 139 + TOK_YIELD, TOK_UNDEF, TOK_NULL, TOK_TRUE, TOK_FALSE, TOK_AS, TOK_STATIC, 118 140 TOK_DOT = 100, TOK_CALL, TOK_BRACKET, TOK_POSTINC, TOK_POSTDEC, TOK_NOT, TOK_TILDA, 119 141 TOK_TYPEOF, TOK_UPLUS, TOK_UMINUS, TOK_EXP, TOK_MUL, TOK_DIV, TOK_REM, 120 142 TOK_OPTIONAL_CHAIN, TOK_REST, ··· 198 220 static jsval_t builtin_array_includes(struct js *js, jsval_t *args, int nargs); 199 221 static jsval_t builtin_array_every(struct js *js, jsval_t *args, int nargs); 200 222 static jsval_t builtin_Error(struct js *js, jsval_t *args, int nargs); 223 + static jsval_t js_import_stmt(struct js *js); 224 + static jsval_t js_export_stmt(struct js *js); 225 + static jsval_t builtin_import(struct js *js, jsval_t *args, int nargs); 201 226 static jsval_t builtin_string_indexOf(struct js *js, jsval_t *args, int nargs); 202 227 static jsval_t builtin_string_substring(struct js *js, jsval_t *args, int nargs); 203 228 static jsval_t builtin_string_split(struct js *js, jsval_t *args, int nargs); ··· 940 965 941 966 static uint8_t parsekeyword(const char *buf, size_t len) { 942 967 switch (buf[0]) { 943 - case 'a': if (streq("async", 5, buf, len)) return TOK_ASYNC; if (streq("await", 5, buf, len)) return TOK_AWAIT; break; 968 + case 'a': if (streq("async", 5, buf, len)) return TOK_ASYNC; if (streq("await", 5, buf, len)) return TOK_AWAIT; if (streq("as", 2, buf, len)) return TOK_AS; break; 944 969 case 'b': if (streq("break", 5, buf, len)) return TOK_BREAK; break; 945 970 case 'c': if (streq("class", 5, buf, len)) return TOK_CLASS; if (streq("case", 4, buf, len)) return TOK_CASE; if (streq("catch", 5, buf, len)) return TOK_CATCH; if (streq("const", 5, buf, len)) return TOK_CONST; if (streq("continue", 8, buf, len)) return TOK_CONTINUE; break; 946 971 case 'd': if (streq("do", 2, buf, len)) return TOK_DO; if (streq("default", 7, buf, len)) return TOK_DEFAULT; if (streq("delete", 6, buf, len)) return TOK_DELETE; break; 947 - case 'e': if (streq("else", 4, buf, len)) return TOK_ELSE; break; 948 - case 'f': if (streq("for", 3, buf, len)) return TOK_FOR; if (streq("function", 8, buf, len)) return TOK_FUNC; if (streq("finally", 7, buf, len)) return TOK_FINALLY; if (streq("false", 5, buf, len)) return TOK_FALSE; break; 949 - case 'i': if (streq("if", 2, buf, len)) return TOK_IF; if (streq("in", 2, buf, len)) return TOK_IN; if (streq("instanceof", 10, buf, len)) return TOK_INSTANCEOF; break; 972 + case 'e': if (streq("else", 4, buf, len)) return TOK_ELSE; if (streq("export", 6, buf, len)) return TOK_EXPORT; break; 973 + case 'f': if (streq("for", 3, buf, len)) return TOK_FOR; if (streq("from", 4, buf, len)) return TOK_FROM; if (streq("function", 8, buf, len)) return TOK_FUNC; if (streq("finally", 7, buf, len)) return TOK_FINALLY; if (streq("false", 5, buf, len)) return TOK_FALSE; break; 974 + case 'i': if (streq("if", 2, buf, len)) return TOK_IF; if (streq("import", 6, buf, len)) return TOK_IMPORT; if (streq("in", 2, buf, len)) return TOK_IN; if (streq("instanceof", 10, buf, len)) return TOK_INSTANCEOF; break; 950 975 case 'l': if (streq("let", 3, buf, len)) return TOK_LET; break; 951 976 case 'n': if (streq("new", 3, buf, len)) return TOK_NEW; if (streq("null", 4, buf, len)) return TOK_NULL; break; 952 977 case 'r': if (streq("return", 6, buf, len)) return TOK_RETURN; break; 953 - case 's': if (streq("switch", 6, buf, len)) return TOK_SWITCH; break; 978 + case 's': if (streq("switch", 6, buf, len)) return TOK_SWITCH; if (streq("static", 6, buf, len)) return TOK_STATIC; break; 954 979 case 't': if (streq("try", 3, buf, len)) return TOK_TRY; if (streq("this", 4, buf, len)) return TOK_THIS; if (streq("throw", 5, buf, len)) return TOK_THROW; if (streq("true", 4, buf, len)) return TOK_TRUE; if (streq("typeof", 6, buf, len)) return TOK_TYPEOF; break; 955 980 case 'u': if (streq("undefined", 9, buf, len)) return TOK_UNDEF; break; 956 981 case 'v': if (streq("var", 3, buf, len)) return TOK_VAR; if (streq("void", 4, buf, len)) return TOK_VOID; break; ··· 1138 1163 uint8_t t = js->tok; 1139 1164 res = js_stmt(js); 1140 1165 if (!is_err(res) && t != TOK_LBRACE && t != TOK_IF && t != TOK_WHILE && 1141 - t != TOK_DO && t != TOK_FUNC && t != TOK_FOR && js->tok != TOK_SEMICOLON) { 1166 + t != TOK_DO && t != TOK_FUNC && t != TOK_FOR && t != TOK_IMPORT && 1167 + t != TOK_EXPORT && js->tok != TOK_SEMICOLON) { 1142 1168 res = js_mkerr(js, "; expected"); 1143 1169 break; 1144 1170 } ··· 4412 4438 jsval_t res3 = setprop(js, func_obj, scope_key, func_scope); 4413 4439 if (is_err(res3)) return res3; 4414 4440 4441 + jsval_t name_key = js_mkstr(js, "name", 4); 4442 + if (is_err(name_key)) return name_key; 4443 + jsval_t name_val = js_mkstr(js, class_name, class_name_len); 4444 + if (is_err(name_val)) return name_val; 4445 + jsval_t res_name = setprop(js, func_obj, name_key, name_val); 4446 + if (is_err(res_name)) return res_name; 4447 + 4415 4448 jsval_t constructor = mkval(T_FUNC, (unsigned long) vdata(func_obj)); 4416 4449 if (lkp(js, js->scope, class_name, class_name_len) > 0) { 4417 4450 return js_mkerr(js, "'%.*s' already declared", (int) class_name_len, class_name); 4418 4451 } 4419 4452 jsval_t x = mkprop(js, js->scope, js_mkstr(js, class_name, class_name_len), constructor, false); 4420 4453 if (is_err(x)) return x; 4454 + 4455 + (void) class_body_start; 4456 + return constructor; 4421 4457 } 4422 4458 4423 4459 (void) class_body_start; ··· 4459 4495 static jsval_t js_stmt(struct js *js) { 4460 4496 jsval_t res; 4461 4497 if (js->brk > js->gct) js_gc(js); 4498 + uint8_t stmt_tok = next(js); 4462 4499 4463 - switch (next(js)) { 4500 + switch (stmt_tok) { 4464 4501 case TOK_CASE: case TOK_CATCH: 4465 4502 case TOK_DEFAULT: case TOK_FINALLY: 4466 4503 res = js_mkerr(js, "SyntaxError '%.*s'", (int) js->tlen, js->code + js->toff); ··· 4468 4505 case TOK_YIELD: 4469 4506 res = js_mkerr(js, " '%.*s' not implemented", (int) js->tlen, js->code + js->toff); 4470 4507 break; 4508 + case TOK_IMPORT: res = js_import_stmt(js); break; 4509 + case TOK_EXPORT: res = js_export_stmt(js); break; 4471 4510 case TOK_THROW: js_throw_handle(js, &res); break; 4472 4511 case TOK_VAR: js_var(js, &res); break; 4473 4512 case TOK_ASYNC: js_async(js, &res); break; ··· 4489 4528 default: res = resolveprop(js, js_expr(js)); break; 4490 4529 } 4491 4530 4492 - if (next(js) != TOK_SEMICOLON && next(js) != TOK_EOF && next(js) != TOK_RBRACE) return js_mkerr(js, "; expected"); 4531 + bool is_block_statement = ( 4532 + stmt_tok == TOK_FUNC || stmt_tok == TOK_CLASS || 4533 + stmt_tok == TOK_EXPORT || stmt_tok == TOK_IMPORT || 4534 + stmt_tok == TOK_IF || stmt_tok == TOK_WHILE || 4535 + stmt_tok == TOK_DO || stmt_tok == TOK_FOR || 4536 + stmt_tok == TOK_SWITCH || stmt_tok == TOK_TRY || 4537 + stmt_tok == TOK_LBRACE 4538 + ); 4539 + 4540 + if (!is_block_statement) { 4541 + int next_tok = next(js); 4542 + bool missing_semicolon = next_tok != TOK_SEMICOLON && next_tok != TOK_EOF && next_tok != TOK_RBRACE; 4543 + if (missing_semicolon) return js_mkerr(js, "; expected"); 4544 + } 4545 + 4493 4546 js->consumed = 1; 4494 4547 return res; 4495 4548 } ··· 6368 6421 return mkval(T_BOOL, found != 0 ? 1 : 0); 6369 6422 } 6370 6423 6424 + static char *esm_get_extension(const char *path) { 6425 + const char *dot = strrchr(path, '.'); 6426 + const char *slash = strrchr(path, '/'); 6427 + 6428 + if (dot && (!slash || dot > slash)) { 6429 + return strdup(dot); 6430 + } 6431 + return strdup(".js"); 6432 + } 6433 + 6434 + static char *esm_try_resolve(const char *dir, const char *spec, const char *suffix) { 6435 + char path[PATH_MAX]; 6436 + snprintf(path, PATH_MAX, "%s/%s%s", dir, spec, suffix); 6437 + return realpath(path, NULL); 6438 + } 6439 + 6440 + static bool esm_has_extension(const char *spec) { 6441 + const char *dot = strrchr(spec, '.'); 6442 + const char *slash = strrchr(spec, '/'); 6443 + return dot && (!slash || dot > slash); 6444 + } 6445 + 6446 + static char *esm_resolve_path(const char *specifier, const char *base_path) { 6447 + if (!(specifier[0] == '/' || 6448 + (specifier[0] == '.' && specifier[1] == '/') || 6449 + (specifier[0] == '.' && specifier[1] == '.' && specifier[2] == '/'))) { 6450 + return strdup(specifier); 6451 + } 6452 + 6453 + char *base_copy = strdup(base_path); 6454 + char *dir = dirname(base_copy); 6455 + char *result = NULL; 6456 + 6457 + const char *spec = (specifier[0] == '.' && specifier[1] == '/') ? specifier + 2 : specifier; 6458 + bool has_ext = esm_has_extension(spec); 6459 + 6460 + if ((result = esm_try_resolve(dir, spec, ""))) goto cleanup; 6461 + if (has_ext) goto cleanup; 6462 + 6463 + char *base_ext = esm_get_extension(base_path); 6464 + 6465 + if ((result = esm_try_resolve(dir, spec, base_ext))) goto cleanup_ext; 6466 + if (strcmp(base_ext, ".js") != 0 && (result = esm_try_resolve(dir, spec, ".js"))) goto cleanup_ext; 6467 + if ((result = esm_try_resolve(dir, spec, ".json"))) goto cleanup_ext; 6468 + 6469 + char idx[PATH_MAX]; 6470 + snprintf(idx, PATH_MAX, "%s/index%s", spec, base_ext); 6471 + if ((result = esm_try_resolve(dir, idx, ""))) goto cleanup_ext; 6472 + 6473 + if (strcmp(base_ext, ".js") != 0) { 6474 + snprintf(idx, PATH_MAX, "%s/index.js", spec); 6475 + if ((result = esm_try_resolve(dir, idx, ""))) goto cleanup_ext; 6476 + } 6477 + 6478 + cleanup_ext: 6479 + free(base_ext); 6480 + cleanup: 6481 + free(base_copy); 6482 + return result; 6483 + } 6484 + 6485 + static bool esm_is_json(const char *path) { 6486 + size_t len = strlen(path); 6487 + return len > 5 && strcmp(path + len - 5, ".json") == 0; 6488 + } 6489 + 6490 + static bool esm_is_text(const char *path) { 6491 + size_t len = strlen(path); 6492 + return (len > 4 && strcmp(path + len - 4, ".txt") == 0) || 6493 + (len > 3 && strcmp(path + len - 3, ".md") == 0) || 6494 + (len > 5 && strcmp(path + len - 5, ".html") == 0) || 6495 + (len > 4 && strcmp(path + len - 4, ".css") == 0); 6496 + } 6497 + 6498 + static bool esm_is_image(const char *path) { 6499 + size_t len = strlen(path); 6500 + return (len > 4 && strcmp(path + len - 4, ".png") == 0) || 6501 + (len > 4 && strcmp(path + len - 4, ".jpg") == 0) || 6502 + (len > 5 && strcmp(path + len - 5, ".jpeg") == 0) || 6503 + (len > 4 && strcmp(path + len - 4, ".gif") == 0) || 6504 + (len > 4 && strcmp(path + len - 4, ".svg") == 0) || 6505 + (len > 5 && strcmp(path + len - 5, ".webp") == 0); 6506 + } 6507 + 6508 + static esm_module_t *esm_find_module(const char *resolved_path) { 6509 + esm_module_t *mod = global_module_cache.head; 6510 + while (mod) { 6511 + if (strcmp(mod->resolved_path, resolved_path) == 0) return mod; 6512 + mod = mod->next; 6513 + } 6514 + return NULL; 6515 + } 6516 + 6517 + static esm_module_t *esm_create_module(const char *path, const char *resolved_path) { 6518 + esm_module_t *mod = (esm_module_t *)malloc(sizeof(esm_module_t)); 6519 + if (!mod) return NULL; 6520 + 6521 + mod->path = strdup(path); 6522 + mod->resolved_path = strdup(resolved_path); 6523 + mod->namespace_obj = js_mkundef(); 6524 + mod->default_export = js_mkundef(); 6525 + mod->is_loaded = false; 6526 + mod->is_loading = false; 6527 + mod->is_json = esm_is_json(resolved_path); 6528 + mod->is_text = esm_is_text(resolved_path); 6529 + mod->is_image = esm_is_image(resolved_path); 6530 + mod->next = NULL; 6531 + 6532 + if (global_module_cache.head == NULL) { 6533 + global_module_cache.head = mod; 6534 + } else { 6535 + esm_module_t *last = global_module_cache.head; 6536 + while (last->next) last = last->next; 6537 + last->next = mod; 6538 + } 6539 + global_module_cache.count++; 6540 + 6541 + return mod; 6542 + } 6543 + 6544 + static jsval_t esm_load_json(struct js *js, const char *path) { 6545 + FILE *fp = fopen(path, "rb"); 6546 + if (!fp) return js_mkerr(js, "Cannot open JSON file: %s", path); 6547 + 6548 + fseek(fp, 0, SEEK_END); 6549 + long size = ftell(fp); 6550 + fseek(fp, 0, SEEK_SET); 6551 + 6552 + char *content = (char *)malloc(size + 1); 6553 + if (!content) { 6554 + fclose(fp); 6555 + return js_mkerr(js, "OOM loading JSON"); 6556 + } 6557 + 6558 + fread(content, 1, size, fp); 6559 + fclose(fp); 6560 + content[size] = '\0'; 6561 + 6562 + jsval_t result = js_eval(js, content, size); 6563 + free(content); 6564 + 6565 + return result; 6566 + } 6567 + 6568 + static jsval_t esm_load_text(struct js *js, const char *path) { 6569 + FILE *fp = fopen(path, "rb"); 6570 + if (!fp) return js_mkerr(js, "Cannot open text file: %s", path); 6571 + 6572 + fseek(fp, 0, SEEK_END); 6573 + long size = ftell(fp); 6574 + fseek(fp, 0, SEEK_SET); 6575 + 6576 + char *content = (char *)malloc(size + 1); 6577 + if (!content) { 6578 + fclose(fp); 6579 + return js_mkerr(js, "OOM loading text"); 6580 + } 6581 + 6582 + fread(content, 1, size, fp); 6583 + fclose(fp); 6584 + content[size] = '\0'; 6585 + 6586 + jsval_t result = js_mkstr(js, content, size); 6587 + free(content); 6588 + 6589 + return result; 6590 + } 6591 + 6592 + static jsval_t esm_load_image(struct js *js, const char *path) { 6593 + FILE *fp = fopen(path, "rb"); 6594 + if (!fp) return js_mkerr(js, "Cannot open image file: %s", path); 6595 + 6596 + fseek(fp, 0, SEEK_END); 6597 + long size = ftell(fp); 6598 + fseek(fp, 0, SEEK_SET); 6599 + 6600 + unsigned char *content = (unsigned char *)malloc(size); 6601 + if (!content) { 6602 + fclose(fp); 6603 + return js_mkerr(js, "OOM loading image"); 6604 + } 6605 + 6606 + fread(content, 1, size, fp); 6607 + fclose(fp); 6608 + 6609 + jsval_t obj = mkobj(js, 0); 6610 + jsval_t data_arr = mkarr(js); 6611 + 6612 + for (long i = 0; i < size; i++) { 6613 + char idx[16]; 6614 + snprintf(idx, sizeof(idx), "%ld", i); 6615 + setprop(js, data_arr, js_mkstr(js, idx, strlen(idx)), tov((double)content[i])); 6616 + } 6617 + setprop(js, data_arr, js_mkstr(js, "length", 6), tov((double)size)); 6618 + 6619 + setprop(js, obj, js_mkstr(js, "data", 4), mkval(T_ARR, vdata(data_arr))); 6620 + setprop(js, obj, js_mkstr(js, "path", 4), js_mkstr(js, path, strlen(path))); 6621 + setprop(js, obj, js_mkstr(js, "size", 4), tov((double)size)); 6622 + 6623 + free(content); 6624 + return obj; 6625 + } 6626 + 6627 + static jsval_t esm_load_module(struct js *js, esm_module_t *mod) { 6628 + if (mod->is_loaded) return mod->namespace_obj; 6629 + if (mod->is_loading) return js_mkerr(js, "Circular dependency detected: %s", mod->path); 6630 + 6631 + mod->is_loading = true; 6632 + 6633 + if (mod->is_json) { 6634 + jsval_t json_val = esm_load_json(js, mod->resolved_path); 6635 + if (is_err(json_val)) { 6636 + mod->is_loading = false; 6637 + return json_val; 6638 + } 6639 + 6640 + jsval_t ns = mkobj(js, 0); 6641 + setprop(js, ns, js_mkstr(js, "default", 7), json_val); 6642 + mod->namespace_obj = ns; 6643 + mod->default_export = json_val; 6644 + mod->is_loaded = true; 6645 + mod->is_loading = false; 6646 + return ns; 6647 + } 6648 + 6649 + if (mod->is_text) { 6650 + jsval_t text_val = esm_load_text(js, mod->resolved_path); 6651 + if (is_err(text_val)) { 6652 + mod->is_loading = false; 6653 + return text_val; 6654 + } 6655 + 6656 + jsval_t ns = mkobj(js, 0); 6657 + setprop(js, ns, js_mkstr(js, "default", 7), text_val); 6658 + mod->namespace_obj = ns; 6659 + mod->default_export = text_val; 6660 + mod->is_loaded = true; 6661 + mod->is_loading = false; 6662 + return ns; 6663 + } 6664 + 6665 + if (mod->is_image) { 6666 + jsval_t img_val = esm_load_image(js, mod->resolved_path); 6667 + if (is_err(img_val)) { 6668 + mod->is_loading = false; 6669 + return img_val; 6670 + } 6671 + 6672 + jsval_t ns = mkobj(js, 0); 6673 + setprop(js, ns, js_mkstr(js, "default", 7), img_val); 6674 + mod->namespace_obj = ns; 6675 + mod->default_export = img_val; 6676 + mod->is_loaded = true; 6677 + mod->is_loading = false; 6678 + return ns; 6679 + } 6680 + 6681 + FILE *fp = fopen(mod->resolved_path, "rb"); 6682 + if (!fp) { 6683 + mod->is_loading = false; 6684 + return js_mkerr(js, "Cannot open module: %s", mod->resolved_path); 6685 + } 6686 + 6687 + fseek(fp, 0, SEEK_END); 6688 + long size = ftell(fp); 6689 + fseek(fp, 0, SEEK_SET); 6690 + 6691 + char *content = (char *)malloc(size + 1); 6692 + if (!content) { 6693 + fclose(fp); 6694 + mod->is_loading = false; 6695 + return js_mkerr(js, "OOM loading module"); 6696 + } 6697 + 6698 + fread(content, 1, size, fp); 6699 + fclose(fp); 6700 + content[size] = '\0'; 6701 + 6702 + jsval_t ns = mkobj(js, 0); 6703 + mod->namespace_obj = ns; 6704 + 6705 + jsval_t glob = js_glob(js); 6706 + jsval_t module_scope = js_get(js, glob, "__esm_module_scope"); 6707 + jsval_t prev_module = module_scope; 6708 + js_set(js, glob, "__esm_module_scope", ns); 6709 + 6710 + const char *prev_filename = js->filename; 6711 + 6712 + js_set_filename(js, mod->resolved_path); 6713 + 6714 + jsval_t result = js_eval(js, content, size); 6715 + 6716 + free(content); 6717 + 6718 + js_set_filename(js, prev_filename); 6719 + js_set(js, glob, "__esm_module_scope", prev_module); 6720 + 6721 + if (is_err(result)) { 6722 + mod->is_loading = false; 6723 + return result; 6724 + } 6725 + 6726 + jsoff_t default_off = lkp(js, ns, "default", 7); 6727 + if (default_off != 0) { 6728 + mod->default_export = resolveprop(js, mkval(T_PROP, default_off)); 6729 + } 6730 + 6731 + mod->is_loaded = true; 6732 + mod->is_loading = false; 6733 + 6734 + return ns; 6735 + } 6736 + 6737 + static jsval_t builtin_import(struct js *js, jsval_t *args, int nargs) { 6738 + if (nargs < 1 || vtype(args[0]) != T_STR) { 6739 + return js_mkerr(js, "import() requires a string specifier"); 6740 + } 6741 + 6742 + jsoff_t spec_len; 6743 + jsoff_t spec_off = vstr(js, args[0], &spec_len); 6744 + const char *specifier = (char *)&js->mem[spec_off]; 6745 + 6746 + const char *base_path = js->filename ? js->filename : "."; 6747 + char *resolved_path = esm_resolve_path(specifier, base_path); 6748 + if (!resolved_path) { 6749 + return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 6750 + } 6751 + 6752 + esm_module_t *mod = esm_find_module(resolved_path); 6753 + if (!mod) { 6754 + mod = esm_create_module(specifier, resolved_path); 6755 + if (!mod) { 6756 + free(resolved_path); 6757 + return js_mkerr(js, "Cannot create module"); 6758 + } 6759 + } 6760 + 6761 + jsval_t ns = esm_load_module(js, mod); 6762 + free(resolved_path); 6763 + 6764 + if (is_err(ns)) return builtin_Promise_reject(js, &ns, 1); 6765 + 6766 + jsval_t promise_args[] = { ns }; 6767 + return builtin_Promise_resolve(js, promise_args, 1); 6768 + } 6769 + 6770 + static jsval_t js_import_stmt(struct js *js) { 6771 + js->consumed = 1; 6772 + 6773 + if (next(js) == TOK_LPAREN) { 6774 + js->consumed = 1; 6775 + jsval_t spec = js_expr(js); 6776 + EXPECT(TOK_RPAREN, ); 6777 + 6778 + if (vtype(spec) != T_STR) { 6779 + return js_mkerr(js, "import() requires string"); 6780 + } 6781 + 6782 + jsval_t args[] = { spec }; 6783 + return builtin_import(js, args, 1); 6784 + } 6785 + 6786 + if (next(js) == TOK_MUL) { 6787 + js->consumed = 1; 6788 + EXPECT(TOK_AS, ); 6789 + EXPECT(TOK_IDENTIFIER, ); 6790 + 6791 + const char *namespace_name = &js->code[js->toff]; 6792 + size_t namespace_len = js->tlen; 6793 + js->consumed = 1; 6794 + 6795 + EXPECT(TOK_FROM, ); 6796 + EXPECT(TOK_STRING, ); 6797 + 6798 + jsval_t spec = js_str_literal(js); 6799 + 6800 + jsoff_t spec_len; 6801 + jsoff_t spec_off = vstr(js, spec, &spec_len); 6802 + const char *specifier = (char *)&js->mem[spec_off]; 6803 + 6804 + const char *base_path = js->filename ? js->filename : "."; 6805 + char *resolved_path = esm_resolve_path(specifier, base_path); 6806 + if (!resolved_path) { 6807 + return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 6808 + } 6809 + 6810 + esm_module_t *mod = esm_find_module(resolved_path); 6811 + if (!mod) { 6812 + mod = esm_create_module(specifier, resolved_path); 6813 + if (!mod) { 6814 + free(resolved_path); 6815 + return js_mkerr(js, "Cannot create module"); 6816 + } 6817 + } 6818 + 6819 + const char *saved_code = js->code; 6820 + jsoff_t saved_clen = js->clen; 6821 + jsoff_t saved_pos = js->pos; 6822 + 6823 + jsval_t ns = esm_load_module(js, mod); 6824 + 6825 + js->code = saved_code; 6826 + js->clen = saved_clen; 6827 + js->pos = saved_pos; 6828 + js->consumed = 1; next(js); js->consumed = 0; 6829 + 6830 + free(resolved_path); 6831 + 6832 + if (is_err(ns)) return ns; 6833 + 6834 + setprop(js, js->scope, js_mkstr(js, namespace_name, namespace_len), ns); 6835 + 6836 + return js_mkundef(); 6837 + } 6838 + 6839 + if (next(js) == TOK_IDENTIFIER) { 6840 + const char *default_name = &js->code[js->toff]; 6841 + size_t default_len = js->tlen; 6842 + js->consumed = 1; 6843 + 6844 + EXPECT(TOK_FROM, ); 6845 + EXPECT(TOK_STRING, ); 6846 + 6847 + jsval_t spec = js_str_literal(js); 6848 + 6849 + jsoff_t spec_len; 6850 + jsoff_t spec_off = vstr(js, spec, &spec_len); 6851 + const char *specifier = (char *)&js->mem[spec_off]; 6852 + 6853 + const char *base_path = js->filename ? js->filename : "."; 6854 + char *resolved_path = esm_resolve_path(specifier, base_path); 6855 + if (!resolved_path) { 6856 + return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 6857 + } 6858 + 6859 + esm_module_t *mod = esm_find_module(resolved_path); 6860 + if (!mod) { 6861 + mod = esm_create_module(specifier, resolved_path); 6862 + if (!mod) { 6863 + free(resolved_path); 6864 + return js_mkerr(js, "Cannot create module"); 6865 + } 6866 + } 6867 + 6868 + const char *saved_code = js->code; 6869 + jsoff_t saved_clen = js->clen; 6870 + jsoff_t saved_pos = js->pos; 6871 + 6872 + jsval_t ns = esm_load_module(js, mod); 6873 + 6874 + js->code = saved_code; 6875 + js->clen = saved_clen; 6876 + js->pos = saved_pos; 6877 + js->consumed = 1; next(js); js->consumed = 0; 6878 + 6879 + free(resolved_path); 6880 + 6881 + if (is_err(ns)) return ns; 6882 + 6883 + jsoff_t default_off = lkp(js, ns, "default", 7); 6884 + jsval_t default_val = default_off != 0 ? resolveprop(js, mkval(T_PROP, default_off)) : js_mkundef(); 6885 + 6886 + setprop(js, js->scope, js_mkstr(js, default_name, default_len), default_val); 6887 + 6888 + return js_mkundef(); 6889 + } 6890 + 6891 + if (next(js) == TOK_LBRACE) { 6892 + js->consumed = 1; 6893 + 6894 + typedef struct { 6895 + const char *import_name; 6896 + size_t import_len; 6897 + const char *local_name; 6898 + size_t local_len; 6899 + } import_binding_t; 6900 + 6901 + import_binding_t bindings[64]; 6902 + int binding_count = 0; 6903 + 6904 + while (next(js) != TOK_RBRACE && binding_count < 64) { 6905 + EXPECT(TOK_IDENTIFIER, ); 6906 + const char *import_name = &js->code[js->toff]; 6907 + size_t import_len = js->tlen; 6908 + js->consumed = 1; 6909 + 6910 + const char *local_name = import_name; 6911 + size_t local_len = import_len; 6912 + 6913 + if (next(js) == TOK_AS) { 6914 + js->consumed = 1; 6915 + EXPECT(TOK_IDENTIFIER, ); 6916 + local_name = &js->code[js->toff]; 6917 + local_len = js->tlen; 6918 + js->consumed = 1; 6919 + } 6920 + 6921 + bindings[binding_count].import_name = import_name; 6922 + bindings[binding_count].import_len = import_len; 6923 + bindings[binding_count].local_name = local_name; 6924 + bindings[binding_count].local_len = local_len; 6925 + binding_count++; 6926 + 6927 + if (next(js) == TOK_COMMA) js->consumed = 1; 6928 + } 6929 + 6930 + EXPECT(TOK_RBRACE, ); 6931 + EXPECT(TOK_FROM, ); 6932 + EXPECT(TOK_STRING, ); 6933 + 6934 + jsval_t spec = js_str_literal(js); 6935 + 6936 + jsoff_t spec_len; 6937 + jsoff_t spec_off = vstr(js, spec, &spec_len); 6938 + const char *specifier = (char *)&js->mem[spec_off]; 6939 + 6940 + const char *base_path = js->filename ? js->filename : "."; 6941 + char *resolved_path = esm_resolve_path(specifier, base_path); 6942 + if (!resolved_path) { 6943 + return js_mkerr(js, "Cannot resolve module: %.*s", (int)spec_len, specifier); 6944 + } 6945 + 6946 + esm_module_t *mod = esm_find_module(resolved_path); 6947 + if (!mod) { 6948 + mod = esm_create_module(specifier, resolved_path); 6949 + if (!mod) { 6950 + free(resolved_path); 6951 + return js_mkerr(js, "Cannot create module"); 6952 + } 6953 + } 6954 + 6955 + const char *saved_code = js->code; 6956 + jsoff_t saved_clen = js->clen; 6957 + jsoff_t saved_pos = js->pos; 6958 + 6959 + jsval_t ns = esm_load_module(js, mod); 6960 + 6961 + js->code = saved_code; 6962 + js->clen = saved_clen; 6963 + js->pos = saved_pos; 6964 + js->consumed = 1; 6965 + next(js); 6966 + js->consumed = 0; 6967 + 6968 + free(resolved_path); 6969 + 6970 + if (is_err(ns)) return ns; 6971 + 6972 + for (int i = 0; i < binding_count; i++) { 6973 + jsoff_t prop_off = lkp(js, ns, bindings[i].import_name, bindings[i].import_len); 6974 + jsval_t imported_val = prop_off != 0 ? resolveprop(js, mkval(T_PROP, prop_off)) : js_mkundef(); 6975 + 6976 + setprop(js, js->scope, js_mkstr(js, bindings[i].local_name, bindings[i].local_len), imported_val); 6977 + } 6978 + 6979 + return js_mkundef(); 6980 + } 6981 + 6982 + return js_mkerr(js, "Invalid import statement"); 6983 + } 6984 + 6985 + static jsval_t js_export_stmt(struct js *js) { 6986 + js->consumed = 1; 6987 + 6988 + jsval_t glob = js_glob(js); 6989 + jsval_t module_ns = js_get(js, glob, "__esm_module_scope"); 6990 + 6991 + if (vtype(module_ns) != T_OBJ) { 6992 + module_ns = mkobj(js, 0); 6993 + js_set(js, glob, "__esm_module_scope", module_ns); 6994 + } 6995 + 6996 + if (next(js) == TOK_DEFAULT) { 6997 + js->consumed = 1; 6998 + jsval_t value = js_assignment(js); 6999 + if (is_err(value)) return value; 7000 + 7001 + setprop(js, module_ns, js_mkstr(js, "default", 7), resolveprop(js, value)); 7002 + return value; 7003 + } 7004 + 7005 + if (next(js) == TOK_CONST || next(js) == TOK_LET || next(js) == TOK_VAR) { 7006 + js->consumed = 1; 7007 + 7008 + EXPECT(TOK_IDENTIFIER, ); 7009 + const char *name = &js->code[js->toff]; 7010 + size_t name_len = js->tlen; 7011 + js->consumed = 1; 7012 + 7013 + jsval_t value = js_mkundef(); 7014 + if (next(js) == TOK_ASSIGN) { 7015 + js->consumed = 1; 7016 + value = js_assignment(js); 7017 + if (is_err(value)) return value; 7018 + } 7019 + 7020 + jsval_t key = js_mkstr(js, name, name_len); 7021 + setprop(js, js->scope, key, resolveprop(js, value)); 7022 + setprop(js, module_ns, key, resolveprop(js, value)); 7023 + 7024 + return value; 7025 + } 7026 + 7027 + if (next(js) == TOK_FUNC) { 7028 + jsval_t func = js_func_literal(js, false); 7029 + if (is_err(func)) return func; 7030 + 7031 + jsval_t func_obj = mkval(T_OBJ, vdata(func)); 7032 + jsoff_t name_off = lkp(js, func_obj, "name", 4); 7033 + if (name_off != 0) { 7034 + jsval_t name_val = resolveprop(js, mkval(T_PROP, name_off)); 7035 + if (vtype(name_val) == T_STR) { 7036 + setprop(js, js->scope, name_val, func); 7037 + setprop(js, module_ns, name_val, func); 7038 + } 7039 + } 7040 + 7041 + return func; 7042 + } 7043 + 7044 + if (next(js) == TOK_CLASS) { 7045 + jsval_t cls = js_class_decl(js); 7046 + if (is_err(cls)) return cls; 7047 + 7048 + jsval_t cls_obj = mkval(T_OBJ, vdata(cls)); 7049 + jsoff_t name_off = lkp(js, cls_obj, "name", 4); 7050 + if (name_off != 0) { 7051 + jsval_t name_val = resolveprop(js, mkval(T_PROP, name_off)); 7052 + if (vtype(name_val) == T_STR) { 7053 + setprop(js, js->scope, name_val, cls); 7054 + setprop(js, module_ns, name_val, cls); 7055 + } 7056 + } 7057 + 7058 + return cls; 7059 + } 7060 + 7061 + if (next(js) == TOK_LBRACE) { 7062 + js->consumed = 1; 7063 + 7064 + while (next(js) != TOK_RBRACE) { 7065 + EXPECT(TOK_IDENTIFIER, ); 7066 + const char *local_name = &js->code[js->toff]; 7067 + size_t local_len = js->tlen; 7068 + js->consumed = 1; 7069 + 7070 + const char *export_name = local_name; 7071 + size_t export_len = local_len; 7072 + 7073 + if (next(js) == TOK_AS) { 7074 + js->consumed = 1; 7075 + EXPECT(TOK_IDENTIFIER, ); 7076 + export_name = &js->code[js->toff]; 7077 + export_len = js->tlen; 7078 + js->consumed = 1; 7079 + } 7080 + 7081 + jsval_t local_val = lookup(js, local_name, local_len); 7082 + if (is_err(local_val)) return local_val; 7083 + 7084 + setprop(js, module_ns, js_mkstr(js, export_name, export_len), resolveprop(js, local_val)); 7085 + 7086 + if (next(js) == TOK_COMMA) js->consumed = 1; 7087 + } 7088 + 7089 + EXPECT(TOK_RBRACE, ); 7090 + return js_mkundef(); 7091 + } 7092 + 7093 + return js_mkerr(js, "Invalid export statement"); 7094 + } 7095 + 6371 7096 struct js *js_create(void *buf, size_t len) { 6372 7097 struct js *js = NULL; 6373 7098 if (len < sizeof(*js) + esize(T_OBJ)) return js; ··· 6423 7148 setprop(js, p_ctor_obj, js_mkstr(js, "prototype", 9), p_proto); 6424 7149 6425 7150 setprop(js, glob, js_mkstr(js, "Promise", 7), mkval(T_FUNC, vdata(p_ctor_obj))); 7151 + setprop(js, glob, js_mkstr(js, "import", 6), js_mkfun(builtin_import)); 7152 + setprop(js, glob, js_mkstr(js, "__esm_module_scope", 18), js_mkundef()); 6426 7153 6427 7154 js->owns_mem = false; 6428 7155 js->max_size = 0;
+1 -82
src/main.c
··· 18 18 #include "modules/json.h" 19 19 #include "modules/fetch.h" 20 20 21 - static struct { 22 - char *path; 23 - jsval_t exports; 24 - } *module_cache = NULL; 25 - 26 - static int module_count = 0; 27 - static int module_capacity = 0; 28 - 29 - static jsval_t js_require(struct js *js, jsval_t *args, int nargs) { 30 - if (nargs != 1) return js_mkundef(); 31 - 32 - char data[8192]; 33 - char *req_path = js_getstr(js, args[0], NULL); 34 - char full_path[PATH_MAX]; 35 - 36 - jsval_t ant_obj = js_get(js, js_glob(js), "Ant"); 37 - jsval_t dirname_val = js_get(js, ant_obj, "__dirname"); 38 - char *base_path = js_getstr(js, dirname_val, NULL); 39 - 40 - if (base_path == NULL) base_path = "."; 41 - 42 - if (req_path[0] == '.') { 43 - snprintf(full_path, sizeof(full_path), "%s/%s", base_path, req_path); 44 - } else { 45 - snprintf(full_path, sizeof(full_path), "%s", req_path); 46 - } 47 - 48 - FILE *fp = fopen(full_path, "rb"); 49 - if (fp == NULL) { 50 - fprintf(stderr, "Error: Could not open required file '%s'\n", full_path); 51 - return js_mkundef(); 52 - } 53 - 54 - size_t len = fread(data, 1, sizeof(data), fp); 55 - fclose(fp); 56 - 57 - for (int i = 0; i < module_count; i++) { 58 - if (strcmp(module_cache[i].path, full_path) == 0) return module_cache[i].exports; 59 - } 60 - 61 - jsval_t module_exports = js_mkobj(js); 62 - jsval_t prev_exports = js_get(js, ant_obj, "exports"); 63 - 64 - js_set(js, ant_obj, "exports", module_exports); 65 - js_mkscope(js); 66 - js_set_filename(js, full_path); 67 - 68 - jsval_t result = js_eval(js, data, len); 69 - js_delscope(js); 70 - 71 - if (js_type(result) == JS_ERR) { 72 - fprintf(stderr, "%s\n", js_str(js, result)); 73 - return js_mkundef(); 74 - } 75 - 76 - jsval_t final_exports = js_get(js, ant_obj, "exports"); 77 - js_set(js, ant_obj, "exports", prev_exports); 78 - 79 - if (module_count >= module_capacity) { 80 - module_capacity = module_capacity == 0 ? 8 : module_capacity * 2; 81 - module_cache = realloc(module_cache, module_capacity * sizeof(*module_cache)); 82 - if (module_cache == NULL) { 83 - fprintf(stderr, "Error: Failed to allocate module cache\n"); 84 - return final_exports; 85 - } 86 - } 87 - 88 - module_cache[module_count].path = strdup(full_path); 89 - module_cache[module_count].exports = final_exports; 90 - module_count++; 91 - 92 - return final_exports; 93 - } 94 - 95 21 static int execute_module(struct js *js, const char *filename) { 96 22 char *filename_copy = strdup(filename); 97 23 char *dir = dirname(filename_copy); 98 24 99 - jsval_t ant_obj = js_get(js, js_glob(js), "Ant"); 100 - js_set(js, ant_obj, "__dirname", js_mkstr(js, dir, strlen(dir))); 101 - 25 + js_set(js, js_glob(js), "__dirname", js_mkstr(js, dir, strlen(dir))); 102 26 free(filename_copy); 103 27 104 28 FILE *fp = fopen(filename, "rb"); ··· 208 132 init_json_module(); 209 133 init_server_module(); 210 134 init_timer_module(); 211 - 212 - jsval_t exports_obj = js_mkobj(js); 213 - 214 - js_set(js, rt->ant_obj, "require", js_mkfun(js_require)); 215 - js_set(js, rt->ant_obj, "exports", exports_obj); 216 135 217 136 int result = execute_module(js, module_file); 218 137
-22
tests/example.cjs
··· 1 - const math = Ant.require('./math.cjs'); 2 - const stuff = Ant.require('./stuff.cjs'); 3 - 4 - const value = math.mul(2, 3); 5 - const result = math.add(value, 3); 6 - 7 - console.log(this); 8 - console.log(); 9 - 10 - function main() { 11 - console.log(result); 12 - console.log(math.PI); 13 - console.log(stuff.test()); 14 - 15 - console.log(Ant.__dirname); 16 - console.log(typeof result); 17 - 18 - console.log(value instanceof Number); 19 - console.log(String(123)); 20 - } 21 - 22 - void main();
+25
tests/example.js
··· 1 + import stuff from './stuff.js'; 2 + import { add, mul, PI } from './math'; 3 + 4 + const value = mul(2, 3); 5 + const result = add(value, 3); 6 + 7 + queueMicrotask(() => { 8 + console.log('hello world'); 9 + console.log(`current dir is '${__dirname}'`); 10 + }); 11 + 12 + console.log(this, '\n'); 13 + 14 + function main() { 15 + console.log(result); 16 + console.log(PI); 17 + console.log(stuff.test()); 18 + 19 + console.log(typeof result); 20 + 21 + console.log(value instanceof Number); 22 + console.log(String(123)); 23 + } 24 + 25 + void main();
-10
tests/math.cjs
··· 1 - Ant.exports = { 2 - add: function (a, b) { 3 - return a + b; 4 - }, 5 - mul: function (a, b) { 6 - return a * b; 7 - } 8 - }; 9 - 10 - Ant.exports.PI = 3.14;
+9
tests/math.js
··· 1 + export function add(a, b) { 2 + return a + b; 3 + } 4 + 5 + export function mul(a, b) { 6 + return a * b; 7 + } 8 + 9 + export const PI = 3.14;
+2 -2
tests/server/html.cjs tests/server/html.js
··· 1 - Ant.exports.html = (strings, ...values) => { 1 + export function html(strings, ...values) { 2 2 let result = ''; 3 3 for (let i = 0; i < strings.length; i++) { 4 4 result = result + strings[i]; ··· 8 8 } 9 9 } 10 10 return result; 11 - }; 11 + }
+1
tests/server/meow.txt
··· 1 + meow
+1 -3
tests/server/radix3.cjs tests/server/radix3.js
··· 9 9 } 10 10 } 11 11 12 - class Radix3 { 12 + export class Radix3 { 13 13 constructor() { 14 14 this.root = new Radix3Node(); 15 15 } ··· 223 223 } 224 224 } 225 225 } 226 - 227 - Ant.exports = Radix3;
+8 -2
tests/server/server.cjs tests/server/server.js
··· 1 - const Radix3 = Ant.require('./radix3.cjs'); 2 - const { html } = Ant.require('./html.cjs'); 1 + import { html } from './html'; 2 + import meow from './meow.txt'; 3 + import { Radix3 } from './radix3'; 3 4 4 5 const router = new Radix3(); 5 6 ··· 8 9 9 10 Available routes: 10 11 GET / 12 + GET /meow 11 13 GET /hello 12 14 GET /status 13 15 GET /users/:id ··· 15 17 GET /files/*path 16 18 GET /api/v1/users 17 19 GET /api/v2/demo`); 20 + }); 21 + 22 + router.insert('/meow', async c => { 23 + return c.res.body(meow); 18 24 }); 19 25 20 26 router.insert('/hello', async c => {
-5
tests/stuff.cjs
··· 1 - Ant.exports = { 2 - test: function () { 3 - return 42; 4 - } 5 - };
+5
tests/stuff.js
··· 1 + function test() { 2 + return 42; 3 + } 4 + 5 + export default { test };