diff --git a/crates/engine/src/tests/script_dollar.rs b/crates/engine/src/tests/script_dollar.rs index c080b6a14..07dbb5381 100644 --- a/crates/engine/src/tests/script_dollar.rs +++ b/crates/engine/src/tests/script_dollar.rs @@ -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" diff --git a/crates/js/src/builtins/extensions.rs b/crates/js/src/builtins/extensions.rs index 0ac7e9ac6..15ebbe734 100644 --- a/crates/js/src/builtins/extensions.rs +++ b/crates/js/src/builtins/extensions.rs @@ -381,7 +381,7 @@ fn stack(interp: &mut Interp, _: Value, _: &[Value], _: bool) -> JsResult } 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 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, usize, bool)], code: &Rc) -> 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(), } } diff --git a/crates/js/src/bytecode.rs b/crates/js/src/bytecode.rs index b8bd2783b..9681d0168 100644 --- a/crates/js/src/bytecode.rs +++ b/crates/js/src/bytecode.rs @@ -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, + /// A function declaration's name, which `$.stack` shows. + pub(crate) decl_name: Option, pub(crate) params: Vec, pub(crate) vars: Vec, /// Function declarations to instantiate on entry: their names and indices in `functions`. diff --git a/crates/js/src/compiler.rs b/crates/js/src/compiler.rs index ae470be44..34e64814b 100644 --- a/crates/js/src/compiler.rs +++ b/crates/js/src/compiler.rs @@ -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, + decl_name: Option, params: Vec, span: (u32, u32), } @@ -1231,6 +1233,7 @@ pub(crate) fn compile_function(ast: &Ast, fid: FunctionId, source: &Rc, 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), diff --git a/crates/js/src/extension_tests.rs b/crates/js/src/extension_tests.rs index 54d1a1029..eb94e678a 100644 --- a/crates/js/src/extension_tests.rs +++ b/crates/js/src/extension_tests.rs @@ -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"); diff --git a/crates/js/src/heap.rs b/crates/js/src/heap.rs index 3c98078b1..0dbe94988 100644 --- a/crates/js/src/heap.rs +++ b/crates/js/src/heap.rs @@ -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)); diff --git a/crates/js/src/interp_tests.rs b/crates/js/src/interp_tests.rs index b6fec2637..d2a12b5fb 100644 --- a/crates/js/src/interp_tests.rs +++ b/crates/js/src/interp_tests.rs @@ -843,6 +843,7 @@ fn code(ops: Vec) -> Code { max_stack: 0, strict: false, self_name: None, + decl_name: None, params: Vec::new(), vars: Vec::new(), fun_decls: Vec::new(),