Compare commits
6 Commits
templatefi
...
cellfix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
970386a04b | ||
|
|
75ddb1fb4f | ||
|
|
bc9d8a338a | ||
|
|
80d314c58f | ||
|
|
611fba2b6f | ||
|
|
2fc7d333ad |
16
debug/js.c
16
debug/js.c
@@ -39,20 +39,22 @@ JSC_CCALL(os_calc_mem,
|
||||
JSC_SSCALL(os_eval,
|
||||
if (!str2) return JS_ThrowReferenceError(js, "Second argument should be the script.");
|
||||
if (!str) return JS_ThrowReferenceError(js, "First argument should be the name of the script.");
|
||||
ret = JS_Eval(js,str2,strlen(str2),str, 0);
|
||||
JSValue bytecode = JS_Compile(js, str2, strlen(str2), str);
|
||||
if (JS_IsException(bytecode)) return bytecode;
|
||||
ret = JS_Integrate(js, bytecode, JS_NULL);
|
||||
)
|
||||
|
||||
// Compile a string of JavaScript code into a function object.
|
||||
JSC_SSCALL(js_compile,
|
||||
if (!str2) return JS_ThrowReferenceError(js, "Second argument should be the script.");
|
||||
if (!str) return JS_ThrowReferenceError(js, "First argument should be the name of the script.");
|
||||
ret = JS_Eval(js, str2, strlen(str2), str, JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_FLAG_BACKTRACE_BARRIER);
|
||||
ret = JS_Compile(js, str2, strlen(str2), str);
|
||||
)
|
||||
|
||||
// Evaluate a function object in the current QuickJS context.
|
||||
JSC_CCALL(js_eval_compile,
|
||||
JS_DupValue(js,argv[0]);
|
||||
ret = JS_EvalFunction(js, argv[0]);
|
||||
// Link compiled bytecode with environment and execute.
|
||||
JSC_CCALL(js_integrate,
|
||||
JSValue env = (argc > 1 && !JS_IsNull(argv[1])) ? argv[1] : JS_NULL;
|
||||
ret = JS_Integrate(js, argv[0], env);
|
||||
)
|
||||
|
||||
// Compile a function object into a bytecode blob.
|
||||
@@ -92,7 +94,7 @@ static const JSCFunctionListEntry js_js_funcs[] = {
|
||||
MIST_FUNC_DEF(os, max_stacksize, 1),
|
||||
MIST_FUNC_DEF(os, eval, 2),
|
||||
MIST_FUNC_DEF(js, compile, 2),
|
||||
MIST_FUNC_DEF(js, eval_compile, 1),
|
||||
MIST_FUNC_DEF(js, integrate, 2),
|
||||
MIST_FUNC_DEF(js, compile_blob, 1),
|
||||
MIST_FUNC_DEF(js, compile_unblob, 1),
|
||||
MIST_FUNC_DEF(js, disassemble, 1),
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
(function engine() {
|
||||
var _cell = globalThis.cell
|
||||
delete globalThis.cell
|
||||
var ACTORDATA = _cell.hidden.actorsym
|
||||
(function engine() {
|
||||
// Hidden vars (os, actorsym, init, core_path) come from env
|
||||
var ACTORDATA = actorsym
|
||||
var SYSYM = '__SYSTEM__'
|
||||
|
||||
var hidden = _cell.hidden
|
||||
|
||||
var os = hidden.os;
|
||||
|
||||
_cell.os = null
|
||||
var _cell = {}
|
||||
|
||||
var dylib_ext
|
||||
|
||||
_cell.id ??= "newguy"
|
||||
|
||||
switch(os.platform()) {
|
||||
case 'Windows': dylib_ext = '.dll'; break;
|
||||
case 'macOS': dylib_ext = '.dylib'; break;
|
||||
@@ -28,8 +21,7 @@ function use_embed(name) {
|
||||
return load_internal("js_" + name + "_use")
|
||||
}
|
||||
|
||||
globalThis.logical = function(val1)
|
||||
{
|
||||
function logical(val1) {
|
||||
if (val1 == 0 || val1 == false || val1 == "false" || val1 == null)
|
||||
return false;
|
||||
if (val1 == 1 || val1 == true || val1 == "true")
|
||||
@@ -37,19 +29,19 @@ globalThis.logical = function(val1)
|
||||
return null;
|
||||
}
|
||||
|
||||
globalThis.some = function(arr, pred) {
|
||||
function some(arr, pred) {
|
||||
return find(arr, pred) != null
|
||||
}
|
||||
|
||||
globalThis.every = function(arr, pred) {
|
||||
function every(arr, pred) {
|
||||
return find(arr, x => not(pred(x))) == null
|
||||
}
|
||||
|
||||
globalThis.starts_with = function(str, prefix) {
|
||||
function starts_with(str, prefix) {
|
||||
return search(str, prefix) == 0
|
||||
}
|
||||
|
||||
globalThis.ends_with = function(str, suffix) {
|
||||
function ends_with(str, suffix) {
|
||||
return search(str, suffix, -length(suffix)) != null
|
||||
}
|
||||
|
||||
@@ -99,8 +91,7 @@ function use_core(path) {
|
||||
|
||||
var blob = use_core('blob')
|
||||
|
||||
globalThis.actor = function()
|
||||
{
|
||||
function actor() {
|
||||
|
||||
}
|
||||
|
||||
@@ -108,7 +99,7 @@ var actor_mod = use_core('actor')
|
||||
var wota = use_core('wota')
|
||||
var nota = use_core('nota')
|
||||
|
||||
globalThis.is_actor = function(value) {
|
||||
function is_actor(value) {
|
||||
return is_object(value) && value[ACTORDATA]
|
||||
}
|
||||
|
||||
@@ -138,10 +129,10 @@ function console_rec(line, file, msg) {
|
||||
// time: [${time.text("mb d yyyy h:nn:ss")}]
|
||||
}
|
||||
|
||||
globalThis.log = function(name, args) {
|
||||
function log(name, args) {
|
||||
var caller = caller_data(1)
|
||||
var msg = args[0]
|
||||
|
||||
|
||||
switch(name) {
|
||||
case 'console':
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
@@ -149,7 +140,7 @@ globalThis.log = function(name, args) {
|
||||
case 'error':
|
||||
msg = msg ?? Error()
|
||||
if (is_proto(msg, Error))
|
||||
msg = msg.name + ": " + msg.message + "\n" + msg.stack
|
||||
msg = msg.name + ": " + msg.message + "\n" + msg.stack
|
||||
os.print(console_rec(caller.line, caller.file, msg))
|
||||
break
|
||||
case 'system':
|
||||
@@ -201,9 +192,8 @@ function disrupt(err)
|
||||
|
||||
actor_mod.on_exception(disrupt)
|
||||
|
||||
_cell.args = _cell.hidden.init
|
||||
_cell.args ??= {}
|
||||
_cell.id ??= "newguy"
|
||||
_cell.args = init ?? {}
|
||||
_cell.id = "newguy"
|
||||
|
||||
function create_actor(desc = {id:guid()}) {
|
||||
var actor = {}
|
||||
@@ -224,10 +214,30 @@ var json = use_core('json')
|
||||
var time = use_core('time')
|
||||
|
||||
var pronto = use_core('pronto')
|
||||
globalThis.fallback = pronto.fallback
|
||||
globalThis.parallel = pronto.parallel
|
||||
globalThis.race = pronto.race
|
||||
globalThis.sequence = pronto.sequence
|
||||
var fallback = pronto.fallback
|
||||
var parallel = pronto.parallel
|
||||
var race = pronto.race
|
||||
var sequence = pronto.sequence
|
||||
|
||||
// Create runtime environment for modules
|
||||
var runtime_env = {
|
||||
logical: logical,
|
||||
some: some,
|
||||
every: every,
|
||||
starts_with: starts_with,
|
||||
ends_with: ends_with,
|
||||
actor: actor,
|
||||
is_actor: is_actor,
|
||||
log: log,
|
||||
send: send,
|
||||
fallback: fallback,
|
||||
parallel: parallel,
|
||||
race: race,
|
||||
sequence: sequence
|
||||
}
|
||||
|
||||
// Pass to os for shop to access
|
||||
os.runtime_env = runtime_env
|
||||
|
||||
$_.time_limit = function(requestor, seconds)
|
||||
{
|
||||
@@ -597,13 +607,13 @@ var need_stop = false
|
||||
|
||||
var replies = {}
|
||||
|
||||
globalThis.send = function send(actor, message, reply) {
|
||||
function send(actor, message, reply) {
|
||||
if (!is_object(actor))
|
||||
throw Error(`Must send to an actor object. Provided: ${actor}`);
|
||||
|
||||
|
||||
if (!is_object(message))
|
||||
throw Error('Message must be an object')
|
||||
var send = {type:"user", data: message}
|
||||
var send_msg = {type:"user", data: message}
|
||||
|
||||
if (actor[HEADER] && actor[HEADER].replycc) {
|
||||
var header = actor[HEADER]
|
||||
@@ -611,7 +621,7 @@ globalThis.send = function send(actor, message, reply) {
|
||||
throw Error(`Supplied actor had a return, but it's not a valid actor! ${actor[HEADER]}`)
|
||||
|
||||
actor = header.replycc
|
||||
send.return = header.reply
|
||||
send_msg.return = header.reply
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
@@ -623,12 +633,12 @@ globalThis.send = function send(actor, message, reply) {
|
||||
delete replies[id]
|
||||
}
|
||||
}, REPLYTIMEOUT)
|
||||
send.reply = id
|
||||
send.replycc = $_.self
|
||||
send_msg.reply = id
|
||||
send_msg.replycc = $_.self
|
||||
}
|
||||
|
||||
// Instead of sending immediately, queue it
|
||||
actor_prep(actor,send);
|
||||
actor_prep(actor, send_msg);
|
||||
}
|
||||
|
||||
stone(send)
|
||||
@@ -785,8 +795,6 @@ if (!locator) {
|
||||
if (!locator)
|
||||
throw Error(`Main program ${_cell.args.program} could not be found`)
|
||||
|
||||
stone(globalThis)
|
||||
|
||||
$_.clock(_ => {
|
||||
// Get capabilities for the main program
|
||||
var file_info = shop.file_info ? shop.file_info(locator.path) : null
|
||||
|
||||
@@ -379,7 +379,16 @@ Shop.get_script_capabilities = function(path) {
|
||||
}
|
||||
|
||||
function inject_env(inject) {
|
||||
// Start with runtime functions from engine
|
||||
var env = {}
|
||||
var rt = my$_.os ? my$_.os.runtime_env : null
|
||||
if (rt) {
|
||||
for (var k in rt) {
|
||||
env[k] = rt[k]
|
||||
}
|
||||
}
|
||||
|
||||
// Add capability injections
|
||||
for (var i = 0; i < length(inject); i++) {
|
||||
var inj = inject[i]
|
||||
var key = trim(inj, '$')
|
||||
@@ -391,6 +400,17 @@ function inject_env(inject) {
|
||||
|
||||
function inject_bindings_code(inject) {
|
||||
var lines = []
|
||||
|
||||
// Runtime function bindings
|
||||
var runtime_fns = ['logical', 'some', 'every', 'starts_with', 'ends_with',
|
||||
'actor', 'is_actor', 'log', 'send',
|
||||
'fallback', 'parallel', 'race', 'sequence']
|
||||
for (var i = 0; i < length(runtime_fns); i++) {
|
||||
var fn = runtime_fns[i]
|
||||
push(lines, `var ${fn} = env["${fn}"];`)
|
||||
}
|
||||
|
||||
// Capability bindings ($delay, $start, etc.)
|
||||
for (var i = 0; i < length(inject); i++) {
|
||||
var inj = inject[i]
|
||||
var key = trim(inj, '$')
|
||||
@@ -428,22 +448,21 @@ function resolve_mod_fn(path, pkg) {
|
||||
var inject = Shop.script_inject_for(file_info)
|
||||
var content = text(fd.slurp(path))
|
||||
var script = script_form(path, content, file_pkg, inject);
|
||||
|
||||
|
||||
var obj = pull_from_cache(stone(blob(script)))
|
||||
if (obj) {
|
||||
var fn = js.compile_unblob(obj)
|
||||
return js.eval_compile(fn)
|
||||
return js.integrate(fn, null)
|
||||
}
|
||||
|
||||
|
||||
// Compile name is just for debug/stack traces
|
||||
// var compile_name = pkg ? pkg + ':' + path : 'local:' + path
|
||||
var compile_name = path
|
||||
|
||||
|
||||
var fn = js.compile(compile_name, script)
|
||||
|
||||
|
||||
put_into_cache(stone(blob(script)), js.compile_blob(fn))
|
||||
|
||||
return js.eval_compile(fn)
|
||||
|
||||
return js.integrate(fn, null)
|
||||
}
|
||||
|
||||
// given a path and a package context
|
||||
|
||||
132
source/cell.c
132
source/cell.c
@@ -121,13 +121,10 @@ JSValue js_math_use(JSContext *js);
|
||||
|
||||
void script_startup(cell_rt *prt)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
|
||||
rt = JS_NewRuntime();
|
||||
|
||||
JSRuntime *rt = JS_NewRuntime();
|
||||
JSContext *js = JS_NewContextRaw(rt);
|
||||
JS_SetInterruptHandler(rt, (JSInterruptHandler *)actor_interrupt_cb, prt);
|
||||
|
||||
|
||||
JS_AddIntrinsicBaseObjects(js);
|
||||
JS_AddIntrinsicEval(js);
|
||||
JS_AddIntrinsicRegExp(js);
|
||||
@@ -139,38 +136,7 @@ void script_startup(cell_rt *prt)
|
||||
cell_rt *crt = JS_GetContextOpaque(js);
|
||||
JS_FreeValue(js, js_blob_use(js));
|
||||
|
||||
JSValue globalThis = JS_GetGlobalObject(js);
|
||||
|
||||
JSValue cell = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js,globalThis,"cell", cell);
|
||||
|
||||
JSValue hidden_fn = JS_NewObject(js);
|
||||
|
||||
JS_SetPropertyStr(js, cell, "hidden", hidden_fn);
|
||||
JS_SetPropertyStr(js, hidden_fn, "os", js_os_use(js));
|
||||
|
||||
crt->actor_sym = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, hidden_fn, "actorsym", JS_DupValue(js,crt->actor_sym));
|
||||
|
||||
if (crt->init_wota) {
|
||||
JS_SetPropertyStr(js, hidden_fn, "init", wota2value(js, crt->init_wota));
|
||||
// init wota can now be freed
|
||||
free(crt->init_wota);
|
||||
crt->init_wota = NULL;
|
||||
}
|
||||
|
||||
// Store the core path for scripts to use
|
||||
JSValue js_cell = JS_GetPropertyStr(js, globalThis, "cell");
|
||||
JSValue hidden = JS_GetPropertyStr(js, js_cell, "hidden");
|
||||
if (core_path) {
|
||||
JS_SetPropertyStr(js, hidden, "core_path", JS_NewString(js, core_path));
|
||||
}
|
||||
JS_FreeValue(js, hidden);
|
||||
JS_FreeValue(js, js_cell);
|
||||
|
||||
JS_FreeValue(js, globalThis);
|
||||
|
||||
// Load engine.cm from the core directory
|
||||
// Load and compile engine.cm
|
||||
size_t engine_size;
|
||||
char *data = load_core_file(ENGINE, &engine_size);
|
||||
if (!data) {
|
||||
@@ -178,9 +144,39 @@ void script_startup(cell_rt *prt)
|
||||
return;
|
||||
}
|
||||
|
||||
crt->state = ACTOR_RUNNING;
|
||||
JSValue v = JS_Eval(js, data, engine_size, ENGINE, 0);
|
||||
JSValue bytecode = JS_Compile(js, data, engine_size, ENGINE);
|
||||
free(data);
|
||||
if (JS_IsException(bytecode)) {
|
||||
uncaught_exception(js, bytecode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create hidden environment
|
||||
JSValue hidden_env = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, hidden_env, "os", js_os_use(js));
|
||||
|
||||
crt->actor_sym = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, hidden_env, "actorsym", JS_DupValue(js, crt->actor_sym));
|
||||
|
||||
// Always set init (even if null)
|
||||
if (crt->init_wota) {
|
||||
JS_SetPropertyStr(js, hidden_env, "init", wota2value(js, crt->init_wota));
|
||||
free(crt->init_wota);
|
||||
crt->init_wota = NULL;
|
||||
} else {
|
||||
JS_SetPropertyStr(js, hidden_env, "init", JS_NULL);
|
||||
}
|
||||
|
||||
if (core_path) {
|
||||
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path));
|
||||
}
|
||||
|
||||
// Stone the environment
|
||||
hidden_env = JS_Stone(js, hidden_env);
|
||||
|
||||
// Integrate and run
|
||||
crt->state = ACTOR_RUNNING;
|
||||
JSValue v = JS_Integrate(js, bytecode, hidden_env);
|
||||
uncaught_exception(js, v);
|
||||
crt->state = ACTOR_IDLE;
|
||||
set_actor_state(crt);
|
||||
@@ -286,55 +282,21 @@ static int run_eval(const char *script_or_file, int print_bytecode)
|
||||
|
||||
int result = 0;
|
||||
|
||||
if (print_bytecode) {
|
||||
/* Compile only, then dump and optionally execute */
|
||||
JSValue func = JS_Eval(ctx, script, strlen(script), filename, JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
if (JS_IsException(func)) {
|
||||
uncaught_exception(ctx, func);
|
||||
result = 1;
|
||||
} else {
|
||||
printf("=== Compiled Bytecode ===\n");
|
||||
JS_DumpFunctionBytecode(ctx, func);
|
||||
|
||||
/* Link - resolve global references */
|
||||
JSValue linked = JS_LinkFunction(ctx, func);
|
||||
if (JS_IsException(linked)) {
|
||||
uncaught_exception(ctx, linked);
|
||||
result = 1;
|
||||
} else {
|
||||
printf("\n=== Linked Bytecode ===\n");
|
||||
JS_DumpFunctionBytecode(ctx, linked);
|
||||
|
||||
/* Now execute the linked bytecode */
|
||||
JSValue v = JS_EvalFunction(ctx, linked);
|
||||
if (JS_IsException(v)) {
|
||||
uncaught_exception(ctx, v);
|
||||
result = 1;
|
||||
} else {
|
||||
JS_FreeValue(ctx, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
JSValue bytecode = JS_Compile(ctx, script, strlen(script), filename);
|
||||
if (JS_IsException(bytecode)) {
|
||||
uncaught_exception(ctx, bytecode);
|
||||
result = 1;
|
||||
} else {
|
||||
/* Compile, link, execute */
|
||||
JSValue func = JS_Eval(ctx, script, strlen(script), filename, JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
if (JS_IsException(func)) {
|
||||
uncaught_exception(ctx, func);
|
||||
if (print_bytecode) {
|
||||
printf("=== Compiled Bytecode ===\n");
|
||||
JS_DumpFunctionBytecode(ctx, bytecode);
|
||||
}
|
||||
JSValue v = JS_Integrate(ctx, bytecode, JS_NULL);
|
||||
if (JS_IsException(v)) {
|
||||
uncaught_exception(ctx, v);
|
||||
result = 1;
|
||||
} else {
|
||||
JSValue linked = JS_LinkFunction(ctx, func);
|
||||
if (JS_IsException(linked)) {
|
||||
uncaught_exception(ctx, linked);
|
||||
result = 1;
|
||||
} else {
|
||||
JSValue v = JS_EvalFunction(ctx, linked);
|
||||
if (JS_IsException(v)) {
|
||||
uncaught_exception(ctx, v);
|
||||
result = 1;
|
||||
} else {
|
||||
JS_FreeValue(ctx, v);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
397
source/quickjs.c
397
source/quickjs.c
@@ -8521,12 +8521,10 @@ restart:
|
||||
int expr_count = get_u16 (pc); pc += 2;
|
||||
uint32_t cpool_idx = get_u32 (pc); pc += 4;
|
||||
|
||||
/* Expression values are on the stack. We'll process them in place,
|
||||
building the result string using pretext. */
|
||||
JSText *result = pretext_init (ctx, 64);
|
||||
/* Use PPretext (system malloc) to avoid GC issues during string building */
|
||||
PPretext *result = ppretext_init (64);
|
||||
if (!result) goto exception;
|
||||
|
||||
/* Re-read format_str after pretext_init (may have triggered GC) */
|
||||
JSValue format_str = b->cpool[cpool_idx];
|
||||
|
||||
/* Parse format string and substitute {N} with stringified stack values.
|
||||
@@ -8537,9 +8535,6 @@ restart:
|
||||
int pos = 0;
|
||||
|
||||
while (pos < fmt_len) {
|
||||
/* Re-read format_str in case GC moved the cpool entry */
|
||||
format_str = b->cpool[cpool_idx];
|
||||
|
||||
/* Find next '{' */
|
||||
int brace_start = -1;
|
||||
for (int i = pos; i < fmt_len; i++) {
|
||||
@@ -8551,23 +8546,20 @@ restart:
|
||||
|
||||
if (brace_start < 0) {
|
||||
/* No more braces, copy rest of string */
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
for (int i = pos; i < fmt_len; i++) {
|
||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
||||
result = ppretext_putc (result, js_string_value_get (format_str, i));
|
||||
if (!result) goto exception;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy text before brace */
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
for (int i = pos; i < brace_start; i++) {
|
||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
||||
result = ppretext_putc (result, js_string_value_get (format_str, i));
|
||||
if (!result) goto exception;
|
||||
}
|
||||
|
||||
/* Find closing '}' */
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
int brace_end = -1;
|
||||
for (int i = brace_start + 1; i < fmt_len; i++) {
|
||||
if (js_string_value_get (format_str, i) == '}') {
|
||||
@@ -8578,7 +8570,7 @@ restart:
|
||||
|
||||
if (brace_end < 0) {
|
||||
/* No closing brace, copy '{' and continue */
|
||||
result = pretext_putc (ctx, result, '{');
|
||||
result = ppretext_putc (result, '{');
|
||||
if (!result) goto exception;
|
||||
pos = brace_start + 1;
|
||||
continue;
|
||||
@@ -8586,7 +8578,6 @@ restart:
|
||||
|
||||
/* Parse index from {N} */
|
||||
int idx = 0;
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
for (int i = brace_start + 1; i < brace_end; i++) {
|
||||
uint32_t c = js_string_value_get (format_str, i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
@@ -8602,19 +8593,21 @@ restart:
|
||||
JSValue val = expr_base[idx];
|
||||
JSValue str_val = JS_ToString (ctx, val);
|
||||
if (JS_IsException (str_val)) {
|
||||
ppretext_free (result);
|
||||
goto exception;
|
||||
}
|
||||
/* Re-read format_str after JS_ToString (may have triggered GC) */
|
||||
format_str = b->cpool[cpool_idx];
|
||||
/* Append stringified value to result */
|
||||
int str_len = js_string_value_len (str_val);
|
||||
for (int i = 0; i < str_len; i++) {
|
||||
result = pretext_putc (ctx, result, js_string_value_get (str_val, i));
|
||||
result = ppretext_putc (result, js_string_value_get (str_val, i));
|
||||
if (!result) goto exception;
|
||||
}
|
||||
} else {
|
||||
/* Invalid index, keep original {N} */
|
||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
||||
for (int i = brace_start; i <= brace_end; i++) {
|
||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
||||
result = ppretext_putc (result, js_string_value_get (format_str, i));
|
||||
if (!result) goto exception;
|
||||
}
|
||||
}
|
||||
@@ -8622,8 +8615,8 @@ restart:
|
||||
pos = brace_end + 1;
|
||||
}
|
||||
|
||||
/* Finalize result string */
|
||||
JSValue result_str = pretext_end (ctx, result);
|
||||
/* Finalize result string - this is the only GC point */
|
||||
JSValue result_str = ppretext_end (ctx, result);
|
||||
if (JS_IsException (result_str)) goto exception;
|
||||
|
||||
/* Pop expr values, push result */
|
||||
@@ -11943,6 +11936,14 @@ static __exception int get_lvalue (JSParseState *s, int *popcode, int *pscope, J
|
||||
}
|
||||
}
|
||||
/* remove the last opcode */
|
||||
{
|
||||
const char *fn = JS_ToCString(s->ctx, fd->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("DEBUG: get_lvalue truncating bc from %zu to %d (opcode=%d)\n",
|
||||
fd->byte_code.size, fd->last_opcode_pos, opcode);
|
||||
}
|
||||
if (fn) JS_FreeCString(s->ctx, fn);
|
||||
}
|
||||
fd->byte_code.size = fd->last_opcode_pos;
|
||||
fd->last_opcode_pos = -1;
|
||||
|
||||
@@ -12069,7 +12070,23 @@ static void put_lvalue (JSParseState *s, int opcode, int scope, JSValue name, in
|
||||
emit_prop_key (s, name); /* name has refcount */
|
||||
break;
|
||||
case OP_get_array_el:
|
||||
{
|
||||
const char *fn = JS_ToCString(s->ctx, s->cur_func->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("DEBUG: emitting put_array_el at bc position %zu\n", s->cur_func->byte_code.size);
|
||||
}
|
||||
if (fn) JS_FreeCString(s->ctx, fn);
|
||||
}
|
||||
emit_op (s, OP_put_array_el);
|
||||
{
|
||||
const char *fn = JS_ToCString(s->ctx, s->cur_func->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("DEBUG: after emit, bc size=%zu, last byte=0x%02x\n",
|
||||
s->cur_func->byte_code.size,
|
||||
s->cur_func->byte_code.buf[s->cur_func->byte_code.size - 1]);
|
||||
}
|
||||
if (fn) JS_FreeCString(s->ctx, fn);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
@@ -13274,6 +13291,14 @@ static void emit_return (JSParseState *s, BOOL hasval) {
|
||||
top = top->prev;
|
||||
}
|
||||
|
||||
{
|
||||
const char *fn = JS_ToCString(s->ctx, s->cur_func->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("DEBUG: emitting return at bc position %zu, hasval=%d\n",
|
||||
s->cur_func->byte_code.size, hasval);
|
||||
}
|
||||
if (fn) JS_FreeCString(s->ctx, fn);
|
||||
}
|
||||
emit_op (s, hasval ? OP_return : OP_return_undef);
|
||||
}
|
||||
|
||||
@@ -15296,10 +15321,28 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
|
||||
}
|
||||
|
||||
line_num = 0; /* avoid warning */
|
||||
|
||||
/* Debug: check if this is create_actor */
|
||||
int is_create_actor = 0;
|
||||
if (!JS_IsNull(s->func_name)) {
|
||||
const char *fn = JS_ToCString(ctx, s->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
is_create_actor = 1;
|
||||
printf("DEBUG resolve_variables: processing create_actor, bc_len=%d\n", bc_len);
|
||||
}
|
||||
if (fn) JS_FreeCString(ctx, fn);
|
||||
}
|
||||
|
||||
for (pos = 0; pos < bc_len; pos = pos_next) {
|
||||
op = bc_buf[pos];
|
||||
len = opcode_info[op].size;
|
||||
pos_next = pos + len;
|
||||
|
||||
if (is_create_actor) {
|
||||
printf("DEBUG: pos=%d op=0x%02x len=%d bc_out.size=%zu\n",
|
||||
pos, op, len, bc_out.size);
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case OP_line_num:
|
||||
line_num = get_u32 (bc_buf + pos + 1);
|
||||
@@ -15387,8 +15430,12 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
|
||||
/* remove dead code */
|
||||
int line = -1;
|
||||
dbuf_put (&bc_out, bc_buf + pos, len);
|
||||
int old_pos = pos + len;
|
||||
pos = skip_dead_code (s, bc_buf, bc_len, pos + len, &line);
|
||||
pos_next = pos;
|
||||
if (is_create_actor && pos != old_pos) {
|
||||
printf("DEBUG: skip_dead_code skipped from %d to %d (skipped %d bytes)\n", old_pos, pos, pos - old_pos);
|
||||
}
|
||||
if (pos < bc_len && line >= 0 && line_num != line) {
|
||||
line_num = line;
|
||||
s->line_number_size++;
|
||||
@@ -15515,6 +15562,10 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
|
||||
}
|
||||
}
|
||||
|
||||
if (is_create_actor) {
|
||||
printf("DEBUG: resolve_variables finished. bc_out.size=%zu (was bc_len=%d)\n", bc_out.size, bc_len);
|
||||
}
|
||||
|
||||
/* set the new byte code */
|
||||
dbuf_free (&s->byte_code);
|
||||
s->byte_code = bc_out;
|
||||
@@ -15778,6 +15829,21 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
|
||||
cc.bc_len = bc_len = s->byte_code.size;
|
||||
js_dbuf_init (ctx, &bc_out);
|
||||
|
||||
/* Debug: dump original bytecode for create_actor */
|
||||
if (!JS_IsNull(s->func_name)) {
|
||||
const char *fn = JS_ToCString(ctx, s->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("=== ORIGINAL bytecode for %s ===\n", fn);
|
||||
printf("bc_len=%d\n", bc_len);
|
||||
for (int di = 0; di < bc_len; di++) {
|
||||
printf("%02x ", bc_buf[di]);
|
||||
if ((di + 1) % 16 == 0) printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
if (fn) JS_FreeCString(ctx, fn);
|
||||
}
|
||||
|
||||
#if SHORT_OPCODES
|
||||
if (s->jump_size) {
|
||||
s->jump_slots
|
||||
@@ -16337,6 +16403,10 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
|
||||
assert (label_slots[i].first_reloc == NULL);
|
||||
}
|
||||
#if SHORT_OPCODES
|
||||
/* NOTE: Jump shrink optimization disabled due to bytecode corruption bug.
|
||||
The memmove-based shrinking was not correctly updating all position
|
||||
references, leading to invalid opcodes (0 bytes) in the output. */
|
||||
#if 0
|
||||
if (OPTIMIZE) {
|
||||
/* more jump optimizations */
|
||||
int patch_offsets = 0;
|
||||
@@ -16410,6 +16480,7 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
pjs_free (s->jump_slots);
|
||||
s->jump_slots = NULL;
|
||||
#endif
|
||||
@@ -16427,6 +16498,20 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
|
||||
JS_ThrowOutOfMemory (ctx);
|
||||
return -1;
|
||||
}
|
||||
/* Debug: dump bytecode for create_actor */
|
||||
if (!JS_IsNull(s->func_name)) {
|
||||
const char *fn = JS_ToCString(ctx, s->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("=== resolve_labels output for %s ===\n", fn);
|
||||
printf("bc_len=%zu\n", bc_out.size);
|
||||
for (size_t di = 0; di < bc_out.size; di++) {
|
||||
printf("%02x ", bc_out.buf[di]);
|
||||
if ((di + 1) % 16 == 0) printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
if (fn) JS_FreeCString(ctx, fn);
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
/* XXX: not safe */
|
||||
@@ -16437,6 +16522,7 @@ fail:
|
||||
/* compute the maximum stack size needed by the function */
|
||||
|
||||
typedef struct StackSizeState {
|
||||
const uint8_t *bc_buf;
|
||||
int bc_len;
|
||||
int stack_len_max;
|
||||
uint16_t *stack_level_tab;
|
||||
@@ -16444,10 +16530,61 @@ typedef struct StackSizeState {
|
||||
int *pc_stack;
|
||||
int pc_stack_len;
|
||||
int pc_stack_size;
|
||||
/* predecessor tracking for debugging */
|
||||
int *pred_pc; /* predecessor PC that first set stack at pos */
|
||||
int *pred_stack; /* stack height at predecessor */
|
||||
uint8_t *is_op_start; /* bitmap: 1 if pos is start of instruction */
|
||||
const char *func_name; /* function name for error messages */
|
||||
} StackSizeState;
|
||||
|
||||
static void print_backtrace_chain (StackSizeState *s, int pos, int max_depth) {
|
||||
printf (" backtrace from pc=%d:\n", pos);
|
||||
int depth = 0;
|
||||
while (pos >= 0 && depth < max_depth) {
|
||||
uint8_t op = s->bc_buf[pos];
|
||||
#ifdef DUMP_BYTECODE
|
||||
const JSOpCode *oi = &short_opcode_info (op);
|
||||
printf (" [%d] pc=%d op=%s stack=%d\n",
|
||||
depth, pos, oi->name, s->stack_level_tab[pos]);
|
||||
#else
|
||||
printf (" [%d] pc=%d op=%d stack=%d\n",
|
||||
depth, pos, op, s->stack_level_tab[pos]);
|
||||
#endif
|
||||
pos = s->pred_pc[pos];
|
||||
depth++;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_instructions_around (StackSizeState *s, int pos, int window) {
|
||||
printf (" bytecode around pc=%d:\n", pos);
|
||||
/* Find start position (scan backwards for opcode boundaries) */
|
||||
int start = pos;
|
||||
for (int i = 0; i < window && start > 0; ) {
|
||||
start--;
|
||||
if (s->is_op_start[start]) i++;
|
||||
}
|
||||
/* Dump instructions */
|
||||
int p = start;
|
||||
while (p < s->bc_len && p < pos + window * 5) {
|
||||
if (!s->is_op_start[p]) { p++; continue; }
|
||||
uint8_t op = s->bc_buf[p];
|
||||
const JSOpCode *oi = &short_opcode_info (op);
|
||||
char marker = (p == pos) ? '>' : ' ';
|
||||
#ifdef DUMP_BYTECODE
|
||||
printf (" %c %5d: %-20s size=%d stack_in=%d\n",
|
||||
marker, p, oi->name, oi->size,
|
||||
s->stack_level_tab[p] != 0xffff ? (int)s->stack_level_tab[p] : -1);
|
||||
#else
|
||||
printf (" %c %5d: op=%-3d size=%d stack_in=%d\n",
|
||||
marker, p, op, oi->size,
|
||||
s->stack_level_tab[p] != 0xffff ? (int)s->stack_level_tab[p] : -1);
|
||||
#endif
|
||||
p += oi->size;
|
||||
}
|
||||
}
|
||||
|
||||
/* 'op' is only used for error indication */
|
||||
static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int op, int stack_len, int catch_pos) {
|
||||
static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int op, int stack_len, int catch_pos, int from_pos) {
|
||||
if ((unsigned)pos >= s->bc_len) {
|
||||
JS_ThrowInternalError (ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
|
||||
return -1;
|
||||
@@ -16462,9 +16599,74 @@ static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int
|
||||
if (s->stack_level_tab[pos] != 0xffff) {
|
||||
/* already explored: check that the stack size is consistent */
|
||||
if (s->stack_level_tab[pos] != stack_len) {
|
||||
JS_ThrowInternalError (ctx, "inconsistent stack size: %d %d (pc=%d)", s->stack_level_tab[pos], stack_len, pos);
|
||||
uint8_t op_at_pos = s->bc_buf[pos];
|
||||
#ifdef DUMP_BYTECODE
|
||||
const JSOpCode *oi = &short_opcode_info (op_at_pos);
|
||||
#endif
|
||||
|
||||
/* Check if pos is a valid opcode boundary */
|
||||
int boundary_valid = s->is_op_start[pos];
|
||||
|
||||
printf ("=== STACK ANALYSIS ERROR ===\n");
|
||||
printf ("Function: %s\n", s->func_name ? s->func_name : "?");
|
||||
printf ("Inconsistent stack at pc=%d:\n", pos);
|
||||
printf (" expected: %d (first path)\n", s->stack_level_tab[pos]);
|
||||
printf (" got: %d (conflicting path)\n", stack_len);
|
||||
#ifdef DUMP_BYTECODE
|
||||
printf (" opcode at pos: %s (%d)\n", oi->name, op_at_pos);
|
||||
#else
|
||||
printf (" opcode at pos: %d\n", op_at_pos);
|
||||
#endif
|
||||
printf (" valid opcode boundary: %s\n", boundary_valid ? "YES" : "NO");
|
||||
|
||||
printf ("\nFirst path that reached pc=%d:\n", pos);
|
||||
print_backtrace_chain (s, pos, 20);
|
||||
|
||||
printf ("\nConflicting path (from pc=%d):\n", from_pos);
|
||||
print_backtrace_chain (s, from_pos, 20);
|
||||
|
||||
printf ("\nBytecode window:\n");
|
||||
dump_instructions_around (s, pos, 10);
|
||||
|
||||
#ifdef DUMP_BYTECODE
|
||||
JS_ThrowInternalError (ctx,
|
||||
"inconsistent stack size: %d vs %d (pc=%d, op=%s, boundary=%s)",
|
||||
s->stack_level_tab[pos], stack_len, pos, oi->name,
|
||||
boundary_valid ? "valid" : "INVALID");
|
||||
#else
|
||||
JS_ThrowInternalError (ctx,
|
||||
"inconsistent stack size: %d vs %d (pc=%d, op=%d, boundary=%s)",
|
||||
s->stack_level_tab[pos], stack_len, pos, op_at_pos,
|
||||
boundary_valid ? "valid" : "INVALID");
|
||||
#endif
|
||||
return -1;
|
||||
} else if (s->catch_pos_tab[pos] != catch_pos) {
|
||||
uint8_t op_at_pos = s->bc_buf[pos];
|
||||
#ifdef DUMP_BYTECODE
|
||||
const JSOpCode *oi = &short_opcode_info (op_at_pos);
|
||||
#endif
|
||||
int boundary_valid = s->is_op_start[pos];
|
||||
|
||||
printf ("=== CATCH POSITION ERROR ===\n");
|
||||
printf ("Inconsistent catch position at pc=%d:\n", pos);
|
||||
printf (" expected: %d (first path)\n", s->catch_pos_tab[pos]);
|
||||
printf (" got: %d (conflicting path)\n", catch_pos);
|
||||
#ifdef DUMP_BYTECODE
|
||||
printf (" opcode at pos: %s (%d)\n", oi->name, op_at_pos);
|
||||
#else
|
||||
printf (" opcode at pos: %d\n", op_at_pos);
|
||||
#endif
|
||||
printf (" valid opcode boundary: %s\n", boundary_valid ? "YES" : "NO");
|
||||
|
||||
printf ("\nFirst path that reached pc=%d:\n", pos);
|
||||
print_backtrace_chain (s, pos, 20);
|
||||
|
||||
printf ("\nConflicting path (from pc=%d):\n", from_pos);
|
||||
print_backtrace_chain (s, from_pos, 20);
|
||||
|
||||
printf ("\nBytecode window:\n");
|
||||
dump_instructions_around (s, pos, 10);
|
||||
|
||||
JS_ThrowInternalError (ctx, "inconsistent catch position: %d %d (pc=%d)", s->catch_pos_tab[pos], catch_pos, pos);
|
||||
return -1;
|
||||
} else {
|
||||
@@ -16475,6 +16677,9 @@ static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int
|
||||
/* mark as explored and store the stack size */
|
||||
s->stack_level_tab[pos] = stack_len;
|
||||
s->catch_pos_tab[pos] = catch_pos;
|
||||
s->pred_pc[pos] = from_pos;
|
||||
s->pred_stack[pos] = stack_len;
|
||||
s->is_op_start[pos] = 1;
|
||||
|
||||
/* queue the new PC to explore */
|
||||
if (pjs_resize_array ((void **)&s->pc_stack, sizeof (s->pc_stack[0]), &s->pc_stack_size, s->pc_stack_len + 1))
|
||||
@@ -16490,7 +16695,10 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
||||
const uint8_t *bc_buf;
|
||||
|
||||
bc_buf = fd->byte_code.buf;
|
||||
s->bc_buf = bc_buf;
|
||||
s->bc_len = fd->byte_code.size;
|
||||
s->func_name = JS_IsNull(fd->func_name) ? "?" : JS_ToCString(ctx, fd->func_name);
|
||||
|
||||
/* bc_len > 0 */
|
||||
s->stack_level_tab
|
||||
= pjs_malloc (sizeof (s->stack_level_tab[0]) * s->bc_len);
|
||||
@@ -16501,12 +16709,24 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
||||
s->catch_pos_tab = pjs_malloc (sizeof (s->catch_pos_tab[0]) * s->bc_len);
|
||||
if (!s->catch_pos_tab) goto fail;
|
||||
|
||||
/* allocate predecessor tracking arrays */
|
||||
s->pred_pc = pjs_malloc (sizeof (s->pred_pc[0]) * s->bc_len);
|
||||
if (!s->pred_pc) goto fail;
|
||||
for (i = 0; i < s->bc_len; i++) s->pred_pc[i] = -1;
|
||||
|
||||
s->pred_stack = pjs_malloc (sizeof (s->pred_stack[0]) * s->bc_len);
|
||||
if (!s->pred_stack) goto fail;
|
||||
|
||||
s->is_op_start = pjs_malloc (sizeof (s->is_op_start[0]) * s->bc_len);
|
||||
if (!s->is_op_start) goto fail;
|
||||
memset (s->is_op_start, 0, s->bc_len);
|
||||
|
||||
s->stack_len_max = 0;
|
||||
s->pc_stack_len = 0;
|
||||
s->pc_stack_size = 0;
|
||||
|
||||
/* breadth-first graph exploration */
|
||||
if (ss_check (ctx, s, 0, OP_invalid, 0, -1)) goto fail;
|
||||
if (ss_check (ctx, s, 0, OP_invalid, 0, -1, -1)) goto fail;
|
||||
|
||||
while (s->pc_stack_len > 0) {
|
||||
pos = s->pc_stack[--s->pc_stack_len];
|
||||
@@ -16514,13 +16734,18 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
||||
catch_pos = s->catch_pos_tab[pos];
|
||||
op = bc_buf[pos];
|
||||
if (op == 0 || op >= OP_COUNT) {
|
||||
JS_ThrowInternalError (ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
|
||||
char fn_buf[64] = "?";
|
||||
if (!JS_IsNull (fd->func_name)) {
|
||||
const char *fn = JS_ToCString (ctx, fd->func_name);
|
||||
if (fn) {
|
||||
snprintf (fn_buf, sizeof (fn_buf), "%s", fn);
|
||||
JS_FreeCString (ctx, fn);
|
||||
}
|
||||
}
|
||||
JS_ThrowInternalError (ctx, "invalid opcode in '%s' (op=%d, pc=%d, bc_len=%d)", fn_buf, op, pos, s->bc_len);
|
||||
goto fail;
|
||||
}
|
||||
oi = &short_opcode_info (op);
|
||||
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
|
||||
printf ("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos);
|
||||
#endif
|
||||
pos_next = pos + oi->size;
|
||||
if (pos_next > s->bc_len) {
|
||||
JS_ThrowInternalError (ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
|
||||
@@ -16573,24 +16798,24 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
||||
case OP_if_true8:
|
||||
case OP_if_false8:
|
||||
diff = (int8_t)bc_buf[pos + 1];
|
||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
|
||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos, pos))
|
||||
goto fail;
|
||||
break;
|
||||
#endif
|
||||
case OP_if_true:
|
||||
case OP_if_false:
|
||||
diff = get_u32 (bc_buf + pos + 1);
|
||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
|
||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos, pos))
|
||||
goto fail;
|
||||
break;
|
||||
case OP_gosub:
|
||||
diff = get_u32 (bc_buf + pos + 1);
|
||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos))
|
||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos, pos))
|
||||
goto fail;
|
||||
break;
|
||||
case OP_catch:
|
||||
diff = get_u32 (bc_buf + pos + 1);
|
||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
|
||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos, pos))
|
||||
goto fail;
|
||||
catch_pos = pos;
|
||||
break;
|
||||
@@ -16624,18 +16849,24 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ss_check (ctx, s, pos_next, op, stack_len, catch_pos)) goto fail;
|
||||
if (ss_check (ctx, s, pos_next, op, stack_len, catch_pos, pos)) goto fail;
|
||||
done_insn:;
|
||||
}
|
||||
pjs_free (s->pc_stack);
|
||||
pjs_free (s->catch_pos_tab);
|
||||
pjs_free (s->stack_level_tab);
|
||||
pjs_free (s->pred_pc);
|
||||
pjs_free (s->pred_stack);
|
||||
pjs_free (s->is_op_start);
|
||||
*pstack_size = s->stack_len_max;
|
||||
return 0;
|
||||
fail:
|
||||
pjs_free (s->pc_stack);
|
||||
pjs_free (s->catch_pos_tab);
|
||||
pjs_free (s->stack_level_tab);
|
||||
pjs_free (s->pred_pc);
|
||||
pjs_free (s->pred_stack);
|
||||
pjs_free (s->is_op_start);
|
||||
*pstack_size = 0;
|
||||
return -1;
|
||||
}
|
||||
@@ -16651,6 +16882,15 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
||||
int function_size, byte_code_offset, cpool_offset;
|
||||
int closure_var_offset, vardefs_offset;
|
||||
|
||||
/* Debug: check initial bytecode size */
|
||||
if (!JS_IsNull(fd->func_name)) {
|
||||
const char *fn = JS_ToCString(ctx, fd->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("DEBUG: js_create_function START, bc_size=%zu\n", fd->byte_code.size);
|
||||
}
|
||||
if (fn) JS_FreeCString(ctx, fn);
|
||||
}
|
||||
|
||||
/* recompute scope linkage */
|
||||
for (scope = 0; scope < fd->scope_count; scope++) {
|
||||
fd->scopes[scope].first = -1;
|
||||
@@ -16698,7 +16938,23 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Debug: check bytecode size before resolve_variables */
|
||||
if (!JS_IsNull(fd->func_name)) {
|
||||
const char *fn = JS_ToCString(ctx, fd->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("DEBUG: before resolve_variables, bc_size=%zu\n", fd->byte_code.size);
|
||||
}
|
||||
if (fn) JS_FreeCString(ctx, fn);
|
||||
}
|
||||
if (resolve_variables (ctx, fd)) goto fail;
|
||||
/* Debug: check bytecode size after resolve_variables */
|
||||
if (!JS_IsNull(fd->func_name)) {
|
||||
const char *fn = JS_ToCString(ctx, fd->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("DEBUG: after resolve_variables, bc_size=%zu\n", fd->byte_code.size);
|
||||
}
|
||||
if (fn) JS_FreeCString(ctx, fn);
|
||||
}
|
||||
|
||||
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
|
||||
if (!fd->strip_debug) {
|
||||
@@ -16708,6 +16964,14 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Debug: check bytecode size before resolve_labels */
|
||||
if (!JS_IsNull(fd->func_name)) {
|
||||
const char *fn = JS_ToCString(ctx, fd->func_name);
|
||||
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||
printf("DEBUG: before resolve_labels, bc_size=%zu\n", fd->byte_code.size);
|
||||
}
|
||||
if (fn) JS_FreeCString(ctx, fn);
|
||||
}
|
||||
if (resolve_labels (ctx, fd)) goto fail;
|
||||
|
||||
if (compute_stack_size (ctx, fd, &stack_size) < 0) goto fail;
|
||||
@@ -17386,18 +17650,24 @@ static JSValue JS_EvalFunctionInternal (JSContext *ctx, JSValue fun_obj, JSValue
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
JSValue JS_EvalFunction (JSContext *ctx, JSValue fun_obj) {
|
||||
return JS_EvalFunctionInternal (ctx, fun_obj, ctx->global_obj, NULL);
|
||||
/* Compile source code to bytecode without executing.
|
||||
Returns unlinked bytecode on success, JS_EXCEPTION on error.
|
||||
'input' must be zero terminated i.e. input[input_len] = '\0'. */
|
||||
JSValue JS_Compile (JSContext *ctx, const char *input, size_t input_len,
|
||||
const char *filename) {
|
||||
return JS_EvalInternal (ctx, ctx->global_obj, input, input_len, filename,
|
||||
JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_FLAG_BACKTRACE_BARRIER, -1);
|
||||
}
|
||||
|
||||
/* Eval function with environment record for variable resolution.
|
||||
The env must be a stoned record. Variables are resolved env first,
|
||||
then global intrinsics. */
|
||||
JSValue JS_EvalFunctionEnv (JSContext *ctx, JSValue fun_obj, JSValue env) {
|
||||
/* Link compiled bytecode with environment and execute.
|
||||
The env should be a stoned record (or null for no env).
|
||||
Variables resolve: env first, then global intrinsics. */
|
||||
JSValue JS_Integrate (JSContext *ctx, JSValue fun_obj, JSValue env) {
|
||||
JSValue ret_val;
|
||||
JSValue saved_env;
|
||||
uint32_t tag;
|
||||
JSGCRef env_ref, fun_ref;
|
||||
JSFunctionBytecode *tpl, *linked_bc;
|
||||
|
||||
tag = JS_VALUE_GET_TAG (fun_obj);
|
||||
/* JSFunctionBytecode uses OBJ_CODE type with JS_TAG_PTR */
|
||||
@@ -17411,13 +17681,15 @@ JSValue JS_EvalFunctionEnv (JSContext *ctx, JSValue fun_obj, JSValue env) {
|
||||
JS_AddGCRef (ctx, &fun_ref);
|
||||
fun_ref.val = fun_obj;
|
||||
|
||||
/* Link with environment */
|
||||
JSValue linked = JS_LinkFunctionEnv (ctx, fun_ref.val, env_ref.val);
|
||||
if (JS_IsException (linked)) {
|
||||
/* Link bytecode with environment */
|
||||
tpl = (JSFunctionBytecode *)JS_VALUE_GET_PTR (fun_ref.val);
|
||||
linked_bc = js_link_bytecode (ctx, tpl, env_ref.val);
|
||||
if (!linked_bc) {
|
||||
JS_DeleteGCRef (ctx, &fun_ref);
|
||||
JS_DeleteGCRef (ctx, &env_ref);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
JSValue linked = JS_MKPTR (linked_bc);
|
||||
|
||||
/* Update env from GC ref (may have moved) */
|
||||
env = env_ref.val;
|
||||
@@ -17438,32 +17710,6 @@ JSValue JS_EvalFunctionEnv (JSContext *ctx, JSValue fun_obj, JSValue env) {
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* Link compiled bytecode to context - resolves global references.
|
||||
Returns linked bytecode on success, JS_EXCEPTION on link error.
|
||||
The linked bytecode is a separate copy that can be modified. */
|
||||
JSValue JS_LinkFunction (JSContext *ctx, JSValue fun_obj) {
|
||||
return JS_LinkFunctionEnv (ctx, fun_obj, JS_NULL);
|
||||
}
|
||||
|
||||
/* Link compiled bytecode with environment record for variable resolution.
|
||||
Variables are resolved: env first, then global intrinsics.
|
||||
Returns linked bytecode on success, JS_EXCEPTION on link error. */
|
||||
JSValue JS_LinkFunctionEnv (JSContext *ctx, JSValue fun_obj, JSValue env) {
|
||||
if (JS_VALUE_GET_TAG (fun_obj) != JS_TAG_PTR)
|
||||
return JS_ThrowTypeError (ctx, "bytecode function expected");
|
||||
|
||||
objhdr_t *hdr = (objhdr_t *)JS_VALUE_GET_PTR (fun_obj);
|
||||
if (objhdr_type (*hdr) != OBJ_CODE)
|
||||
return JS_ThrowTypeError (ctx, "bytecode function expected");
|
||||
|
||||
JSFunctionBytecode *tpl = (JSFunctionBytecode *)hdr;
|
||||
JSFunctionBytecode *linked = js_link_bytecode (ctx, tpl, env);
|
||||
if (!linked)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
return JS_MKPTR (linked);
|
||||
}
|
||||
|
||||
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
|
||||
static JSValue __JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx) {
|
||||
JSParseState s1, *s = &s1;
|
||||
@@ -17542,19 +17788,6 @@ static JSValue JS_EvalObject (JSContext *ctx, JSValue this_obj, JSValue val, int
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue JS_EvalThis (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int eval_flags) {
|
||||
int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
|
||||
JSValue ret;
|
||||
|
||||
assert (eval_type == JS_EVAL_TYPE_GLOBAL);
|
||||
ret = JS_EvalInternal (ctx, this_obj, input, input_len, filename, eval_flags, -1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue JS_Eval (JSContext *ctx, const char *input, size_t input_len, const char *filename, int eval_flags) {
|
||||
return JS_EvalThis (ctx, ctx->global_obj, input, input_len, filename, eval_flags);
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
/* object list */
|
||||
|
||||
@@ -24343,7 +24576,7 @@ static JSValue js_cell_eval (JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||
}
|
||||
|
||||
/* Compile the text */
|
||||
JSValue fun = JS_Eval (ctx, str, len, "<eval>", JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
JSValue fun = JS_Compile (ctx, str, len, "<eval>");
|
||||
JS_FreeCString (ctx, str);
|
||||
if (JS_IsException (fun)) {
|
||||
JS_DeleteGCRef (ctx, &env_ref);
|
||||
@@ -24354,8 +24587,8 @@ static JSValue js_cell_eval (JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||
env = env_ref.val;
|
||||
JS_DeleteGCRef (ctx, &env_ref);
|
||||
|
||||
/* Eval with environment */
|
||||
result = JS_EvalFunctionEnv (ctx, fun, env);
|
||||
/* Integrate with environment */
|
||||
result = JS_Integrate (ctx, fun, env);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -334,18 +334,13 @@ JS_IsShortFloat (JSValue v) {
|
||||
#define JS_DEFAULT_STACK_SIZE (1024 * 1024)
|
||||
#endif
|
||||
|
||||
/* JS_Eval() flags */
|
||||
/* Internal eval flags */
|
||||
#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */
|
||||
#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */
|
||||
#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */
|
||||
#define JS_EVAL_TYPE_MASK (3 << 0)
|
||||
|
||||
/* compile but do not run. The result is an object with a
|
||||
JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
|
||||
with JS_EvalFunction(). */
|
||||
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
|
||||
/* don't include the stack frames before this eval in the Error() backtraces */
|
||||
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
|
||||
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) /* internal use */
|
||||
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) /* internal use */
|
||||
|
||||
typedef JSValue JSCFunction (JSContext *ctx, JSValue this_val, int argc,
|
||||
JSValue *argv);
|
||||
@@ -727,12 +722,17 @@ int JS_SetPropertyInt64 (JSContext *ctx, JSValue this_obj, int64_t idx, JSValue
|
||||
JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj);
|
||||
|
||||
JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
|
||||
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
|
||||
JSValue JS_Eval (JSContext *ctx, const char *input, size_t input_len,
|
||||
const char *filename, int eval_flags);
|
||||
/* same as JS_Eval() but with an explicit 'this_obj' parameter */
|
||||
JSValue JS_EvalThis (JSContext *ctx, JSValue this_obj, const char *input,
|
||||
size_t input_len, const char *filename, int eval_flags);
|
||||
|
||||
/* Compile source code to bytecode without executing.
|
||||
'input' must be zero terminated i.e. input[input_len] = '\0'.
|
||||
Returns unlinked bytecode on success, JS_EXCEPTION on error. */
|
||||
JSValue JS_Compile (JSContext *ctx, const char *input, size_t input_len,
|
||||
const char *filename);
|
||||
|
||||
/* Link compiled bytecode with environment and execute.
|
||||
env should be stoned record or null.
|
||||
Variables resolve: env first, then global intrinsics. */
|
||||
JSValue JS_Integrate (JSContext *ctx, JSValue bytecode, JSValue env);
|
||||
JSValue JS_GetGlobalObject (JSContext *ctx);
|
||||
void JS_SetOpaque (JSValue obj, void *opaque);
|
||||
void *JS_GetOpaque (JSValue obj, JSClassID class_id);
|
||||
@@ -779,27 +779,9 @@ uint8_t *JS_WriteObject2 (JSContext *ctx, size_t *psize, JSValue obj,
|
||||
#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */
|
||||
JSValue JS_ReadObject (JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
||||
int flags);
|
||||
/* instantiate and evaluate a bytecode function. Only used when
|
||||
reading a script or module with JS_ReadObject() */
|
||||
JSValue JS_EvalFunction (JSContext *ctx, JSValue fun_obj);
|
||||
|
||||
/* Eval function with environment record for variable resolution.
|
||||
The env must be a stoned record. Variables are resolved env first,
|
||||
then global intrinsics. */
|
||||
JSValue JS_EvalFunctionEnv (JSContext *ctx, JSValue fun_obj, JSValue env);
|
||||
|
||||
/* Dump bytecode of a compiled function (for debugging) */
|
||||
void JS_DumpFunctionBytecode (JSContext *ctx, JSValue func_val);
|
||||
|
||||
/* Link compiled bytecode to context - resolves global references.
|
||||
Returns linked bytecode on success, JS_EXCEPTION on link error. */
|
||||
JSValue JS_LinkFunction (JSContext *ctx, JSValue func_val);
|
||||
|
||||
/* Link compiled bytecode with environment record for variable resolution.
|
||||
Variables are resolved: env first, then global intrinsics.
|
||||
Returns linked bytecode on success, JS_EXCEPTION on link error. */
|
||||
JSValue JS_LinkFunctionEnv (JSContext *ctx, JSValue func_val, JSValue env);
|
||||
|
||||
/* C function definition */
|
||||
typedef enum JSCFunctionEnum {
|
||||
JS_CFUNC_generic,
|
||||
|
||||
Reference in New Issue
Block a user