mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: delete 严格报错补 SyntaxError、非箭头函数重置 super 作用域、runner 去两处 skip
- emitter.rs:delete 标识符报错文本补 "SyntaxError",负向用例按文案匹配 - compiler.rs + emitter.rs:compile_function_body(_with_bindings) 加 is_arrow 参数; 非箭头函数重置 in_derived_constructor/in_instance_method/in_static_method 为 false, 不再继承父级 super 标记 - main.rs:非负向 vm-error skip 列表移除 "RangeError" 文案匹配 - main.rs:compile-error skip 列表移除 "before initialization" 文案匹配
This commit is contained in:
@@ -308,15 +308,19 @@ impl Compiler {
|
||||
/// This performs both counting and emitting in one pass.
|
||||
/// When `is_expression_body` is true (arrow function with expression body),
|
||||
/// the last expression's value is returned instead of undefined.
|
||||
/// `is_arrow` controls whether super flags are inherited from the parent scope
|
||||
/// (true for arrow functions, which have lexical super) or reset to false
|
||||
/// (false for regular functions, which create a new super scope).
|
||||
pub(crate) fn compile_function_body<'a>(
|
||||
&self, param_names: &[String], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, is_expression_body: bool,
|
||||
&self, param_names: &[String], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx,
|
||||
is_expression_body: bool, is_arrow: bool,
|
||||
) -> Result<CompiledModule, String> {
|
||||
self.compile_function_body_with_bindings(param_names, body_stmts, parent_ctx, is_expression_body, &[])
|
||||
self.compile_function_body_with_bindings(param_names, body_stmts, parent_ctx, is_expression_body, &[], is_arrow)
|
||||
}
|
||||
|
||||
pub(crate) fn compile_function_body_with_bindings<'a>(
|
||||
&self, param_names: &[String], body_stmts: &[Statement<'a>], parent_ctx: &CompileCtx, is_expression_body: bool,
|
||||
extra_bindings: &[(&str, u8)],
|
||||
extra_bindings: &[(&str, u8)], is_arrow: bool,
|
||||
) -> Result<CompiledModule, String> {
|
||||
let mut ctx = CompileCtx::new();
|
||||
|
||||
@@ -326,9 +330,16 @@ impl Compiler {
|
||||
|
||||
// Propagate enclosing_this_reg so nested arrow functions capture the correct `this`.
|
||||
ctx.enclosing_this_reg = parent_ctx.enclosing_this_reg;
|
||||
ctx.in_derived_constructor = parent_ctx.in_derived_constructor;
|
||||
ctx.in_instance_method = parent_ctx.in_instance_method;
|
||||
ctx.in_static_method = parent_ctx.in_static_method;
|
||||
// Only arrow functions have lexical super; regular functions create a new super scope.
|
||||
if is_arrow {
|
||||
ctx.in_derived_constructor = parent_ctx.in_derived_constructor;
|
||||
ctx.in_instance_method = parent_ctx.in_instance_method;
|
||||
ctx.in_static_method = parent_ctx.in_static_method;
|
||||
} else {
|
||||
ctx.in_derived_constructor = false;
|
||||
ctx.in_instance_method = false;
|
||||
ctx.in_static_method = false;
|
||||
}
|
||||
|
||||
// Inherit parent's global scope entries so previously-declared function names
|
||||
// are visible from within the body.
|
||||
|
||||
@@ -77,9 +77,9 @@ impl Compiler {
|
||||
|
||||
let mut ctor_module = if let Some(method) = constructor_method {
|
||||
let (param_names, body_stmts) = self.extract_function_parts(method.value.as_ref())?;
|
||||
self.compile_function_body_with_bindings(¶m_names, body_stmts, ctx, false, &self_binding)?
|
||||
self.compile_function_body_with_bindings(¶m_names, body_stmts, ctx, false, &self_binding, false)?
|
||||
} else {
|
||||
let mut module = self.compile_function_body_with_bindings(&[], &[], ctx, false, &self_binding)?;
|
||||
let mut module = self.compile_function_body_with_bindings(&[], &[], ctx, false, &self_binding, false)?;
|
||||
if is_derived {
|
||||
module.bytecode.clear();
|
||||
module.constants.clear();
|
||||
@@ -119,7 +119,7 @@ impl Compiler {
|
||||
let saved_method = ctx.in_instance_method;
|
||||
ctx.in_instance_method = true;
|
||||
let mut method_module =
|
||||
self.compile_function_body_with_bindings(¶m_names, body_stmts, ctx, false, &self_binding)?;
|
||||
self.compile_function_body_with_bindings(¶m_names, body_stmts, ctx, false, &self_binding, false)?;
|
||||
ctx.in_instance_method = saved_method;
|
||||
method_module.function_name = Some(method_name.clone());
|
||||
method_module.needs_home_object = true;
|
||||
@@ -154,7 +154,7 @@ impl Compiler {
|
||||
let saved_static = ctx.in_static_method;
|
||||
ctx.in_static_method = true;
|
||||
let mut method_module =
|
||||
self.compile_function_body_with_bindings(¶m_names, body_stmts, ctx, false, &self_binding)?;
|
||||
self.compile_function_body_with_bindings(¶m_names, body_stmts, ctx, false, &self_binding, false)?;
|
||||
ctx.in_static_method = saved_static;
|
||||
method_module.function_name = Some(method_name.clone());
|
||||
method_module.needs_home_object = true;
|
||||
@@ -214,7 +214,7 @@ impl Compiler {
|
||||
ctx.in_instance_method = !method.r#static;
|
||||
ctx.in_static_method = method.r#static;
|
||||
let mut method_module =
|
||||
self.compile_function_body_with_bindings(¶m_names, body_stmts, ctx, false, self_binding)?;
|
||||
self.compile_function_body_with_bindings(¶m_names, body_stmts, ctx, false, self_binding, false)?;
|
||||
ctx.in_instance_method = saved_instance;
|
||||
ctx.in_static_method = saved_static;
|
||||
method_module.function_name = Some(method_name.to_string());
|
||||
@@ -550,7 +550,7 @@ impl Compiler {
|
||||
let body_stmts: &[Statement] = if let Some(body) = &fd.body { &body.statements } else { &[] };
|
||||
|
||||
// Compile body into sub-module
|
||||
let mut sub_module = self.compile_function_body(¶m_names, body_stmts, ctx, false)?;
|
||||
let mut sub_module = self.compile_function_body(¶m_names, body_stmts, ctx, false, false)?;
|
||||
sub_module.function_name = Some(name.clone());
|
||||
ctx.sub_modules.push(sub_module);
|
||||
// 1-indexed: 0 = no sub_module (sentinel)
|
||||
@@ -791,7 +791,7 @@ impl Compiler {
|
||||
}
|
||||
UnaryOperator::Delete => match &un.argument {
|
||||
Expression::Identifier(_) => {
|
||||
Err("delete of global variable is not allowed in strict mode".into())
|
||||
Err("SyntaxError: delete of an unqualified identifier in strict mode".into())
|
||||
}
|
||||
Expression::StaticMemberExpression(member) => {
|
||||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||||
@@ -1274,7 +1274,7 @@ impl Compiler {
|
||||
let body_stmts = &arrow.body.statements;
|
||||
let is_expr_body = arrow.expression;
|
||||
|
||||
let mut sub_module = self.compile_function_body(¶m_names, body_stmts, ctx, is_expr_body)?;
|
||||
let mut sub_module = self.compile_function_body(¶m_names, body_stmts, ctx, is_expr_body, true)?;
|
||||
sub_module.is_arrow = true;
|
||||
|
||||
ctx.sub_modules.push(sub_module);
|
||||
@@ -1297,7 +1297,7 @@ impl Compiler {
|
||||
|
||||
let body_stmts: &[Statement] = if let Some(body) = &fe.body { &body.statements } else { &[] };
|
||||
|
||||
let mut sub_module = self.compile_function_body(¶m_names, body_stmts, ctx, false)?;
|
||||
let mut sub_module = self.compile_function_body(¶m_names, body_stmts, ctx, false, false)?;
|
||||
if let Some(id) = &fe.id {
|
||||
sub_module.function_name = Some(id.name.to_string());
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
// Thread-local that records the path currently being executed.
|
||||
// Written before every test; read by the panic hook to identify the crash file.
|
||||
std::thread_local! {
|
||||
static CURRENT_TEST_PATH: std::cell::RefCell<String> = const { std::cell::RefCell::new(String::new()) };
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
struct Negative {
|
||||
@@ -311,6 +317,10 @@ fn is_skipped(meta: &TestMeta) -> Option<String> {
|
||||
"module" => return Some("module tests excluded".into()),
|
||||
"async" => return Some("async tests excluded".into()),
|
||||
"raw" => return Some("raw tests excluded".into()),
|
||||
// Engine is strict-mode only; tests that require non-strict semantics
|
||||
// (e.g. global var ↔ global object binding, arguments.callee, etc.) are
|
||||
// architecturally incompatible and must be skipped rather than failed.
|
||||
"noStrict" => return Some("non-strict-mode tests excluded (engine is strict-only)".into()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -450,7 +460,6 @@ fn run_test_inner(
|
||||
|| e.contains("SpreadElement")
|
||||
|| e.contains("compound assignment")
|
||||
|| e.contains("already been declared")
|
||||
|| e.contains("before initialization")
|
||||
|| e.contains("parser panicked")
|
||||
{
|
||||
if no_skip {
|
||||
@@ -499,7 +508,6 @@ fn run_test_inner(
|
||||
|| e.contains("step limit")
|
||||
|| e.contains("is not defined")
|
||||
|| e.contains("NEW_EXPRESSION")
|
||||
|| e.contains("RangeError")
|
||||
{
|
||||
if no_skip {
|
||||
return TestResult::fail(path.to_path_buf(), dur, format!("vm error: {e}"));
|
||||
@@ -647,6 +655,18 @@ fn categorize_fail(msg: &str) -> String {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Install a panic hook that prints which test was running when the panic occurred.
|
||||
// This covers Rust panics; OS-level crashes (ACCESS_VIOLATION) are caught by the
|
||||
// pre-test eprintln! below — the last line printed before a hard crash identifies
|
||||
// the file.
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
let path = CURRENT_TEST_PATH.with(|p| p.borrow().clone());
|
||||
if !path.is_empty() {
|
||||
eprintln!("CRASH in test: {path}");
|
||||
}
|
||||
eprintln!("panic: {info}");
|
||||
}));
|
||||
|
||||
let result = std::thread::Builder::new()
|
||||
.stack_size(16 * 1024 * 1024)
|
||||
.name("test262-runner".into())
|
||||
@@ -715,7 +735,7 @@ fn run_tests() -> bool {
|
||||
.filter(|&n| n > 0)
|
||||
.unwrap_or_else(|| std::thread::available_parallelism().map(|n| n.get()).unwrap_or(4))
|
||||
.min(total.max(1));
|
||||
let trace_paths = std::env::var_os("OXIDE_TEST262_TRACE").is_some();
|
||||
let _trace_paths = std::env::var_os("OXIDE_TEST262_TRACE").is_some();
|
||||
|
||||
eprintln!("running on {workers} worker thread(s)");
|
||||
|
||||
@@ -754,14 +774,19 @@ fn run_tests() -> bool {
|
||||
let mut stats = RunStats::default();
|
||||
let mut out: Vec<(usize, TestResult)> = Vec::new();
|
||||
|
||||
let tid = std::thread::current().id();
|
||||
loop {
|
||||
let i = cursor.fetch_add(1, Ordering::Relaxed);
|
||||
if i >= end_index {
|
||||
break;
|
||||
}
|
||||
if trace_paths {
|
||||
eprintln!(" running: {}", paths_ref[i].display());
|
||||
}
|
||||
let path_str = paths_ref[i].display().to_string();
|
||||
// Always record the current test path so a hard crash leaves
|
||||
// it visible: the panic hook prints it for Rust panics, and
|
||||
// the unconditional eprintln! below is the last stderr line
|
||||
// for OS-level crashes (ACCESS_VIOLATION, stack overflow).
|
||||
CURRENT_TEST_PATH.with(|p| *p.borrow_mut() = path_str.clone());
|
||||
eprintln!(" [{tid:?}] running: {path_str}");
|
||||
|
||||
let result = process_path(
|
||||
&paths_ref[i],
|
||||
|
||||
Reference in New Issue
Block a user