diff --git a/bench.ce b/bench.ce index 9638ad3e..0435cbf3 100644 --- a/bench.ce +++ b/bench.ce @@ -6,7 +6,7 @@ var fd = use('fd') var time = use('time') var json = use('json') var blob = use('blob') -var os = use('os') +var os = use('internal/os') var testlib = use('internal/testlib') var math = use('math/radians') diff --git a/bench_native.ce b/bench_native.ce index de5c8b5c..ce27654e 100644 --- a/bench_native.ce +++ b/bench_native.ce @@ -6,7 +6,7 @@ // Compiles (if needed) and benchmarks a module via both VM and native dylib. // Reports median/mean timing per benchmark + speedup ratio. -var os = use('os') +var os = use('internal/os') var fd = use('fd') if (length(args) < 1) { diff --git a/benches/encoders.cm b/benches/encoders.cm index 4c53ad48..1364720c 100644 --- a/benches/encoders.cm +++ b/benches/encoders.cm @@ -1,8 +1,8 @@ // encoders.cm — nota/wota/json encode+decode benchmark // Isolates per-type bottlenecks across all three serializers. -var nota = use('nota') -var wota = use('wota') +var nota = use('internal/nota') +var wota = use('internal/wota') var json = use('json') // --- Test data shapes --- diff --git a/benchmarks/nota.ce b/benchmarks/nota.ce index eb212cfc..05cf8ae9 100644 --- a/benchmarks/nota.ce +++ b/benchmarks/nota.ce @@ -1,5 +1,5 @@ -var nota = use('nota') -var os = use('os') +var nota = use('internal/nota') +var os = use('internal/os') var io = use('fd') var json = use('json') diff --git a/benchmarks/wota.ce b/benchmarks/wota.ce index b4a654a2..bf94b94e 100644 --- a/benchmarks/wota.ce +++ b/benchmarks/wota.ce @@ -1,5 +1,5 @@ -var wota = use('wota'); -var os = use('os'); +var wota = use('internal/wota'); +var os = use('internal/os'); var i = 0 diff --git a/benchmarks/wota_nota_json.ce b/benchmarks/wota_nota_json.ce index 68045b2e..6d1a71dd 100644 --- a/benchmarks/wota_nota_json.ce +++ b/benchmarks/wota_nota_json.ce @@ -1,8 +1,8 @@ -var wota = use('wota'); -var nota = use('nota'); +var wota = use('internal/wota'); +var nota = use('internal/nota'); var json = use('json'); var jswota = use('jswota') -var os = use('os'); +var os = use('internal/os'); if (length(arg) != 2) { log.console('Usage: cell benchmark_wota_nota_json.ce '); diff --git a/boot/bootstrap.cm.mcode b/boot/bootstrap.cm.mcode index a8102e96..24baff15 100644 --- a/boot/bootstrap.cm.mcode +++ b/boot/bootstrap.cm.mcode @@ -1486,7 +1486,7 @@ ["setarg", 11, 1, 9, 10, 16], ["invoke", 11, 9, 10, 16], ["move", 11, 9, 10, 16], - ["access", 9, "crypto", 11, 24], + ["access", 9, "internal_crypto", 11, 24], ["frame", 12, 2, 1, 11, 14], ["null", 13, 11, 14], ["setarg", 12, 0, 13, 11, 14], diff --git a/build.cm b/build.cm index 5767fa53..e8b5cc88 100644 --- a/build.cm +++ b/build.cm @@ -7,9 +7,9 @@ // Build.build_static(packages, target, output) - Build static binary var fd = use('fd') -var crypto = use('crypto') +var crypto = use('internal/crypto') var blob = use('blob') -var os = use('os') +var os = use('internal/os') var toolchains = use('toolchains') var shop = use('internal/shop') var pkg_tools = use('package') @@ -718,7 +718,7 @@ Build.compile_native = function(src_path, target, buildtype, pkg) { // Compile QBE runtime stubs if needed var rc = null if (!fd.is_file(rt_o_path)) { - qbe_rt_path = shop.get_package_dir('core') + '/qbe_rt.c' + qbe_rt_path = shop.get_package_dir('core') + '/src/qbe_rt.c' rc = os.system(cc + san_flags + ' -c ' + qbe_rt_path + ' -o ' + rt_o_path + ' -fPIC') if (rc != 0) { print('QBE runtime stubs compilation failed'); disrupt @@ -790,7 +790,7 @@ Build.compile_native_ir = function(optimized, src_path, opts) { // Compile QBE runtime stubs if needed var rc = null if (!fd.is_file(rt_o_path)) { - qbe_rt_path = shop.get_package_dir('core') + '/qbe_rt.c' + qbe_rt_path = shop.get_package_dir('core') + '/src/qbe_rt.c' rc = os.system(cc + san_flags + ' -c ' + qbe_rt_path + ' -o ' + rt_o_path + ' -fPIC') if (rc != 0) { print('QBE runtime stubs compilation failed'); disrupt diff --git a/cellfs.cm b/cellfs.cm index d6a009bc..3d4ed959 100644 --- a/cellfs.cm +++ b/cellfs.cm @@ -2,8 +2,8 @@ var cellfs = {} var fd = use('fd') var miniz = use('miniz') -var qop = use('qop') -var wildstar = use('wildstar') +var qop = use('internal/qop') +var wildstar = use('internal/wildstar') var mounts = [] diff --git a/compare_aot.ce b/compare_aot.ce index 12a83b99..24b855a3 100644 --- a/compare_aot.ce +++ b/compare_aot.ce @@ -5,7 +5,7 @@ var build = use('build') var fd_mod = use('fd') -var os = use('os') +var os = use('internal/os') var json = use('json') var time = use('time') diff --git a/diff.ce b/diff.ce index d5e75e8b..4fe58b2f 100644 --- a/diff.ce +++ b/diff.ce @@ -11,9 +11,9 @@ var time = use('time') var _args = args == null ? [] : args -var analyze = use('os').analyze -var run_ast_fn = use('os').run_ast_fn -var run_ast_noopt_fn = use('os').run_ast_noopt_fn +var analyze = use('internal/os').analyze +var run_ast_fn = use('internal/os').run_ast_fn +var run_ast_noopt_fn = use('internal/os').run_ast_noopt_fn if (!run_ast_noopt_fn) { log.console("error: run_ast_noopt_fn not available (rebuild bootstrap)") diff --git a/fd.cm b/fd.cm index d9287eb5..ea5fbfd6 100644 --- a/fd.cm +++ b/fd.cm @@ -1,5 +1,5 @@ var fd = use('internal/fd') -var wildstar = use('wildstar') +var wildstar = use('internal/wildstar') function last_pos(str, sep) { var last = null diff --git a/fuzz.ce b/fuzz.ce index fa131090..832410a2 100644 --- a/fuzz.ce +++ b/fuzz.ce @@ -13,7 +13,7 @@ var fd = use('fd') var time = use('time') var json = use('json') -var os_ref = use('os') +var os_ref = use('internal/os') var analyze = os_ref.analyze var run_ast_fn = os_ref.run_ast_fn var run_ast_noopt_fn = os_ref.run_ast_noopt_fn diff --git a/internal/bootstrap.cm b/internal/bootstrap.cm index d2fb6590..25a036ac 100644 --- a/internal/bootstrap.cm +++ b/internal/bootstrap.cm @@ -8,7 +8,7 @@ function use_embed(name) { var fd = use_embed('internal_fd') var json_mod = use_embed('json') -var crypto = use_embed('crypto') +var crypto = use_embed('internal_crypto') function content_hash(content) { var data = content diff --git a/crypto.c b/internal/crypto.c similarity index 99% rename from crypto.c rename to internal/crypto.c index f10f7afc..24a8f02c 100644 --- a/crypto.c +++ b/internal/crypto.c @@ -238,7 +238,7 @@ static const JSCFunctionListEntry js_crypto_funcs[] = { JS_CFUNC_DEF("unlock", 3, js_crypto_unlock), }; -JSValue js_core_crypto_use(JSContext *js) +JSValue js_core_internal_crypto_use(JSContext *js) { JS_FRAME(js); JS_ROOT(mod, JS_NewObject(js)); diff --git a/internal/engine.cm b/internal/engine.cm index 7ff2b6ae..eaf62fb7 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -25,7 +25,7 @@ function use_embed(name) { var fd = use_embed('internal_fd') var js = use_embed('js') -var crypto = use_embed('crypto') +var crypto = use_embed('internal_crypto') // core_path and shop_path come from env (C runtime passes them through) // shop_path may be null if --core was used without --shop @@ -255,7 +255,9 @@ function extract_module_summaries(compiled) { result_slot = instr[2] summary = _summary_resolver(path) if (summary != null) { - summaries[] = {slot: result_slot, summary: summary} + if (summary._native != true) { + summaries[] = {slot: result_slot, summary: summary} + } } else { inv_n = length(instr) unresolved[] = {path: path, line: instr[inv_n - 2], col: instr[inv_n - 1]} @@ -343,6 +345,13 @@ function run_ast_noopt_fn(name, ast, env) { // Compile AST to blob without loading (for caching) function compile_to_blob(name, ast) { + var compiled = mcode_mod(ast) + var optimized = streamline_mod(compiled) + return mach_compile_mcode_bin(name, json.encode(optimized)) +} + +// Compile user program AST to blob with diagnostics +function compile_user_blob(name, ast) { var compiled = mcode_mod(ast) var ms = null var _ui = 0 @@ -415,7 +424,7 @@ if (args != null && (_init == null || !_init.program)) { } } -use_cache['core/os'] = os +use_cache['core/internal/os'] = os // Extra env properties added as engine initializes (log, runtime fns, etc.) var core_extras = {} @@ -487,8 +496,8 @@ function actor() { } var actor_mod = use_core('actor') -var wota = use_core('wota') -var nota = use_core('nota') +var wota = use_core('internal/wota') +var nota = use_core('internal/nota') var ENETSERVICE = 0.1 @@ -600,7 +609,10 @@ use_core('build') // Wire up module summary resolver now that shop is available _summary_resolver = function(path) { - var resolved = shop.resolve_use_path(path, null) + var info = shop.resolve_import_info(path, null) + if (info == null) return null + if (info.type == 'native') return {_native: true} + var resolved = info.resolved_path if (resolved == null) return null var summary_fn = function() { return shop.summary_file(resolved) @@ -1492,7 +1504,7 @@ $_.clock(_ => { } else { script = text(source_blob) ast = analyze(script, prog_path) - mach_blob = compile_to_blob(prog, ast) + mach_blob = compile_user_blob(prog, ast) if (cached_path) { ensure_build_dir() fd.slurpwrite(cached_path, mach_blob) diff --git a/fd_playdate.c b/internal/fd_playdate.c similarity index 100% rename from fd_playdate.c rename to internal/fd_playdate.c diff --git a/internal/kim.c b/internal/kim.c index 94139373..121b4689 100644 --- a/internal/kim.c +++ b/internal/kim.c @@ -73,7 +73,7 @@ static const JSCFunctionListEntry js_kim_funcs[] = { MIST_FUNC_DEF(kim, decode, 1), }; -JSValue js_core_kim_use(JSContext *js) +JSValue js_core_internal_kim_use(JSContext *js) { JS_FRAME(js); JS_ROOT(mod, JS_NewObject(js)); diff --git a/internal/nota.c b/internal/nota.c new file mode 100644 index 00000000..fe4999f2 --- /dev/null +++ b/internal/nota.c @@ -0,0 +1,431 @@ +#define NOTA_IMPLEMENTATION +#include "quickjs-internal.h" +#include "cell.h" + +static int nota_get_arr_len (JSContext *ctx, JSValue arr) { + int64_t len; + JS_GetLength (ctx, arr, &len); + return (int)len; +} + +typedef struct NotaVisitedNode { + JSGCRef ref; + struct NotaVisitedNode *next; +} NotaVisitedNode; + +typedef struct NotaEncodeContext { + JSContext *ctx; + NotaVisitedNode *visited_list; + NotaBuffer nb; + int cycle; + JSGCRef *replacer_ref; /* pointer to GC-rooted ref */ +} NotaEncodeContext; + +static void nota_stack_push (NotaEncodeContext *enc, JSValueConst val) { + NotaVisitedNode *node = (NotaVisitedNode *)sys_malloc (sizeof (NotaVisitedNode)); + JS_PushGCRef (enc->ctx, &node->ref); + node->ref.val = JS_DupValue (enc->ctx, val); + node->next = enc->visited_list; + enc->visited_list = node; +} + +static void nota_stack_pop (NotaEncodeContext *enc) { + NotaVisitedNode *node = enc->visited_list; + enc->visited_list = node->next; + JS_FreeValue (enc->ctx, node->ref.val); + JS_PopGCRef (enc->ctx, &node->ref); + sys_free (node); +} + +static int nota_stack_has (NotaEncodeContext *enc, JSValueConst val) { + NotaVisitedNode *node = enc->visited_list; + while (node) { + if (JS_StrictEq (enc->ctx, node->ref.val, val)) + return 1; + node = node->next; + } + return 0; +} + +static JSValue nota_apply_replacer (NotaEncodeContext *enc, JSValueConst holder, JSValueConst key, JSValueConst val) { + if (!enc->replacer_ref || JS_IsNull (enc->replacer_ref->val)) return JS_DupValue (enc->ctx, val); + + JSValue args[2] = { JS_DupValue (enc->ctx, key), JS_DupValue (enc->ctx, val) }; + JSValue result = JS_Call (enc->ctx, enc->replacer_ref->val, holder, 2, args); + JS_FreeValue (enc->ctx, args[0]); + JS_FreeValue (enc->ctx, args[1]); + + if (JS_IsException (result)) return JS_DupValue (enc->ctx, val); + return result; +} + +static char *js_do_nota_decode (JSContext *js, JSValue *tmp, char *nota, JSValue holder, JSValue key, JSValue reviver) { + int type = nota_type (nota); + JSValue ret2; + long long n; + double d; + int b; + char *str; + uint8_t *blob; + + switch (type) { + case NOTA_BLOB: + nota = nota_read_blob (&n, (char **)&blob, nota); + *tmp = js_new_blob_stoned_copy (js, blob, n); + sys_free (blob); + break; + case NOTA_TEXT: + nota = nota_read_text (&str, nota); + *tmp = JS_NewString (js, str); + sys_free (str); + break; + case NOTA_ARR: + nota = nota_read_array (&n, nota); + *tmp = JS_NewArrayLen (js, n); + for (int i = 0; i < n; i++) { + nota = js_do_nota_decode (js, &ret2, nota, *tmp, JS_NewInt32 (js, i), reviver); + JS_SetPropertyNumber (js, *tmp, i, ret2); + } + break; + case NOTA_REC: + nota = nota_read_record (&n, nota); + *tmp = JS_NewObject (js); + for (int i = 0; i < n; i++) { + JSGCRef prop_key_ref, sub_val_ref; + JS_PushGCRef (js, &prop_key_ref); + JS_PushGCRef (js, &sub_val_ref); + nota = nota_read_text (&str, nota); + prop_key_ref.val = JS_NewString (js, str); + sub_val_ref.val = JS_NULL; + nota = js_do_nota_decode (js, &sub_val_ref.val, nota, *tmp, prop_key_ref.val, reviver); + JS_SetPropertyStr (js, *tmp, str, sub_val_ref.val); + JS_PopGCRef (js, &sub_val_ref); + JS_PopGCRef (js, &prop_key_ref); + sys_free (str); + } + break; + case NOTA_INT: + nota = nota_read_int (&n, nota); + *tmp = JS_NewInt64 (js, n); + break; + case NOTA_SYM: + nota = nota_read_sym (&b, nota); + if (b == NOTA_PRIVATE) { + JSGCRef inner_ref, obj_ref2; + JS_PushGCRef (js, &inner_ref); + JS_PushGCRef (js, &obj_ref2); + inner_ref.val = JS_NULL; + nota = js_do_nota_decode (js, &inner_ref.val, nota, holder, JS_NULL, reviver); + obj_ref2.val = JS_NewObject (js); + if (!JS_IsNull (js->actor_sym)) + JS_SetPropertyKey (js, obj_ref2.val, js->actor_sym, inner_ref.val); + JS_CellStone (js, obj_ref2.val); + *tmp = obj_ref2.val; + JS_PopGCRef (js, &obj_ref2); + JS_PopGCRef (js, &inner_ref); + } else { + switch (b) { + case NOTA_NULL: *tmp = JS_NULL; break; + case NOTA_FALSE: *tmp = JS_NewBool (js, 0); break; + case NOTA_TRUE: *tmp = JS_NewBool (js, 1); break; + default: *tmp = JS_NULL; break; + } + } + break; + default: + case NOTA_FLOAT: + nota = nota_read_float (&d, nota); + *tmp = JS_NewFloat64 (js, d); + break; + } + + if (!JS_IsNull (reviver)) { + JSValue args[2] = { JS_DupValue (js, key), JS_DupValue (js, *tmp) }; + JSValue revived = JS_Call (js, reviver, holder, 2, args); + JS_FreeValue (js, args[0]); + JS_FreeValue (js, args[1]); + if (!JS_IsException (revived)) { + JS_FreeValue (js, *tmp); + *tmp = revived; + } else { + JS_FreeValue (js, revived); + } + } + + return nota; +} + +static void nota_encode_value (NotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValueConst key) { + JSContext *ctx = enc->ctx; + JSGCRef replaced_ref, keys_ref, elem_ref, prop_ref; + JS_PushGCRef (ctx, &replaced_ref); + replaced_ref.val = nota_apply_replacer (enc, holder, key, val); + int tag = JS_VALUE_GET_TAG (replaced_ref.val); + + switch (tag) { + case JS_TAG_INT: + case JS_TAG_FLOAT64: { + double d; + JS_ToFloat64 (ctx, &d, replaced_ref.val); + nota_write_number (&enc->nb, d); + break; + } + case JS_TAG_STRING: { + const char *str = JS_ToCString (ctx, replaced_ref.val); + nota_write_text (&enc->nb, str); + JS_FreeCString (ctx, str); + break; + } + case JS_TAG_BOOL: + if (JS_VALUE_GET_BOOL (replaced_ref.val)) nota_write_sym (&enc->nb, NOTA_TRUE); + else nota_write_sym (&enc->nb, NOTA_FALSE); + break; + case JS_TAG_NULL: + nota_write_sym (&enc->nb, NOTA_NULL); + break; + case JS_TAG_PTR: { + if (JS_IsText (replaced_ref.val)) { + const char *str = JS_ToCString (ctx, replaced_ref.val); + nota_write_text (&enc->nb, str); + JS_FreeCString (ctx, str); + break; + } + + if (js_is_blob (ctx, replaced_ref.val)) { + size_t buf_len; + void *buf_data = js_get_blob_data (ctx, &buf_len, replaced_ref.val); + if (buf_data == (void *)-1) { + JS_PopGCRef (ctx, &replaced_ref); + return; + } + nota_write_blob (&enc->nb, (unsigned long long)buf_len * 8, (const char *)buf_data); + break; + } + + if (JS_IsArray (replaced_ref.val)) { + if (nota_stack_has (enc, replaced_ref.val)) { + enc->cycle = 1; + break; + } + nota_stack_push (enc, replaced_ref.val); + int arr_len = nota_get_arr_len (ctx, replaced_ref.val); + nota_write_array (&enc->nb, arr_len); + JS_PushGCRef (ctx, &elem_ref); + for (int i = 0; i < arr_len; i++) { + elem_ref.val = JS_GetPropertyNumber (ctx, replaced_ref.val, i); + JSValue elem_key = JS_NewInt32 (ctx, i); + nota_encode_value (enc, elem_ref.val, replaced_ref.val, elem_key); + } + JS_PopGCRef (ctx, &elem_ref); + nota_stack_pop (enc); + break; + } + + JSValue adata = JS_NULL; + if (!JS_IsNull (ctx->actor_sym)) { + int has = JS_HasPropertyKey (ctx, replaced_ref.val, ctx->actor_sym); + if (has > 0) adata = JS_GetPropertyKey (ctx, replaced_ref.val, ctx->actor_sym); + } + if (!JS_IsNull (adata)) { + nota_write_sym (&enc->nb, NOTA_PRIVATE); + nota_encode_value (enc, adata, replaced_ref.val, JS_NULL); + JS_FreeValue (ctx, adata); + break; + } + JS_FreeValue (ctx, adata); + if (nota_stack_has (enc, replaced_ref.val)) { + enc->cycle = 1; + break; + } + nota_stack_push (enc, replaced_ref.val); + + JSValue to_json = JS_GetPropertyStr (ctx, replaced_ref.val, "toJSON"); + if (JS_IsFunction (to_json)) { + JSValue result = JS_Call (ctx, to_json, replaced_ref.val, 0, NULL); + if (!JS_IsException (result)) { + nota_encode_value (enc, result, holder, key); + } else { + nota_write_sym (&enc->nb, NOTA_NULL); + } + nota_stack_pop (enc); + break; + } + + JS_PushGCRef (ctx, &keys_ref); + keys_ref.val = JS_GetOwnPropertyNames (ctx, replaced_ref.val); + if (JS_IsException (keys_ref.val)) { + nota_write_sym (&enc->nb, NOTA_NULL); + nota_stack_pop (enc); + JS_PopGCRef (ctx, &keys_ref); + break; + } + int64_t plen64; + if (JS_GetLength (ctx, keys_ref.val, &plen64) < 0) { + nota_write_sym (&enc->nb, NOTA_NULL); + nota_stack_pop (enc); + JS_PopGCRef (ctx, &keys_ref); + break; + } + uint32_t plen = (uint32_t)plen64; + + JS_PushGCRef (ctx, &prop_ref); + JS_PushGCRef (ctx, &elem_ref); + uint32_t non_function_count = 0; + for (uint32_t i = 0; i < plen; i++) { + elem_ref.val = JS_GetPropertyNumber (ctx, keys_ref.val, i); + prop_ref.val = JS_GetProperty (ctx, replaced_ref.val, elem_ref.val); + if (!JS_IsFunction (prop_ref.val)) non_function_count++; + } + + nota_write_record (&enc->nb, non_function_count); + for (uint32_t i = 0; i < plen; i++) { + elem_ref.val = JS_GetPropertyNumber (ctx, keys_ref.val, i); + prop_ref.val = JS_GetProperty (ctx, replaced_ref.val, elem_ref.val); + if (!JS_IsFunction (prop_ref.val)) { + const char *prop_name = JS_ToCString (ctx, elem_ref.val); + nota_write_text (&enc->nb, prop_name ? prop_name : ""); + nota_encode_value (enc, prop_ref.val, replaced_ref.val, elem_ref.val); + JS_FreeCString (ctx, prop_name); + } + } + JS_PopGCRef (ctx, &elem_ref); + JS_PopGCRef (ctx, &prop_ref); + JS_PopGCRef (ctx, &keys_ref); + nota_stack_pop (enc); + break; + } + default: + nota_write_sym (&enc->nb, NOTA_NULL); + break; + } + JS_PopGCRef (ctx, &replaced_ref); +} + +void *value2nota (JSContext *ctx, JSValue v) { + JSGCRef val_ref, key_ref; + JS_PushGCRef (ctx, &val_ref); + JS_PushGCRef (ctx, &key_ref); + val_ref.val = v; + + NotaEncodeContext enc_s, *enc = &enc_s; + enc->ctx = ctx; + enc->visited_list = NULL; + enc->cycle = 0; + enc->replacer_ref = NULL; + + nota_buffer_init (&enc->nb, 128); + key_ref.val = JS_NewString (ctx, ""); + nota_encode_value (enc, val_ref.val, JS_NULL, key_ref.val); + + if (enc->cycle) { + JS_PopGCRef (ctx, &key_ref); + JS_PopGCRef (ctx, &val_ref); + nota_buffer_free (&enc->nb); + return NULL; + } + + JS_PopGCRef (ctx, &key_ref); + JS_PopGCRef (ctx, &val_ref); + void *data_ptr = enc->nb.data; + enc->nb.data = NULL; + nota_buffer_free (&enc->nb); + return data_ptr; +} + +JSValue nota2value (JSContext *js, void *nota) { + if (!nota) return JS_NULL; + JSGCRef holder_ref, key_ref, ret_ref; + JS_PushGCRef (js, &holder_ref); + JS_PushGCRef (js, &key_ref); + JS_PushGCRef (js, &ret_ref); + holder_ref.val = JS_NewObject (js); + key_ref.val = JS_NewString (js, ""); + ret_ref.val = JS_NULL; + js_do_nota_decode (js, &ret_ref.val, nota, holder_ref.val, key_ref.val, JS_NULL); + JSValue result = ret_ref.val; + JS_PopGCRef (js, &ret_ref); + JS_PopGCRef (js, &key_ref); + JS_PopGCRef (js, &holder_ref); + return result; +} + +static JSValue js_nota_encode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { + if (argc < 1) return JS_RaiseDisrupt (ctx, "nota.encode requires at least 1 argument"); + + JSGCRef val_ref, replacer_ref, key_ref; + JS_PushGCRef (ctx, &val_ref); + JS_PushGCRef (ctx, &replacer_ref); + JS_PushGCRef (ctx, &key_ref); + val_ref.val = argv[0]; + replacer_ref.val = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL; + + NotaEncodeContext enc_s, *enc = &enc_s; + enc->ctx = ctx; + enc->visited_list = NULL; + enc->cycle = 0; + enc->replacer_ref = &replacer_ref; + + nota_buffer_init (&enc->nb, 128); + key_ref.val = JS_NewString (ctx, ""); + nota_encode_value (enc, val_ref.val, JS_NULL, key_ref.val); + + JSValue ret; + if (enc->cycle) { + nota_buffer_free (&enc->nb); + ret = JS_RaiseDisrupt (ctx, "Tried to encode something to nota with a cycle."); + } else { + size_t total_len = enc->nb.size; + void *data_ptr = enc->nb.data; + ret = js_new_blob_stoned_copy (ctx, (uint8_t *)data_ptr, total_len); + nota_buffer_free (&enc->nb); + } + + JS_PopGCRef (ctx, &key_ref); + JS_PopGCRef (ctx, &replacer_ref); + JS_PopGCRef (ctx, &val_ref); + return ret; +} + +static JSValue js_nota_decode (JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 1) return JS_NULL; + + size_t len; + unsigned char *nota = js_get_blob_data (js, &len, argv[0]); + if (nota == (unsigned char *)-1) return JS_EXCEPTION; + if (!nota) return JS_NULL; + + JSGCRef holder_ref, key_ref, ret_ref, reviver_ref; + JS_PushGCRef (js, &holder_ref); + JS_PushGCRef (js, &key_ref); + JS_PushGCRef (js, &ret_ref); + JS_PushGCRef (js, &reviver_ref); + + reviver_ref.val = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL; + holder_ref.val = JS_NewObject (js); + key_ref.val = JS_NewString (js, ""); + ret_ref.val = JS_NULL; + + js_do_nota_decode (js, &ret_ref.val, (char *)nota, holder_ref.val, key_ref.val, reviver_ref.val); + + JSValue result = ret_ref.val; + JS_PopGCRef (js, &reviver_ref); + JS_PopGCRef (js, &ret_ref); + JS_PopGCRef (js, &key_ref); + JS_PopGCRef (js, &holder_ref); + return result; +} + +static const JSCFunctionListEntry js_nota_funcs[] = { + JS_CFUNC_DEF ("encode", 1, js_nota_encode), + JS_CFUNC_DEF ("decode", 1, js_nota_decode), +}; + +JSValue js_core_internal_nota_use (JSContext *js) { + JSGCRef export_ref; + JS_PushGCRef (js, &export_ref); + export_ref.val = JS_NewObject (js); + JS_SetPropertyFunctionList (js, export_ref.val, js_nota_funcs, sizeof (js_nota_funcs) / sizeof (JSCFunctionListEntry)); + JSValue result = export_ref.val; + JS_PopGCRef (js, &export_ref); + return result; +} diff --git a/internal/os.c b/internal/os.c index 3638c059..0e48db47 100644 --- a/internal/os.c +++ b/internal/os.c @@ -733,7 +733,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, stack, 1), }; -JSValue js_core_os_use(JSContext *js) { +JSValue js_core_internal_os_use(JSContext *js) { JS_NewClassID(&js_dylib_class_id); JS_NewClass(js, js_dylib_class_id, &js_dylib_class); diff --git a/qop.c b/internal/qop.c similarity index 99% rename from qop.c rename to internal/qop.c index 9d443f8a..f16f99d6 100644 --- a/qop.c +++ b/internal/qop.c @@ -455,7 +455,7 @@ static const JSCFunctionListEntry js_qop_funcs[] = { JS_PROP_INT32_DEF("FLAG_ENCRYPTED", QOP_FLAG_ENCRYPTED, 0), }; -JSValue js_core_qop_use(JSContext *js) { +JSValue js_core_internal_qop_use(JSContext *js) { JS_FRAME(js); JS_NewClassID(&js_qop_archive_class_id); JS_NewClass(js, js_qop_archive_class_id, &js_qop_archive_class); diff --git a/internal/shop.cm b/internal/shop.cm index d66c9732..aa8a539b 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -5,11 +5,11 @@ var fd = use('fd') var http = use('http') var miniz = use('miniz') var time = use('time') -var crypto = use('crypto') +var crypto = use('internal/crypto') var blob = use('blob') var pkg_tools = use('package') -var os = use('os') +var os = use('internal/os') var link = use('link') // These come from env (via core_extras in engine.cm): diff --git a/time_playdate.c b/internal/time_playdate.c similarity index 100% rename from time_playdate.c rename to internal/time_playdate.c diff --git a/wildstar.c b/internal/wildstar.c similarity index 95% rename from wildstar.c rename to internal/wildstar.c index 7bd8e8be..94d8aef3 100644 --- a/wildstar.c +++ b/internal/wildstar.c @@ -28,7 +28,7 @@ static const JSCFunctionListEntry js_wildstar_funcs[] = { JS_PROP_INT32_DEF("WM_WILDSTAR", WM_WILDSTAR, 0), }; -JSValue js_core_wildstar_use(JSContext *js) { +JSValue js_core_internal_wildstar_use(JSContext *js) { JS_FRAME(js); JS_ROOT(mod, JS_NewObject(js)); JS_SetPropertyFunctionList(js, mod.val, js_wildstar_funcs, countof(js_wildstar_funcs)); diff --git a/internal/wota.c b/internal/wota.c new file mode 100644 index 00000000..ce32c800 --- /dev/null +++ b/internal/wota.c @@ -0,0 +1,461 @@ +#define WOTA_IMPLEMENTATION +#include "quickjs-internal.h" +#include "cell.h" + +typedef struct ObjectRef { + void *ptr; + struct ObjectRef *next; +} ObjectRef; + +typedef struct WotaEncodeContext { + JSContext *ctx; + ObjectRef *visited_stack; + WotaBuffer wb; + int cycle; + JSValue replacer; +} WotaEncodeContext; + +static void wota_stack_push (WotaEncodeContext *enc, JSValueConst val) { + (void)enc; (void)val; + /* Cycle detection disabled for performance */ +} + +static void wota_stack_pop (WotaEncodeContext *enc) { + if (!enc->visited_stack) return; + + ObjectRef *top = enc->visited_stack; + enc->visited_stack = top->next; + sys_free (top); +} + +static int wota_stack_has (WotaEncodeContext *enc, JSValueConst val) { + (void)enc; (void)val; + return 0; + /* Cycle detection disabled for performance */ +} + +static void wota_stack_free (WotaEncodeContext *enc) { + while (enc->visited_stack) { + wota_stack_pop (enc); + } +} + +static JSValue wota_apply_replacer (WotaEncodeContext *enc, JSValueConst holder, JSValue key, JSValueConst val) { + if (JS_IsNull (enc->replacer)) return JS_DupValue (enc->ctx, val); + JSValue key_val = JS_IsNull (key) ? JS_NULL : JS_DupValue (enc->ctx, key); + JSValue args[2] = { key_val, JS_DupValue (enc->ctx, val) }; + JSValue result = JS_Call (enc->ctx, enc->replacer, holder, 2, args); + JS_FreeValue (enc->ctx, args[0]); + JS_FreeValue (enc->ctx, args[1]); + if (JS_IsException (result)) return JS_DupValue (enc->ctx, val); + return result; +} + +static void wota_encode_value (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValue key); + +static void encode_object_properties (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder) { + JSContext *ctx = enc->ctx; + + /* Root the input value to protect it during property enumeration */ + JSGCRef val_ref, keys_ref; + JS_PushGCRef (ctx, &val_ref); + JS_PushGCRef (ctx, &keys_ref); + val_ref.val = JS_DupValue (ctx, val); + + keys_ref.val = JS_GetOwnPropertyNames (ctx, val_ref.val); + if (JS_IsException (keys_ref.val)) { + wota_write_sym (&enc->wb, WOTA_NULL); + JS_FreeValue (ctx, val_ref.val); + JS_PopGCRef (ctx, &keys_ref); + JS_PopGCRef (ctx, &val_ref); + return; + } + int64_t plen64; + if (JS_GetLength (ctx, keys_ref.val, &plen64) < 0) { + JS_FreeValue (ctx, keys_ref.val); + JS_FreeValue (ctx, val_ref.val); + wota_write_sym (&enc->wb, WOTA_NULL); + JS_PopGCRef (ctx, &keys_ref); + JS_PopGCRef (ctx, &val_ref); + return; + } + uint32_t plen = (uint32_t)plen64; + uint32_t non_function_count = 0; + + /* Allocate GC-rooted arrays for props and keys */ + JSGCRef *prop_refs = sys_malloc (sizeof (JSGCRef) * plen); + JSGCRef *key_refs = sys_malloc (sizeof (JSGCRef) * plen); + for (uint32_t i = 0; i < plen; i++) { + JS_PushGCRef (ctx, &prop_refs[i]); + JS_PushGCRef (ctx, &key_refs[i]); + prop_refs[i].val = JS_NULL; + key_refs[i].val = JS_NULL; + } + + for (uint32_t i = 0; i < plen; i++) { + /* Store key into its GCRef slot immediately so it's rooted before + JS_GetProperty can trigger GC and relocate the string. */ + key_refs[i].val = JS_GetPropertyNumber (ctx, keys_ref.val, i); + JSValue prop_val = JS_GetProperty (ctx, val_ref.val, key_refs[i].val); + if (!JS_IsFunction (prop_val)) { + if (i != non_function_count) { + key_refs[non_function_count].val = key_refs[i].val; + key_refs[i].val = JS_NULL; + } + prop_refs[non_function_count].val = prop_val; + non_function_count++; + } else { + JS_FreeValue (ctx, prop_val); + JS_FreeValue (ctx, key_refs[i].val); + key_refs[i].val = JS_NULL; + } + } + JS_FreeValue (ctx, keys_ref.val); + wota_write_record (&enc->wb, non_function_count); + for (uint32_t i = 0; i < non_function_count; i++) { + size_t klen; + const char *prop_name = JS_ToCStringLen (ctx, &klen, key_refs[i].val); + wota_write_text_len (&enc->wb, prop_name ? prop_name : "", prop_name ? klen : 0); + wota_encode_value (enc, prop_refs[i].val, val_ref.val, key_refs[i].val); + JS_FreeCString (ctx, prop_name); + JS_FreeValue (ctx, prop_refs[i].val); + JS_FreeValue (ctx, key_refs[i].val); + } + /* Pop all GC refs in reverse order */ + for (int i = plen - 1; i >= 0; i--) { + JS_PopGCRef (ctx, &key_refs[i]); + JS_PopGCRef (ctx, &prop_refs[i]); + } + sys_free (prop_refs); + sys_free (key_refs); + JS_FreeValue (ctx, val_ref.val); + JS_PopGCRef (ctx, &keys_ref); + JS_PopGCRef (ctx, &val_ref); +} + +static void wota_encode_value (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValue key) { + JSContext *ctx = enc->ctx; + JSValue replaced; + if (!JS_IsNull (enc->replacer) && !JS_IsNull (key)) + replaced = wota_apply_replacer (enc, holder, key, val); + else + replaced = JS_DupValue (enc->ctx, val); + + int tag = JS_VALUE_GET_TAG (replaced); + switch (tag) { + case JS_TAG_INT: { + int32_t d; + JS_ToInt32 (ctx, &d, replaced); + wota_write_int_word (&enc->wb, d); + break; + } + case JS_TAG_FLOAT64: { + double d; + if (JS_ToFloat64 (ctx, &d, replaced) < 0) { + wota_write_sym (&enc->wb, WOTA_NULL); + break; + } + wota_write_float_word (&enc->wb, d); + break; + } + case JS_TAG_STRING: { + size_t plen; + const char *str = JS_ToCStringLen (ctx, &plen, replaced); + wota_write_text_len (&enc->wb, str ? str : "", str ? plen : 0); + JS_FreeCString (ctx, str); + break; + } + case JS_TAG_BOOL: + wota_write_sym (&enc->wb, JS_VALUE_GET_BOOL (replaced) ? WOTA_TRUE : WOTA_FALSE); + break; + case JS_TAG_NULL: + wota_write_sym (&enc->wb, WOTA_NULL); + break; + case JS_TAG_PTR: { + if (JS_IsText (replaced)) { + size_t plen; + const char *str = JS_ToCStringLen (ctx, &plen, replaced); + wota_write_text_len (&enc->wb, str ? str : "", str ? plen : 0); + JS_FreeCString (ctx, str); + break; + } + if (js_is_blob (ctx, replaced)) { + size_t buf_len; + void *buf_data = js_get_blob_data (ctx, &buf_len, replaced); + if (buf_data == (void *)-1) { + JS_FreeValue (ctx, replaced); + return; + } + if (buf_len == 0) { + wota_write_blob (&enc->wb, 0, ""); + } else { + wota_write_blob (&enc->wb, (unsigned long long)buf_len * 8, (const char *)buf_data); + } + break; + } + if (JS_IsArray (replaced)) { + if (wota_stack_has (enc, replaced)) { + enc->cycle = 1; + break; + } + wota_stack_push (enc, replaced); + int64_t arr_len; + JS_GetLength (ctx, replaced, &arr_len); + wota_write_array (&enc->wb, arr_len); + for (int64_t i = 0; i < arr_len; i++) { + JSValue elem_val = JS_GetPropertyNumber (ctx, replaced, i); + wota_encode_value (enc, elem_val, replaced, JS_NewInt32 (ctx, (int32_t)i)); + JS_FreeValue (ctx, elem_val); + } + wota_stack_pop (enc); + break; + } + JSValue adata = JS_NULL; + if (!JS_IsNull (ctx->actor_sym)) { + int has = JS_HasPropertyKey (ctx, replaced, ctx->actor_sym); + if (has > 0) adata = JS_GetPropertyKey (ctx, replaced, ctx->actor_sym); + } + if (!JS_IsNull (adata)) { + wota_write_sym (&enc->wb, WOTA_PRIVATE); + wota_encode_value (enc, adata, replaced, JS_NULL); + JS_FreeValue (ctx, adata); + break; + } + JS_FreeValue (ctx, adata); + if (wota_stack_has (enc, replaced)) { + enc->cycle = 1; + break; + } + wota_stack_push (enc, replaced); + JSValue to_json = JS_GetPropertyStr (ctx, replaced, "toJSON"); + if (JS_IsFunction (to_json)) { + JSValue result = JS_Call (ctx, to_json, replaced, 0, NULL); + JS_FreeValue (ctx, to_json); + if (!JS_IsException (result)) { + wota_encode_value (enc, result, holder, key); + JS_FreeValue (ctx, result); + } else + wota_write_sym (&enc->wb, WOTA_NULL); + wota_stack_pop (enc); + break; + } + JS_FreeValue (ctx, to_json); + encode_object_properties (enc, replaced, holder); + wota_stack_pop (enc); + break; + } + default: + wota_write_sym (&enc->wb, WOTA_NULL); + break; + } + JS_FreeValue (ctx, replaced); +} + +static char *decode_wota_value (JSContext *ctx, char *data_ptr, JSValue *out_val, JSValue holder, JSValue key, JSValue reviver) { + uint64_t first_word = *(uint64_t *)data_ptr; + int type = (int)(first_word & 0xffU); + switch (type) { + case WOTA_INT: { + long long val; + data_ptr = wota_read_int (&val, data_ptr); + *out_val = JS_NewInt64 (ctx, val); + break; + } + case WOTA_FLOAT: { + double d; + data_ptr = wota_read_float (&d, data_ptr); + *out_val = JS_NewFloat64 (ctx, d); + break; + } + case WOTA_SYM: { + int scode; + data_ptr = wota_read_sym (&scode, data_ptr); + if (scode == WOTA_PRIVATE) { + JSGCRef inner_ref, obj_ref2; + JS_PushGCRef (ctx, &inner_ref); + JS_PushGCRef (ctx, &obj_ref2); + inner_ref.val = JS_NULL; + data_ptr = decode_wota_value (ctx, data_ptr, &inner_ref.val, holder, JS_NULL, reviver); + obj_ref2.val = JS_NewObject (ctx); + if (!JS_IsNull (ctx->actor_sym)) + JS_SetPropertyKey (ctx, obj_ref2.val, ctx->actor_sym, inner_ref.val); + JS_CellStone (ctx, obj_ref2.val); + *out_val = obj_ref2.val; + JS_PopGCRef (ctx, &obj_ref2); + JS_PopGCRef (ctx, &inner_ref); + } else if (scode == WOTA_NULL) *out_val = JS_NULL; + else if (scode == WOTA_FALSE) *out_val = JS_NewBool (ctx, 0); + else if (scode == WOTA_TRUE) *out_val = JS_NewBool (ctx, 1); + else *out_val = JS_NULL; + break; + } + case WOTA_BLOB: { + long long blen; + char *bdata = NULL; + data_ptr = wota_read_blob (&blen, &bdata, data_ptr); + *out_val = bdata ? js_new_blob_stoned_copy (ctx, (uint8_t *)bdata, (size_t)blen) : js_new_blob_stoned_copy (ctx, NULL, 0); + if (bdata) sys_free (bdata); + break; + } + case WOTA_TEXT: { + char *utf8 = NULL; + data_ptr = wota_read_text (&utf8, data_ptr); + *out_val = JS_NewString (ctx, utf8 ? utf8 : ""); + if (utf8) sys_free (utf8); + break; + } + case WOTA_ARR: { + long long c; + data_ptr = wota_read_array (&c, data_ptr); + JSGCRef arr_ref; + JS_PushGCRef (ctx, &arr_ref); + arr_ref.val = JS_NewArrayLen (ctx, c); + for (long long i = 0; i < c; i++) { + JSGCRef elem_ref; + JS_PushGCRef (ctx, &elem_ref); + elem_ref.val = JS_NULL; + JSValue idx_key = JS_NewInt32 (ctx, (int32_t)i); + data_ptr = decode_wota_value (ctx, data_ptr, &elem_ref.val, arr_ref.val, idx_key, reviver); + JS_SetPropertyNumber (ctx, arr_ref.val, i, elem_ref.val); + JS_PopGCRef (ctx, &elem_ref); + } + *out_val = arr_ref.val; + JS_PopGCRef (ctx, &arr_ref); + break; + } + case WOTA_REC: { + long long c; + data_ptr = wota_read_record (&c, data_ptr); + JSGCRef obj_ref; + JS_PushGCRef (ctx, &obj_ref); + obj_ref.val = JS_NewObject (ctx); + for (long long i = 0; i < c; i++) { + char *tkey = NULL; + size_t key_len; + data_ptr = wota_read_text_len (&key_len, &tkey, data_ptr); + if (!tkey) continue; + JSGCRef prop_key_ref, sub_val_ref; + JS_PushGCRef (ctx, &prop_key_ref); + JS_PushGCRef (ctx, &sub_val_ref); + prop_key_ref.val = JS_NewStringLen (ctx, tkey, key_len); + sub_val_ref.val = JS_NULL; + data_ptr = decode_wota_value (ctx, data_ptr, &sub_val_ref.val, obj_ref.val, prop_key_ref.val, reviver); + JS_SetPropertyStr (ctx, obj_ref.val, tkey, sub_val_ref.val); + JS_PopGCRef (ctx, &sub_val_ref); + JS_PopGCRef (ctx, &prop_key_ref); + sys_free (tkey); + } + *out_val = obj_ref.val; + JS_PopGCRef (ctx, &obj_ref); + break; + } + default: + data_ptr += 8; + *out_val = JS_NULL; + break; + } + if (!JS_IsNull (reviver)) { + JSValue key_val = JS_IsNull (key) ? JS_NULL : JS_DupValue (ctx, key); + JSValue args[2] = { key_val, JS_DupValue (ctx, *out_val) }; + JSValue revived = JS_Call (ctx, reviver, holder, 2, args); + JS_FreeValue (ctx, args[0]); + JS_FreeValue (ctx, args[1]); + if (!JS_IsException (revived)) { + JS_FreeValue (ctx, *out_val); + *out_val = revived; + } else + JS_FreeValue (ctx, revived); + } + return data_ptr; +} + +void *value2wota (JSContext *ctx, JSValue v, JSValue replacer, size_t *bytes) { + JSGCRef val_ref, rep_ref; + JS_PushGCRef (ctx, &val_ref); + JS_PushGCRef (ctx, &rep_ref); + val_ref.val = v; + rep_ref.val = replacer; + + WotaEncodeContext enc_s, *enc = &enc_s; + + enc->ctx = ctx; + enc->visited_stack = NULL; + enc->cycle = 0; + enc->replacer = rep_ref.val; + wota_buffer_init (&enc->wb, 16); + wota_encode_value (enc, val_ref.val, JS_NULL, JS_NULL); + if (enc->cycle) { + wota_stack_free (enc); + wota_buffer_free (&enc->wb); + JS_PopGCRef (ctx, &rep_ref); + JS_PopGCRef (ctx, &val_ref); + return NULL; + } + wota_stack_free (enc); + size_t total_bytes = enc->wb.size * sizeof (uint64_t); + void *wota = sys_realloc (enc->wb.data, total_bytes); + if (bytes) *bytes = total_bytes; + JS_PopGCRef (ctx, &rep_ref); + JS_PopGCRef (ctx, &val_ref); + return wota; +} + +JSValue wota2value (JSContext *ctx, void *wota) { + JSGCRef holder_ref, result_ref; + JS_PushGCRef (ctx, &holder_ref); + JS_PushGCRef (ctx, &result_ref); + result_ref.val = JS_NULL; + holder_ref.val = JS_NewObject (ctx); + decode_wota_value (ctx, wota, &result_ref.val, holder_ref.val, JS_NULL, JS_NULL); + JSValue result = result_ref.val; + JS_PopGCRef (ctx, &result_ref); + JS_PopGCRef (ctx, &holder_ref); + return result; +} + +static JSValue js_wota_encode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { + if (argc < 1) return JS_RaiseDisrupt (ctx, "wota.encode requires at least 1 argument"); + size_t total_bytes; + void *wota = value2wota (ctx, argv[0], JS_IsFunction (argv[1]) ? argv[1] : JS_NULL, &total_bytes); + JSValue ret = js_new_blob_stoned_copy (ctx, wota, total_bytes); + sys_free (wota); + return ret; +} + +static JSValue js_wota_decode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { + if (argc < 1) return JS_NULL; + size_t len; + uint8_t *buf = js_get_blob_data (ctx, &len, argv[0]); + if (buf == (uint8_t *)-1) return JS_EXCEPTION; + if (!buf || len == 0) return JS_RaiseDisrupt (ctx, "No blob data present"); + JSValue reviver = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL; + char *data_ptr = (char *)buf; + JSGCRef result_ref, holder_ref, empty_key_ref; + JS_PushGCRef (ctx, &result_ref); + JS_PushGCRef (ctx, &holder_ref); + JS_PushGCRef (ctx, &empty_key_ref); + result_ref.val = JS_NULL; + holder_ref.val = JS_NewObject (ctx); + empty_key_ref.val = JS_NewString (ctx, ""); + decode_wota_value (ctx, data_ptr, &result_ref.val, holder_ref.val, empty_key_ref.val, reviver); + JSValue result = result_ref.val; + JS_PopGCRef (ctx, &empty_key_ref); + JS_PopGCRef (ctx, &holder_ref); + JS_PopGCRef (ctx, &result_ref); + return result; +} + +static const JSCFunctionListEntry js_wota_funcs[] = { + JS_CFUNC_DEF ("encode", 2, js_wota_encode), + JS_CFUNC_DEF ("decode", 2, js_wota_decode), +}; + +JSValue js_core_internal_wota_use (JSContext *ctx) { + JSGCRef exports_ref; + JS_PushGCRef (ctx, &exports_ref); + exports_ref.val = JS_NewObject (ctx); + JS_SetPropertyFunctionList (ctx, exports_ref.val, js_wota_funcs, sizeof (js_wota_funcs) / sizeof (js_wota_funcs[0])); + JSValue result = exports_ref.val; + JS_PopGCRef (ctx, &exports_ref); + return result; +} diff --git a/json.c b/json.c new file mode 100644 index 00000000..c479d941 --- /dev/null +++ b/json.c @@ -0,0 +1,67 @@ +#include "cell.h" +#include + +/* JSON uses JS_KEY_empty for reviver — define locally from public constants */ +#ifndef JS_KEY_empty +#define JS_KEY_empty ((JSValue)JS_TAG_STRING_IMM) +#endif + +static JSValue js_cell_json_encode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + if (argc < 1) + return JS_RaiseDisrupt (ctx, "json.encode requires at least 1 argument"); + + int pretty = argc <= 1 || JS_ToBool (ctx, argv[1]); + JSValue space = pretty ? JS_NewInt32 (ctx, 2) : JS_NULL; + JSValue replacer = JS_NULL; + if (argc > 2 && JS_IsFunction (argv[2])) + replacer = argv[2]; + else if (argc > 3 && JS_IsArray (argv[3])) + replacer = argv[3]; + JSValue result = JS_JSONStringify (ctx, argv[0], replacer, space, pretty); + return result; +} + +static JSValue js_cell_json_decode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + if (argc < 1) + return JS_RaiseDisrupt (ctx, "json.decode requires at least 1 argument"); + + if (!JS_IsText (argv[0])) + return JS_RaiseDisrupt (ctx, "couldn't parse text: not a string"); + + const char *str = JS_ToCString (ctx, argv[0]); + if (!str) return JS_EXCEPTION; + + size_t len = strlen (str); + JSValue result = JS_ParseJSON (ctx, str, len, ""); + JS_FreeCString (ctx, str); + + /* Apply reviver if provided */ + if (argc > 1 && JS_IsFunction (argv[1]) && !JS_IsException (result)) { + /* Create wrapper object to pass to reviver */ + JSValue wrapper = JS_NewObject (ctx); + JS_SetPropertyStr (ctx, wrapper, "", result); + + JSValue holder = wrapper; + JSValue key = JS_KEY_empty; + JSValue args[2] = { key, JS_GetProperty (ctx, holder, key) }; + JSValue final = JS_Call (ctx, argv[1], holder, 2, args); + result = final; + } + + return result; +} + +static const JSCFunctionListEntry js_cell_json_funcs[] = { + JS_CFUNC_DEF ("encode", 1, js_cell_json_encode), + JS_CFUNC_DEF ("decode", 1, js_cell_json_decode), +}; + +JSValue js_core_json_use (JSContext *ctx) { + JSGCRef obj_ref; + JS_PushGCRef (ctx, &obj_ref); + obj_ref.val = JS_NewObject (ctx); + JS_SetPropertyFunctionList (ctx, obj_ref.val, js_cell_json_funcs, countof (js_cell_json_funcs)); + JSValue result = obj_ref.val; + JS_PopGCRef (ctx, &obj_ref); + return result; +} diff --git a/math/cycles.c b/math/cycles.c new file mode 100644 index 00000000..fccdd791 --- /dev/null +++ b/math/cycles.c @@ -0,0 +1,66 @@ +#include "cell.h" +#include "cell_math.h" +#include + +#define TWOPI (2.0 * 3.14159265358979323846264338327950288419716939937510) + +static JSValue js_math_cyc_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, acos (x) / TWOPI); +} + +static JSValue js_math_cyc_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, asin (x) / TWOPI); +} + +static JSValue js_math_cyc_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, atan (x) / TWOPI); +} + +static JSValue js_math_cyc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, cos (x * TWOPI)); +} + +static JSValue js_math_cyc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, sin (x * TWOPI)); +} + +static JSValue js_math_cyc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, tan (x * TWOPI)); +} + +static const JSCFunctionListEntry js_math_cycles_funcs[] + = { JS_CFUNC_DEF ("arc_cosine", 1, js_math_cyc_arc_cosine), + JS_CFUNC_DEF ("arc_sine", 1, js_math_cyc_arc_sine), + JS_CFUNC_DEF ("arc_tangent", 1, js_math_cyc_arc_tangent), + JS_CFUNC_DEF ("cosine", 1, js_math_cyc_cosine), + JS_CFUNC_DEF ("sine", 1, js_math_cyc_sine), + JS_CFUNC_DEF ("tangent", 1, js_math_cyc_tangent), + JS_CFUNC_DEF ("ln", 1, js_math_ln), + JS_CFUNC_DEF ("log", 1, js_math_log10), + JS_CFUNC_DEF ("log2", 1, js_math_log2), + JS_CFUNC_DEF ("power", 2, js_math_power), + JS_CFUNC_DEF ("root", 2, js_math_root), + JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt), + JS_CFUNC_DEF ("e", 1, js_math_e) }; + +JSValue js_core_math_cycles_use (JSContext *ctx) { + JSGCRef obj_ref; + JS_PushGCRef (ctx, &obj_ref); + obj_ref.val = JS_NewObject (ctx); + JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_cycles_funcs, countof (js_math_cycles_funcs)); + JSValue result = obj_ref.val; + JS_PopGCRef (ctx, &obj_ref); + return result; +} diff --git a/math/degrees.c b/math/degrees.c new file mode 100644 index 00000000..84585759 --- /dev/null +++ b/math/degrees.c @@ -0,0 +1,67 @@ +#include "cell.h" +#include "cell_math.h" +#include + +#define DEG2RAD (3.14159265358979323846264338327950288419716939937510 / 180.0) +#define RAD2DEG (180.0 / 3.14159265358979323846264338327950288419716939937510) + +static JSValue js_math_deg_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, acos (x) * RAD2DEG); +} + +static JSValue js_math_deg_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, asin (x) * RAD2DEG); +} + +static JSValue js_math_deg_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, atan (x) * RAD2DEG); +} + +static JSValue js_math_deg_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, cos (x * DEG2RAD)); +} + +static JSValue js_math_deg_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, sin (x * DEG2RAD)); +} + +static JSValue js_math_deg_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, tan (x * DEG2RAD)); +} + +static const JSCFunctionListEntry js_math_degrees_funcs[] + = { JS_CFUNC_DEF ("arc_cosine", 1, js_math_deg_arc_cosine), + JS_CFUNC_DEF ("arc_sine", 1, js_math_deg_arc_sine), + JS_CFUNC_DEF ("arc_tangent", 1, js_math_deg_arc_tangent), + JS_CFUNC_DEF ("cosine", 1, js_math_deg_cosine), + JS_CFUNC_DEF ("sine", 1, js_math_deg_sine), + JS_CFUNC_DEF ("tangent", 1, js_math_deg_tangent), + JS_CFUNC_DEF ("ln", 1, js_math_ln), + JS_CFUNC_DEF ("log", 1, js_math_log10), + JS_CFUNC_DEF ("log2", 1, js_math_log2), + JS_CFUNC_DEF ("power", 2, js_math_power), + JS_CFUNC_DEF ("root", 2, js_math_root), + JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt), + JS_CFUNC_DEF ("e", 1, js_math_e) }; + +JSValue js_core_math_degrees_use (JSContext *ctx) { + JSGCRef obj_ref; + JS_PushGCRef (ctx, &obj_ref); + obj_ref.val = JS_NewObject (ctx); + JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_degrees_funcs, countof (js_math_degrees_funcs)); + JSValue result = obj_ref.val; + JS_PopGCRef (ctx, &obj_ref); + return result; +} diff --git a/math/radians.c b/math/radians.c new file mode 100644 index 00000000..fbbd43fc --- /dev/null +++ b/math/radians.c @@ -0,0 +1,64 @@ +#include "cell.h" +#include "cell_math.h" +#include + +static JSValue js_math_rad_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, acos (x)); +} + +static JSValue js_math_rad_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, asin (x)); +} + +static JSValue js_math_rad_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, atan (x)); +} + +static JSValue js_math_rad_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, cos (x)); +} + +static JSValue js_math_rad_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, sin (x)); +} + +static JSValue js_math_rad_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, tan (x)); +} + +static const JSCFunctionListEntry js_math_radians_funcs[] + = { JS_CFUNC_DEF ("arc_cosine", 1, js_math_rad_arc_cosine), + JS_CFUNC_DEF ("arc_sine", 1, js_math_rad_arc_sine), + JS_CFUNC_DEF ("arc_tangent", 1, js_math_rad_arc_tangent), + JS_CFUNC_DEF ("cosine", 1, js_math_rad_cosine), + JS_CFUNC_DEF ("sine", 1, js_math_rad_sine), + JS_CFUNC_DEF ("tangent", 1, js_math_rad_tangent), + JS_CFUNC_DEF ("ln", 1, js_math_ln), + JS_CFUNC_DEF ("log", 1, js_math_log10), + JS_CFUNC_DEF ("log2", 1, js_math_log2), + JS_CFUNC_DEF ("power", 2, js_math_power), + JS_CFUNC_DEF ("root", 2, js_math_root), + JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt), + JS_CFUNC_DEF ("e", 1, js_math_e) }; + +JSValue js_core_math_radians_use (JSContext *ctx) { + JSGCRef obj_ref; + JS_PushGCRef (ctx, &obj_ref); + obj_ref.val = JS_NewObject (ctx); + JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_radians_funcs, countof (js_math_radians_funcs)); + JSValue result = obj_ref.val; + JS_PopGCRef (ctx, &obj_ref); + return result; +} diff --git a/meson.build b/meson.build index 28ea6cc9..21d08001 100644 --- a/meson.build +++ b/meson.build @@ -64,11 +64,18 @@ src += ['qbe_helpers.c'] src += ['qbe_backend.c'] scripts = [ + 'json.c', + 'internal/nota.c', + 'internal/wota.c', + 'math/radians.c', + 'math/degrees.c', + 'math/cycles.c', + 'src/cell_math.c', 'debug/js.c', - 'qop.c', - 'wildstar.c', + 'internal/qop.c', + 'internal/wildstar.c', 'fit.c', - 'crypto.c', + 'internal/crypto.c', 'internal/kim.c', 'internal/time.c', 'debug/debug.c', @@ -76,7 +83,6 @@ scripts = [ 'internal/fd.c', 'net/http.c', 'net/enet.c', - 'wildstar.c', 'archive/miniz.c', 'source/cJSON.c' ] @@ -86,7 +92,7 @@ foreach file: scripts endforeach srceng = 'source' -includes = [srceng, 'internal', 'debug', 'net', 'archive', 'src/qbe'] +includes = [srceng, 'internal', 'debug', 'net', 'archive', 'src/qbe', 'src'] foreach file : src full_path = join_paths(srceng, file) diff --git a/qopconv.ce b/qopconv.ce index 151fcf18..2e2d56e2 100644 --- a/qopconv.ce +++ b/qopconv.ce @@ -1,7 +1,7 @@ // cell qopconv - Convert QOP archive formats var fd = use('fd') -var qop = use('qop') +var qop = use('internal/qop') function print_usage() { log.console("Usage: qopconv [OPTION...] FILE...") diff --git a/random.cm b/random.cm index 1b7b5e7a..8731ace4 100644 --- a/random.cm +++ b/random.cm @@ -1,6 +1,6 @@ var rnd = {} -var os = use('os') +var os = use('internal/os') rnd.random = function() { diff --git a/run_native.ce b/run_native.ce index 0d803521..432ff60e 100644 --- a/run_native.ce +++ b/run_native.ce @@ -6,7 +6,7 @@ // Loads .cm via use() (interpreted) and via shop.use_native() (native), // runs both and compares results and timing. -var os = use('os') +var os = use('internal/os') var fd = use('fd') var shop = use('internal/shop') diff --git a/run_native_seed.ce b/run_native_seed.ce index ae837557..5de27347 100644 --- a/run_native_seed.ce +++ b/run_native_seed.ce @@ -2,7 +2,7 @@ // Usage: ./cell --dev --seed run_native_seed benches/fibonacci var fd = use("fd") -var os = use("os") +var os = use("internal/os") if (length(args) < 1) { print("usage: cell --dev --seed run_native_seed ") diff --git a/seed.ce b/seed.ce index d9aa2cd1..eeb0bce9 100644 --- a/seed.ce +++ b/seed.ce @@ -17,7 +17,7 @@ var fd = use("fd") var json = use("json") -var os = use("os") +var os = use("internal/os") var shop = use("internal/shop") var tokenize = use("tokenize") var parse = use("parse") diff --git a/source/cell.c b/source/cell.c index 95a8174e..79f8e5e3 100644 --- a/source/cell.c +++ b/source/cell.c @@ -261,7 +261,7 @@ void actor_disrupt(cell_rt *crt) actor_free(crt); } -JSValue js_core_os_use(JSContext *js); +JSValue js_core_internal_os_use(JSContext *js); JSValue js_core_json_use(JSContext *js); void script_startup(cell_rt *prt) @@ -317,7 +317,7 @@ void script_startup(cell_rt *prt) JS_AddGCRef(js, &boot_env_ref); boot_env_ref.val = JS_NewObject(js); JSValue btmp; - btmp = js_core_os_use(js); + btmp = js_core_internal_os_use(js); JS_SetPropertyStr(js, boot_env_ref.val, "os", btmp); if (core_path) { btmp = JS_NewString(js, core_path); @@ -347,7 +347,7 @@ void script_startup(cell_rt *prt) JS_AddGCRef(js, &env_ref); env_ref.val = JS_NewObject(js); JSValue tmp; - tmp = js_core_os_use(js); + tmp = js_core_internal_os_use(js); JS_SetPropertyStr(js, env_ref.val, "os", tmp); tmp = js_core_json_use(js); JS_SetPropertyStr(js, env_ref.val, "json", tmp); @@ -612,7 +612,7 @@ int cell_init(int argc, char **argv) JS_AddGCRef(ctx, &boot_env_ref); boot_env_ref.val = JS_NewObject(ctx); JSValue btmp; - btmp = js_core_os_use(ctx); + btmp = js_core_internal_os_use(ctx); JS_SetPropertyStr(ctx, boot_env_ref.val, "os", btmp); btmp = JS_NewString(ctx, core_path); JS_SetPropertyStr(ctx, boot_env_ref.val, "core_path", btmp); @@ -656,7 +656,7 @@ int cell_init(int argc, char **argv) JS_AddGCRef(ctx, &env_ref); env_ref.val = JS_NewObject(ctx); JSValue tmp; - tmp = js_core_os_use(ctx); + tmp = js_core_internal_os_use(ctx); JS_SetPropertyStr(ctx, env_ref.val, "os", tmp); tmp = JS_NewString(ctx, core_path); JS_SetPropertyStr(ctx, env_ref.val, "core_path", tmp); diff --git a/source/quickjs-internal.h b/source/quickjs-internal.h index db5546d6..fad78be9 100644 --- a/source/quickjs-internal.h +++ b/source/quickjs-internal.h @@ -1594,6 +1594,8 @@ int js_string_compare_value_nocase (JSContext *ctx, JSValue op1, JSValue op2); JSValue js_regexp_constructor (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); int JS_HasProperty (JSContext *ctx, JSValue obj, JSValue prop); int JS_HasPropertyKey (JSContext *ctx, JSValue obj, JSValue key); +JSValue JS_GetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key); +int JS_SetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key, JSValue val); void *js_realloc_rt (void *ptr, size_t size); char *js_strdup_rt (const char *str); JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2); diff --git a/source/runtime.c b/source/runtime.c index 920d0754..1b47971b 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -24,8 +24,6 @@ */ #define BLOB_IMPLEMENTATION -#define NOTA_IMPLEMENTATION -#define WOTA_IMPLEMENTATION #include "quickjs-internal.h" // #define DUMP_BUDDY @@ -11545,1223 +11543,7 @@ void js_debug_sethook (JSContext *ctx, js_hook hook, int type, void *user) { ctx->trace_data = user; } -/* ============================================================================ - * Cell Script Module: json - * Provides json.encode() and json.decode() using pure C implementation - * ============================================================================ - */ -static JSValue js_cell_json_encode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - if (argc < 1) - return JS_RaiseDisrupt (ctx, "json.encode requires at least 1 argument"); - - BOOL pretty = argc <= 1 || JS_ToBool (ctx, argv[1]); - JSValue space = pretty ? JS_NewInt32 (ctx, 2) : JS_NULL; - JSValue replacer = JS_NULL; - if (argc > 2 && JS_IsFunction (argv[2])) - replacer = argv[2]; - else if (argc > 3 && JS_IsArray (argv[3])) - replacer = argv[3]; - JSValue result = JS_JSONStringify (ctx, argv[0], replacer, space, pretty); - return result; -} - -static JSValue js_cell_json_decode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - if (argc < 1) - return JS_RaiseDisrupt (ctx, "json.decode requires at least 1 argument"); - - if (!JS_IsText (argv[0])) - return JS_RaiseDisrupt (ctx, "couldn't parse text: not a string"); - - const char *str = JS_ToCString (ctx, argv[0]); - if (!str) return JS_EXCEPTION; - - size_t len = strlen (str); - JSValue result = JS_ParseJSON (ctx, str, len, ""); - JS_FreeCString (ctx, str); - - /* Apply reviver if provided */ - if (argc > 1 && JS_IsFunction (argv[1]) && !JS_IsException (result)) { - /* Create wrapper object to pass to reviver */ - JSValue wrapper = JS_NewObject (ctx); - JS_SetPropertyStr (ctx, wrapper, "", result); - - JSValue holder = wrapper; - JSValue key = JS_KEY_empty; - JSValue args[2] = { key, JS_GetProperty (ctx, holder, key) }; - JSValue final = JS_Call (ctx, argv[1], holder, 2, args); - result = final; - } - - return result; -} - -static const JSCFunctionListEntry js_cell_json_funcs[] = { - JS_CFUNC_DEF ("encode", 1, js_cell_json_encode), - JS_CFUNC_DEF ("decode", 1, js_cell_json_decode), -}; - -JSValue js_core_json_use (JSContext *ctx) { - JSGCRef obj_ref; - JS_PushGCRef (ctx, &obj_ref); - obj_ref.val = JS_NewObject (ctx); - JS_SetPropertyFunctionList (ctx, obj_ref.val, js_cell_json_funcs, countof (js_cell_json_funcs)); - JSValue result = obj_ref.val; - JS_PopGCRef (ctx, &obj_ref); - return result; -} - -/* ============================================================================ - * Cell Script Module: nota - * Provides nota.encode() and nota.decode() for NOTA binary serialization - * ============================================================================ - */ - -static int nota_get_arr_len (JSContext *ctx, JSValue arr) { - int64_t len; - JS_GetLength (ctx, arr, &len); - return (int)len; -} - -typedef struct NotaVisitedNode { - JSGCRef ref; - struct NotaVisitedNode *next; -} NotaVisitedNode; - -typedef struct NotaEncodeContext { - JSContext *ctx; - NotaVisitedNode *visited_list; - NotaBuffer nb; - int cycle; - JSGCRef *replacer_ref; /* pointer to GC-rooted ref */ -} NotaEncodeContext; - -static void nota_stack_push (NotaEncodeContext *enc, JSValueConst val) { - NotaVisitedNode *node = (NotaVisitedNode *)sys_malloc (sizeof (NotaVisitedNode)); - JS_PushGCRef (enc->ctx, &node->ref); - node->ref.val = JS_DupValue (enc->ctx, val); - node->next = enc->visited_list; - enc->visited_list = node; -} - -static void nota_stack_pop (NotaEncodeContext *enc) { - NotaVisitedNode *node = enc->visited_list; - enc->visited_list = node->next; - JS_FreeValue (enc->ctx, node->ref.val); - JS_PopGCRef (enc->ctx, &node->ref); - sys_free (node); -} - -static int nota_stack_has (NotaEncodeContext *enc, JSValueConst val) { - NotaVisitedNode *node = enc->visited_list; - while (node) { - if (JS_StrictEq (enc->ctx, node->ref.val, val)) - return 1; - node = node->next; - } - return 0; -} - -static JSValue nota_apply_replacer (NotaEncodeContext *enc, JSValueConst holder, JSValueConst key, JSValueConst val) { - if (!enc->replacer_ref || JS_IsNull (enc->replacer_ref->val)) return JS_DupValue (enc->ctx, val); - - JSValue args[2] = { JS_DupValue (enc->ctx, key), JS_DupValue (enc->ctx, val) }; - JSValue result = JS_Call (enc->ctx, enc->replacer_ref->val, holder, 2, args); - JS_FreeValue (enc->ctx, args[0]); - JS_FreeValue (enc->ctx, args[1]); - - if (JS_IsException (result)) return JS_DupValue (enc->ctx, val); - return result; -} - -static char *js_do_nota_decode (JSContext *js, JSValue *tmp, char *nota, JSValue holder, JSValue key, JSValue reviver) { - int type = nota_type (nota); - JSValue ret2; - long long n; - double d; - int b; - char *str; - uint8_t *blob; - - switch (type) { - case NOTA_BLOB: - nota = nota_read_blob (&n, (char **)&blob, nota); - *tmp = js_new_blob_stoned_copy (js, blob, n); - sys_free (blob); - break; - case NOTA_TEXT: - nota = nota_read_text (&str, nota); - *tmp = JS_NewString (js, str); - sys_free (str); - break; - case NOTA_ARR: - nota = nota_read_array (&n, nota); - *tmp = JS_NewArrayLen (js, n); - for (int i = 0; i < n; i++) { - nota = js_do_nota_decode (js, &ret2, nota, *tmp, JS_NewInt32 (js, i), reviver); - JS_SetPropertyNumber (js, *tmp, i, ret2); - } - break; - case NOTA_REC: - nota = nota_read_record (&n, nota); - *tmp = JS_NewObject (js); - for (int i = 0; i < n; i++) { - JSGCRef prop_key_ref, sub_val_ref; - JS_PushGCRef (js, &prop_key_ref); - JS_PushGCRef (js, &sub_val_ref); - nota = nota_read_text (&str, nota); - prop_key_ref.val = JS_NewString (js, str); - sub_val_ref.val = JS_NULL; - nota = js_do_nota_decode (js, &sub_val_ref.val, nota, *tmp, prop_key_ref.val, reviver); - JS_SetPropertyStr (js, *tmp, str, sub_val_ref.val); - JS_PopGCRef (js, &sub_val_ref); - JS_PopGCRef (js, &prop_key_ref); - sys_free (str); - } - break; - case NOTA_INT: - nota = nota_read_int (&n, nota); - *tmp = JS_NewInt64 (js, n); - break; - case NOTA_SYM: - nota = nota_read_sym (&b, nota); - if (b == NOTA_PRIVATE) { - JSGCRef inner_ref, obj_ref2; - JS_PushGCRef (js, &inner_ref); - JS_PushGCRef (js, &obj_ref2); - inner_ref.val = JS_NULL; - nota = js_do_nota_decode (js, &inner_ref.val, nota, holder, JS_NULL, reviver); - obj_ref2.val = JS_NewObject (js); - if (!JS_IsNull (js->actor_sym)) - JS_SetPropertyKey (js, obj_ref2.val, js->actor_sym, inner_ref.val); - JS_CellStone (js, obj_ref2.val); - *tmp = obj_ref2.val; - JS_PopGCRef (js, &obj_ref2); - JS_PopGCRef (js, &inner_ref); - } else { - switch (b) { - case NOTA_NULL: *tmp = JS_NULL; break; - case NOTA_FALSE: *tmp = JS_NewBool (js, 0); break; - case NOTA_TRUE: *tmp = JS_NewBool (js, 1); break; - default: *tmp = JS_NULL; break; - } - } - break; - default: - case NOTA_FLOAT: - nota = nota_read_float (&d, nota); - *tmp = JS_NewFloat64 (js, d); - break; - } - - if (!JS_IsNull (reviver)) { - JSValue args[2] = { JS_DupValue (js, key), JS_DupValue (js, *tmp) }; - JSValue revived = JS_Call (js, reviver, holder, 2, args); - JS_FreeValue (js, args[0]); - JS_FreeValue (js, args[1]); - if (!JS_IsException (revived)) { - JS_FreeValue (js, *tmp); - *tmp = revived; - } else { - JS_FreeValue (js, revived); - } - } - - return nota; -} - -static void nota_encode_value (NotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValueConst key) { - JSContext *ctx = enc->ctx; - JSGCRef replaced_ref, keys_ref, elem_ref, prop_ref; - JS_PushGCRef (ctx, &replaced_ref); - replaced_ref.val = nota_apply_replacer (enc, holder, key, val); - int tag = JS_VALUE_GET_TAG (replaced_ref.val); - - switch (tag) { - case JS_TAG_INT: - case JS_TAG_FLOAT64: { - double d; - JS_ToFloat64 (ctx, &d, replaced_ref.val); - nota_write_number (&enc->nb, d); - break; - } - case JS_TAG_STRING: { - const char *str = JS_ToCString (ctx, replaced_ref.val); - nota_write_text (&enc->nb, str); - JS_FreeCString (ctx, str); - break; - } - case JS_TAG_BOOL: - if (JS_VALUE_GET_BOOL (replaced_ref.val)) nota_write_sym (&enc->nb, NOTA_TRUE); - else nota_write_sym (&enc->nb, NOTA_FALSE); - break; - case JS_TAG_NULL: - nota_write_sym (&enc->nb, NOTA_NULL); - break; - case JS_TAG_PTR: { - if (JS_IsText (replaced_ref.val)) { - const char *str = JS_ToCString (ctx, replaced_ref.val); - nota_write_text (&enc->nb, str); - JS_FreeCString (ctx, str); - break; - } - - if (js_is_blob (ctx, replaced_ref.val)) { - size_t buf_len; - void *buf_data = js_get_blob_data (ctx, &buf_len, replaced_ref.val); - if (buf_data == (void *)-1) { - JS_PopGCRef (ctx, &replaced_ref); - return; - } - nota_write_blob (&enc->nb, (unsigned long long)buf_len * 8, (const char *)buf_data); - break; - } - - if (JS_IsArray (replaced_ref.val)) { - if (nota_stack_has (enc, replaced_ref.val)) { - enc->cycle = 1; - break; - } - nota_stack_push (enc, replaced_ref.val); - int arr_len = nota_get_arr_len (ctx, replaced_ref.val); - nota_write_array (&enc->nb, arr_len); - JS_PushGCRef (ctx, &elem_ref); - for (int i = 0; i < arr_len; i++) { - elem_ref.val = JS_GetPropertyNumber (ctx, replaced_ref.val, i); - JSValue elem_key = JS_NewInt32 (ctx, i); - nota_encode_value (enc, elem_ref.val, replaced_ref.val, elem_key); - } - JS_PopGCRef (ctx, &elem_ref); - nota_stack_pop (enc); - break; - } - - JSValue adata = JS_NULL; - if (!JS_IsNull (ctx->actor_sym)) { - int has = JS_HasPropertyKey (ctx, replaced_ref.val, ctx->actor_sym); - if (has > 0) adata = JS_GetPropertyKey (ctx, replaced_ref.val, ctx->actor_sym); - } - if (!JS_IsNull (adata)) { - nota_write_sym (&enc->nb, NOTA_PRIVATE); - nota_encode_value (enc, adata, replaced_ref.val, JS_NULL); - JS_FreeValue (ctx, adata); - break; - } - JS_FreeValue (ctx, adata); - if (nota_stack_has (enc, replaced_ref.val)) { - enc->cycle = 1; - break; - } - nota_stack_push (enc, replaced_ref.val); - - JSValue to_json = JS_GetPropertyStr (ctx, replaced_ref.val, "toJSON"); - if (JS_IsFunction (to_json)) { - JSValue result = JS_Call (ctx, to_json, replaced_ref.val, 0, NULL); - if (!JS_IsException (result)) { - nota_encode_value (enc, result, holder, key); - } else { - nota_write_sym (&enc->nb, NOTA_NULL); - } - nota_stack_pop (enc); - break; - } - - JS_PushGCRef (ctx, &keys_ref); - keys_ref.val = JS_GetOwnPropertyNames (ctx, replaced_ref.val); - if (JS_IsException (keys_ref.val)) { - nota_write_sym (&enc->nb, NOTA_NULL); - nota_stack_pop (enc); - JS_PopGCRef (ctx, &keys_ref); - break; - } - int64_t plen64; - if (JS_GetLength (ctx, keys_ref.val, &plen64) < 0) { - nota_write_sym (&enc->nb, NOTA_NULL); - nota_stack_pop (enc); - JS_PopGCRef (ctx, &keys_ref); - break; - } - uint32_t plen = (uint32_t)plen64; - - JS_PushGCRef (ctx, &prop_ref); - JS_PushGCRef (ctx, &elem_ref); - uint32_t non_function_count = 0; - for (uint32_t i = 0; i < plen; i++) { - elem_ref.val = JS_GetPropertyNumber (ctx, keys_ref.val, i); - prop_ref.val = JS_GetProperty (ctx, replaced_ref.val, elem_ref.val); - if (!JS_IsFunction (prop_ref.val)) non_function_count++; - } - - nota_write_record (&enc->nb, non_function_count); - for (uint32_t i = 0; i < plen; i++) { - elem_ref.val = JS_GetPropertyNumber (ctx, keys_ref.val, i); - prop_ref.val = JS_GetProperty (ctx, replaced_ref.val, elem_ref.val); - if (!JS_IsFunction (prop_ref.val)) { - const char *prop_name = JS_ToCString (ctx, elem_ref.val); - nota_write_text (&enc->nb, prop_name ? prop_name : ""); - nota_encode_value (enc, prop_ref.val, replaced_ref.val, elem_ref.val); - JS_FreeCString (ctx, prop_name); - } - } - JS_PopGCRef (ctx, &elem_ref); - JS_PopGCRef (ctx, &prop_ref); - JS_PopGCRef (ctx, &keys_ref); - nota_stack_pop (enc); - break; - } - default: - nota_write_sym (&enc->nb, NOTA_NULL); - break; - } - JS_PopGCRef (ctx, &replaced_ref); -} - -void *value2nota (JSContext *ctx, JSValue v) { - JSGCRef val_ref, key_ref; - JS_PushGCRef (ctx, &val_ref); - JS_PushGCRef (ctx, &key_ref); - val_ref.val = v; - - NotaEncodeContext enc_s, *enc = &enc_s; - enc->ctx = ctx; - enc->visited_list = NULL; - enc->cycle = 0; - enc->replacer_ref = NULL; - - nota_buffer_init (&enc->nb, 128); - key_ref.val = JS_NewString (ctx, ""); - nota_encode_value (enc, val_ref.val, JS_NULL, key_ref.val); - - if (enc->cycle) { - JS_PopGCRef (ctx, &key_ref); - JS_PopGCRef (ctx, &val_ref); - nota_buffer_free (&enc->nb); - return NULL; - } - - JS_PopGCRef (ctx, &key_ref); - JS_PopGCRef (ctx, &val_ref); - void *data_ptr = enc->nb.data; - enc->nb.data = NULL; - nota_buffer_free (&enc->nb); - return data_ptr; -} - -JSValue nota2value (JSContext *js, void *nota) { - if (!nota) return JS_NULL; - JSGCRef holder_ref, key_ref, ret_ref; - JS_PushGCRef (js, &holder_ref); - JS_PushGCRef (js, &key_ref); - JS_PushGCRef (js, &ret_ref); - holder_ref.val = JS_NewObject (js); - key_ref.val = JS_NewString (js, ""); - ret_ref.val = JS_NULL; - js_do_nota_decode (js, &ret_ref.val, nota, holder_ref.val, key_ref.val, JS_NULL); - JSValue result = ret_ref.val; - JS_PopGCRef (js, &ret_ref); - JS_PopGCRef (js, &key_ref); - JS_PopGCRef (js, &holder_ref); - return result; -} - -static JSValue js_nota_encode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - if (argc < 1) return JS_RaiseDisrupt (ctx, "nota.encode requires at least 1 argument"); - - JSGCRef val_ref, replacer_ref, key_ref; - JS_PushGCRef (ctx, &val_ref); - JS_PushGCRef (ctx, &replacer_ref); - JS_PushGCRef (ctx, &key_ref); - val_ref.val = argv[0]; - replacer_ref.val = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL; - - NotaEncodeContext enc_s, *enc = &enc_s; - enc->ctx = ctx; - enc->visited_list = NULL; - enc->cycle = 0; - enc->replacer_ref = &replacer_ref; - - nota_buffer_init (&enc->nb, 128); - key_ref.val = JS_NewString (ctx, ""); - nota_encode_value (enc, val_ref.val, JS_NULL, key_ref.val); - - JSValue ret; - if (enc->cycle) { - nota_buffer_free (&enc->nb); - ret = JS_RaiseDisrupt (ctx, "Tried to encode something to nota with a cycle."); - } else { - size_t total_len = enc->nb.size; - void *data_ptr = enc->nb.data; - ret = js_new_blob_stoned_copy (ctx, (uint8_t *)data_ptr, total_len); - nota_buffer_free (&enc->nb); - } - - JS_PopGCRef (ctx, &key_ref); - JS_PopGCRef (ctx, &replacer_ref); - JS_PopGCRef (ctx, &val_ref); - return ret; -} - -static JSValue js_nota_decode (JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - if (argc < 1) return JS_NULL; - - size_t len; - unsigned char *nota = js_get_blob_data (js, &len, argv[0]); - if (nota == (unsigned char *)-1) return JS_EXCEPTION; - if (!nota) return JS_NULL; - - JSGCRef holder_ref, key_ref, ret_ref, reviver_ref; - JS_PushGCRef (js, &holder_ref); - JS_PushGCRef (js, &key_ref); - JS_PushGCRef (js, &ret_ref); - JS_PushGCRef (js, &reviver_ref); - - reviver_ref.val = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL; - holder_ref.val = JS_NewObject (js); - key_ref.val = JS_NewString (js, ""); - ret_ref.val = JS_NULL; - - js_do_nota_decode (js, &ret_ref.val, (char *)nota, holder_ref.val, key_ref.val, reviver_ref.val); - - JSValue result = ret_ref.val; - JS_PopGCRef (js, &reviver_ref); - JS_PopGCRef (js, &ret_ref); - JS_PopGCRef (js, &key_ref); - JS_PopGCRef (js, &holder_ref); - return result; -} - -static const JSCFunctionListEntry js_nota_funcs[] = { - JS_CFUNC_DEF ("encode", 1, js_nota_encode), - JS_CFUNC_DEF ("decode", 1, js_nota_decode), -}; - -JSValue js_core_nota_use (JSContext *js) { - JSGCRef export_ref; - JS_PushGCRef (js, &export_ref); - export_ref.val = JS_NewObject (js); - JS_SetPropertyFunctionList (js, export_ref.val, js_nota_funcs, sizeof (js_nota_funcs) / sizeof (JSCFunctionListEntry)); - JSValue result = export_ref.val; - JS_PopGCRef (js, &export_ref); - return result; -} - -/* ============================================================================ - * Cell Script Module: wota - * Provides wota.encode() and wota.decode() for WOTA binary serialization - * ============================================================================ - */ - -typedef struct ObjectRef { - void *ptr; - struct ObjectRef *next; -} ObjectRef; - -typedef struct WotaEncodeContext { - JSContext *ctx; - ObjectRef *visited_stack; - WotaBuffer wb; - int cycle; - JSValue replacer; -} WotaEncodeContext; - -static void wota_stack_push (WotaEncodeContext *enc, JSValueConst val) { - (void)enc; (void)val; - /* Cycle detection disabled for performance */ -} - -static void wota_stack_pop (WotaEncodeContext *enc) { - if (!enc->visited_stack) return; - - ObjectRef *top = enc->visited_stack; - enc->visited_stack = top->next; - sys_free (top); -} - -static int wota_stack_has (WotaEncodeContext *enc, JSValueConst val) { - (void)enc; (void)val; - return 0; - /* Cycle detection disabled for performance */ -} - -static void wota_stack_free (WotaEncodeContext *enc) { - while (enc->visited_stack) { - wota_stack_pop (enc); - } -} - -static JSValue wota_apply_replacer (WotaEncodeContext *enc, JSValueConst holder, JSValue key, JSValueConst val) { - if (JS_IsNull (enc->replacer)) return JS_DupValue (enc->ctx, val); - JSValue key_val = JS_IsNull (key) ? JS_NULL : JS_DupValue (enc->ctx, key); - JSValue args[2] = { key_val, JS_DupValue (enc->ctx, val) }; - JSValue result = JS_Call (enc->ctx, enc->replacer, holder, 2, args); - JS_FreeValue (enc->ctx, args[0]); - JS_FreeValue (enc->ctx, args[1]); - if (JS_IsException (result)) return JS_DupValue (enc->ctx, val); - return result; -} - -static void wota_encode_value (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValue key); - -static void encode_object_properties (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder) { - JSContext *ctx = enc->ctx; - - /* Root the input value to protect it during property enumeration */ - JSGCRef val_ref, keys_ref; - JS_PushGCRef (ctx, &val_ref); - JS_PushGCRef (ctx, &keys_ref); - val_ref.val = JS_DupValue (ctx, val); - - keys_ref.val = JS_GetOwnPropertyNames (ctx, val_ref.val); - if (JS_IsException (keys_ref.val)) { - wota_write_sym (&enc->wb, WOTA_NULL); - JS_FreeValue (ctx, val_ref.val); - JS_PopGCRef (ctx, &keys_ref); - JS_PopGCRef (ctx, &val_ref); - return; - } - int64_t plen64; - if (JS_GetLength (ctx, keys_ref.val, &plen64) < 0) { - JS_FreeValue (ctx, keys_ref.val); - JS_FreeValue (ctx, val_ref.val); - wota_write_sym (&enc->wb, WOTA_NULL); - JS_PopGCRef (ctx, &keys_ref); - JS_PopGCRef (ctx, &val_ref); - return; - } - uint32_t plen = (uint32_t)plen64; - uint32_t non_function_count = 0; - - /* Allocate GC-rooted arrays for props and keys */ - JSGCRef *prop_refs = sys_malloc (sizeof (JSGCRef) * plen); - JSGCRef *key_refs = sys_malloc (sizeof (JSGCRef) * plen); - for (uint32_t i = 0; i < plen; i++) { - JS_PushGCRef (ctx, &prop_refs[i]); - JS_PushGCRef (ctx, &key_refs[i]); - prop_refs[i].val = JS_NULL; - key_refs[i].val = JS_NULL; - } - - for (uint32_t i = 0; i < plen; i++) { - /* Store key into its GCRef slot immediately so it's rooted before - JS_GetProperty can trigger GC and relocate the string. */ - key_refs[i].val = JS_GetPropertyNumber (ctx, keys_ref.val, i); - JSValue prop_val = JS_GetProperty (ctx, val_ref.val, key_refs[i].val); - if (!JS_IsFunction (prop_val)) { - if (i != non_function_count) { - key_refs[non_function_count].val = key_refs[i].val; - key_refs[i].val = JS_NULL; - } - prop_refs[non_function_count].val = prop_val; - non_function_count++; - } else { - JS_FreeValue (ctx, prop_val); - JS_FreeValue (ctx, key_refs[i].val); - key_refs[i].val = JS_NULL; - } - } - JS_FreeValue (ctx, keys_ref.val); - wota_write_record (&enc->wb, non_function_count); - for (uint32_t i = 0; i < non_function_count; i++) { - size_t klen; - const char *prop_name = JS_ToCStringLen (ctx, &klen, key_refs[i].val); - wota_write_text_len (&enc->wb, prop_name ? prop_name : "", prop_name ? klen : 0); - wota_encode_value (enc, prop_refs[i].val, val_ref.val, key_refs[i].val); - JS_FreeCString (ctx, prop_name); - JS_FreeValue (ctx, prop_refs[i].val); - JS_FreeValue (ctx, key_refs[i].val); - } - /* Pop all GC refs in reverse order */ - for (int i = plen - 1; i >= 0; i--) { - JS_PopGCRef (ctx, &key_refs[i]); - JS_PopGCRef (ctx, &prop_refs[i]); - } - sys_free (prop_refs); - sys_free (key_refs); - JS_FreeValue (ctx, val_ref.val); - JS_PopGCRef (ctx, &keys_ref); - JS_PopGCRef (ctx, &val_ref); -} - -static void wota_encode_value (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValue key) { - JSContext *ctx = enc->ctx; - JSValue replaced; - if (!JS_IsNull (enc->replacer) && !JS_IsNull (key)) - replaced = wota_apply_replacer (enc, holder, key, val); - else - replaced = JS_DupValue (enc->ctx, val); - - int tag = JS_VALUE_GET_TAG (replaced); - switch (tag) { - case JS_TAG_INT: { - int32_t d; - JS_ToInt32 (ctx, &d, replaced); - wota_write_int_word (&enc->wb, d); - break; - } - case JS_TAG_FLOAT64: { - double d; - if (JS_ToFloat64 (ctx, &d, replaced) < 0) { - wota_write_sym (&enc->wb, WOTA_NULL); - break; - } - wota_write_float_word (&enc->wb, d); - break; - } - case JS_TAG_STRING: { - size_t plen; - const char *str = JS_ToCStringLen (ctx, &plen, replaced); - wota_write_text_len (&enc->wb, str ? str : "", str ? plen : 0); - JS_FreeCString (ctx, str); - break; - } - case JS_TAG_BOOL: - wota_write_sym (&enc->wb, JS_VALUE_GET_BOOL (replaced) ? WOTA_TRUE : WOTA_FALSE); - break; - case JS_TAG_NULL: - wota_write_sym (&enc->wb, WOTA_NULL); - break; - case JS_TAG_PTR: { - if (JS_IsText (replaced)) { - size_t plen; - const char *str = JS_ToCStringLen (ctx, &plen, replaced); - wota_write_text_len (&enc->wb, str ? str : "", str ? plen : 0); - JS_FreeCString (ctx, str); - break; - } - if (js_is_blob (ctx, replaced)) { - size_t buf_len; - void *buf_data = js_get_blob_data (ctx, &buf_len, replaced); - if (buf_data == (void *)-1) { - JS_FreeValue (ctx, replaced); - return; - } - if (buf_len == 0) { - wota_write_blob (&enc->wb, 0, ""); - } else { - wota_write_blob (&enc->wb, (unsigned long long)buf_len * 8, (const char *)buf_data); - } - break; - } - if (JS_IsArray (replaced)) { - if (wota_stack_has (enc, replaced)) { - enc->cycle = 1; - break; - } - wota_stack_push (enc, replaced); - int64_t arr_len; - JS_GetLength (ctx, replaced, &arr_len); - wota_write_array (&enc->wb, arr_len); - for (int64_t i = 0; i < arr_len; i++) { - JSValue elem_val = JS_GetPropertyNumber (ctx, replaced, i); - wota_encode_value (enc, elem_val, replaced, JS_NewInt32 (ctx, (int32_t)i)); - JS_FreeValue (ctx, elem_val); - } - wota_stack_pop (enc); - break; - } - JSValue adata = JS_NULL; - if (!JS_IsNull (ctx->actor_sym)) { - int has = JS_HasPropertyKey (ctx, replaced, ctx->actor_sym); - if (has > 0) adata = JS_GetPropertyKey (ctx, replaced, ctx->actor_sym); - } - if (!JS_IsNull (adata)) { - wota_write_sym (&enc->wb, WOTA_PRIVATE); - wota_encode_value (enc, adata, replaced, JS_NULL); - JS_FreeValue (ctx, adata); - break; - } - JS_FreeValue (ctx, adata); - if (wota_stack_has (enc, replaced)) { - enc->cycle = 1; - break; - } - wota_stack_push (enc, replaced); - JSValue to_json = JS_GetPropertyStr (ctx, replaced, "toJSON"); - if (JS_IsFunction (to_json)) { - JSValue result = JS_Call (ctx, to_json, replaced, 0, NULL); - JS_FreeValue (ctx, to_json); - if (!JS_IsException (result)) { - wota_encode_value (enc, result, holder, key); - JS_FreeValue (ctx, result); - } else - wota_write_sym (&enc->wb, WOTA_NULL); - wota_stack_pop (enc); - break; - } - JS_FreeValue (ctx, to_json); - encode_object_properties (enc, replaced, holder); - wota_stack_pop (enc); - break; - } - default: - wota_write_sym (&enc->wb, WOTA_NULL); - break; - } - JS_FreeValue (ctx, replaced); -} - -static char *decode_wota_value (JSContext *ctx, char *data_ptr, JSValue *out_val, JSValue holder, JSValue key, JSValue reviver) { - uint64_t first_word = *(uint64_t *)data_ptr; - int type = (int)(first_word & 0xffU); - switch (type) { - case WOTA_INT: { - long long val; - data_ptr = wota_read_int (&val, data_ptr); - *out_val = JS_NewInt64 (ctx, val); - break; - } - case WOTA_FLOAT: { - double d; - data_ptr = wota_read_float (&d, data_ptr); - *out_val = JS_NewFloat64 (ctx, d); - break; - } - case WOTA_SYM: { - int scode; - data_ptr = wota_read_sym (&scode, data_ptr); - if (scode == WOTA_PRIVATE) { - JSGCRef inner_ref, obj_ref2; - JS_PushGCRef (ctx, &inner_ref); - JS_PushGCRef (ctx, &obj_ref2); - inner_ref.val = JS_NULL; - data_ptr = decode_wota_value (ctx, data_ptr, &inner_ref.val, holder, JS_NULL, reviver); - obj_ref2.val = JS_NewObject (ctx); - if (!JS_IsNull (ctx->actor_sym)) - JS_SetPropertyKey (ctx, obj_ref2.val, ctx->actor_sym, inner_ref.val); - JS_CellStone (ctx, obj_ref2.val); - *out_val = obj_ref2.val; - JS_PopGCRef (ctx, &obj_ref2); - JS_PopGCRef (ctx, &inner_ref); - } else if (scode == WOTA_NULL) *out_val = JS_NULL; - else if (scode == WOTA_FALSE) *out_val = JS_NewBool (ctx, 0); - else if (scode == WOTA_TRUE) *out_val = JS_NewBool (ctx, 1); - else *out_val = JS_NULL; - break; - } - case WOTA_BLOB: { - long long blen; - char *bdata = NULL; - data_ptr = wota_read_blob (&blen, &bdata, data_ptr); - *out_val = bdata ? js_new_blob_stoned_copy (ctx, (uint8_t *)bdata, (size_t)blen) : js_new_blob_stoned_copy (ctx, NULL, 0); - if (bdata) sys_free (bdata); - break; - } - case WOTA_TEXT: { - char *utf8 = NULL; - data_ptr = wota_read_text (&utf8, data_ptr); - *out_val = JS_NewString (ctx, utf8 ? utf8 : ""); - if (utf8) sys_free (utf8); - break; - } - case WOTA_ARR: { - long long c; - data_ptr = wota_read_array (&c, data_ptr); - JSGCRef arr_ref; - JS_PushGCRef (ctx, &arr_ref); - arr_ref.val = JS_NewArrayLen (ctx, c); - for (long long i = 0; i < c; i++) { - JSGCRef elem_ref; - JS_PushGCRef (ctx, &elem_ref); - elem_ref.val = JS_NULL; - JSValue idx_key = JS_NewInt32 (ctx, (int32_t)i); - data_ptr = decode_wota_value (ctx, data_ptr, &elem_ref.val, arr_ref.val, idx_key, reviver); - JS_SetPropertyNumber (ctx, arr_ref.val, i, elem_ref.val); - JS_PopGCRef (ctx, &elem_ref); - } - *out_val = arr_ref.val; - JS_PopGCRef (ctx, &arr_ref); - break; - } - case WOTA_REC: { - long long c; - data_ptr = wota_read_record (&c, data_ptr); - JSGCRef obj_ref; - JS_PushGCRef (ctx, &obj_ref); - obj_ref.val = JS_NewObject (ctx); - for (long long i = 0; i < c; i++) { - char *tkey = NULL; - size_t key_len; - data_ptr = wota_read_text_len (&key_len, &tkey, data_ptr); - if (!tkey) continue; - JSGCRef prop_key_ref, sub_val_ref; - JS_PushGCRef (ctx, &prop_key_ref); - JS_PushGCRef (ctx, &sub_val_ref); - prop_key_ref.val = JS_NewStringLen (ctx, tkey, key_len); - sub_val_ref.val = JS_NULL; - data_ptr = decode_wota_value (ctx, data_ptr, &sub_val_ref.val, obj_ref.val, prop_key_ref.val, reviver); - JS_SetPropertyStr (ctx, obj_ref.val, tkey, sub_val_ref.val); - JS_PopGCRef (ctx, &sub_val_ref); - JS_PopGCRef (ctx, &prop_key_ref); - sys_free (tkey); - } - *out_val = obj_ref.val; - JS_PopGCRef (ctx, &obj_ref); - break; - } - default: - data_ptr += 8; - *out_val = JS_NULL; - break; - } - if (!JS_IsNull (reviver)) { - JSValue key_val = JS_IsNull (key) ? JS_NULL : JS_DupValue (ctx, key); - JSValue args[2] = { key_val, JS_DupValue (ctx, *out_val) }; - JSValue revived = JS_Call (ctx, reviver, holder, 2, args); - JS_FreeValue (ctx, args[0]); - JS_FreeValue (ctx, args[1]); - if (!JS_IsException (revived)) { - JS_FreeValue (ctx, *out_val); - *out_val = revived; - } else - JS_FreeValue (ctx, revived); - } - return data_ptr; -} - -void *value2wota (JSContext *ctx, JSValue v, JSValue replacer, size_t *bytes) { - JSGCRef val_ref, rep_ref; - JS_PushGCRef (ctx, &val_ref); - JS_PushGCRef (ctx, &rep_ref); - val_ref.val = v; - rep_ref.val = replacer; - - WotaEncodeContext enc_s, *enc = &enc_s; - - enc->ctx = ctx; - enc->visited_stack = NULL; - enc->cycle = 0; - enc->replacer = rep_ref.val; - wota_buffer_init (&enc->wb, 16); - wota_encode_value (enc, val_ref.val, JS_NULL, JS_NULL); - if (enc->cycle) { - wota_stack_free (enc); - wota_buffer_free (&enc->wb); - JS_PopGCRef (ctx, &rep_ref); - JS_PopGCRef (ctx, &val_ref); - return NULL; - } - wota_stack_free (enc); - size_t total_bytes = enc->wb.size * sizeof (uint64_t); - void *wota = sys_realloc (enc->wb.data, total_bytes); - if (bytes) *bytes = total_bytes; - JS_PopGCRef (ctx, &rep_ref); - JS_PopGCRef (ctx, &val_ref); - return wota; -} - -JSValue wota2value (JSContext *ctx, void *wota) { - JSGCRef holder_ref, result_ref; - JS_PushGCRef (ctx, &holder_ref); - JS_PushGCRef (ctx, &result_ref); - result_ref.val = JS_NULL; - holder_ref.val = JS_NewObject (ctx); - decode_wota_value (ctx, wota, &result_ref.val, holder_ref.val, JS_NULL, JS_NULL); - JSValue result = result_ref.val; - JS_PopGCRef (ctx, &result_ref); - JS_PopGCRef (ctx, &holder_ref); - return result; -} - -static JSValue js_wota_encode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - if (argc < 1) return JS_RaiseDisrupt (ctx, "wota.encode requires at least 1 argument"); - size_t total_bytes; - void *wota = value2wota (ctx, argv[0], JS_IsFunction (argv[1]) ? argv[1] : JS_NULL, &total_bytes); - JSValue ret = js_new_blob_stoned_copy (ctx, wota, total_bytes); - sys_free (wota); - return ret; -} - -static JSValue js_wota_decode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - if (argc < 1) return JS_NULL; - size_t len; - uint8_t *buf = js_get_blob_data (ctx, &len, argv[0]); - if (buf == (uint8_t *)-1) return JS_EXCEPTION; - if (!buf || len == 0) return JS_RaiseDisrupt (ctx, "No blob data present"); - JSValue reviver = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL; - char *data_ptr = (char *)buf; - JSGCRef result_ref, holder_ref, empty_key_ref; - JS_PushGCRef (ctx, &result_ref); - JS_PushGCRef (ctx, &holder_ref); - JS_PushGCRef (ctx, &empty_key_ref); - result_ref.val = JS_NULL; - holder_ref.val = JS_NewObject (ctx); - empty_key_ref.val = JS_NewString (ctx, ""); - decode_wota_value (ctx, data_ptr, &result_ref.val, holder_ref.val, empty_key_ref.val, reviver); - JSValue result = result_ref.val; - JS_PopGCRef (ctx, &empty_key_ref); - JS_PopGCRef (ctx, &holder_ref); - JS_PopGCRef (ctx, &result_ref); - return result; -} - -static const JSCFunctionListEntry js_wota_funcs[] = { - JS_CFUNC_DEF ("encode", 2, js_wota_encode), - JS_CFUNC_DEF ("decode", 2, js_wota_decode), -}; - -JSValue js_core_wota_use (JSContext *ctx) { - JSGCRef exports_ref; - JS_PushGCRef (ctx, &exports_ref); - exports_ref.val = JS_NewObject (ctx); - JS_SetPropertyFunctionList (ctx, exports_ref.val, js_wota_funcs, sizeof (js_wota_funcs) / sizeof (js_wota_funcs[0])); - JSValue result = exports_ref.val; - JS_PopGCRef (ctx, &exports_ref); - return result; -} - -static JSValue js_math_e (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double power = 1.0; - if (argc > 0 && !JS_IsNull (argv[0])) { - if (JS_ToFloat64 (ctx, &power, argv[0]) < 0) return JS_EXCEPTION; - } - return JS_NewFloat64 (ctx, exp (power)); -} - -/* ============================================================================ - * Cell Script Module: math/radians - * Provides trigonometric and math functions using radians - * ============================================================================ - */ - -static JSValue js_math_rad_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, acos (x)); -} - -static JSValue js_math_rad_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, asin (x)); -} - -static JSValue js_math_rad_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, atan (x)); -} - -static JSValue js_math_rad_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, cos (x)); -} - -static JSValue js_math_rad_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, sin (x)); -} - -static JSValue js_math_rad_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, tan (x)); -} - -static JSValue js_math_ln (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, log (x)); -} - -static JSValue js_math_log10 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, log10 (x)); -} - -static JSValue js_math_log2 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, log2 (x)); -} - -static JSValue js_math_power (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x, y; - if (argc < 2) return JS_NULL; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - if (JS_ToFloat64 (ctx, &y, argv[1]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, pow (x, y)); -} - -static JSValue js_math_root (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x, n; - if (argc < 2) return JS_NULL; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - if (JS_ToFloat64 (ctx, &n, argv[1]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, pow (x, 1.0 / n)); -} - -static JSValue js_math_sqrt (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, sqrt (x)); -} - -static const JSCFunctionListEntry js_math_radians_funcs[] - = { JS_CFUNC_DEF ("arc_cosine", 1, js_math_rad_arc_cosine), - JS_CFUNC_DEF ("arc_sine", 1, js_math_rad_arc_sine), - JS_CFUNC_DEF ("arc_tangent", 1, js_math_rad_arc_tangent), - JS_CFUNC_DEF ("cosine", 1, js_math_rad_cosine), - JS_CFUNC_DEF ("sine", 1, js_math_rad_sine), - JS_CFUNC_DEF ("tangent", 1, js_math_rad_tangent), - JS_CFUNC_DEF ("ln", 1, js_math_ln), - JS_CFUNC_DEF ("log", 1, js_math_log10), - JS_CFUNC_DEF ("log2", 1, js_math_log2), - JS_CFUNC_DEF ("power", 2, js_math_power), - JS_CFUNC_DEF ("root", 2, js_math_root), - JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt), - JS_CFUNC_DEF ("e", 1, js_math_e) }; - -JSValue js_core_math_radians_use (JSContext *ctx) { - JSGCRef obj_ref; - JS_PushGCRef (ctx, &obj_ref); - obj_ref.val = JS_NewObject (ctx); - JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_radians_funcs, countof (js_math_radians_funcs)); - JSValue result = obj_ref.val; - JS_PopGCRef (ctx, &obj_ref); - return result; -} - -/* ============================================================================ - * Cell Script Module: math/degrees - * Provides trigonometric and math functions using degrees - * ============================================================================ - */ - -#define DEG2RAD (3.14159265358979323846264338327950288419716939937510 / 180.0) -#define RAD2DEG (180.0 / 3.14159265358979323846264338327950288419716939937510) - -static JSValue js_math_deg_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, acos (x) * RAD2DEG); -} - -static JSValue js_math_deg_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, asin (x) * RAD2DEG); -} - -static JSValue js_math_deg_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, atan (x) * RAD2DEG); -} - -static JSValue js_math_deg_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, cos (x * DEG2RAD)); -} - -static JSValue js_math_deg_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, sin (x * DEG2RAD)); -} - -static JSValue js_math_deg_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, tan (x * DEG2RAD)); -} - -static const JSCFunctionListEntry js_math_degrees_funcs[] - = { JS_CFUNC_DEF ("arc_cosine", 1, js_math_deg_arc_cosine), - JS_CFUNC_DEF ("arc_sine", 1, js_math_deg_arc_sine), - JS_CFUNC_DEF ("arc_tangent", 1, js_math_deg_arc_tangent), - JS_CFUNC_DEF ("cosine", 1, js_math_deg_cosine), - JS_CFUNC_DEF ("sine", 1, js_math_deg_sine), - JS_CFUNC_DEF ("tangent", 1, js_math_deg_tangent), - JS_CFUNC_DEF ("ln", 1, js_math_ln), - JS_CFUNC_DEF ("log", 1, js_math_log10), - JS_CFUNC_DEF ("log2", 1, js_math_log2), - JS_CFUNC_DEF ("power", 2, js_math_power), - JS_CFUNC_DEF ("root", 2, js_math_root), - JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt), - JS_CFUNC_DEF ("e", 1, js_math_e) }; - -JSValue js_core_math_degrees_use (JSContext *ctx) { - JSGCRef obj_ref; - JS_PushGCRef (ctx, &obj_ref); - obj_ref.val = JS_NewObject (ctx); - JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_degrees_funcs, countof (js_math_degrees_funcs)); - JSValue result = obj_ref.val; - JS_PopGCRef (ctx, &obj_ref); - return result; -} - -/* ============================================================================ - * Cell Script Module: math/cycles - * Provides trigonometric and math functions using cycles (0-1 = full rotation) - * ============================================================================ - */ - -#define TWOPI (2.0 * 3.14159265358979323846264338327950288419716939937510) - -static JSValue js_math_cyc_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, acos (x) / TWOPI); -} - -static JSValue js_math_cyc_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, asin (x) / TWOPI); -} - -static JSValue js_math_cyc_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, atan (x) / TWOPI); -} - -static JSValue js_math_cyc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, cos (x * TWOPI)); -} - -static JSValue js_math_cyc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, sin (x * TWOPI)); -} - -static JSValue js_math_cyc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { - double x; - if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; - return JS_NewFloat64 (ctx, tan (x * TWOPI)); -} - -static const JSCFunctionListEntry js_math_cycles_funcs[] - = { JS_CFUNC_DEF ("arc_cosine", 1, js_math_cyc_arc_cosine), - JS_CFUNC_DEF ("arc_sine", 1, js_math_cyc_arc_sine), - JS_CFUNC_DEF ("arc_tangent", 1, js_math_cyc_arc_tangent), - JS_CFUNC_DEF ("cosine", 1, js_math_cyc_cosine), - JS_CFUNC_DEF ("sine", 1, js_math_cyc_sine), - JS_CFUNC_DEF ("tangent", 1, js_math_cyc_tangent), - JS_CFUNC_DEF ("ln", 1, js_math_ln), - JS_CFUNC_DEF ("log", 1, js_math_log10), - JS_CFUNC_DEF ("log2", 1, js_math_log2), - JS_CFUNC_DEF ("power", 2, js_math_power), - JS_CFUNC_DEF ("root", 2, js_math_root), - JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt), - JS_CFUNC_DEF ("e", 1, js_math_e) }; - -JSValue js_core_math_cycles_use (JSContext *ctx) { - JSGCRef obj_ref; - JS_PushGCRef (ctx, &obj_ref); - obj_ref.val = JS_NewObject (ctx); - JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_cycles_funcs, countof (js_math_cycles_funcs)); - JSValue result = obj_ref.val; - JS_PopGCRef (ctx, &obj_ref); - return result; -} /* Public API: get stack trace as JS array of {fn, file, line, col} objects. Does NOT clear reg_current_frame — caller is responsible if needed. Two-phase approach for GC safety: Phase 1 collects raw C data on the C stack diff --git a/src/cell_math.c b/src/cell_math.c new file mode 100644 index 00000000..59c79ef8 --- /dev/null +++ b/src/cell_math.c @@ -0,0 +1,50 @@ +#include "cell_math.h" +#include + +JSValue js_math_e (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double power = 1.0; + if (argc > 0 && !JS_IsNull (argv[0])) { + if (JS_ToFloat64 (ctx, &power, argv[0]) < 0) return JS_EXCEPTION; + } + return JS_NewFloat64 (ctx, exp (power)); +} + +JSValue js_math_ln (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, log (x)); +} + +JSValue js_math_log10 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, log10 (x)); +} + +JSValue js_math_log2 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, log2 (x)); +} + +JSValue js_math_power (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x, y; + if (argc < 2) return JS_NULL; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + if (JS_ToFloat64 (ctx, &y, argv[1]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, pow (x, y)); +} + +JSValue js_math_root (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x, n; + if (argc < 2) return JS_NULL; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + if (JS_ToFloat64 (ctx, &n, argv[1]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, pow (x, 1.0 / n)); +} + +JSValue js_math_sqrt (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + double x; + if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION; + return JS_NewFloat64 (ctx, sqrt (x)); +} diff --git a/src/cell_math.h b/src/cell_math.h new file mode 100644 index 00000000..acd6970d --- /dev/null +++ b/src/cell_math.h @@ -0,0 +1,14 @@ +#ifndef CELL_MATH_H +#define CELL_MATH_H + +#include "cell.h" + +JSValue js_math_e (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); +JSValue js_math_ln (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); +JSValue js_math_log10 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); +JSValue js_math_log2 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); +JSValue js_math_power (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); +JSValue js_math_root (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); +JSValue js_math_sqrt (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); + +#endif diff --git a/fash.c b/src/fash.c similarity index 100% rename from fash.c rename to src/fash.c diff --git a/qbe_rt.c b/src/qbe_rt.c similarity index 100% rename from qbe_rt.c rename to src/qbe_rt.c diff --git a/streamline.cm b/streamline.cm index a53f1106..97d28baa 100644 --- a/streamline.cm +++ b/streamline.cm @@ -1949,7 +1949,7 @@ var streamline = function(ir, log) { known = cur_types[callee_slot] if (known == T_NULL) { emit("error", line, col, "invoking null — will always disrupt") - } else if (known != null && known != T_UNKNOWN && known != T_FUNCTION) { + } else if (known != null && known != T_UNKNOWN && known != T_FUNCTION && known != T_RECORD) { emit("error", line, col, `invoking ${known} — will always disrupt`) } } diff --git a/test.ce b/test.ce index 16112456..6cfe1a84 100644 --- a/test.ce +++ b/test.ce @@ -19,7 +19,7 @@ var gc_after_each_test = false var verify_ir = false var diff_mode = false -var os_ref = use('os') +var os_ref = use('internal/os') var analyze = os_ref.analyze var run_ast_fn = os_ref.run_ast_fn var run_ast_noopt_fn = os_ref.run_ast_noopt_fn diff --git a/tests/blob.cm b/tests/blob.cm index b8671b12..65a83cf1 100644 --- a/tests/blob.cm +++ b/tests/blob.cm @@ -1,5 +1,5 @@ var Blob = use('blob'); -var os = use('os'); +var os = use('internal/os'); function assert(condition, message) { if (!condition) disrupt diff --git a/tests/kim.cm b/tests/kim.cm index d5d4ca1b..7743aac8 100644 --- a/tests/kim.cm +++ b/tests/kim.cm @@ -1,4 +1,4 @@ -var kim = use("kim"); +var kim = use("internal/kim"); var blob = use('blob') return { diff --git a/tests/nota.cm b/tests/nota.cm index 75681dc2..d9d5c68a 100644 --- a/tests/nota.cm +++ b/tests/nota.cm @@ -1,5 +1,5 @@ -var nota = use('nota'); -var os = use('os'); +var nota = use('internal/nota'); +var os = use('internal/os'); var blob = use('blob') var EPSILON = 1e-12 diff --git a/tests/wota.cm b/tests/wota.cm index 5e40bf19..ae01b10d 100644 --- a/tests/wota.cm +++ b/tests/wota.cm @@ -1,5 +1,5 @@ -var wota = use('wota') -var os = use('os') +var wota = use('internal/wota') +var os = use('internal/os') var blob = use('blob') var math = use('math/radians') diff --git a/update.ce b/update.ce index 3c40e2f7..ceb395c5 100644 --- a/update.ce +++ b/update.ce @@ -14,7 +14,7 @@ var shop = use('internal/shop') var build = use('build') var fd = use('fd') -var os = use('os') +var os = use('internal/os') var target_pkg = null var run_build = false