remove internal scripts

This commit is contained in:
2025-12-29 15:45:21 -06:00
parent b586df63ad
commit d0674e7921
13 changed files with 686 additions and 518 deletions

View File

@@ -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))))
}
}

View File

@@ -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() {

View File

@@ -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

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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

View File

@@ -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) }
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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, "<input>", 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, "<json>");
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;
}

View File

@@ -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))))
}
}

View File

@@ -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"
},
}

210
utf8.c
View File

@@ -1,210 +0,0 @@
#include "cell.h"
#include <string.h>
#include <stdlib.h>
#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;
}