Compare commits
3 Commits
mcode2
...
7469383e66
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7469383e66 | ||
|
|
1fee8f9f8b | ||
|
|
d18ea1b330 |
@@ -382,13 +382,13 @@ static const JSCFunctionListEntry js_reader_funcs[] = {
|
||||
JSValue js_miniz_use(JSContext *js)
|
||||
{
|
||||
JS_NewClassID(&js_reader_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_reader_class_id, &js_reader_class);
|
||||
JS_NewClass(js, js_reader_class_id, &js_reader_class);
|
||||
JSValue reader_proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, reader_proto, js_reader_funcs, sizeof(js_reader_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(js, js_reader_class_id, reader_proto);
|
||||
|
||||
JS_NewClassID(&js_writer_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_writer_class_id, &js_writer_class);
|
||||
JS_NewClass(js, js_writer_class_id, &js_writer_class);
|
||||
JSValue writer_proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, writer_proto, js_writer_funcs, sizeof(js_writer_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(js, js_writer_class_id, writer_proto);
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
(function engine() {
|
||||
// Hidden vars (os, actorsym, init, core_path) come from env
|
||||
var ACTORDATA = actorsym
|
||||
var SYSYM = '__SYSTEM__'
|
||||
|
||||
var _cell = {}
|
||||
var need_stop = false
|
||||
|
||||
var dylib_ext
|
||||
|
||||
switch(os.platform()) {
|
||||
case 'Windows': dylib_ext = '.dll'; break;
|
||||
case 'macOS': dylib_ext = '.dylib'; break;
|
||||
case 'Linux': dylib_ext = '.so'; break;
|
||||
var cases = {
|
||||
Windows: '.dll',
|
||||
macOS: '.dylib',
|
||||
Linux: '.so'
|
||||
}
|
||||
|
||||
print(os.platform())
|
||||
|
||||
dylib_ext = cases[os.platform()]
|
||||
|
||||
var MOD_EXT = '.cm'
|
||||
var ACTOR_EXT = '.ce'
|
||||
|
||||
@@ -51,14 +55,16 @@ var fd = use_embed('fd')
|
||||
// Get the shop path from HOME environment
|
||||
var home = os.getenv('HOME') || os.getenv('USERPROFILE')
|
||||
if (!home) {
|
||||
throw Error('Could not determine home directory')
|
||||
os.print('Could not determine home directory\n')
|
||||
os.exit(1)
|
||||
}
|
||||
var shop_path = home + '/.cell'
|
||||
var packages_path = shop_path + '/packages'
|
||||
var core_path = packages_path + '/core'
|
||||
|
||||
if (!fd.is_dir(core_path)) {
|
||||
throw Error('Cell shop not found at ' + shop_path + '. Run "cell install" to set up.')
|
||||
os.print('Cell shop not found at ' + shop_path + '. Run "cell install" to set up.\n')
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
var use_cache = {}
|
||||
@@ -79,7 +85,7 @@ function use_core(path) {
|
||||
var script_blob = fd.slurp(file_path)
|
||||
var script = text(script_blob)
|
||||
var mod = `(function setup_module(use){${script}})`
|
||||
var fn = js.eval('core:' + path, mod)
|
||||
var fn = mach_eval('core:' + path, mod)
|
||||
var result = call(fn,sym, [use_core])
|
||||
use_cache[cache_key] = result;
|
||||
return result;
|
||||
@@ -133,32 +139,27 @@ function log(name, args) {
|
||||
var caller = caller_data(1)
|
||||
var msg = args[0]
|
||||
|
||||
switch(name) {
|
||||
case 'console':
|
||||
if (name == 'console') {
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
break
|
||||
case 'error':
|
||||
msg = msg ?? Error()
|
||||
} else if (name == 'error') {
|
||||
if (msg == null) msg = Error()
|
||||
if (is_proto(msg, Error))
|
||||
msg = msg.name + ": " + msg.message + "\n" + msg.stack
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
break
|
||||
case 'system':
|
||||
} else if (name == 'system') {
|
||||
msg = "[SYSTEM] " + msg
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
break
|
||||
default:
|
||||
} else {
|
||||
log.console(`unknown log type: ${name}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function disrupt(err)
|
||||
function actor_die(err)
|
||||
{
|
||||
if (is_function(err.toString)) {
|
||||
if (err && is_function(err.toString)) {
|
||||
os.print(err.toString())
|
||||
os.print("\n")
|
||||
os.print(err.stack)
|
||||
if (err.stack) os.print(err.stack)
|
||||
}
|
||||
|
||||
if (overling) {
|
||||
@@ -185,14 +186,14 @@ function disrupt(err)
|
||||
log.console(err.stack)
|
||||
}
|
||||
|
||||
actor_mod.disrupt()
|
||||
actor_mod["disrupt"]()
|
||||
}
|
||||
|
||||
|
||||
|
||||
actor_mod.on_exception(disrupt)
|
||||
actor_mod.on_exception(actor_die)
|
||||
|
||||
_cell.args = init ?? {}
|
||||
_cell.args = init != null ? init : {}
|
||||
_cell.id = "newguy"
|
||||
|
||||
function create_actor(desc = {id:guid()}) {
|
||||
@@ -241,10 +242,14 @@ os.runtime_env = runtime_env
|
||||
|
||||
$_.time_limit = function(requestor, seconds)
|
||||
{
|
||||
if (!pronto.is_requestor(requestor))
|
||||
throw Error('time_limit: first argument must be a requestor');
|
||||
if (!is_number(seconds) || seconds <= 0)
|
||||
throw Error('time_limit: seconds must be a positive number');
|
||||
if (!pronto.is_requestor(requestor)) {
|
||||
log.error('time_limit: first argument must be a requestor')
|
||||
disrupt
|
||||
}
|
||||
if (!is_number(seconds) || seconds <= 0) {
|
||||
log.error('time_limit: seconds must be a positive number')
|
||||
disrupt
|
||||
}
|
||||
|
||||
return function time_limit_requestor(callback, value) {
|
||||
pronto.check_callback(callback, 'time_limit')
|
||||
@@ -260,7 +265,14 @@ $_.time_limit = function(requestor, seconds)
|
||||
timer_cancel = null
|
||||
}
|
||||
if (requestor_cancel) {
|
||||
try { pronto.requestor_cancel(reason) } catch (_) {}
|
||||
requestor_cancel(reason)
|
||||
requestor_cancel = null
|
||||
}
|
||||
}
|
||||
|
||||
function safe_cancel_requestor(reason) {
|
||||
if (requestor_cancel) {
|
||||
requestor_cancel(reason)
|
||||
requestor_cancel = null
|
||||
}
|
||||
}
|
||||
@@ -268,15 +280,12 @@ $_.time_limit = function(requestor, seconds)
|
||||
timer_cancel = $_.delay(function() {
|
||||
if (finished) return
|
||||
def reason = make_reason(factory, 'Timeout.', seconds)
|
||||
if (requestor_cancel) {
|
||||
try { requestor_cancel(reason) } catch (_) {}
|
||||
requestor_cancel = null
|
||||
}
|
||||
safe_cancel_requestor(reason)
|
||||
finished = true
|
||||
callback(null, reason)
|
||||
}, seconds)
|
||||
|
||||
try {
|
||||
function do_request() {
|
||||
requestor_cancel = requestor(function(val, reason) {
|
||||
if (finished) return
|
||||
finished = true
|
||||
@@ -286,16 +295,14 @@ $_.time_limit = function(requestor, seconds)
|
||||
}
|
||||
callback(val, reason)
|
||||
}, value)
|
||||
} catch (ex) {
|
||||
cancel(ex)
|
||||
callback(null, ex)
|
||||
} disruption {
|
||||
cancel(Error('requestor failed'))
|
||||
callback(null, Error('requestor failed'))
|
||||
}
|
||||
do_request()
|
||||
|
||||
return function(reason) {
|
||||
if (requestor_cancel) {
|
||||
try { requestor_cancel(reason) } catch (_) {}
|
||||
requestor_cancel = null
|
||||
}
|
||||
safe_cancel_requestor(reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,16 +415,21 @@ var portal_fn = null
|
||||
|
||||
// takes a function input value that will eventually be called with the current time in number form.
|
||||
$_.portal = function(fn, port) {
|
||||
if (portal) throw Error(`Already started a portal listening on ${portal.port}`)
|
||||
if (!port) throw Error("Requires a valid port.")
|
||||
if (portal) {
|
||||
log.error(`Already started a portal listening on ${portal.port}`)
|
||||
disrupt
|
||||
}
|
||||
if (!port) {
|
||||
log.error("Requires a valid port.")
|
||||
disrupt
|
||||
}
|
||||
log.system(`starting a portal on port ${port}`)
|
||||
portal = enet.create_host({address: "any", port})
|
||||
portal_fn = fn
|
||||
}
|
||||
|
||||
function handle_host(e) {
|
||||
switch (e.type) {
|
||||
case "connect":
|
||||
if (e.type == "connect") {
|
||||
log.system(`connected a new peer: ${e.peer.address}:${e.peer.port}`)
|
||||
peers[`${e.peer.address}:${e.peer.port}`] = e.peer
|
||||
var queue = peer_queue.get(e.peer)
|
||||
@@ -426,15 +438,13 @@ function handle_host(e) {
|
||||
log.system(`sent ${msg} out of queue`)
|
||||
peer_queue.delete(e.peer)
|
||||
}
|
||||
break
|
||||
case "disconnect":
|
||||
} else if (e.type == "disconnect") {
|
||||
peer_queue.delete(e.peer)
|
||||
arrfor(array(peers), function(id, index) {
|
||||
if (peers[id] == e.peer) delete peers[id]
|
||||
})
|
||||
log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port)
|
||||
break
|
||||
case "receive":
|
||||
} else if (e.type == "receive") {
|
||||
var data = nota.decode(e.data)
|
||||
if (data.replycc && !data.replycc.address) {
|
||||
data.replycc[ACTORDATA].address = e.peer.address
|
||||
@@ -453,7 +463,6 @@ function handle_host(e) {
|
||||
}
|
||||
if (data.data) populate_actor_addresses(data.data)
|
||||
turn(data)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,10 +496,14 @@ $_.stop = function stop(actor) {
|
||||
need_stop = true
|
||||
return
|
||||
}
|
||||
if (!is_actor(actor))
|
||||
throw Error('Can only call stop on an actor.')
|
||||
if (is_null(underlings[actor[ACTORDATA].id]))
|
||||
throw Error('Can only call stop on an underling or self.')
|
||||
if (!is_actor(actor)) {
|
||||
log.error('Can only call stop on an actor.')
|
||||
disrupt
|
||||
}
|
||||
if (is_null(underlings[actor[ACTORDATA].id])) {
|
||||
log.error('Can only call stop on an underling or self.')
|
||||
disrupt
|
||||
}
|
||||
|
||||
sys_msg(actor, {kind:"stop"})
|
||||
}
|
||||
@@ -527,20 +540,22 @@ function actor_prep(actor, send) {
|
||||
|
||||
// Send a message immediately without queuing
|
||||
function actor_send_immediate(actor, send) {
|
||||
try {
|
||||
actor_send(actor, send);
|
||||
} catch (err) {
|
||||
log.error("Failed to send immediate message:", err);
|
||||
}
|
||||
actor_send(actor, send)
|
||||
}
|
||||
|
||||
function actor_send(actor, message) {
|
||||
if (actor[HEADER] && !actor[HEADER].replycc) // attempting to respond to a message but sender is not expecting; silently drop
|
||||
return
|
||||
|
||||
if (!is_actor(actor) && !is_actor(actor.replycc)) throw Error(`Must send to an actor object. Attempted send to ${actor}`)
|
||||
if (!is_actor(actor) && !is_actor(actor.replycc)) {
|
||||
log.error(`Must send to an actor object. Attempted send to ${actor}`)
|
||||
disrupt
|
||||
}
|
||||
|
||||
if (!is_object(message)) throw Error('Must send an object record.')
|
||||
if (!is_object(message)) {
|
||||
log.error('Must send an object record.')
|
||||
disrupt
|
||||
}
|
||||
|
||||
// message to self
|
||||
if (actor[ACTORDATA].id == _cell.id) {
|
||||
@@ -583,12 +598,10 @@ function actor_send(actor, message) {
|
||||
// Holds all messages queued during the current turn.
|
||||
var message_queue = []
|
||||
|
||||
var need_stop = false
|
||||
|
||||
function send_messages() {
|
||||
function send_messages() {
|
||||
// if we've been flagged to stop, bail out before doing anything
|
||||
if (need_stop) {
|
||||
disrupt()
|
||||
actor_die()
|
||||
message_queue = []
|
||||
return
|
||||
}
|
||||
@@ -608,19 +621,26 @@ var need_stop = false
|
||||
var replies = {}
|
||||
|
||||
function send(actor, message, reply) {
|
||||
if (!is_object(actor))
|
||||
throw Error(`Must send to an actor object. Provided: ${actor}`);
|
||||
if (!is_object(actor)) {
|
||||
log.error(`Must send to an actor object. Provided: ${actor}`)
|
||||
disrupt
|
||||
}
|
||||
|
||||
if (!is_object(message))
|
||||
throw Error('Message must be an object')
|
||||
if (!is_object(message)) {
|
||||
log.error('Message must be an object')
|
||||
disrupt
|
||||
}
|
||||
var send_msg = {type:"user", data: message}
|
||||
var target = actor
|
||||
|
||||
if (actor[HEADER] && actor[HEADER].replycc) {
|
||||
var header = actor[HEADER]
|
||||
if (!header.replycc || !is_actor(header.replycc))
|
||||
throw Error(`Supplied actor had a return, but it's not a valid actor! ${actor[HEADER]}`)
|
||||
if (!header.replycc || !is_actor(header.replycc)) {
|
||||
log.error(`Supplied actor had a return, but it's not a valid actor! ${actor[HEADER]}`)
|
||||
disrupt
|
||||
}
|
||||
|
||||
actor = header.replycc
|
||||
target = header.replycc
|
||||
send_msg.return = header.reply
|
||||
}
|
||||
|
||||
@@ -638,7 +658,7 @@ function send(actor, message, reply) {
|
||||
}
|
||||
|
||||
// Instead of sending immediately, queue it
|
||||
actor_prep(actor, send_msg);
|
||||
actor_prep(target, send_msg);
|
||||
}
|
||||
|
||||
stone(send)
|
||||
@@ -669,7 +689,7 @@ overling = _cell.args.overling
|
||||
$_.overling = overling
|
||||
|
||||
root = _cell.args.root
|
||||
root ??= $_.self
|
||||
if (root == null) root = $_.self
|
||||
|
||||
if (overling) {
|
||||
$_.couple(overling) // auto couple to overling
|
||||
@@ -705,36 +725,35 @@ function handle_actor_disconnect(id) {
|
||||
delete greeters[id]
|
||||
}
|
||||
log.system(`actor ${id} disconnected`)
|
||||
if (!is_null(couplings[id])) disrupt("coupled actor died") // couplings now disrupts instead of stop
|
||||
if (!is_null(couplings[id])) actor_die("coupled actor died") // couplings now disrupts instead of stop
|
||||
}
|
||||
|
||||
function handle_sysym(msg)
|
||||
{
|
||||
var from
|
||||
switch(msg.kind) {
|
||||
case 'stop':
|
||||
disrupt("got stop message")
|
||||
break
|
||||
case 'underling':
|
||||
if (msg.kind == 'stop') {
|
||||
actor_die("got stop message")
|
||||
} else if (msg.kind == 'underling') {
|
||||
from = msg.from
|
||||
var greeter = greeters[from[ACTORDATA].id]
|
||||
if (greeter) greeter(msg.message)
|
||||
if (msg.message.type == 'disrupt')
|
||||
delete underlings[from[ACTORDATA].id]
|
||||
break
|
||||
case 'contact':
|
||||
} else if (msg.kind == 'contact') {
|
||||
if (portal_fn) {
|
||||
var letter2 = msg.data
|
||||
letter2[HEADER] = msg
|
||||
delete msg.data
|
||||
portal_fn(letter2)
|
||||
} else throw Error('Got a contact message, but no portal is established.')
|
||||
break
|
||||
case 'couple': // from must be notified when we die
|
||||
} else {
|
||||
log.error('Got a contact message, but no portal is established.')
|
||||
disrupt
|
||||
}
|
||||
} else if (msg.kind == 'couple') {
|
||||
// from must be notified when we die
|
||||
from = msg.from
|
||||
underlings[from[ACTORDATA].id] = true
|
||||
log.system(`actor ${from} is coupled to me`)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,8 +763,7 @@ function handle_message(msg) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (msg.type) {
|
||||
case "user":
|
||||
if (msg.type == "user") {
|
||||
var letter = msg.data // what the sender really sent
|
||||
_ObjectDefineProperty(letter, HEADER, {
|
||||
value: msg, enumerable: false
|
||||
@@ -762,12 +780,10 @@ function handle_message(msg) {
|
||||
}
|
||||
|
||||
if (receive_fn) receive_fn(letter)
|
||||
return
|
||||
case "stopped":
|
||||
} else if (msg.type == "stopped") {
|
||||
handle_actor_disconnect(msg.id)
|
||||
break
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function enet_check()
|
||||
{
|
||||
@@ -792,8 +808,10 @@ if (!locator) {
|
||||
locator = shop.resolve_locator(_cell.args.program + ".ce", pkg)
|
||||
}
|
||||
|
||||
if (!locator)
|
||||
throw Error(`Main program ${_cell.args.program} could not be found`)
|
||||
if (!locator) {
|
||||
os.print(`Main program ${_cell.args.program} could not be found\n`)
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
$_.clock(_ => {
|
||||
// Get capabilities for the main program
|
||||
@@ -818,7 +836,6 @@ $_.clock(_ => {
|
||||
var val = call(locator.symbol, null, [_cell.args.arg, use_fn, env])
|
||||
|
||||
if (val)
|
||||
throw Error('Program must not return anything');
|
||||
log.error('Program must not return anything')
|
||||
disrupt
|
||||
})
|
||||
|
||||
})()
|
||||
@@ -577,7 +577,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||
|
||||
JSValue js_os_use(JSContext *js) {
|
||||
JS_NewClassID(&js_dylib_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_dylib_class_id, &js_dylib_class);
|
||||
JS_NewClass(js, js_dylib_class_id, &js_dylib_class);
|
||||
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs));
|
||||
|
||||
@@ -44,7 +44,12 @@ src += [ # core
|
||||
'wildmatch.c',
|
||||
'qjs_actor.c',
|
||||
'miniz.c',
|
||||
'quickjs.c',
|
||||
'runtime.c',
|
||||
'cell_js.c',
|
||||
'tokenize.c',
|
||||
'parse.c',
|
||||
'mach.c',
|
||||
'mcode.c',
|
||||
'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c'
|
||||
]
|
||||
|
||||
|
||||
@@ -571,13 +571,13 @@ static const JSCFunctionListEntry js_enet_peer_funcs[] = {
|
||||
JSValue js_enet_use(JSContext *ctx)
|
||||
{
|
||||
JS_NewClassID(&enet_host_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), enet_host_id, &enet_host);
|
||||
JS_NewClass(ctx, enet_host_id, &enet_host);
|
||||
JSValue host_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, host_proto, js_enet_host_funcs, countof(js_enet_host_funcs));
|
||||
JS_SetClassProto(ctx, enet_host_id, host_proto);
|
||||
|
||||
JS_NewClassID(&enet_peer_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), enet_peer_class_id, &enet_peer_class);
|
||||
JS_NewClass(ctx, enet_peer_class_id, &enet_peer_class);
|
||||
JSValue peer_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, peer_proto, js_enet_peer_funcs, countof(js_enet_peer_funcs));
|
||||
JS_SetClassProto(ctx, enet_peer_class_id, peer_proto);
|
||||
|
||||
4
qop.c
4
qop.c
@@ -458,13 +458,13 @@ static const JSCFunctionListEntry js_qop_funcs[] = {
|
||||
|
||||
JSValue js_qop_use(JSContext *js) {
|
||||
JS_NewClassID(&js_qop_archive_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_qop_archive_class_id, &js_qop_archive_class);
|
||||
JS_NewClass(js, js_qop_archive_class_id, &js_qop_archive_class);
|
||||
JSValue archive_proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, archive_proto, js_qop_archive_funcs, countof(js_qop_archive_funcs));
|
||||
JS_SetClassProto(js, js_qop_archive_class_id, archive_proto);
|
||||
|
||||
JS_NewClassID(&js_qop_writer_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_qop_writer_class_id, &js_qop_writer_class);
|
||||
JS_NewClass(js, js_qop_writer_class_id, &js_qop_writer_class);
|
||||
JSValue writer_proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, writer_proto, js_qop_writer_funcs, countof(js_qop_writer_funcs));
|
||||
JS_SetClassProto(js, js_qop_writer_class_id, writer_proto);
|
||||
|
||||
@@ -26,6 +26,7 @@ static int run_test_suite(size_t heap_size);
|
||||
|
||||
cell_rt *root_cell = NULL;
|
||||
static char *core_path = NULL;
|
||||
static JSRuntime *g_runtime = NULL;
|
||||
|
||||
// Get the home directory
|
||||
static const char* get_home_dir(void) {
|
||||
@@ -165,17 +166,31 @@ JSValue js_wota_use(JSContext *js);
|
||||
|
||||
void script_startup(cell_rt *prt)
|
||||
{
|
||||
JSRuntime *rt = JS_NewRuntime();
|
||||
JS_SetInterruptHandler(rt, (JSInterruptHandler *)actor_interrupt_cb, prt);
|
||||
JSContext *js = JS_NewContext(rt);
|
||||
if (!g_runtime) {
|
||||
g_runtime = JS_NewRuntime();
|
||||
}
|
||||
JSContext *js = JS_NewContext(g_runtime);
|
||||
JS_SetInterruptHandler(js, (JSInterruptHandler *)actor_interrupt_cb, prt);
|
||||
|
||||
JS_SetContextOpaque(js, prt);
|
||||
prt->context = js;
|
||||
|
||||
/* Register all GCRef fields so the Cheney GC can relocate them. */
|
||||
JS_AddGCRef(js, &prt->idx_buffer_ref);
|
||||
JS_AddGCRef(js, &prt->on_exception_ref);
|
||||
JS_AddGCRef(js, &prt->message_handle_ref);
|
||||
JS_AddGCRef(js, &prt->unneeded_ref);
|
||||
JS_AddGCRef(js, &prt->actor_sym_ref);
|
||||
prt->idx_buffer_ref.val = JS_NULL;
|
||||
prt->on_exception_ref.val = JS_NULL;
|
||||
prt->message_handle_ref.val = JS_NULL;
|
||||
prt->unneeded_ref.val = JS_NULL;
|
||||
prt->actor_sym_ref.val = JS_NULL;
|
||||
|
||||
cell_rt *crt = JS_GetContextOpaque(js);
|
||||
JS_FreeValue(js, js_blob_use(js));
|
||||
|
||||
// Load and compile engine.cm
|
||||
// Load and parse engine.cm to AST
|
||||
size_t engine_size;
|
||||
char *data = load_core_file(ENGINE, &engine_size);
|
||||
if (!data) {
|
||||
@@ -183,10 +198,15 @@ void script_startup(cell_rt *prt)
|
||||
return;
|
||||
}
|
||||
|
||||
JSValue bytecode = JS_Compile(js, data, engine_size, ENGINE);
|
||||
cJSON *ast = JS_ASTTree(data, engine_size, ENGINE);
|
||||
free(data);
|
||||
if (JS_IsException(bytecode)) {
|
||||
uncaught_exception(js, bytecode);
|
||||
if (!ast) {
|
||||
printf("ERROR: Failed to parse %s\n", ENGINE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (print_tree_errors(ast)) {
|
||||
cJSON_Delete(ast);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -197,8 +217,8 @@ void script_startup(cell_rt *prt)
|
||||
JS_SetPropertyStr(js, hidden_env, "nota", js_nota_use(js));
|
||||
JS_SetPropertyStr(js, hidden_env, "wota", js_wota_use(js));
|
||||
|
||||
crt->actor_sym = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, hidden_env, "actorsym", JS_DupValue(js, crt->actor_sym));
|
||||
crt->actor_sym_ref.val = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, hidden_env, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val));
|
||||
|
||||
// Always set init (even if null)
|
||||
if (crt->init_wota) {
|
||||
@@ -216,9 +236,10 @@ void script_startup(cell_rt *prt)
|
||||
// Stone the environment
|
||||
hidden_env = JS_Stone(js, hidden_env);
|
||||
|
||||
// Integrate and run
|
||||
// Run through MACH VM
|
||||
crt->state = ACTOR_RUNNING;
|
||||
JSValue v = JS_Integrate(js, bytecode, hidden_env);
|
||||
JSValue v = JS_RunMachTree(js, ast, hidden_env);
|
||||
cJSON_Delete(ast);
|
||||
uncaught_exception(js, v);
|
||||
crt->state = ACTOR_IDLE;
|
||||
set_actor_state(crt);
|
||||
|
||||
@@ -144,7 +144,7 @@ JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYP
|
||||
|
||||
#define QJSCLASSPREP_NO_FUNCS(TYPE) \
|
||||
JS_NewClassID(&js_##TYPE##_id);\
|
||||
JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\
|
||||
JS_NewClass(js, js_##TYPE##_id, &js_##TYPE##_class);\
|
||||
JSValue TYPE##_proto = JS_NewObject(js); \
|
||||
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
|
||||
|
||||
|
||||
@@ -24,9 +24,14 @@ typedef struct letter {
|
||||
|
||||
typedef struct cell_rt {
|
||||
JSContext *context;
|
||||
JSValue idx_buffer;
|
||||
JSValue on_exception;
|
||||
JSValue message_handle;
|
||||
|
||||
/* JSValues on the GC heap — each paired with a JSGCRef so the
|
||||
Cheney GC can relocate them during compaction. */
|
||||
JSGCRef idx_buffer_ref;
|
||||
JSGCRef on_exception_ref;
|
||||
JSGCRef message_handle_ref;
|
||||
JSGCRef unneeded_ref;
|
||||
JSGCRef actor_sym_ref;
|
||||
|
||||
void *init_wota;
|
||||
|
||||
@@ -38,7 +43,7 @@ typedef struct cell_rt {
|
||||
|
||||
int idx_count;
|
||||
|
||||
/* The “mailbox” for incoming messages + a dedicated lock for it: */
|
||||
/* The "mailbox" for incoming messages + a dedicated lock for it: */
|
||||
letter *letters;
|
||||
|
||||
/* CHANGED FOR EVENTS: a separate lock for the actor->events queue */
|
||||
@@ -47,14 +52,11 @@ typedef struct cell_rt {
|
||||
int state;
|
||||
uint32_t ar; // timer for unneeded
|
||||
double ar_secs; // time for unneeded
|
||||
JSValue unneeded; // fn to call before unneeded
|
||||
|
||||
int disrupt;
|
||||
int main_thread_only;
|
||||
int affinity;
|
||||
|
||||
JSValue actor_sym;
|
||||
|
||||
const char *name; // human friendly name
|
||||
cell_hook trace_hook;
|
||||
} cell_rt;
|
||||
@@ -63,8 +65,6 @@ cell_rt *create_actor(void *wota);
|
||||
const char *register_actor(const char *id, cell_rt *actor, int mainthread, double ar);
|
||||
void actor_disrupt(cell_rt *actor);
|
||||
|
||||
JSValue actor_sym(cell_rt *actor);
|
||||
|
||||
const char *send_message(const char *id, void *msg);
|
||||
const char *register_actor(const char *id, cell_rt *actor, int mainthread, double ar);
|
||||
void actor_unneeded(cell_rt *actor, JSValue fn, double seconds);
|
||||
|
||||
12740
source/cell_js.c
Normal file
12740
source/cell_js.c
Normal file
File diff suppressed because it is too large
Load Diff
3316
source/mach.c
Normal file
3316
source/mach.c
Normal file
File diff suppressed because it is too large
Load Diff
3461
source/mcode.c
Normal file
3461
source/mcode.c
Normal file
File diff suppressed because it is too large
Load Diff
2087
source/parse.c
Normal file
2087
source/parse.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -74,7 +74,7 @@ JSC_CCALL(os_register_actor,
|
||||
JS_ToFloat64(js, &ar, argv[3]);
|
||||
const char *err = register_actor(id, rt, JS_ToBool(js, argv[2]), ar);
|
||||
if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err);
|
||||
rt->message_handle = JS_DupValue(js, argv[1]);
|
||||
rt->message_handle_ref.val = argv[1];
|
||||
rt->context = js;
|
||||
JS_FreeCString(js, id);
|
||||
)
|
||||
@@ -106,8 +106,7 @@ JSC_SCALL(actor_setname,
|
||||
|
||||
JSC_CCALL(actor_on_exception,
|
||||
cell_rt *rt = JS_GetContextOpaque(js);
|
||||
JS_FreeValue(js, rt->on_exception);
|
||||
rt->on_exception = JS_DupValue(js,argv[0]);
|
||||
rt->on_exception_ref.val = argv[0];
|
||||
)
|
||||
|
||||
JSC_CCALL(actor_clock,
|
||||
|
||||
2007
source/quickjs-internal.h
Normal file
2007
source/quickjs-internal.h
Normal file
File diff suppressed because it is too large
Load Diff
38471
source/quickjs.c
38471
source/quickjs.c
File diff suppressed because it is too large
Load Diff
@@ -367,10 +367,10 @@ JSRuntime *JS_NewRuntime (void);
|
||||
void JS_SetRuntimeInfo (JSRuntime *rt, const char *info);
|
||||
void JS_SetMemoryLimit (JSRuntime *rt, size_t limit);
|
||||
/* use 0 to disable maximum stack size check */
|
||||
void JS_SetMaxStackSize (JSRuntime *rt, size_t stack_size);
|
||||
void JS_SetMaxStackSize (JSContext *ctx, size_t stack_size);
|
||||
/* should be called when changing thread to update the stack top value
|
||||
used to check stack overflow. */
|
||||
void JS_UpdateStackTop (JSRuntime *rt);
|
||||
void JS_UpdateStackTop (JSContext *ctx);
|
||||
void JS_FreeRuntime (JSRuntime *rt);
|
||||
void *JS_GetRuntimeOpaque (JSRuntime *rt);
|
||||
void JS_SetRuntimeOpaque (JSRuntime *rt, void *opaque);
|
||||
@@ -429,9 +429,9 @@ JSClassID JS_NewClassID (JSClassID *pclass_id);
|
||||
/* Returns the class ID if `v` is an object, otherwise returns
|
||||
* JS_INVALID_CLASS_ID. */
|
||||
JSClassID JS_GetClassID (JSValue v);
|
||||
int JS_NewClass (JSRuntime *rt, JSClassID class_id,
|
||||
int JS_NewClass (JSContext *ctx, JSClassID class_id,
|
||||
const JSClassDef *class_def);
|
||||
int JS_IsRegisteredClass (JSRuntime *rt, JSClassID class_id);
|
||||
int JS_IsRegisteredClass (JSContext *ctx, JSClassID class_id);
|
||||
|
||||
extern JSClassID js_class_id_alloc;
|
||||
|
||||
@@ -740,7 +740,7 @@ JSValue JS_JSONStringify (JSContext *ctx, JSValue obj,
|
||||
|
||||
/* return != 0 if the JS code needs to be interrupted */
|
||||
typedef int JSInterruptHandler (JSRuntime *rt, void *opaque);
|
||||
void JS_SetInterruptHandler (JSRuntime *rt, JSInterruptHandler *cb,
|
||||
void JS_SetInterruptHandler (JSContext *ctx, JSInterruptHandler *cb,
|
||||
void *opaque);
|
||||
/* select which debug info is stripped from the compiled code */
|
||||
#define JS_STRIP_SOURCE (1 << 0) /* strip source code */
|
||||
|
||||
13756
source/runtime.c
Normal file
13756
source/runtime.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -266,11 +266,11 @@ void actor_free(cell_rt *actor)
|
||||
|
||||
JSContext *js = actor->context;
|
||||
|
||||
JS_FreeValue(js, actor->idx_buffer);
|
||||
JS_FreeValue(js, actor->message_handle);
|
||||
JS_FreeValue(js, actor->on_exception);
|
||||
JS_FreeValue(js, actor->unneeded);
|
||||
JS_FreeValue(js, actor->actor_sym);
|
||||
JS_DeleteGCRef(js, &actor->idx_buffer_ref);
|
||||
JS_DeleteGCRef(js, &actor->on_exception_ref);
|
||||
JS_DeleteGCRef(js, &actor->message_handle_ref);
|
||||
JS_DeleteGCRef(js, &actor->unneeded_ref);
|
||||
JS_DeleteGCRef(js, &actor->actor_sym_ref);
|
||||
|
||||
for (int i = 0; i < hmlen(actor->timers); i++) {
|
||||
JS_FreeValue(js, actor->timers[i].value);
|
||||
@@ -289,10 +289,8 @@ void actor_free(cell_rt *actor)
|
||||
|
||||
arrfree(actor->letters);
|
||||
|
||||
JSRuntime *rt = JS_GetRuntime(js);
|
||||
JS_SetInterruptHandler(rt, NULL, NULL);
|
||||
JS_SetInterruptHandler(js, NULL, NULL);
|
||||
JS_FreeContext(js);
|
||||
JS_FreeRuntime(rt);
|
||||
free(actor->id);
|
||||
|
||||
pthread_mutex_unlock(actor->mutex);
|
||||
@@ -419,8 +417,8 @@ uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
||||
|
||||
actor->disrupt = 1;
|
||||
|
||||
if (!JS_IsNull(actor->unneeded)) {
|
||||
JSValue ret = JS_Call(actor->context, actor->unneeded, JS_NULL, 0, NULL);
|
||||
if (!JS_IsNull(actor->unneeded_ref.val)) {
|
||||
JSValue ret = JS_Call(actor->context, actor->unneeded_ref.val, JS_NULL, 0, NULL);
|
||||
uncaught_exception(actor->context, ret);
|
||||
}
|
||||
|
||||
@@ -434,14 +432,13 @@ uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
||||
void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
|
||||
{
|
||||
if (actor->disrupt) return;
|
||||
JS_FreeValue(actor->context, actor->unneeded);
|
||||
|
||||
if (!JS_IsFunction(fn)) {
|
||||
actor->unneeded = JS_NULL;
|
||||
actor->unneeded_ref.val = JS_NULL;
|
||||
goto END;
|
||||
}
|
||||
|
||||
actor->unneeded = JS_DupValue(actor->context, fn);
|
||||
actor->unneeded_ref.val = fn;
|
||||
actor->ar_secs = seconds;
|
||||
|
||||
END:
|
||||
@@ -493,11 +490,10 @@ cell_rt *create_actor(void *wota)
|
||||
actor->heap = mi_heap_new();
|
||||
#endif
|
||||
actor->init_wota = wota;
|
||||
actor->idx_buffer = JS_NULL;
|
||||
actor->message_handle = JS_NULL;
|
||||
actor->unneeded = JS_NULL;
|
||||
actor->on_exception = JS_NULL;
|
||||
actor->actor_sym = JS_NULL;
|
||||
/* GCRef fields are registered after JSContext creation in script_startup.
|
||||
For now, zero-init from calloc is sufficient (val = 0 = JS_MKVAL(JS_TAG_INT,0),
|
||||
which is not a pointer so GC-safe). The actual JS_NULL assignment and
|
||||
JS_AddGCRef happen in script_startup. */
|
||||
|
||||
arrsetcap(actor->letters, 5);
|
||||
|
||||
@@ -587,7 +583,7 @@ void actor_turn(cell_rt *actor)
|
||||
size_t size = blob_length(l.blob_data) / 8; // Convert bits to bytes
|
||||
JSValue arg = js_new_blob_stoned_copy(actor->context, (void*)blob_data(l.blob_data), size);
|
||||
blob_destroy(l.blob_data);
|
||||
result = JS_Call(actor->context, actor->message_handle, JS_NULL, 1, &arg);
|
||||
result = JS_Call(actor->context, actor->message_handle_ref.val, JS_NULL, 1, &arg);
|
||||
uncaught_exception(actor->context, result);
|
||||
JS_FreeValue(actor->context, arg);
|
||||
} else if (l.type == LETTER_CALLBACK) {
|
||||
|
||||
@@ -118,11 +118,11 @@ void actor_free(cell_rt *actor)
|
||||
|
||||
JSContext *js = actor->context;
|
||||
|
||||
JS_FreeValue(js, actor->idx_buffer);
|
||||
JS_FreeValue(js, actor->message_handle);
|
||||
JS_FreeValue(js, actor->on_exception);
|
||||
JS_FreeValue(js, actor->unneeded);
|
||||
JS_FreeValue(js, actor->actor_sym);
|
||||
JS_DeleteGCRef(js, &actor->idx_buffer_ref);
|
||||
JS_DeleteGCRef(js, &actor->on_exception_ref);
|
||||
JS_DeleteGCRef(js, &actor->message_handle_ref);
|
||||
JS_DeleteGCRef(js, &actor->unneeded_ref);
|
||||
JS_DeleteGCRef(js, &actor->actor_sym_ref);
|
||||
|
||||
/* Free timer callbacks stored in actor */
|
||||
for (int i = 0; i < hmlen(actor->timers); i++) {
|
||||
@@ -142,10 +142,8 @@ void actor_free(cell_rt *actor)
|
||||
|
||||
arrfree(actor->letters);
|
||||
|
||||
JSRuntime *rt = JS_GetRuntime(js);
|
||||
JS_SetInterruptHandler(rt, NULL, NULL);
|
||||
JS_SetInterruptHandler(js, NULL, NULL);
|
||||
JS_FreeContext(js);
|
||||
JS_FreeRuntime(rt);
|
||||
free(actor->id);
|
||||
|
||||
free(actor);
|
||||
@@ -157,14 +155,13 @@ void actor_free(cell_rt *actor)
|
||||
void actor_unneeded(cell_rt *actor, JSValue fn, double seconds)
|
||||
{
|
||||
if (actor->disrupt) return;
|
||||
JS_FreeValue(actor->context, actor->unneeded);
|
||||
|
||||
if (!JS_IsFunction(actor->context, fn)) {
|
||||
actor->unneeded = JS_NULL;
|
||||
if (!JS_IsFunction(fn)) {
|
||||
actor->unneeded_ref.val = JS_NULL;
|
||||
goto END;
|
||||
}
|
||||
|
||||
actor->unneeded = JS_DupValue(actor->context, fn);
|
||||
actor->unneeded_ref.val = fn;
|
||||
actor->ar_secs = seconds;
|
||||
|
||||
END:
|
||||
@@ -257,8 +254,8 @@ uint32_t actor_remove_cb(cell_rt *actor, uint32_t id, uint32_t interval)
|
||||
|
||||
actor->disrupt = 1;
|
||||
|
||||
if (!JS_IsNull(actor->unneeded)) {
|
||||
JSValue ret = JS_Call(actor->context, actor->unneeded, JS_NULL, 0, NULL);
|
||||
if (!JS_IsNull(actor->unneeded_ref.val)) {
|
||||
JSValue ret = JS_Call(actor->context, actor->unneeded_ref.val, JS_NULL, 0, NULL);
|
||||
uncaught_exception(actor->context, ret);
|
||||
}
|
||||
|
||||
@@ -328,11 +325,7 @@ cell_rt *create_actor(void *wota)
|
||||
{
|
||||
cell_rt *actor = calloc(sizeof(*actor), 1);
|
||||
actor->init_wota = wota;
|
||||
actor->idx_buffer = JS_NULL;
|
||||
actor->message_handle = JS_NULL;
|
||||
actor->unneeded = JS_NULL;
|
||||
actor->on_exception = JS_NULL;
|
||||
actor->actor_sym = JS_NULL;
|
||||
/* GCRef fields are registered after JSContext creation in script_startup. */
|
||||
|
||||
arrsetcap(actor->letters, 5);
|
||||
|
||||
@@ -408,7 +401,7 @@ void actor_turn(cell_rt *actor)
|
||||
size_t size = blob_length(l.blob_data) / 8; // Convert bits to bytes
|
||||
JSValue arg = js_new_blob_stoned_copy(actor->context, (void *)blob_data(l.blob_data), size);
|
||||
blob_destroy(l.blob_data);
|
||||
result = JS_Call(actor->context, actor->message_handle, JS_NULL, 1, &arg);
|
||||
result = JS_Call(actor->context, actor->message_handle_ref.val, JS_NULL, 1, &arg);
|
||||
uncaught_exception(actor->context, result);
|
||||
JS_FreeValue(actor->context, arg);
|
||||
} else if (l.type == LETTER_CALLBACK) {
|
||||
|
||||
1432
source/tokenize.c
Normal file
1432
source/tokenize.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user