diff --git a/bench.ce b/bench.ce
index 8dc505b9..338cfdbd 100644
--- a/bench.ce
+++ b/bench.ce
@@ -3,7 +3,7 @@ var pkg = use('package')
var fd = use('fd')
var time = use('time')
var json = use('json')
-var utf8 = use('utf8')
+var blob = use('blob')
var os = use('os')
var testlib = use('internal/testlib')
var math = use('math/radians')
@@ -524,7 +524,7 @@ log.console(`Benchmarks: ${total_benches} total`)
// Generate reports
function generate_reports() {
- var timestamp = number.floor(time.number()).toString()
+ var timestamp = text(number.floor(time.number()))
var report_dir = shop.get_reports_dir() + '/bench_' + timestamp
testlib.ensure_dir(report_dir)
@@ -578,7 +578,7 @@ Total benchmarks: ${total_benches}
}
testlib.ensure_dir(report_dir)
- fd.slurpwrite(`${report_dir}/bench.txt`, utf8.encode(txt_report))
+ fd.slurpwrite(`${report_dir}/bench.txt`, stone(new blob(txt_report)))
log.console(`Report written to ${report_dir}/bench.txt`)
// Generate JSON per package
@@ -595,7 +595,7 @@ Total benchmarks: ${total_benches}
}
var json_path = `${report_dir}/${pkg_res.package.replace(/\//g, '_')}.json`
- fd.slurpwrite(json_path, utf8.encode(json.encode(pkg_benches)))
+ fd.slurpwrite(json_path, stone(new blob(json.encode(pkg_benches))))
}
}
diff --git a/build.cm b/build.cm
index 1d36eb39..c4c2d869 100644
--- a/build.cm
+++ b/build.cm
@@ -8,7 +8,7 @@
var fd = use('fd')
var crypto = use('crypto')
-var utf8 = use('utf8')
+var blob = use('blob')
var os = use('os')
var toolchains = use('toolchains')
var shop = use('internal/shop')
@@ -73,7 +73,7 @@ Build.detect_host_target = function() {
// ============================================================================
function content_hash(str) {
- return text(crypto.blake2(utf8.encode(str)), 'h')
+ return text(crypto.blake2(new blob(str)), 'h')
}
function get_build_dir() {
diff --git a/internal/engine.cm b/internal/engine.cm
index 9107b249..11e0da36 100644
--- a/internal/engine.cm
+++ b/internal/engine.cm
@@ -28,18 +28,6 @@ function use_embed(name) {
return load_internal(`js_${name}_use`)
}
-globalThis.meme = function(obj, ...mixins) {
- var result = _ObjectCreate(obj)
-
- array.for(mixins, mix => {
- if (is_object(mix)) {
- for (var key in mix)
- result[key] = mix[key]
- }
- })
- return result
-}
-
globalThis.logical = function(val1)
{
if (val1 == 0 || val1 == false || val1 == "false" || val1 == null)
@@ -49,7 +37,6 @@ globalThis.logical = function(val1)
return null;
}
-var utf8 = use_embed('utf8')
var js = use_embed('js')
var fd = use_embed('fd')
@@ -69,13 +56,6 @@ if (!fd.is_dir(core_path)) {
var use_cache = {}
use_cache['core/os'] = os
-var _Symbol = Symbol
-
-globalThis.key = function()
-{
- return _Symbol()
-}
-
// Load a core module from the file system
function use_core(path) {
var cache_key = 'core/' + path
@@ -89,7 +69,7 @@ function use_core(path) {
if (fd.is_file(file_path)) {
var script_blob = fd.slurp(file_path)
- var script = utf8.decode(script_blob)
+ var script = text(script_blob)
var mod = `(function setup_module(use){${script}})`
var fn = js.eval('core:' + path, mod)
var result = fn.call(sym, use_core);
@@ -104,13 +84,6 @@ function use_core(path) {
var blob = use_core('blob')
// Capture Object and Array methods before they're deleted
-var _Object = Object
-var _ObjectKeys = Object.keys
-var _ObjectIsFrozen = Object.isFrozen
-var _ObjectDefineProperty = Object.defineProperty
-var _ObjectGetPrototypeOf = Object.getPrototypeOf
-var _ObjectCreate = Object.create
-
Object.prototype.toString = function()
{
return json.encode(this)
@@ -125,41 +98,9 @@ var actor_mod = use_core('actor')
var wota = use_core('wota')
var nota = use_core('nota')
-// Global utility functions (use already-captured references)
-var _keys = _ObjectKeys
-var _getPrototypeOf = _ObjectGetPrototypeOf
-var _create = _ObjectCreate
-
-globalThis.reverse = function(value) {
- if (_isArray(value)) {
- var result = []
- for (var i = value.length - 1; i >= 0; i--) {
- result.push(value[i])
- }
- return result
- }
-
- // For blobs, would need blob module support
- if (isa(value, blob)) {
- // Simplified: return null for now, would need proper blob reversal
- return null
- }
-
- return null
-}
-
globalThis.isa = function(value, master) {
if (master == null) return false
- // isa(value, array) - check if object has all keys
- if (_isArray(master)) {
- if (typeof value != 'object' || value == null) return false
- for (var i = 0; i < master.length; i++) {
- if (!(master[i] in value)) return false
- }
- return true
- }
-
// isa(value, function) - check if function.prototype is in chain
if (typeof master == 'function') {
// Special type checks
@@ -196,55 +137,9 @@ globalThis.isa = function(value, master) {
return false
}
-globalThis.proto = function(obj) {
- if (!isa(obj, object)) return null
- var p = _getPrototypeOf(obj)
- if (p == _Object.prototype) return null
- return p
-}
-
-globalThis.splat = function(obj) {
- if (typeof obj != 'object' || obj == null) return null
-
- var result = {}
- var current = obj
-
- // Walk prototype chain and collect text keys
- while (current != null) {
- var keys = _keys(current)
- for (var i = 0; i < keys.length; i++) {
- var k = keys[i]
- if (!(k in result)) {
- var val = current[k]
- // Only include serializable types
- if (typeof val == 'object' || typeof val == 'number' ||
- typeof val == 'string' || typeof val == 'boolean') {
- result[k] = val
- }
- }
- }
- current = _getPrototypeOf(current)
- }
-
- // Call to_data if present
- if (typeof obj.to_data == 'function') {
- var extra = obj.to_data(result)
- if (typeof extra == 'object' && extra != null) {
- var extraKeys = _keys(extra)
- for (var i = 0; i < extraKeys.length; i++) {
- result[extraKeys[i]] = extra[extraKeys[i]]
- }
- }
- }
-
- return result
-}
-
var ENETSERVICE = 0.1
var REPLYTIMEOUT = 60 // seconds before replies are ignored
-globalThis.pi = 3.14159265358979323846264338327950288419716939937510
-
function caller_data(depth = 0)
{
var file = "nofile"
@@ -453,7 +348,7 @@ function guid(bits = 256)
return text(guid,'h')
}
-var HEADER = _Symbol()
+var HEADER = key()
// takes a function input value that will eventually be called with the current time in number form.
$_.clock = function(fn) {
@@ -771,7 +666,6 @@ function turn(msg)
}
//log.console(`FIXME: need to get main from config, not just set to true`)
-//log.console(`FIXME: remove global access (ie globalThis.use)`)
//log.console(`FIXME: add freeze/unfreeze at this level, so we can do it (but scripts cannot)`)
actor_mod.register_actor(_cell.id, turn, true, config.ar_timer)
@@ -911,69 +805,11 @@ if (!locator) {
if (!locator)
throw new Error(`Main program ${_cell.args.program} could not be found`)
-// Hide JavaScript built-ins - make them inaccessible
-// Store references we need internally before deleting
-var _Array = Array
-var _String = String
-var _Math = Math
-var _Function = Function
-
-var _JSON = JSON
-
-// juicing these before Math is gone
-
-use_core('math/radians')
-use_core('math/cycles')
-use_core('math/degrees')
-
-// Delete from globalThis
-delete globalThis.Object
-delete globalThis.Math
-delete globalThis.Number
-delete globalThis.String
-delete globalThis.Array
-delete globalThis.Boolean
-delete globalThis.Date
-delete globalThis.Function
-delete globalThis.Reflect
-delete globalThis.Proxy
-delete globalThis.WeakMap
-delete globalThis.WeakSet
-delete globalThis.WeakRef
-delete globalThis.BigInt
-delete globalThis.Symbol
-//delete globalThis.Map
-//delete globalThis.Set
-delete globalThis.Promise
-delete globalThis.ArrayBuffer
-delete globalThis.DataView
-delete globalThis.Int8Array
-delete globalThis.Uint8Array
-delete globalThis.Uint8ClampedArray
-delete globalThis.Int16Array
-delete globalThis.Uint16Array
-delete globalThis.Int32Array
-delete globalThis.Uint32Array
-delete globalThis.Float32Array
-delete globalThis.Float64Array
-delete globalThis.BigInt64Array
-delete globalThis.BigUint64Array
-delete globalThis.eval
-delete globalThis.parseInt
-delete globalThis.parseFloat
-delete globalThis.isNaN
-delete globalThis.isFinite
-delete globalThis.decodeURI
-delete globalThis.decodeURIComponent
-delete globalThis.encodeURI
-delete globalThis.encodeURIComponent
-delete globalThis.escape
-delete globalThis.unescape
-delete globalThis.Intl
-delete globalThis.RegExp
-
stone(globalThis)
+var rads = use_core("math/radians")
+log.console(rads)
+
$_.clock(_ => {
// Get capabilities for the main program
var file_info = shop.file_info ? shop.file_info(locator.path) : null
diff --git a/internal/shop.cm b/internal/shop.cm
index c1e87d44..fa171b74 100644
--- a/internal/shop.cm
+++ b/internal/shop.cm
@@ -6,7 +6,6 @@ var miniz = use('miniz')
var time = use('time')
var js = use('js')
var crypto = use('crypto')
-var utf8 = use('utf8')
var blob = use('blob')
var pkg_tools = use('package')
var os = use('os')
@@ -277,7 +276,7 @@ Shop.load_lock = function() {
// Save lock.toml configuration (to global shop)
Shop.save_lock = function(lock) {
var path = global_shop_path + '/lock.toml'
- fd.slurpwrite(path, utf8.encode(toml.encode(lock)));
+ fd.slurpwrite(path, stone(new blob(toml.encode(lock))));
}
@@ -415,7 +414,7 @@ function resolve_mod_fn(path, pkg) {
var content = text(fd.slurp(path))
var script = script_form(path, content, file_pkg, inject);
- var obj = pull_from_cache(utf8.encode(script))
+ var obj = pull_from_cache(stone(new blob(script)))
if (obj) {
var fn = js.compile_unblob(obj)
return js.eval_compile(fn)
@@ -427,7 +426,7 @@ function resolve_mod_fn(path, pkg) {
var fn = js.compile(compile_name, script)
- put_into_cache(utf8.encode(script), js.compile_blob(fn))
+ put_into_cache(stone(new blob(script)), js.compile_blob(fn))
return js.eval_compile(fn)
}
@@ -608,7 +607,8 @@ Shop.open_package_dylib = function(pkg) {
}
// If no package context, only check core internal symbols
- if (!package_context) {
+ if (!package_context || package_context == 'core') {
+ path = path.replace('/', '_')
var core_sym = `js_${path}_use`
if (os.internal_exists(core_sym)) {
return {
diff --git a/link.cm b/link.cm
index 78747a08..867066f1 100644
--- a/link.cm
+++ b/link.cm
@@ -3,7 +3,7 @@
var toml = use('toml')
var fd = use('fd')
-var utf8 = use('utf8')
+var blob = use('blob')
var os = use('os')
var global_shop_path = os.global_shop_path
@@ -81,7 +81,7 @@ Link.save = function(links) {
link_cache = links
var cfg = { links: links }
var path = get_links_path()
- fd.slurpwrite(path, utf8.encode(toml.encode(cfg)))
+ fd.slurpwrite(path, new blob(toml.encode(cfg)))
}
Link.add = function(canonical, target, shop) {
diff --git a/math/cycles.cm b/math/cycles.cm
deleted file mode 100644
index d0167a1c..00000000
--- a/math/cycles.cm
+++ /dev/null
@@ -1,18 +0,0 @@
-var cycles = {}
-var Math_obj = Math
-
-cycles.arc_cosine = function(x) { return Math_obj.acos(x) / (2 * pi) }
-cycles.arc_sine = function(x) { return Math_obj.asin(x) / (2 * pi) }
-cycles.arc_tangent = function(x) { return Math_obj.atan(x) / (2 * pi) }
-cycles.cosine = function(x) { return Math_obj.cos(x * 2 * pi) }
-cycles.e = Math_obj.E
-cycles.ln = function(x) { return Math_obj.log(x) }
-cycles.log = function(x) { return Math_obj.log10(x) }
-cycles.log2 = function(x) { return Math_obj.log2(x) }
-cycles.power = function(x, y) { return Math_obj.pow(x, y) }
-cycles.root = function(x, y) { return Math_obj.pow(x, 1 / y) }
-cycles.sine = function(x) { return Math_obj.sin(x * 2 * pi) }
-cycles.sqrt = function(x) { return Math_obj.sqrt(x) }
-cycles.tangent = function(x) { return Math_obj.tan(x * 2 * pi) }
-
-return cycles
diff --git a/math/degrees.cm b/math/degrees.cm
deleted file mode 100644
index 176eea06..00000000
--- a/math/degrees.cm
+++ /dev/null
@@ -1,21 +0,0 @@
-var degrees = {}
-var Math_obj = Math
-
-var deg2rad = pi / 180
-var rad2deg = 180 / pi
-
-return {
- arc_cosine: function(x) { return Math_obj.acos(x) * rad2deg },
- arc_sine: function(x) { return Math_obj.asin(x) * rad2deg },
- arc_tangent: function(x) { return Math_obj.atan(x) * rad2deg },
- cosine: function(x) { return Math_obj.cos(x * deg2rad) },
- e: Math_obj.E,
- ln: function(x) { return Math_obj.log(x) },
- log: function(x) { return Math_obj.log10(x) },
- log2: function(x) { return Math_obj.log2(x) },
- power: function(x, y) { return Math_obj.pow(x, y) },
- root: function(x, y) { return Math_obj.pow(x, 1/y) },
- sine: function(x) { return Math_obj.sin(x * deg2rad) },
- sqrt: function(x) { return Math_obj.sqrt(x) },
- tangent: function(x) { return Math_obj.tan(x * deg2rad) }
-}
diff --git a/math/radians.cm b/math/radians.cm
deleted file mode 100644
index 9fbc29c6..00000000
--- a/math/radians.cm
+++ /dev/null
@@ -1,17 +0,0 @@
-var Math = globalThis.Math
-
-return {
- arc_cosine: Math.acos,
- arc_sine: Math.asin,
- arc_tangent: Math.atan,
- cosine: Math.cos,
- e: Math.E,
- ln: Math.log,
- log: Math.log10,
- log2: Math.log2,
- power: Math.pow,
- root: function(x, n) { return Math.pow(x, 1/n) },
- sine: Math.sin,
- sqrt: Math.sqrt,
- tangent: Math.tan
-}
diff --git a/meson.build b/meson.build
index 62684339..c102f144 100644
--- a/meson.build
+++ b/meson.build
@@ -57,7 +57,6 @@ scripts = [
'wildstar.c',
'fit.c',
'crypto.c',
- 'utf8.c',
'internal/kim.c',
'time.c',
'internal/nota.c',
@@ -68,7 +67,6 @@ scripts = [
'net/enet.c',
'wildstar.c',
'archive/miniz.c',
- 'internal/json.c',
]
foreach file: scripts
diff --git a/source/quickjs.c b/source/quickjs.c
index 1b915a0e..f441d337 100644
--- a/source/quickjs.c
+++ b/source/quickjs.c
@@ -1928,7 +1928,6 @@ JSContext *JS_NewContext(JSRuntime *rt)
JS_AddIntrinsicEval(ctx);
JS_AddIntrinsicStringNormalize(ctx);
JS_AddIntrinsicRegExp(ctx);
- JS_AddIntrinsicJSON(ctx);
JS_AddIntrinsicMapSet(ctx);
return ctx;
@@ -26385,7 +26384,6 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
ret = JS_EvalInternal(ctx, this_obj, str, len, "", flags, scope_idx);
JS_FreeCString(ctx, str);
return ret;
-
}
JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
@@ -39499,6 +39497,19 @@ static JSValue js_blob_constructor(JSContext *ctx, JSValueConst new_target,
}
bd = blob_new_from_blob(src, (size_t)from, (size_t)to);
}
+ /* blob(text) - create blob from UTF-8 string */
+ else if (argc == 1 && (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_STRING || JS_VALUE_GET_TAG(argv[0]) == JS_TAG_STRING_ROPE)) {
+ const char *str = JS_ToCString(ctx, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ size_t len = strlen(str);
+ bd = blob_new(len * 8);
+ if (bd) {
+ memcpy(bd->data, str, len);
+ bd->length = len * 8;
+ }
+ JS_FreeCString(ctx, str);
+ }
else {
return JS_ThrowTypeError(ctx, "blob constructor: invalid arguments");
}
@@ -40057,6 +40068,230 @@ static const JSCFunctionListEntry js_cell_stone_funcs[] = {
JS_CFUNC_DEF("p", 1, js_cell_stone_p),
};
+/* ============================================================================
+ * key() function - create a unique symbol
+ * ============================================================================ */
+
+static JSValue js_cell_key(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewSymbol(ctx, JS_ATOM_NULL, JS_ATOM_TYPE_SYMBOL);
+}
+
+/* ============================================================================
+ * reverse() function - reverse an array
+ * ============================================================================ */
+
+static JSValue js_cell_reverse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (argc < 1)
+ return JS_NULL;
+
+ JSValue value = argv[0];
+
+ /* Handle arrays */
+ if (JS_IsArray(ctx, value)) {
+ JSValue length_val = JS_GetPropertyStr(ctx, value, "length");
+ int64_t len;
+ if (JS_ToInt64(ctx, &len, length_val) < 0) {
+ JS_FreeValue(ctx, length_val);
+ return JS_NULL;
+ }
+ JS_FreeValue(ctx, length_val);
+
+ JSValue result = JS_NewArray(ctx);
+ for (int64_t i = len - 1, j = 0; i >= 0; i--, j++) {
+ JSValue elem = JS_GetPropertyInt64(ctx, value, i);
+ JS_SetPropertyInt64(ctx, result, j, elem);
+ }
+ return result;
+ }
+
+ /* Handle blobs */
+ blob *bd = js_get_blob(ctx, value);
+ if (bd) {
+ /* Blobs need proper blob reversal support - return null for now */
+ return JS_NULL;
+ }
+
+ return JS_NULL;
+}
+
+/* ============================================================================
+ * proto() function - get prototype of an object
+ * ============================================================================ */
+
+static JSValue js_cell_proto(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (argc < 1)
+ return JS_NULL;
+
+ JSValue obj = argv[0];
+ if (!JS_IsObject(obj))
+ return JS_NULL;
+
+ JSValue proto = JS_GetPrototype(ctx, obj);
+ if (JS_IsException(proto))
+ return JS_NULL;
+
+ /* If prototype is Object.prototype, return null */
+ if (JS_IsObject(proto)) {
+ JSValue obj_proto = JS_GetPropertyStr(ctx, ctx->class_proto[JS_CLASS_OBJECT], "");
+ if (JS_VALUE_GET_OBJ(proto) == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) {
+ JS_FreeValue(ctx, proto);
+ JS_FreeValue(ctx, obj_proto);
+ return JS_NULL;
+ }
+ JS_FreeValue(ctx, obj_proto);
+ }
+
+ return proto;
+}
+
+static JSValue js_cell_meme(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue proto = JS_NULL;
+ if (argc > 0 && !JS_IsNull(argv[0]))
+ proto = argv[0];
+
+ JSValue result = JS_NewObjectProto(ctx, proto);
+ if (JS_IsException(result))
+ return result;
+
+ /* Apply mixins */
+ for (int i = 1; i < argc; i++) {
+ JSValue mix = argv[i];
+ if (!JS_IsObject(mix) || JS_IsNull(mix) || JS_IsArray(ctx, mix))
+ continue;
+
+ JSPropertyEnum *tab;
+ uint32_t len;
+
+ if (JS_GetOwnPropertyNames(ctx, &tab, &len, mix,
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) {
+ JS_FreeValue(ctx, result);
+ return JS_EXCEPTION;
+ }
+
+ for (uint32_t j = 0; j < len; j++) {
+ JSValue val = JS_GetProperty(ctx, mix, tab[j].atom);
+ if (JS_IsException(val)) {
+ for (uint32_t k = j; k < len; k++)
+ JS_FreeAtom(ctx, tab[k].atom);
+ js_free(ctx, tab);
+ JS_FreeValue(ctx, result);
+ return JS_EXCEPTION;
+ }
+ JS_SetProperty(ctx, result, tab[j].atom, val);
+ JS_FreeAtom(ctx, tab[j].atom);
+ }
+ js_free(ctx, tab);
+ }
+
+ return result;
+}
+
+/* ============================================================================
+ * splat() function - flatten object with prototype chain
+ * ============================================================================ */
+
+static JSValue js_cell_splat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (argc < 1)
+ return JS_NULL;
+
+ JSValue obj = argv[0];
+ if (!JS_IsObject(obj) || JS_IsNull(obj))
+ return JS_NULL;
+
+ JSValue result = JS_NewObject(ctx);
+ if (JS_IsException(result))
+ return JS_EXCEPTION;
+
+ JSValue current = JS_DupValue(ctx, obj);
+
+ /* Walk prototype chain and collect text keys */
+ while (!JS_IsNull(current)) {
+ JSPropertyEnum *tab;
+ uint32_t len;
+
+ if (JS_GetOwnPropertyNames(ctx, &tab, &len, current,
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) {
+ JS_FreeValue(ctx, current);
+ JS_FreeValue(ctx, result);
+ return JS_EXCEPTION;
+ }
+
+ for (uint32_t i = 0; i < len; i++) {
+ JSAtom atom = tab[i].atom;
+ /* Check if property not already in result */
+ int has = JS_HasProperty(ctx, result, atom);
+ if (has < 0) {
+ for (uint32_t j = i; j < len; j++)
+ JS_FreeAtom(ctx, tab[j].atom);
+ js_free(ctx, tab);
+ JS_FreeValue(ctx, current);
+ JS_FreeValue(ctx, result);
+ return JS_EXCEPTION;
+ }
+ if (!has) {
+ JSValue val = JS_GetProperty(ctx, current, atom);
+ if (JS_IsException(val)) {
+ for (uint32_t j = i; j < len; j++)
+ JS_FreeAtom(ctx, tab[j].atom);
+ js_free(ctx, tab);
+ JS_FreeValue(ctx, current);
+ JS_FreeValue(ctx, result);
+ return JS_EXCEPTION;
+ }
+ /* Only include serializable types */
+ int tag = JS_VALUE_GET_TAG(val);
+ if (JS_IsObject(val) || JS_IsNumber(val) ||
+ tag == JS_TAG_STRING || tag == JS_TAG_STRING_ROPE ||
+ tag == JS_TAG_BOOL) {
+ JS_SetProperty(ctx, result, atom, val);
+ } else {
+ JS_FreeValue(ctx, val);
+ }
+ }
+ JS_FreeAtom(ctx, tab[i].atom);
+ }
+ js_free(ctx, tab);
+
+ JSValue next = JS_GetPrototype(ctx, current);
+ JS_FreeValue(ctx, current);
+ current = next;
+ }
+
+ /* Call to_data if present */
+ JSValue to_data = JS_GetPropertyStr(ctx, obj, "to_data");
+ if (JS_IsFunction(ctx, to_data)) {
+ JSValue args[1] = { result };
+ JSValue extra = JS_Call(ctx, to_data, obj, 1, args);
+ if (!JS_IsException(extra) && JS_IsObject(extra)) {
+ JSPropertyEnum *tab;
+ uint32_t len;
+ if (JS_GetOwnPropertyNames(ctx, &tab, &len, extra,
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) >= 0) {
+ for (uint32_t i = 0; i < len; i++) {
+ JSValue val = JS_GetProperty(ctx, extra, tab[i].atom);
+ JS_SetProperty(ctx, result, tab[i].atom, val);
+ JS_FreeAtom(ctx, tab[i].atom);
+ }
+ js_free(ctx, tab);
+ }
+ }
+ JS_FreeValue(ctx, extra);
+ }
+ JS_FreeValue(ctx, to_data);
+
+ return result;
+}
+
/* ============================================================================
* length() function
* ============================================================================ */
@@ -40291,20 +40526,6 @@ static JSValue js_cell_is_proto(JSContext *ctx, JSValueConst this_val,
return JS_FALSE;
}
-/* ============================================================================
- * Cell Script Global Function Registration
- * ============================================================================ */
-
-static const JSCFunctionListEntry js_global_funcs[] = {
- JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
- JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
- JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
- JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
- JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
- JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
-};
-
/* eval */
void JS_AddIntrinsicEval(JSContext *ctx)
@@ -40474,12 +40695,6 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
js_array_iterator_proto_funcs,
countof(js_array_iterator_proto_funcs));
- /* parseFloat and parseInteger must be defined before Number
- because of the Number.parseFloat and Number.parseInteger
- aliases */
- JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,
- countof(js_global_funcs));
-
/* String */
ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
JS_CLASS_STRING);
@@ -40496,10 +40711,6 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
js_string_iterator_proto_funcs,
countof(js_string_iterator_proto_funcs));
- /* Math: create as autoinit object */
- js_random_init(ctx);
- JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));
-
/* ES6 Symbol */
ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,
@@ -40518,11 +40729,6 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
}
/* global properties */
- ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);
- JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,
- JS_DupValue(ctx, ctx->eval_obj),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
-
JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,
JS_DupValue(ctx, ctx->global_obj),
JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
@@ -40620,6 +40826,36 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
JS_DefinePropertyValueStr(ctx, ctx->global_obj, "is_proto",
JS_NewCFunction(ctx, js_cell_is_proto, "is_proto", 2),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ /* key() - create a unique symbol */
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "key",
+ JS_NewCFunction(ctx, js_cell_key, "key", 0),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ /* reverse() - reverse an array */
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "reverse",
+ JS_NewCFunction(ctx, js_cell_reverse, "reverse", 1),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ /* proto() - get prototype of an object */
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "proto",
+ JS_NewCFunction(ctx, js_cell_proto, "proto", 1),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ /* splat() - flatten object with prototype chain */
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "splat",
+ JS_NewCFunction(ctx, js_cell_splat, "splat", 1),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ /* splat() - flatten object with prototype chain */
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "meme",
+ JS_NewCFunction(ctx, js_cell_meme, "meme", 1),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ /* pi - mathematical constant */
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "pi",
+ JS_NewFloat64(ctx, 3.14159265358979323846264338327950288419716939937510),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
}
}
@@ -41295,6 +41531,404 @@ void *js_debugger_val_address(JSContext *ctx, JSValue val) {
return JS_VALUE_GET_PTR(val);
}
+/* ============================================================================
+ * Cell Script Module: json
+ * Provides json.encode() and json.decode() using pure C implementation
+ * ============================================================================ */
+
+static JSValue js_cell_json_encode(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (argc < 1)
+ return JS_ThrowTypeError(ctx, "json.encode requires at least 1 argument");
+
+ JSValue replacer = argc > 1 ? argv[1] : JS_NULL;
+ JSValue space = argc > 2 ? argv[2] : JS_NewInt32(ctx, 1);
+ JSValue result = JS_JSONStringify(ctx, argv[0], replacer, space);
+ if (argc <= 2)
+ JS_FreeValue(ctx, space);
+ return result;
+}
+
+static JSValue js_cell_json_decode(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (argc < 1)
+ return JS_ThrowTypeError(ctx, "json.decode requires at least 1 argument");
+
+ int tag = JS_VALUE_GET_TAG(argv[0]);
+ if (tag != JS_TAG_STRING && tag != JS_TAG_STRING_ROPE) {
+ JSValue err = JS_NewError(ctx);
+ JS_DefinePropertyValueStr(ctx, err, "message",
+ JS_NewString(ctx, "couldn't parse text: not a string"),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ return JS_Throw(ctx, err);
+ }
+
+ 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(ctx, argv[1]) && !JS_IsException(result)) {
+ /* Create wrapper object to pass to reviver */
+ JSValue wrapper = JS_NewObject(ctx);
+ JS_DefinePropertyValueStr(ctx, wrapper, "", result, JS_PROP_C_W_E);
+
+ JSValue holder = wrapper;
+ JSAtom key = JS_NewAtom(ctx, "");
+ JSValue args[2] = { JS_AtomToString(ctx, key), JS_GetProperty(ctx, holder, key) };
+ JSValue final = JS_Call(ctx, argv[1], holder, 2, args);
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ JS_FreeAtom(ctx, key);
+ JS_FreeValue(ctx, wrapper);
+ 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_json_use(JSContext *ctx)
+{
+ JSValue obj = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, obj, js_cell_json_funcs, countof(js_cell_json_funcs));
+ return obj;
+}
+
+static JSValue js_math_e(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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_math_radians_use(JSContext *ctx)
+{
+ JSValue obj = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, obj, js_math_radians_funcs, countof(js_math_radians_funcs));
+ return obj;
+}
+
+/* ============================================================================
+ * 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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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_math_degrees_use(JSContext *ctx)
+{
+ JSValue obj = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, obj, js_math_degrees_funcs, countof(js_math_degrees_funcs));
+ return obj;
+}
+
+/* ============================================================================
+ * 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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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, JSValueConst this_val,
+ int argc, JSValueConst *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_math_cycles_use(JSContext *ctx)
+{
+ JSValue obj = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, obj, js_math_cycles_funcs, countof(js_math_cycles_funcs));
+ return obj;
+}
+
JSContext *JS_GetContext(JSRuntime *rt) {
return rt->js;
}
diff --git a/test.ce b/test.ce
index 9c1bb340..8b3f4415 100644
--- a/test.ce
+++ b/test.ce
@@ -3,7 +3,7 @@ var pkg = use('package')
var fd = use('fd')
var time = use('time')
var json = use('json')
-var utf8 = use('utf8')
+var blob = use('blob')
if (!args) args = []
@@ -628,7 +628,7 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed}
}
}
ensure_dir(report_dir)
- fd.slurpwrite(`${report_dir}/test.txt`, utf8.encode(txt_report))
+ fd.slurpwrite(`${report_dir}/test.txt`, stone(new blob(txt_report)))
log.console(`Report written to ${report_dir}/test.txt`)
// Generate JSON per package
@@ -645,7 +645,7 @@ Total: ${totals.total}, Passed: ${totals.passed}, Failed: ${totals.failed}
}
var json_path = `${report_dir}/${pkg_res.package.replace(/\//g, '_')}.json`
- fd.slurpwrite(json_path, utf8.encode(json.encode(pkg_tests)))
+ fd.slurpwrite(json_path, stone(new blob(json.encode(pkg_tests))))
}
}
diff --git a/tests/utf8.cm b/tests/utf8.cm
deleted file mode 100644
index 7e9f29c9..00000000
--- a/tests/utf8.cm
+++ /dev/null
@@ -1,34 +0,0 @@
-var blob = use('blob');
-var utf8 = use('utf8');
-
-return {
- test_blob_to_text: function() {
- // Test blob to text conversion
- var test_string = "Hello, δΈη! π";
- var encoded_blob = utf8.encode(test_string);
- var decoded_text = text(encoded_blob);
- if (test_string != decoded_text) throw "Blob to text failed"
- },
-
- test_codepoints_to_text: function() {
- // Test array of codepoints conversion
- var test_string = "Hello, δΈη! π";
- var codepoints = [72, 101, 108, 108, 111, 44, 32, 19990, 30028, 33, 32, 127757];
- var from_codepoints = text(codepoints);
- if (from_codepoints != test_string) throw "Codepoints to text failed"
- },
-
- test_array_separator: function() {
- // Test array with separator
- var words = ["Hello", "world", "from", "text"];
- var joined = text(words, " ");
- if (joined != "Hello world from text") throw "Array with separator failed"
- },
-
- test_mixed_array: function() {
- // Test mixed array with codepoints
- var mixed = [72, "ello", 32, "world"];
- var mixed_result = text(mixed, "");
- if (mixed_result != "Hello world") throw "Mixed array test failed"
- },
-}
diff --git a/utf8.c b/utf8.c
deleted file mode 100644
index 1eee19f2..00000000
--- a/utf8.c
+++ /dev/null
@@ -1,210 +0,0 @@
-#include "cell.h"
-#include
-#include
-
-#include "kim.h"
-
-// Get codepoints from a UTF-8 string
-JSC_CCALL(utf8_codepoints,
- const char *str = JS_ToCString(js, argv[0]);
- if (!str) return JS_EXCEPTION;
-
- JSValue arr = JS_NewArray(js);
- int idx = 0;
-
- char *ptr = (char*)str;
- while (*ptr) {
- int codepoint = decode_utf8(&ptr);
- JS_SetPropertyUint32(js, arr, idx++, JS_NewInt32(js, codepoint));
- }
-
- JS_FreeCString(js, str);
- ret = arr;
-)
-
-// Create UTF-8 string from codepoints
-JSC_CCALL(utf8_from_codepoints,
- int len = JS_ArrayLength(js, argv[0]);
-
- // Allocate buffer (worst case: 4 bytes per codepoint + null)
- char *buffer = malloc(len * 4 + 1);
- char *ptr = buffer;
-
- for (int i = 0; i < len; i++) {
- JSValue val = JS_GetPropertyUint32(js, argv[0], i);
- int codepoint;
- JS_ToInt32(js, &codepoint, val);
- JS_FreeValue(js, val);
-
- encode_utf8(&ptr, codepoint);
- }
-
- *ptr = '\0';
- ret = JS_NewString(js, buffer);
- free(buffer);
-)
-
-// Count UTF-8 characters (runes) in a string
-JSC_SCALL(utf8_length,
- int count = utf8_count(str);
- ret = JS_NewInt32(js, count);
-)
-
-// Validate UTF-8 string
-JSC_SCALL(utf8_validate,
- char *ptr = (char*)str;
- int valid = 1;
-
- while (*ptr) {
- int start_pos = ptr - str;
- int codepoint = decode_utf8(&ptr);
-
- // Check for invalid sequences
- if (codepoint < 0 || codepoint > 0x10FFFF ||
- (codepoint >= 0xD800 && codepoint <= 0xDFFF)) {
- valid = 0;
- break;
- }
-
- // Check for overlong encodings
- int bytes_used = ptr - (str + start_pos);
- if ((codepoint <= 0x7F && bytes_used != 1) ||
- (codepoint <= 0x7FF && bytes_used != 2) ||
- (codepoint <= 0xFFFF && bytes_used != 3) ||
- (codepoint <= 0x10FFFF && bytes_used != 4)) {
- valid = 0;
- break;
- }
- }
-
- ret = JS_NewBool(js, valid);
-)
-
-// Get byte length of UTF-8 string
-JSC_SCALL(utf8_byte_length,
- ret = JS_NewInt32(js, strlen(str));
-)
-
-// Encode string to UTF-8 bytes
-JSC_SCALL(utf8_encode,
- size_t len = strlen(str);
- ret = js_new_blob_stoned_copy(js, str, len);
-)
-
-// Decode UTF-8 bytes to string
-JSC_CCALL(utf8_decode,
- size_t len;
- void *data = js_get_blob_data(js, &len, argv[0]);
- if (data == (void*)-1) return JS_EXCEPTION;
- if (!data || len == 0) return JS_ThrowTypeError(js, "No data present in blob");
-
- // Create null-terminated string
- char *str = malloc(len + 1);
- memcpy(str, data, len);
- str[len] = '\0';
-
- ret = JS_NewString(js, str);
- free(str);
-)
-
-// Slice UTF-8 string by character indices (not byte indices)
-JSC_CCALL(utf8_slice,
- const char *str = JS_ToCString(js, argv[0]);
- if (!str) return JS_EXCEPTION;
-
- int start = 0;
- int end = utf8_count(str);
-
- if (argc > 1) JS_ToInt32(js, &start, argv[1]);
- if (argc > 2) JS_ToInt32(js, &end, argv[2]);
-
- // Handle negative indices
- int total = end;
- if (start < 0) start = total + start;
- if (end < 0) end = total + end;
-
- // Clamp values
- if (start < 0) start = 0;
- if (end > total) end = total;
- if (start >= end) {
- JS_FreeCString(js, str);
- return JS_NewString(js, "");
- }
-
- // Find start position
- char *ptr = (char*)str;
- for (int i = 0; i < start && *ptr; i++) {
- decode_utf8(&ptr);
- }
- char *start_ptr = ptr;
-
- // Find end position
- for (int i = start; i < end && *ptr; i++) {
- decode_utf8(&ptr);
- }
-
- // Create substring
- size_t slice_len = ptr - start_ptr;
- char *slice = malloc(slice_len + 1);
- memcpy(slice, start_ptr, slice_len);
- slice[slice_len] = '\0';
-
- ret = JS_NewString(js, slice);
- free(slice);
- JS_FreeCString(js, str);
-)
-
-// Get character at index
-JSC_CCALL(utf8_char_at,
- const char *str = JS_ToCString(js, argv[0]);
- if (!str) return JS_EXCEPTION;
-
- int index;
- JS_ToInt32(js, &index, argv[1]);
-
- char *ptr = (char*)str;
- int count = 0;
-
- // Skip to index
- while (*ptr && count < index) {
- decode_utf8(&ptr);
- count++;
- }
-
- if (!*ptr || count != index) {
- JS_FreeCString(js, str);
- return JS_NULL;
- }
-
- // Get the character
- char *char_start = ptr;
- decode_utf8(&ptr);
-
- size_t char_len = ptr - char_start;
- char *result = malloc(char_len + 1);
- memcpy(result, char_start, char_len);
- result[char_len] = '\0';
-
- ret = JS_NewString(js, result);
- free(result);
- JS_FreeCString(js, str);
-)
-
-static const JSCFunctionListEntry js_utf8_funcs[] = {
- MIST_FUNC_DEF(utf8, codepoints, 1),
- MIST_FUNC_DEF(utf8, from_codepoints, 1),
- MIST_FUNC_DEF(utf8, length, 1),
- MIST_FUNC_DEF(utf8, validate, 1),
- MIST_FUNC_DEF(utf8, byte_length, 1),
- MIST_FUNC_DEF(utf8, encode, 1),
- MIST_FUNC_DEF(utf8, decode, 1),
- MIST_FUNC_DEF(utf8, slice, 3),
- MIST_FUNC_DEF(utf8, char_at, 2),
-};
-
-JSValue js_utf8_use(JSContext *js)
-{
- JSValue mod = JS_NewObject(js);
- JS_SetPropertyFunctionList(js, mod, js_utf8_funcs, countof(js_utf8_funcs));
- return mod;
-}
\ No newline at end of file