diff --git a/.gitignore b/.gitignore index 1dd225e..a191522 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,9 @@ *.trace spec.txt -todo.txt \ No newline at end of file +todo.txt + +node_modules +bun.lock +pnpm-lock.yaml +package-lock.json \ No newline at end of file diff --git a/examples/demo/events.js b/examples/demo/events.js index 6c763f4..713da4a 100644 --- a/examples/demo/events.js +++ b/examples/demo/events.js @@ -15,7 +15,7 @@ dispatchEvent('userLogin', { }); console.log('\nExample 3: Object Events'); -const button = createEventTarget(); +const button = new EventTarget(); button.name = 'MyButton'; button.addEventListener('click', event => { @@ -27,10 +27,10 @@ button.dispatchEvent('click'); button.dispatchEvent('click'); console.log('\nExample 4: Multiple Event Targets'); -const user = createEventTarget(); +const user = new EventTarget(); user.name = 'Alice'; -const socket = createEventTarget(); +const socket = new EventTarget(); socket.id = 'socket-123'; user.addEventListener('statusChange', event => { @@ -45,7 +45,7 @@ user.dispatchEvent('statusChange', { status: 'online' }); socket.dispatchEvent('message', { data: 'Hello!' }); console.log('\nExample 5: Once Listeners'); -const startup = createEventTarget(); +const startup = new EventTarget(); let initCount = 0; startup.addEventListener( @@ -65,7 +65,7 @@ console.log(' Total init calls:', initCount); console.log('\nExample 6: Event Emitter Pattern'); class MyEmitter { constructor() { - const target = createEventTarget(); + const target = new EventTarget(); this.on = target.addEventListener.bind(target); this.off = target.removeEventListener.bind(target); this.emit = target.dispatchEvent.bind(target); @@ -82,7 +82,7 @@ emitter.emit('data', { value: 42, type: 'number' }); emitter.emit('data', { value: 'hello', type: 'string' }); console.log('\nExample 7: Removing Listeners'); -const temp = createEventTarget(); +const temp = new EventTarget(); function handler(event) { console.log(' Handler called with:', event.detail); diff --git a/meson.build b/meson.build index 02c3f7f..aa9255b 100644 --- a/meson.build +++ b/meson.build @@ -96,7 +96,7 @@ endif build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() version_conf = configuration_data() -version_conf.set('ANT_VERSION', '0.3.2.43') +version_conf.set('ANT_VERSION', '0.3.3.6') version_conf.set('ANT_GIT_HASH', git_hash) version_conf.set('ANT_BUILD_DATE', build_date) @@ -120,21 +120,22 @@ if host_machine.system() == 'linux' add_project_link_arguments('-no-pie', language: 'c') endif -gen_snapshot = executable( - 'gen_snapshot', - files('src/tools/gen_snapshot.c'), - include_directories: [include, build_include], - c_args: ['-D ANT_SNAPSHOT_GENERATOR'], - native: true -) +node = find_program('node', required: true) +gen_snapshot = files('src/tools/gen_snapshot.js') + +core_files = run_command('sh', '-c', + 'cd "$MESON_SOURCE_ROOT" && find src/core -name "*.js" ! -name "index.js" | sort', + check: true +).stdout().strip().split() snapshot_h = custom_target( 'snapshot', - input: 'src/core/index.js', + input: ['src/core/index.ts'] + files(core_files), output: 'snapshot_data.h', command: [ - gen_snapshot, - '@INPUT@', + node, + gen_snapshot, + '@INPUT0@', '@OUTPUT@', 'VERSION=' + version_conf.get('ANT_VERSION'), 'GIT_HASH=' + version_conf.get('ANT_GIT_HASH'), diff --git a/src/core/index.js b/src/core/ant.ts similarity index 94% rename from src/core/index.js rename to src/core/ant.ts index 666b9a7..8e86f43 100644 --- a/src/core/index.js +++ b/src/core/ant.ts @@ -1,5 +1,3 @@ -snapshot_inline('./events.js'); - Ant.version = '{{VERSION}}'; Ant.revision = '{{GIT_HASH}}'; Ant.buildDate = '{{BUILD_DATE}}'; @@ -26,7 +24,7 @@ Ant.typeof = function (t) { T_PROPREF: 'propref', T_SYMBOL: 'symbol', T_GENERATOR: 'generator' - }; + } as const; const names = Object.values(types); return value < names.length ? names[value] : '??'; diff --git a/src/core/events.js b/src/core/events.js deleted file mode 100644 index 3997661..0000000 --- a/src/core/events.js +++ /dev/null @@ -1,7 +0,0 @@ -function createEventTarget() { - const obj = {}; - obj.addEventListener = EventTargetPrototype.addEventListener; - obj.removeEventListener = EventTargetPrototype.removeEventListener; - obj.dispatchEvent = EventTargetPrototype.dispatchEvent; - return obj; -} diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..94d9af2 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1 @@ +snapshot_inline('./ant.ts'); diff --git a/src/modules/events.c b/src/modules/events.c index 7b0876f..992064d 100644 --- a/src/modules/events.c +++ b/src/modules/events.c @@ -635,6 +635,18 @@ jsval_t events_library(struct js *js) { return lib; } +static jsval_t EventTarget(struct js *js, jsval_t *args, int nargs) { + (void)args; (void)nargs; + + jsval_t obj = js_mkobj(js); + js_set(js, obj, "addEventListener", js_mkfun(js_add_event_listener_method)); + js_set(js, obj, "removeEventListener", js_mkfun(js_remove_event_listener_method)); + js_set(js, obj, "dispatchEvent", js_mkfun(js_dispatch_event_method)); + js_set(js, obj, get_toStringTag_sym_key(), js_mkstr(js, "EventTarget", 11)); + + return obj; +} + void init_events_module() { struct js *js = rt->js; jsval_t global = js_glob(js); @@ -643,16 +655,5 @@ void init_events_module() { js_set(js, global, "removeEventListener", js_mkfun(js_remove_event_listener)); js_set(js, global, "dispatchEvent", js_mkfun(js_dispatch_event)); js_set(js, global, "getEventListeners", js_mkfun(js_get_event_listeners)); - - jsval_t event_target_proto = js_mkobj(js); - js_set(js, event_target_proto, "addEventListener", js_mkfun(js_add_event_listener_method)); - js_set(js, event_target_proto, "removeEventListener", js_mkfun(js_remove_event_listener_method)); - js_set(js, event_target_proto, "dispatchEvent", js_mkfun(js_dispatch_event_method)); - js_set(js, event_target_proto, get_toStringTag_sym_key(), js_mkstr(js, "EventTarget", 11)); - - jsval_t event_target = js_mkobj(js); - js_setprop_nonconfigurable(js, event_target, "prototype", 9, event_target_proto); - js_set(js, global, "EventTarget", event_target); + js_set(js, global, "EventTarget", js_mkfun(EventTarget)); } - - diff --git a/src/tools/gen_snapshot.c b/src/tools/gen_snapshot.c deleted file mode 100644 index ef3e031..0000000 --- a/src/tools/gen_snapshot.c +++ /dev/null @@ -1,641 +0,0 @@ -#include -#include -#include -#include -#include - -typedef struct { - char *placeholder; - char *value; -} replacement_t; - -typedef struct { - char **paths; - int count; - int capacity; -} module_cache_t; - -static char *resolve_path(const char *base_path, const char *import_path) { - char *resolved = malloc(PATH_MAX); - if (!resolved) return NULL; - - char *base_dir = strdup(base_path); - char *dir = dirname(base_dir); - - snprintf(resolved, PATH_MAX, "%s/%s", dir, import_path); - free(base_dir); - - return resolved; -} - -static char *read_file(const char *path, size_t *len) { - FILE *f = fopen(path, "r"); - if (!f) return NULL; - - fseek(f, 0, SEEK_END); - long size = ftell(f); - fseek(f, 0, SEEK_SET); - - char *content = malloc(size + 1); - if (!content) { - fclose(f); - return NULL; - } - - fread(content, 1, size, f); - content[size] = '\0'; - fclose(f); - - if (len) *len = size; - return content; -} - -static int module_already_processed(module_cache_t *cache, const char *path) { - for (int i = 0; i < cache->count; i++) { - if (strcmp(cache->paths[i], path) == 0) { - return 1; - } - } - return 0; -} - -static void add_to_cache(module_cache_t *cache, const char *path) { - if (cache->count >= cache->capacity) { - cache->capacity = cache->capacity == 0 ? 10 : cache->capacity * 2; - cache->paths = realloc(cache->paths, sizeof(char *) * cache->capacity); - } - - cache->paths[cache->count] = strdup(path); - cache->count++; -} - -static char *process_snapshot_includes(const char *file_path, const char *content, size_t content_len, module_cache_t *cache, size_t *output_len); - -static char *wrap_module(const char *content, size_t content_len, size_t *output_len) { - size_t output_capacity = content_len + 1024; - char *output = malloc(output_capacity); - if (!output) return NULL; - - size_t output_pos = 0; - const char *pos = content; - const char *end = content + content_len; - - const char *iife_start = "(function() {\n"; - memcpy(output + output_pos, iife_start, strlen(iife_start)); - output_pos += strlen(iife_start); - - char **exports = NULL; - int export_count = 0; - int export_capacity = 0; - - while (pos < end) { - const char *export_start = strstr(pos, "export "); - if (!export_start || export_start >= end) { - size_t remaining = end - pos; - if (output_pos + remaining >= output_capacity) { - output_capacity = (output_pos + remaining) * 2; - output = realloc(output, output_capacity); - } - memcpy(output + output_pos, pos, remaining); - output_pos += remaining; - break; - } - - memcpy(output + output_pos, pos, export_start - pos); - output_pos += export_start - pos; - - const char *decl_start = export_start + 7; - while (*decl_start == ' ') decl_start++; - - const char *const_pos = strstr(decl_start, "const "); - const char *let_pos = strstr(decl_start, "let "); - const char *var_pos = strstr(decl_start, "var "); - const char *function_pos = strstr(decl_start, "function "); - - const char *keyword_start = NULL; - const char *name_start = NULL; - - if (const_pos == decl_start) { - keyword_start = const_pos; - name_start = const_pos + 6; - } else if (let_pos == decl_start) { - keyword_start = let_pos; - name_start = let_pos + 4; - } else if (var_pos == decl_start) { - keyword_start = var_pos; - name_start = var_pos + 4; - } else if (function_pos == decl_start) { - keyword_start = function_pos; - name_start = function_pos + 9; - } - - if (keyword_start) { - while (*name_start == ' ') name_start++; - - const char *name_end = name_start; - while (*name_end && (*name_end == '_' || (*name_end >= 'a' && *name_end <= 'z') || - (*name_end >= 'A' && *name_end <= 'Z') || (*name_end >= '0' && *name_end <= '9'))) { - name_end++; - } - - size_t name_len = name_end - name_start; - if (name_len > 0) { - if (export_count >= export_capacity) { - export_capacity = export_capacity == 0 ? 10 : export_capacity * 2; - exports = realloc(exports, sizeof(char *) * export_capacity); - } - - exports[export_count] = malloc(name_len + 1); - memcpy(exports[export_count], name_start, name_len); - exports[export_count][name_len] = '\0'; - export_count++; - } - - const char *stmt_start = keyword_start; - const char *line_end = strchr(export_start, ';'); - if (!line_end) line_end = strchr(export_start, '\n'); - if (line_end) { - size_t stmt_len = line_end - stmt_start + 1; - if (output_pos + stmt_len >= output_capacity) { - output_capacity = (output_pos + stmt_len) * 2; - output = realloc(output, output_capacity); - } - memcpy(output + output_pos, stmt_start, stmt_len); - output_pos += stmt_len; - pos = line_end + 1; - } else { - pos = export_start + 7; - } - } else { - pos = export_start + 7; - } - } - - if (export_count > 0) { - const char *return_start = "\nreturn { "; - if (output_pos + strlen(return_start) >= output_capacity) { - output_capacity = (output_pos + strlen(return_start) + 1024) * 2; - output = realloc(output, output_capacity); - } - memcpy(output + output_pos, return_start, strlen(return_start)); - output_pos += strlen(return_start); - - for (int i = 0; i < export_count; i++) { - size_t name_len = strlen(exports[i]); - - if (output_pos + name_len * 2 + 10 >= output_capacity) { - output_capacity = (output_pos + name_len * 2 + 10) * 2; - output = realloc(output, output_capacity); - } - - memcpy(output + output_pos, exports[i], name_len); - output_pos += name_len; - - if (i < export_count - 1) { - memcpy(output + output_pos, ", ", 2); - output_pos += 2; - } - } - - const char *return_end = " };\n})()"; - memcpy(output + output_pos, return_end, strlen(return_end)); - output_pos += strlen(return_end); - } else { - const char *iife_end = "})()"; - memcpy(output + output_pos, iife_end, strlen(iife_end)); - output_pos += strlen(iife_end); - } - - for (int i = 0; i < export_count; i++) { - free(exports[i]); - } - free(exports); - - output[output_pos] = '\0'; - *output_len = output_pos; - return output; -} - -static char *process_snapshot_inlines(const char *file_path, const char *content, size_t content_len, module_cache_t *cache, size_t *output_len) { - size_t output_capacity = content_len * 2; - char *output = malloc(output_capacity); - if (!output) return NULL; - - size_t output_pos = 0; - const char *pos = content; - const char *end = content + content_len; - - while (pos < end) { - const char *inline_start = strstr(pos, "snapshot_inline("); - if (!inline_start || inline_start >= end) { - size_t remaining = end - pos; - if (output_pos + remaining >= output_capacity) { - output_capacity = (output_pos + remaining) * 2; - output = realloc(output, output_capacity); - } - memcpy(output + output_pos, pos, remaining); - output_pos += remaining; - break; - } - - memcpy(output + output_pos, pos, inline_start - pos); - output_pos += inline_start - pos; - - const char *quote_start = strchr(inline_start, '\''); - if (!quote_start) quote_start = strchr(inline_start, '"'); - if (!quote_start) { - pos = inline_start + 16; - continue; - } - - char quote_char = *quote_start; - const char *quote_end = strchr(quote_start + 1, quote_char); - if (!quote_end) { - pos = inline_start + 16; - continue; - } - - size_t path_len = quote_end - quote_start - 1; - char *import_path = malloc(path_len + 1); - memcpy(import_path, quote_start + 1, path_len); - import_path[path_len] = '\0'; - - char *resolved_path = resolve_path(file_path, import_path); - free(import_path); - - if (!resolved_path) { - pos = inline_start + 16; - continue; - } - - size_t module_len; - char *module_content = read_file(resolved_path, &module_len); - - if (!module_content) { - fprintf(stderr, "Error: Cannot read inline file: %s\n", resolved_path); - free(resolved_path); - free(output); - return NULL; - } - - free(resolved_path); - - if (output_pos + module_len >= output_capacity) { - output_capacity = (output_pos + module_len) * 2; - output = realloc(output, output_capacity); - } - - memcpy(output + output_pos, module_content, module_len); - output_pos += module_len; - free(module_content); - - const char *paren_close = strchr(quote_end, ')'); - if (paren_close) { - pos = paren_close + 1; - if (*pos == ';') pos++; - } else { - pos = quote_end + 1; - } - } - - output[output_pos] = '\0'; - *output_len = output_pos; - return output; -} - -static char *process_snapshot_includes(const char *file_path, const char *content, size_t content_len, module_cache_t *cache, size_t *output_len) { - size_t output_capacity = content_len * 2; - char *output = malloc(output_capacity); - if (!output) return NULL; - - size_t output_pos = 0; - const char *pos = content; - const char *end = content + content_len; - - while (pos < end) { - const char *include_start = strstr(pos, "snapshot_include("); - if (!include_start || include_start >= end) { - size_t remaining = end - pos; - if (output_pos + remaining >= output_capacity) { - output_capacity = (output_pos + remaining) * 2; - output = realloc(output, output_capacity); - } - memcpy(output + output_pos, pos, remaining); - output_pos += remaining; - break; - } - - memcpy(output + output_pos, pos, include_start - pos); - output_pos += include_start - pos; - - const char *quote_start = strchr(include_start, '\''); - if (!quote_start) quote_start = strchr(include_start, '"'); - if (!quote_start) { - pos = include_start + 17; - continue; - } - - char quote_char = *quote_start; - const char *quote_end = strchr(quote_start + 1, quote_char); - if (!quote_end) { - pos = include_start + 17; - continue; - } - - size_t path_len = quote_end - quote_start - 1; - char *import_path = malloc(path_len + 1); - memcpy(import_path, quote_start + 1, path_len); - import_path[path_len] = '\0'; - - char *resolved_path = resolve_path(file_path, import_path); - free(import_path); - - if (!resolved_path) { - pos = include_start + 17; - continue; - } - - if (module_already_processed(cache, resolved_path)) { - fprintf(stderr, "Warning: Circular dependency detected: %s\n", resolved_path); - free(resolved_path); - pos = quote_end + 2; - continue; - } - - add_to_cache(cache, resolved_path); - - size_t module_len; - char *module_content = read_file(resolved_path, &module_len); - - if (!module_content) { - fprintf(stderr, "Error: Cannot read module: %s\n", resolved_path); - free(resolved_path); - free(output); - return NULL; - } - - size_t processed_len; - char *processed = process_snapshot_includes(resolved_path, module_content, module_len, cache, &processed_len); - free(module_content); - - if (!processed) { - free(resolved_path); - free(output); - return NULL; - } - - size_t wrapped_len; - char *wrapped = wrap_module(processed, processed_len, &wrapped_len); - free(processed); - free(resolved_path); - - if (!wrapped) { - free(output); - return NULL; - } - - if (output_pos + wrapped_len >= output_capacity) { - output_capacity = (output_pos + wrapped_len) * 2; - output = realloc(output, output_capacity); - } - - memcpy(output + output_pos, wrapped, wrapped_len); - output_pos += wrapped_len; - free(wrapped); - - const char *paren_close = strchr(quote_end, ')'); - if (paren_close) { - pos = paren_close + 1; - } else { - pos = quote_end + 1; - } - } - - output[output_pos] = '\0'; - *output_len = output_pos; - return output; -} - -static char *replace_templates(const char *input, size_t input_len, replacement_t *replacements, int num_replacements, size_t *output_len) { - size_t output_size = input_len; - - for (int i = 0; i < num_replacements; i++) { - const char *placeholder = replacements[i].placeholder; - const char *value = replacements[i].value; - size_t placeholder_len = strlen(placeholder); - size_t value_len = strlen(value); - - const char *pos = input; - while ((pos = strstr(pos, placeholder)) != NULL) { - output_size = output_size - placeholder_len + value_len; - pos += placeholder_len; - } - } - - char *output = malloc(output_size + 1); - if (!output) return NULL; - - const char *read_pos = input; - char *write_pos = output; - size_t remaining = input_len; - - while (remaining > 0) { - const char *nearest_match = NULL; - size_t nearest_match_len = 0; - const char *nearest_match_value = NULL; - - for (int i = 0; i < num_replacements; i++) { - const char *match = strstr(read_pos, replacements[i].placeholder); - if (match && (!nearest_match || match < nearest_match)) { - nearest_match = match; - nearest_match_len = strlen(replacements[i].placeholder); - nearest_match_value = replacements[i].value; - } - } - - if (nearest_match) { - size_t before_len = nearest_match - read_pos; - memcpy(write_pos, read_pos, before_len); - write_pos += before_len; - - size_t value_len = strlen(nearest_match_value); - memcpy(write_pos, nearest_match_value, value_len); - write_pos += value_len; - - read_pos = nearest_match + nearest_match_len; - remaining = input_len - (read_pos - input); - } else { - memcpy(write_pos, read_pos, remaining); - write_pos += remaining; - remaining = 0; - } - } - - *write_pos = '\0'; - *output_len = write_pos - output; - return output; -} - -int main(int argc, char **argv) { - if (argc < 3) { - fprintf(stderr, "Usage: %s [KEY=value...]\n", argv[0]); - fprintf(stderr, "Example: %s core.js snapshot.h VERSION=1.0.0 GIT_HASH=abc123\n", argv[0]); - return 1; - } - - const char *input_file = argv[1]; - const char *output_file = argv[2]; - - int num_replacements = argc - 3; - replacement_t *replacements = malloc(sizeof(replacement_t) * num_replacements); - if (!replacements && num_replacements > 0) { - fprintf(stderr, "Error: Failed to allocate memory for replacements\n"); - return 1; - } - - int replacement_idx = 0; - for (int i = 3; i < argc; i++) { - char *arg = strdup(argv[i]); - char *equals = strchr(arg, '='); - if (equals) { - *equals = '\0'; - - replacements[replacement_idx].placeholder = malloc(strlen(arg) + 5); - sprintf(replacements[replacement_idx].placeholder, "{{%s}}", arg); - replacements[replacement_idx].value = strdup(equals + 1); - - printf("template replacement: %s -> %s\n", replacements[replacement_idx].placeholder, replacements[replacement_idx].value); - replacement_idx++; - } - free(arg); - } - - num_replacements = replacement_idx; - - FILE *in = fopen(input_file, "r"); - if (!in) { - fprintf(stderr, "Error: Cannot open input file: %s\n", input_file); - return 1; - } - - fseek(in, 0, SEEK_END); - long file_size = ftell(in); - fseek(in, 0, SEEK_SET); - - char *js_code_original = malloc(file_size + 1); - if (!js_code_original) { - fprintf(stderr, "Error: Memory allocation failed\n"); - fclose(in); - return 1; - } - - fread(js_code_original, 1, file_size, in); - js_code_original[file_size] = '\0'; - fclose(in); - - module_cache_t cache = {0}; - - size_t inlined_len; - char *inlined_code = process_snapshot_inlines(input_file, js_code_original, file_size, &cache, &inlined_len); - - if (!inlined_code) { - fprintf(stderr, "Error: Inline processing failed\n"); - free(js_code_original); - return 1; - } - - size_t bundled_len; - char *bundled_code = process_snapshot_includes(input_file, inlined_code, inlined_len, &cache, &bundled_len); - free(inlined_code); - - if (!bundled_code) { - fprintf(stderr, "Error: Module bundling failed\n"); - free(js_code_original); - return 1; - } - - size_t processed_len; - char *js_code = replace_templates(bundled_code, bundled_len, replacements, num_replacements, &processed_len); - - if (!js_code) { - fprintf(stderr, "Error: Template replacement failed\n"); - free(js_code_original); - free(bundled_code); - return 1; - } - - free(js_code_original); - free(bundled_code); - - char *compacted = malloc(processed_len + 1); - if (!compacted) { - fprintf(stderr, "Error: Memory allocation failed for compaction\n"); - free(js_code); - return 1; - } - - size_t compact_pos = 0; - for (size_t i = 0; i < processed_len; i++) { - if (js_code[i] != '\n' && js_code[i] != '\r') compacted[compact_pos++] = js_code[i]; - } - compacted[compact_pos] = '\0'; - - printf("%s", compacted); - free(js_code); - js_code = compacted; - processed_len = compact_pos; - - for (int i = 0; i < cache.count; i++) { - free(cache.paths[i]); - } - free(cache.paths); - - FILE *out = fopen(output_file, "w"); - if (!out) { - fprintf(stderr, "Error: Cannot open output file: %s\n", output_file); - free(js_code); - return 1; - } - - fprintf(out, "/* Auto-generated snapshot from %s */\n", input_file); - fprintf(out, "/* DO NOT EDIT - Generated during build */\n\n"); - fprintf(out, "#ifndef ANT_SNAPSHOT_DATA_H\n"); - fprintf(out, "#define ANT_SNAPSHOT_DATA_H\n\n"); - fprintf(out, "#include \n"); - fprintf(out, "#include \n\n"); - - fprintf(out, "static const uint8_t ant_snapshot_source[] = {"); - - for (size_t i = 0; i < processed_len; i++) { - if (i % 16 == 0) { - fprintf(out, "\n "); - } - fprintf(out, "0x%02x", (unsigned char)js_code[i]); - if (i < processed_len - 1) { - fprintf(out, ", "); - } - } - - fprintf(out, "\n};\n\n"); - fprintf(out, "static const size_t ant_snapshot_source_len = %zu;\n\n", processed_len); - fprintf(out, "/* bundled source size: %zu bytes */\n", processed_len); - fprintf(out, "static const size_t ant_snapshot_mem_size = %zu;\n\n", processed_len); - - fprintf(out, "#endif /* ANT_SNAPSHOT_DATA_H */\n"); - - fclose(out); - free(js_code); - - for (int i = 0; i < num_replacements; i++) { - free(replacements[i].placeholder); - free(replacements[i].value); - } - free(replacements); - - printf("snapshot generated successfully: %s\n", output_file); - printf(" original size: %ld bytes\n", file_size); - printf(" bundled size: %zu bytes\n", processed_len); - printf(" replacements: %d\n", num_replacements); - - return 0; -} diff --git a/src/tools/gen_snapshot.js b/src/tools/gen_snapshot.js new file mode 100644 index 0000000..585acb6 --- /dev/null +++ b/src/tools/gen_snapshot.js @@ -0,0 +1,162 @@ +import * as esbuild from 'esbuild'; +import { dirname, resolve } from 'node:path'; +import { readFileSync, writeFileSync } from 'node:fs'; + +const processedModules = new Set(); + +function wrapModule(content) { + const exports = []; + let output = '(function() {\n'; + + const exportRegex = /export\s+(const|let|var|function)\s+(\w+)/g; + let match; + let lastIndex = 0; + let processed = ''; + + while ((match = exportRegex.exec(content)) !== null) { + processed += content.slice(lastIndex, match.index); + processed += `${match[1]} ${match[2]}`; + exports.push(match[2]); + lastIndex = match.index + match[0].length; + } + + processed += content.slice(lastIndex); + output += processed; + + if (exports.length > 0) { + output += `\nreturn { ${exports.join(', ')} };\n})()`; + } else output += '})()'; + + return output; +} + +function processInlines(filePath, content) { + const dir = dirname(filePath); + + return content.replace(/snapshot_inline\s*\(\s*['"](.+?)['"]\s*\);?/g, (_, importPath) => { + const resolved = resolve(dir, importPath); + + try { + return readFileSync(resolved, 'utf-8'); + } catch (err) { + throw Error(`Error: Cannot read inline file: ${resolved}`); + } + }); +} + +function processIncludes(filePath, content) { + const dir = dirname(filePath); + + return content.replace(/snapshot_include\s*\(\s*['"](.+?)['"]\s*\)/g, (_, importPath) => { + const resolved = resolve(dir, importPath); + + if (processedModules.has(resolved)) { + console.error(`Warning: Circular dependency detected: ${resolved}`); + return ''; + } + + processedModules.add(resolved); + + try { + let moduleContent = readFileSync(resolved, 'utf-8'); + moduleContent = processIncludes(resolved, moduleContent); + return wrapModule(moduleContent); + } catch (err) { + throw Error(`Error: Cannot read module: ${resolved}`); + } + }); +} + +function replaceTemplates(content, replacements) { + for (const [key, value] of Object.entries(replacements)) { + content = content.replaceAll(`{{${key}}}`, value); + } + return content; +} + +function generateHeader(inputFile, data) { + const bytes = [...Buffer.from(data)].map(b => `0x${b.toString(16).padStart(2, '0')}`); + + const lines = []; + for (let i = 0; i < bytes.length; i += 16) { + lines.push(' ' + bytes.slice(i, i + 16).join(', ')); + } + + return `/* Auto-generated snapshot from ${inputFile} */ +/* DO NOT EDIT - Generated during build */ + +#ifndef ANT_SNAPSHOT_DATA_H +#define ANT_SNAPSHOT_DATA_H + +#include +#include + +static const uint8_t ant_snapshot_source[] = { +${lines.join(',\n')} +}; + +static const size_t ant_snapshot_source_len = ${data.length}; + +/* bundled source size: ${data.length} bytes */ +static const size_t ant_snapshot_mem_size = ${data.length}; + +#endif /* ANT_SNAPSHOT_DATA_H */ +`; +} + +async function main() { + const args = process.argv.slice(2); + + if (args.length < 2) { + console.error(`Usage: ${process.argv[1]} [KEY=value...]`); + console.error(`Example: ${process.argv[1]} core.js snapshot.h VERSION=1.0.0 GIT_HASH=abc123`); + process.exit(1); + } + + const [inputFile, outputFile, ...replacementArgs] = args; + + const replacements = {}; + for (const arg of replacementArgs) { + const idx = arg.indexOf('='); + if (idx !== -1) { + const key = arg.slice(0, idx); + const value = arg.slice(idx + 1); + replacements[key] = value; + console.log(`template replacement: {{${key}}} -> ${value}`); + } + } + + let content; + try { + content = readFileSync(inputFile, 'utf-8'); + } catch (err) { + throw Error(`Error: Cannot open input file: ${inputFile}`); + } + + const originalSize = content.length; + + content = processInlines(inputFile, content); + content = processIncludes(inputFile, content); + content = replaceTemplates(content, replacements); + + const result = await esbuild.transform(content, { + minify: true, + loader: 'ts' + }); + + const minified = result.code.trimEnd(); + console.log(minified); + + const header = generateHeader(inputFile, minified); + writeFileSync(outputFile, header); + + console.log(`snapshot generated successfully: ${outputFile}`); + console.log(` original size: ${originalSize} bytes`); + console.log(` bundled size: ${minified.length} bytes`); + console.log(` replacements: ${Object.keys(replacements).length}`); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/src/tools/package.json b/src/tools/package.json new file mode 100644 index 0000000..9406b4f --- /dev/null +++ b/src/tools/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "esbuild": "^0.27.2" + } +} diff --git a/src/types/ant.d.ts b/src/types/ant.d.ts new file mode 100644 index 0000000..dc03498 --- /dev/null +++ b/src/types/ant.d.ts @@ -0,0 +1,73 @@ +type AntType = + | 'object' + | 'prop' + | 'string' + | 'undefined' + | 'null' + | 'number' + | 'boolean' + | 'function' + | 'coderef' + | 'cfunc' + | 'err' + | 'array' + | 'promise' + | 'typedarray' + | 'bigint' + | 'propref' + | 'symbol' + | 'generator'; + +interface AntGcResult { + heapBefore: number; + heapAfter: number; + usedBefore: number; + usedAfter: number; + freed: number; + arenaBefore: number; + arenaAfter: number; + arenaFreed: number; +} + +interface AntAllocResult { + arenaSize: number; + heapSize: number; + freeBytes: number; + usedBytes: number; + totalBytes: number; +} + +interface AntStatsResult { + arenaUsed: number; + arenaLwm: number; + cstack: number; + gcHeapSize: number; + gcFreeBytes: number; + gcUsedBytes: number; +} + +interface AntRaw { + typeof(t: unknown): number; +} + +interface AntStatic { + version: string; + revision: string; + buildDate: string; + + typeof(t: unknown): AntType | '??'; + raw: AntRaw; + + gc(): AntGcResult; + alloc(): AntAllocResult; + stats(): AntStatsResult; + signal(signum: number, handler: (signum: number) => void): void; + sleep(seconds: number): void; + msleep(milliseconds: number): void; + usleep(microseconds: number): void; +} + +declare const Ant: AntStatic; + +export = Ant; +export as namespace Ant; diff --git a/src/types/snapshot.d.ts b/src/types/snapshot.d.ts new file mode 100644 index 0000000..ebbc961 --- /dev/null +++ b/src/types/snapshot.d.ts @@ -0,0 +1,2 @@ +declare function snapshot_inline(path: string): void; +declare function snapshot_include(path: string): unknown; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a7e5dbd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "lib": ["dom", "ES2017"], + "typeRoots": ["./node_modules/@types"] + }, + + "include": ["src/types/*.d.ts", "src/core/**/*"] +}