Scripts: $.stack names a declared function after its declaring function returns

The compiler now keeps a function declaration's name with its code, so
$.stack no longer shows [anonymous] for a declared function that is
called after the function that declared it has returned. The member test
expects "Web" for $.os in the wasm build, as the engine gives there.
This commit is contained in:
dotCooCoo
2026-11-11 04:23:32 -08:00
parent d50ea29772
commit af22ac8f50
7 changed files with 18 additions and 11 deletions
+3 -1
View File
@@ -30,7 +30,9 @@ fn thrown(s: &mut Session, statement: &str) -> String {
fn each_member_has_its_value() {
let mut s = Session::new();
let version = env!("CARGO_PKG_VERSION");
let os = if cfg!(windows) {
let os = if cfg!(target_arch = "wasm32") {
"Web"
} else if cfg!(windows) {
"Windows"
} else if cfg!(target_os = "macos") {
"macOS"
+6 -9
View File
@@ -381,7 +381,7 @@ fn stack(interp: &mut Interp, _: Value, _: &[Value], _: bool) -> JsResult<Value>
}
for (code, pc, function) in frames.iter().skip(skip) {
interp.use_fuel()?;
let name = if *function { function_name(&frames, code) } else { "[global]".to_string() };
let name = if *function { function_name(code) } else { "[global]".to_string() };
let line = code.line_column(pc.saturating_sub(1)).0;
text.push_str(&format!("{name}:{line}\n"));
}
@@ -390,14 +390,11 @@ fn stack(interp: &mut Interp, _: Value, _: &[Value], _: bool) -> JsResult<Value>
Ok(out.finish(interp))
}
/// The name `code` has: its own as a function expression's, or the one a function declaration of
/// a frame's code gives it, cut to [`STACK_NAME_UNITS`]; `[anonymous]` without one.
fn function_name(frames: &[(Rc<Code>, usize, bool)], code: &Rc<Code>) -> String {
let declared = frames.iter().find_map(|(outer, ..)| {
outer.fun_decls.iter().find(|(_, i)| outer.functions.get(*i as usize).is_some_and(|f| Rc::ptr_eq(f, code))).map(|(name, _)| name.clone())
});
match code.self_name.clone().or(declared) {
Some(name) => String::from_utf16_lossy(name.get(..STACK_NAME_UNITS).unwrap_or(&*name)),
/// The name `code` has: its own as a function expression's, or its declaration's, cut to
/// [`STACK_NAME_UNITS`]; `[anonymous]` without one.
fn function_name(code: &Code) -> String {
match code.self_name.as_ref().or(code.decl_name.as_ref()) {
Some(name) => String::from_utf16_lossy(name.get(..STACK_NAME_UNITS).unwrap_or(&**name)),
None => "[anonymous]".to_string(),
}
}
+2
View File
@@ -195,6 +195,8 @@ pub(crate) struct Code {
pub(crate) strict: bool,
/// A function expression's own name, bound inside it.
pub(crate) self_name: Option<Key>,
/// A function declaration's name, which `$.stack` shows.
pub(crate) decl_name: Option<Key>,
pub(crate) params: Vec<Key>,
pub(crate) vars: Vec<Key>,
/// Function declarations to instantiate on entry: their names and indices in `functions`.
+3
View File
@@ -1193,6 +1193,7 @@ impl<'a> FnCompiler<'a> {
max_stack: 0,
strict: self.strict,
self_name: meta.self_name,
decl_name: meta.decl_name,
params: meta.params,
vars: hoisted.vars,
fun_decls: hoisted.fun_decls,
@@ -1209,6 +1210,7 @@ impl<'a> FnCompiler<'a> {
#[derive(Default)]
struct Meta {
self_name: Option<Key>,
decl_name: Option<Key>,
params: Vec<Key>,
span: (u32, u32),
}
@@ -1231,6 +1233,7 @@ pub(crate) fn compile_function(ast: &Ast, fid: FunctionId, source: &Rc<Source>,
c.emit(Op::Return);
let name = f.name.as_ref().map(|n| key_of(&n.name));
let meta = Meta {
decl_name: if f.kind == FunctionKind::Declaration { name.clone() } else { None },
self_name: if bind_name && f.kind == FunctionKind::Expression { name } else { None },
params: f.params.iter().map(|p| key_of(&p.name)).collect(),
span: (f.span.start, f.span.end),
+2
View File
@@ -163,6 +163,8 @@ fn script_items_read_the_global_object_the_last_error_the_line_and_the_stack() {
assert_eq!(r.run("\n\n h.line").unwrap(), JsValue::Number(3.0));
let source = "function outer() {\n return inner();\n}\nfunction inner() {\n return h.stack;\n}\nvar f = function () { return outer(); };\nf()";
assert_eq!(text(&mut r, source), "[global]:8\n[anonymous]:7\nouter:2\ninner:5\n");
let source = "function maker() {\n function named() {\n return h.stack;\n }\n return named;\n}\nvar later = maker();\nlater()";
assert_eq!(text(&mut r, source), "[global]:8\nnamed:3\n", "a declared function keeps its name after its declaring function returns");
let deep = "function d(n) { return n ? d(n - 1) : h.stack; } var s = d(100).split('\\n'); s.length + ',' + s[0]";
let lines = text(&mut r, deep);
assert_eq!(lines, "66,…", "64 frames, the line that some are left out, and the last line's end");
+1 -1
View File
@@ -529,7 +529,7 @@ fn mark_code(code: &Code, strings: &mut StringSizes) {
strings.add(flags);
}
let keys = code.names.iter().chain(&code.params).chain(&code.vars).chain(code.fun_decls.iter().map(|(k, _)| k));
for k in keys.chain(&code.self_name) {
for k in keys.chain(&code.self_name).chain(&code.decl_name) {
strings.add_key(k);
}
stack.extend(code.functions.iter().map(|f| &**f));
+1
View File
@@ -843,6 +843,7 @@ fn code(ops: Vec<Op>) -> Code {
max_stack: 0,
strict: false,
self_name: None,
decl_name: None,
params: Vec::new(),
vars: Vec::new(),
fun_decls: Vec::new(),