LibJs/Rust: Migrate to edition 2024

This commit is contained in:
xnacly 2026-02-24 12:36:14 +01:00 committed by Andreas Kling
parent 0c6133176b
commit bbb6121df4
13 changed files with 3662 additions and 3480 deletions

View file

@ -1,7 +1,7 @@
[package]
name = "libjs_rust"
version = "0.1.0"
edition = "2021"
edition = "2024"
[lib]
crate-type = ["staticlib"]

View file

@ -883,11 +883,10 @@ impl BindingPattern {
if entry.initializer.is_some() {
return true;
}
if let Some(BindingEntryAlias::BindingPattern(ref nested)) = entry.alias {
if nested.contains_expression() {
if let Some(BindingEntryAlias::BindingPattern(ref nested)) = entry.alias
&& nested.contains_expression() {
return true;
}
}
}
false
}
@ -1038,7 +1037,7 @@ pub struct TemplateLiteralData {
// RegExp literal
// =============================================================================
extern "C" {
unsafe extern "C" {
fn rust_free_compiled_regex(ptr: *mut c_void);
}

View file

@ -13,7 +13,7 @@ use crate::ast::*;
use std::cell::RefCell;
use std::fmt::Write;
extern "C" {
unsafe extern "C" {
// FIXME: This FFI workaround exists only to match C++ float-to-string
// formatting in the AST dump. Once the C++ pipeline is removed,
// this can be deleted and we can use our own formatting.
@ -33,13 +33,13 @@ macro_rules! op_to_string {
/// Prints a node header with the node name, optional extras, and source position.
macro_rules! dump_node {
($state:expr, $name:expr, $range:expr) => {
($state:expr_2021, $name:expr_2021, $range:expr_2021) => {
print_node(
$state,
&format!("{}{}", color_node_name($state, $name), format_position($state, $range)),
)
};
($state:expr, $name:expr, $range:expr, $($extra:expr),+ $(,)?) => {
($state:expr_2021, $name:expr_2021, $range:expr_2021, $($extra:expr_2021),+ $(,)?) => {
print_node(
$state,
&{

File diff suppressed because it is too large Load diff

View file

@ -185,7 +185,7 @@ pub struct FFIExecutableData {
pub regex_count: usize,
}
extern "C" {
unsafe extern "C" {
fn rust_create_executable(
vm_ptr: *mut c_void,
source_code_ptr: *const c_void,
@ -296,83 +296,85 @@ pub unsafe fn create_shared_function_data(
is_strict: bool,
name_override: Option<&[u16]>,
) -> *mut c_void {
use crate::ast::FunctionParameterBinding;
unsafe {
use crate::ast::FunctionParameterBinding;
let source_start = function_data.source_text_start as usize;
let source_end = function_data.source_text_end as usize;
let source_text_len = source_end - source_start;
let source_start = function_data.source_text_start as usize;
let source_end = function_data.source_text_end as usize;
let source_text_len = source_end - source_start;
let (name_ptr, name_len) = if let Some(name) = name_override {
(name.as_ptr(), name.len())
} else if let Some(ref name_ident) = function_data.name {
(name_ident.name.as_ptr(), name_ident.name.len())
} else {
(std::ptr::null(), 0)
};
let (name_ptr, name_len) = if let Some(name) = name_override {
(name.as_ptr(), name.len())
} else if let Some(ref name_ident) = function_data.name {
(name_ident.name.as_ptr(), name_ident.name.len())
} else {
(std::ptr::null(), 0)
};
let has_simple_parameter_list = function_data.parameters.iter().all(|p| {
!p.is_rest
&& p.default_value.is_none()
&& matches!(p.binding, FunctionParameterBinding::Identifier(_))
});
let has_simple_parameter_list = function_data.parameters.iter().all(|p| {
!p.is_rest
&& p.default_value.is_none()
&& matches!(p.binding, FunctionParameterBinding::Identifier(_))
});
let parameter_name_slices: Vec<FFIUtf16Slice> = if has_simple_parameter_list {
function_data
.parameters
.iter()
.map(|p| {
if let FunctionParameterBinding::Identifier(ref id) = p.binding {
FFIUtf16Slice::from(id.name.as_ref())
} else {
unreachable!(
"has_simple_parameter_list guarantees all bindings are identifiers"
)
}
})
.collect()
} else {
Vec::new()
};
let parameter_name_slices: Vec<FFIUtf16Slice> = if has_simple_parameter_list {
function_data
.parameters
.iter()
.map(|p| {
if let FunctionParameterBinding::Identifier(ref id) = p.binding {
FFIUtf16Slice::from(id.name.as_ref())
} else {
unreachable!(
"has_simple_parameter_list guarantees all bindings are identifiers"
)
}
})
.collect()
} else {
Vec::new()
};
let function_kind = function_data.kind as u8;
let strict = function_data.is_strict_mode || is_strict;
let function_length = function_data.function_length;
let formal_parameter_count = u32_from_usize(function_data.parameters.len());
let is_arrow = function_data.is_arrow_function;
let uses_this = function_data.parsing_insights.uses_this;
let uses_this_from_environment = function_data.parsing_insights.uses_this_from_environment;
let function_kind = function_data.kind as u8;
let strict = function_data.is_strict_mode || is_strict;
let function_length = function_data.function_length;
let formal_parameter_count = u32_from_usize(function_data.parameters.len());
let is_arrow = function_data.is_arrow_function;
let uses_this = function_data.parsing_insights.uses_this;
let uses_this_from_environment = function_data.parsing_insights.uses_this_from_environment;
let payload = Box::new(crate::ast::FunctionPayload {
data: *function_data,
function_table: subtable,
});
let rust_ast_ptr = Box::into_raw(payload) as *mut c_void;
let payload = Box::new(crate::ast::FunctionPayload {
data: *function_data,
function_table: subtable,
});
let rust_ast_ptr = Box::into_raw(payload) as *mut c_void;
let ffi_data = FFISharedFunctionData {
name: name_ptr,
name_len,
function_kind,
function_length,
formal_parameter_count,
strict,
is_arrow,
has_simple_parameter_list,
parameter_names: parameter_name_slices.as_ptr(),
parameter_name_count: parameter_name_slices.len(),
source_text_offset: source_start,
source_text_length: source_text_len,
rust_function_ast: rust_ast_ptr,
uses_this,
uses_this_from_environment,
};
let ffi_data = FFISharedFunctionData {
name: name_ptr,
name_len,
function_kind,
function_length,
formal_parameter_count,
strict,
is_arrow,
has_simple_parameter_list,
parameter_names: parameter_name_slices.as_ptr(),
parameter_name_count: parameter_name_slices.len(),
source_text_offset: source_start,
source_text_length: source_text_len,
rust_function_ast: rust_ast_ptr,
uses_this,
uses_this_from_environment,
};
let sfd_ptr = rust_create_sfd(vm_ptr, source_code_ptr, &ffi_data);
let sfd_ptr = rust_create_sfd(vm_ptr, source_code_ptr, &ffi_data);
assert!(
!sfd_ptr.is_null(),
"create_shared_function_data: rust_create_sfd returned null"
);
sfd_ptr
assert!(
!sfd_ptr.is_null(),
"create_shared_function_data: rust_create_sfd returned null"
);
sfd_ptr
}
}
/// Create a SharedFunctionInstanceData for GDI use (no name override).
@ -386,14 +388,16 @@ pub unsafe fn create_sfd_for_gdi(
source_code_ptr: *const c_void,
is_strict: bool,
) -> *mut c_void {
create_shared_function_data(
function_data,
subtable,
vm_ptr,
source_code_ptr,
is_strict,
None,
)
unsafe {
create_shared_function_data(
function_data,
subtable,
vm_ptr,
source_code_ptr,
is_strict,
None,
)
}
}
/// Constant tags for the FFI constant buffer (ABI-compatible with BytecodeFactory).
@ -453,108 +457,112 @@ fn encode_constants(constants: &[ConstantValue]) -> Vec<u8> {
/// `vm_ptr` must be a valid `JS::VM*` and `source_code_ptr` a valid
/// `JS::SourceCode const*`.
pub unsafe fn create_executable(
gen: &Generator,
generator: &Generator,
assembled: &AssembledBytecode,
vm_ptr: *mut c_void,
source_code_ptr: *const c_void,
) -> ExecutableHandle {
// Build FFI slices for tables
let ident_slices: Vec<FFIUtf16Slice> = gen
.identifier_table
.iter()
.map(|s| FFIUtf16Slice::from(s.as_ref()))
.collect();
unsafe {
// Build FFI slices for tables
let ident_slices: Vec<FFIUtf16Slice> = generator
.identifier_table
.iter()
.map(|s| FFIUtf16Slice::from(s.as_ref()))
.collect();
let property_key_slices: Vec<FFIUtf16Slice> = gen
.property_key_table
.iter()
.map(|s| FFIUtf16Slice::from(s.as_ref()))
.collect();
let property_key_slices: Vec<FFIUtf16Slice> = generator
.property_key_table
.iter()
.map(|s| FFIUtf16Slice::from(s.as_ref()))
.collect();
let string_slices: Vec<FFIUtf16Slice> = gen
.string_table
.iter()
.map(|s| FFIUtf16Slice::from(s.as_ref()))
.collect();
let string_slices: Vec<FFIUtf16Slice> = generator
.string_table
.iter()
.map(|s| FFIUtf16Slice::from(s.as_ref()))
.collect();
// Encode constants
let constants_buffer = encode_constants(&gen.constants);
// Encode constants
let constants_buffer = encode_constants(&generator.constants);
// Build FFI exception handlers
let ffi_handlers: Vec<FFIExceptionHandler> = assembled
.exception_handlers
.iter()
.map(|h| FFIExceptionHandler {
start_offset: h.start_offset,
end_offset: h.end_offset,
handler_offset: h.handler_offset,
})
.collect();
// Build FFI exception handlers
let ffi_handlers: Vec<FFIExceptionHandler> = assembled
.exception_handlers
.iter()
.map(|h| FFIExceptionHandler {
start_offset: h.start_offset,
end_offset: h.end_offset,
handler_offset: h.handler_offset,
})
.collect();
// Build FFI source map
let ffi_source_map: Vec<FFISourceMapEntry> = assembled
.source_map
.iter()
.map(|e| FFISourceMapEntry {
bytecode_offset: e.bytecode_offset,
source_start: e.source_start,
source_end: e.source_end,
})
.collect();
// Build FFI source map
let ffi_source_map: Vec<FFISourceMapEntry> = assembled
.source_map
.iter()
.map(|e| FFISourceMapEntry {
bytecode_offset: e.bytecode_offset,
source_start: e.source_start,
source_end: e.source_end,
})
.collect();
// Build local variable name slices
let local_var_slices: Vec<FFIUtf16Slice> = gen
.local_variables
.iter()
.map(|v| FFIUtf16Slice::from(v.name.as_ref()))
.collect();
// Build local variable name slices
let local_var_slices: Vec<FFIUtf16Slice> = generator
.local_variables
.iter()
.map(|v| FFIUtf16Slice::from(v.name.as_ref()))
.collect();
// Collect shared function data pointers
let sfd_ptrs: Vec<*const c_void> = gen
.shared_function_data
.iter()
.map(|ptr| *ptr as *const c_void)
.collect();
// Collect shared function data pointers
let sfd_ptrs: Vec<*const c_void> = generator
.shared_function_data
.iter()
.map(|ptr| *ptr as *const c_void)
.collect();
// Collect class blueprint pointers
let bp_ptrs = &gen.class_blueprints;
// Collect class blueprint pointers
let bp_ptrs = &generator.class_blueprints;
let ffi_data = FFIExecutableData {
bytecode: assembled.bytecode.as_ptr(),
bytecode_length: assembled.bytecode.len(),
identifier_table: ident_slices.as_ptr(),
identifier_count: ident_slices.len(),
property_key_table: property_key_slices.as_ptr(),
property_key_count: property_key_slices.len(),
string_table: string_slices.as_ptr(),
string_count: string_slices.len(),
constants_data: constants_buffer.as_ptr(),
constants_data_length: constants_buffer.len(),
constants_count: gen.constants.len(),
exception_handlers: ffi_handlers.as_ptr(),
exception_handler_count: ffi_handlers.len(),
source_map: ffi_source_map.as_ptr(),
source_map_count: ffi_source_map.len(),
basic_block_offsets: assembled.basic_block_start_offsets.as_ptr(),
basic_block_count: assembled.basic_block_start_offsets.len(),
local_variable_names: local_var_slices.as_ptr(),
local_variable_count: local_var_slices.len(),
property_lookup_cache_count: gen.next_property_lookup_cache,
global_variable_cache_count: gen.next_global_variable_cache,
template_object_cache_count: gen.next_template_object_cache,
object_shape_cache_count: gen.next_object_shape_cache,
number_of_registers: assembled.number_of_registers,
is_strict: gen.strict,
length_identifier: FFIOptionalU32::from(gen.length_identifier.map(|index| index.0)),
shared_function_data: sfd_ptrs.as_ptr(),
shared_function_data_count: sfd_ptrs.len(),
class_blueprints: bp_ptrs.as_ptr(),
class_blueprint_count: bp_ptrs.len(),
compiled_regexes: gen.compiled_regexes.as_ptr(),
regex_count: gen.compiled_regexes.len(),
};
let ffi_data = FFIExecutableData {
bytecode: assembled.bytecode.as_ptr(),
bytecode_length: assembled.bytecode.len(),
identifier_table: ident_slices.as_ptr(),
identifier_count: ident_slices.len(),
property_key_table: property_key_slices.as_ptr(),
property_key_count: property_key_slices.len(),
string_table: string_slices.as_ptr(),
string_count: string_slices.len(),
constants_data: constants_buffer.as_ptr(),
constants_data_length: constants_buffer.len(),
constants_count: generator.constants.len(),
exception_handlers: ffi_handlers.as_ptr(),
exception_handler_count: ffi_handlers.len(),
source_map: ffi_source_map.as_ptr(),
source_map_count: ffi_source_map.len(),
basic_block_offsets: assembled.basic_block_start_offsets.as_ptr(),
basic_block_count: assembled.basic_block_start_offsets.len(),
local_variable_names: local_var_slices.as_ptr(),
local_variable_count: local_var_slices.len(),
property_lookup_cache_count: generator.next_property_lookup_cache,
global_variable_cache_count: generator.next_global_variable_cache,
template_object_cache_count: generator.next_template_object_cache,
object_shape_cache_count: generator.next_object_shape_cache,
number_of_registers: assembled.number_of_registers,
is_strict: generator.strict,
length_identifier: FFIOptionalU32::from(
generator.length_identifier.map(|index| index.0),
),
shared_function_data: sfd_ptrs.as_ptr(),
shared_function_data_count: sfd_ptrs.len(),
class_blueprints: bp_ptrs.as_ptr(),
class_blueprint_count: bp_ptrs.len(),
compiled_regexes: generator.compiled_regexes.as_ptr(),
regex_count: generator.compiled_regexes.len(),
};
rust_create_executable(vm_ptr, source_code_ptr, &ffi_data)
rust_create_executable(vm_ptr, source_code_ptr, &ffi_data)
}
}
/// Convert a JS number to its UTF-16 string representation using the

View file

@ -236,7 +236,7 @@ pub struct Generator {
}
macro_rules! singleton_constant {
($self:expr, $field:ident, $value:expr) => {{
($self:expr_2021, $field:ident, $value:expr_2021) => {{
if let Some(op) = &$self.$field {
return op.clone();
}
@ -691,14 +691,13 @@ impl Generator {
false_target: Label,
) {
// OPTIMIZATION: If condition is a constant, emit an unconditional jump.
if let Some(constant) = self.get_constant(condition) {
if let Some(is_truthy) = constant_to_boolean(constant) {
if let Some(constant) = self.get_constant(condition)
&& let Some(is_truthy) = constant_to_boolean(constant) {
self.emit(Instruction::Jump {
target: if is_truthy { true_target } else { false_target },
});
return;
}
}
// OPTIMIZATION: If the condition is a register with ref_count == 1 and the last
// instruction is a comparison whose dst matches condition, fuse into a JumpXxx.
@ -1030,11 +1029,9 @@ impl Generator {
let completion = target_scope.completion_register.clone();
if let (Some(cur), Some(tgt)) =
(self.current_completion_register.clone(), completion)
{
if cur != tgt {
&& cur != tgt {
self.emit_mov(&tgt, &cur);
}
}
self.emit(Instruction::Jump { target });
self.current_finally_context = saved_ctx;
return;
@ -1048,11 +1045,9 @@ impl Generator {
let completion = target_scope.completion_register.clone();
if let (Some(cur), Some(tgt)) =
(self.current_completion_register.clone(), completion)
{
if cur != tgt {
&& cur != tgt {
self.emit_mov(&tgt, &cur);
}
}
self.emit(Instruction::Jump { target });
self.current_finally_context = saved_ctx;
return;
@ -1079,11 +1074,9 @@ impl Generator {
let completion = target_scope.completion_register.clone();
if let (Some(cur), Some(tgt)) =
(self.current_completion_register.clone(), completion)
{
if cur != tgt {
&& cur != tgt {
self.emit_mov(&tgt, &cur);
}
}
self.register_jump_in_finally_context(target);
self.current_finally_context = saved_ctx;
return;
@ -1148,11 +1141,9 @@ impl Generator {
{
if let (Some(cur), Some(tgt)) =
(self.current_completion_register.clone(), completion.clone())
{
if cur != tgt {
&& cur != tgt {
self.emit_mov(&tgt, &cur);
}
}
self.register_jump_in_finally_context(*target);
self.current_finally_context = saved_ctx;
return;
@ -1171,11 +1162,9 @@ impl Generator {
if label_set.iter().any(|l| l == label) {
if let (Some(cur), Some(tgt)) =
(self.current_completion_register.clone(), completion.clone())
{
if cur != tgt {
&& cur != tgt {
self.emit_mov(&tgt, &cur);
}
}
self.emit(Instruction::Jump { target: *target });
self.current_finally_context = saved_ctx;
return;
@ -1565,14 +1554,13 @@ impl Generator {
// (matching C++ Generator.cpp behavior).
let mut merged_handlers: Vec<ExceptionHandler> = Vec::new();
for handler in &exception_handlers {
if let Some(last) = merged_handlers.last_mut() {
if last.end_offset == handler.start_offset
if let Some(last) = merged_handlers.last_mut()
&& last.end_offset == handler.start_offset
&& last.handler_offset == handler.handler_offset
{
last.end_offset = handler.end_offset;
continue;
}
}
merged_handlers.push(handler.clone());
}
merged_handlers.sort_by_key(|h| h.start_offset);

View file

@ -729,11 +729,10 @@ impl<'a> Lexer<'a> {
fn is_identifier_start(&self) -> Option<(u32, usize)> {
let cp = self.current_code_point();
if cp == '\\' as u32 {
if let Some((escaped_cp, len)) = self.is_identifier_unicode_escape() {
if is_identifier_start_cp(escaped_cp) {
if let Some((escaped_cp, len)) = self.is_identifier_unicode_escape()
&& is_identifier_start_cp(escaped_cp) {
return Some((escaped_cp, len));
}
}
return None;
}
@ -750,11 +749,10 @@ impl<'a> Lexer<'a> {
fn is_identifier_middle(&self) -> Option<(u32, usize)> {
let cp = self.current_code_point();
if cp == '\\' as u32 {
if let Some((escaped_cp, len)) = self.is_identifier_unicode_escape() {
if is_identifier_continue_cp(escaped_cp) {
if let Some((escaped_cp, len)) = self.is_identifier_unicode_escape()
&& is_identifier_continue_cp(escaped_cp) {
return Some((escaped_cp, len));
}
}
return None;
}

File diff suppressed because it is too large Load diff

View file

@ -1070,7 +1070,7 @@ impl<'a> Parser<'a> {
}
}
StatementKind::FunctionDeclaration {
name: Some(ref name),
name: Some(name),
..
} => {
declared_names.insert(name.name.clone());
@ -1094,7 +1094,7 @@ impl<'a> Parser<'a> {
}
}
StatementKind::FunctionDeclaration {
name: Some(ref name),
name: Some(name),
..
} => {
declared_names.insert(name.name.clone());
@ -1125,8 +1125,8 @@ impl<'a> Parser<'a> {
if entry.kind == ExportEntryKind::EmptyNamedExport {
continue;
}
if let Some(ref local_name) = entry.local_or_import_name {
if !declared_names.contains(local_name.as_slice()) {
if let Some(ref local_name) = entry.local_or_import_name
&& !declared_names.contains(local_name.as_slice()) {
self.syntax_error_at_position(
&format!(
"'{}' in export is not declared",
@ -1135,7 +1135,6 @@ impl<'a> Parser<'a> {
child.range.start,
);
}
}
}
}
}

View file

@ -44,8 +44,8 @@ fn get_declaration_export_names(statement: &Statement) -> Vec<Utf16String> {
}
names
}
StatementKind::FunctionDeclaration { ref name, .. } => {
if let Some(ref name) = name {
StatementKind::FunctionDeclaration { name, .. } => {
if let Some(name) = name {
vec![name.name.clone()]
} else {
Vec::new()
@ -76,11 +76,10 @@ fn collect_pattern_names(pat: &BindingPattern, names: &mut Vec<Utf16String>) {
Some(BindingEntryAlias::BindingPattern(nested)) => collect_pattern_names(nested, names),
_ => {}
}
if entry.alias.is_none() {
if let Some(BindingEntryName::Identifier(id)) = &entry.name {
if entry.alias.is_none()
&& let Some(BindingEntryName::Identifier(id)) = &entry.name {
names.push(id.name.clone());
}
}
}
}
@ -1801,13 +1800,12 @@ impl<'a> Parser<'a> {
} else if self.match_token(TokenType::StringLiteral) {
let token = self.consume();
let (name, _) = self.parse_string_value(&token);
if let Some(&last) = name.last() {
if (0xD800..=0xDBFF).contains(&last) {
if let Some(&last) = name.last()
&& (0xD800..=0xDBFF).contains(&last) {
self.syntax_error(
"StringValue ending with unpaired high surrogate",
);
}
}
if !self.match_as() {
self.expected("'as'");
@ -1890,26 +1888,24 @@ impl<'a> Parser<'a> {
if matches_function != MatchesFunctionDeclaration::No {
let has_default_name = matches_function == MatchesFunctionDeclaration::WithoutName;
let declaration = self.parse_function_declaration_for_export(has_default_name);
if !has_default_name {
if let StatementKind::FunctionDeclaration {
if !has_default_name
&& let StatementKind::FunctionDeclaration {
name: Some(ref name_id),
..
} = declaration.inner
{
local_name = Some(name_id.name.clone());
}
}
statement = Some(Box::new(declaration));
} else if self.match_token(TokenType::Class) {
let next = self.next_token();
if next.token_type != TokenType::CurlyOpen && next.token_type != TokenType::Extends
{
let declaration = self.parse_class_declaration();
if let StatementKind::ClassDeclaration(ref class) = declaration.inner {
if let Some(ref name_id) = class.name {
if let StatementKind::ClassDeclaration(ref class) = declaration.inner
&& let Some(ref name_id) = class.name {
local_name = Some(name_id.name.clone());
}
}
statement = Some(Box::new(declaration));
} else {
// Unnamed class declaration - don't consume semicolon,
@ -2071,8 +2067,8 @@ impl<'a> Parser<'a> {
// Check for duplicate exported names.
for entry in &entries {
if let Some(ref name) = entry.export_name {
if !self.exported_names.insert(name.clone()) {
if let Some(ref name) = entry.export_name
&& !self.exported_names.insert(name.clone()) {
self.syntax_error_at_position(
&format!(
"Duplicate export with name: '{}'",
@ -2081,7 +2077,6 @@ impl<'a> Parser<'a> {
start,
);
}
}
}
self.statement(
@ -2131,11 +2126,10 @@ impl<'a> Parser<'a> {
// https://tc39.es/ecma262/#sec-module-semantics-static-semantics-early-errors
// It is a Syntax Error if IsStringWellFormedUnicode of the StringValue
// of StringLiteral is false.
if let Some(&last) = value.last() {
if (0xD800..=0xDBFF).contains(&last) {
if let Some(&last) = value.last()
&& (0xD800..=0xDBFF).contains(&last) {
self.syntax_error("StringValue ending with unpaired high surrogate");
}
}
(value, true)
} else {
self.expected("export specifier (string or identifier)");

View file

@ -199,8 +199,8 @@ impl<'a> Parser<'a> {
// C++ checks for freestanding `arguments` references here (after
// parse_primary_expression), NOT during consume(). This avoids
// falsely flagging parameter names like `function f(arguments)`.
if let ExpressionKind::Identifier(ref id) = expression.inner {
if id.name == utf16!("arguments")
if let ExpressionKind::Identifier(ref id) = expression.inner
&& id.name == utf16!("arguments")
&& !self.flags.strict_mode
&& !self
.scope_collector
@ -209,7 +209,6 @@ impl<'a> Parser<'a> {
self.scope_collector
.set_contains_access_to_arguments_object_in_non_strict_mode();
}
}
if !should_continue {
// Yield/Await expressions don't participate in secondary expression
@ -796,8 +795,8 @@ impl<'a> Parser<'a> {
// lhs_start. When the expression is parenthesized (e.g.
// `([a,b]) = ...`), lhs_start points to `(` but we need
// to re-lex from `[` to correctly synthesize the pattern.
if let Some(binding_pattern) = self.synthesize_binding_pattern(lhs.range.start)
{
match self.synthesize_binding_pattern(lhs.range.start)
{ Some(binding_pattern) => {
// Register synthesized identifiers with the scope collector so
// they get resolved as locals during analyze().
for (name, id) in self.pattern_bound_names.drain(..) {
@ -818,9 +817,9 @@ impl<'a> Parser<'a> {
),
ForbiddenTokens::none(),
);
} else {
} _ => {
self.pattern_bound_names = saved_bound_names;
}
}}
}
let allow_call = !matches!(
tt,
@ -1091,11 +1090,10 @@ impl<'a> Parser<'a> {
rhs_start.column,
);
}
if let ExpressionKind::Member { property, .. } = &expression.inner {
if matches!(property.inner, ExpressionKind::PrivateIdentifier(_)) {
if let ExpressionKind::Member { property, .. } = &expression.inner
&& matches!(property.inner, ExpressionKind::PrivateIdentifier(_)) {
self.syntax_error("Private fields cannot be deleted");
}
}
self.expression(
start,
ExpressionKind::Unary {
@ -1194,12 +1192,11 @@ impl<'a> Parser<'a> {
let arguments = self.parse_arguments();
// Check the actual callee expression kind, matching C++ which does
// is<Identifier>(callee) && callee.string() == "eval".
if let ExpressionKind::Identifier(ref id) = callee.inner {
if id.name == utf16!("eval") {
if let ExpressionKind::Identifier(ref id) = callee.inner
&& id.name == utf16!("eval") {
self.scope_collector.set_contains_direct_call_to_eval();
self.scope_collector.set_uses_this();
}
}
self.expression(
start,
ExpressionKind::Call(CallExpressionData {
@ -1623,8 +1620,8 @@ impl<'a> Parser<'a> {
// target. We parse the initializer to advance the lexer, but roll back scope records
// since this expression is discarded. synthesize_binding_pattern will
// re-parse from source and create the real scope records.
if self.match_token(TokenType::Equals) && is_identifier {
if let Some(kv) = &key_value {
if self.match_token(TokenType::Equals) && is_identifier
&& let Some(kv) = &key_value {
let id = self.make_identifier(obj_start, kv.clone());
self.scope_collector
.register_identifier(id.clone(), &id.name, None);
@ -1647,7 +1644,6 @@ impl<'a> Parser<'a> {
is_computed: false,
};
}
}
// Shorthand property: { x }
// Only identifiers can be shorthand properties, not string/numeric literals.

View file

@ -56,13 +56,12 @@ impl<'a> Parser<'a> {
if self.match_invalid_escaped_keyword() {
self.syntax_error("Keyword must not contain escaped characters");
}
if self.match_identifier_name() {
if let Some(labelled) =
if self.match_identifier_name()
&& let Some(labelled) =
self.try_parse_labelled_statement(allow_labelled_function)
{
return labelled;
}
}
if self.match_expression() {
self.parse_expression_statement()
} else {
@ -477,15 +476,12 @@ impl<'a> Parser<'a> {
} else {
self.validate_for_in_of_lhs(&init);
// https://tc39.es/ecma262/#sec-for-in-and-for-of-statements
if let LocalForInit::Expression(ref expression) = init {
if let ExpressionKind::Member { ref object, .. } = expression.inner {
if let ExpressionKind::Identifier(ref ident) = object.inner {
if ident.name == utf16!("let") {
if let LocalForInit::Expression(ref expression) = init
&& let ExpressionKind::Member { ref object, .. } = expression.inner
&& let ExpressionKind::Identifier(ref ident) = object.inner
&& ident.name == utf16!("let") {
self.syntax_error("For of statement may not start with let.");
}
}
}
}
}
self.consume();
let rhs = self.parse_assignment_expression();
@ -513,8 +509,8 @@ impl<'a> Parser<'a> {
}
// Standard for loop — const requires initializer.
if let LocalForInit::Declaration(ref declaration) = init {
if let StatementKind::VariableDeclaration {
if let LocalForInit::Declaration(ref declaration) = init
&& let StatementKind::VariableDeclaration {
kind: DeclarationKind::Const,
ref declarations,
} = declaration.inner
@ -525,7 +521,6 @@ impl<'a> Parser<'a> {
}
}
}
}
self.consume_token(TokenType::Semicolon);
let for_init = match init {
LocalForInit::Declaration(declaration) => {
@ -869,15 +864,14 @@ impl<'a> Parser<'a> {
};
let is_iteration = body_starts_iteration || self.last_inner_label_is_iteration;
if !is_iteration {
if let Some(Some((line, col))) = self.labels_in_scope.get(label.as_slice()) {
if !is_iteration
&& let Some(Some((line, col))) = self.labels_in_scope.get(label.as_slice()) {
self.syntax_error_at(
"labelled continue statement cannot use non iterating statement",
*line,
*col,
);
}
}
self.labels_in_scope.remove(label.as_slice());
self.flags.in_break_context = break_before;
@ -916,8 +910,8 @@ impl<'a> Parser<'a> {
/// Validate that an expression-form LHS is valid for for-in/for-of.
fn validate_for_in_of_lhs(&mut self, init: &LocalForInit) {
if let LocalForInit::Expression(ref expression) = *init {
if !Self::is_identifier(expression)
if let LocalForInit::Expression(ref expression) = *init
&& !Self::is_identifier(expression)
&& !Self::is_member_expression(expression)
&& !Self::is_call_expression(expression)
&& !Self::is_object_expression(expression)
@ -925,7 +919,6 @@ impl<'a> Parser<'a> {
{
self.syntax_error("Invalid left-hand side in for-loop");
}
}
}
/// Convert a `LocalForInit` into a `ForInOfLhs`, synthesizing a binding
@ -938,14 +931,14 @@ impl<'a> Parser<'a> {
LocalForInit::Expression(expression) => {
if Self::is_array_expression(&expression) || Self::is_object_expression(&expression)
{
if let Some(pattern) = self.synthesize_binding_pattern(init_start) {
match self.synthesize_binding_pattern(init_start) { Some(pattern) => {
for (name, id) in self.pattern_bound_names.drain(..) {
self.scope_collector.register_identifier(id, &name, None);
}
ForInOfLhs::Pattern(pattern)
} else {
} _ => {
ForInOfLhs::Expression(Box::new(expression))
}
}}
} else {
ForInOfLhs::Expression(Box::new(expression))
}

View file

@ -423,10 +423,11 @@ impl ScopeCollector {
let mut record = ScopeRecord::new(scope_type, scope_level, scope_data);
record.parent = self.current;
if scope_type != ScopeType::Function && record.scope_data.is_none() {
if let Some(parent_index) = self.current {
record.scope_data = self.records[parent_index].scope_data.clone();
}
if scope_type != ScopeType::Function
&& record.scope_data.is_none()
&& let Some(parent_index) = self.current
{
record.scope_data = self.records[parent_index].scope_data.clone();
}
if scope_level == ScopeLevel::NotTopLevel {
@ -447,17 +448,17 @@ impl ScopeCollector {
pub fn close_scope(&mut self) {
let index = self.current.expect("close_scope with no current scope");
if let Some(parent_index) = self.records[index].parent {
if !self.records[index].has_function_parameters {
let c = &self.records[index];
let arguments = c.contains_access_to_arguments_object_in_non_strict_mode;
let eval = c.contains_direct_call_to_eval;
let contains_await = c.contains_await_expression;
self.records[parent_index]
.contains_access_to_arguments_object_in_non_strict_mode |= arguments;
self.records[parent_index].contains_direct_call_to_eval |= eval;
self.records[parent_index].contains_await_expression |= contains_await;
}
if let Some(parent_index) = self.records[index].parent
&& !self.records[index].has_function_parameters
{
let c = &self.records[index];
let arguments = c.contains_access_to_arguments_object_in_non_strict_mode;
let eval = c.contains_direct_call_to_eval;
let contains_await = c.contains_await_expression;
self.records[parent_index].contains_access_to_arguments_object_in_non_strict_mode |=
arguments;
self.records[parent_index].contains_direct_call_to_eval |= eval;
self.records[parent_index].contains_await_expression |= contains_await;
}
self.current = self.records[index].parent;
@ -1139,19 +1140,18 @@ impl ScopeCollector {
&& group.captured_by_nested_function
&& var_flags.intersects(VarFlags::VAR)
&& !var_flags.intersects(VarFlags::FORBIDDEN_LEXICAL)
&& let Some(parent_index) = records[index].parent
{
if let Some(parent_index) = records[index].parent {
records[parent_index]
.identifier_groups
.entry(name.clone())
.or_insert_with(|| IdentifierGroup {
captured_by_nested_function: false,
used_inside_with_statement: false,
identifiers: Vec::new(),
declaration_kind: None,
})
.captured_by_nested_function = true;
}
records[parent_index]
.identifier_groups
.entry(name.clone())
.or_insert_with(|| IdentifierGroup {
captured_by_nested_function: false,
used_inside_with_statement: false,
identifiers: Vec::new(),
declaration_kind: None,
})
.captured_by_nested_function = true;
}
if !group.captured_by_nested_function && !group.used_inside_with_statement {
@ -1167,41 +1167,41 @@ impl ScopeCollector {
local_scope = records[index].top_level;
}
if let Some(ls) = local_scope {
if let Some(ref scope_data) = records[ls].scope_data {
let mut sd = scope_data.borrow_mut();
if let Some(ls) = local_scope
&& let Some(ref scope_data) = records[ls].scope_data
{
let mut sd = scope_data.borrow_mut();
if is_function_parameter {
let argument_index = records[ls].get_parameter_index(&name);
if let Some(ai) = argument_index {
for id in &group.identifiers {
id.local_index.set(ai);
id.local_type.set(Some(crate::ast::LocalType::Argument));
}
} else {
let lvi = u32_from_usize(sd.local_variables.len());
sd.local_variables.push(LocalVariable {
name: name.clone(),
kind: LocalVarKind::Var,
});
for id in &group.identifiers {
id.local_index.set(lvi);
id.local_type.set(Some(crate::ast::LocalType::Variable));
}
if is_function_parameter {
let argument_index = records[ls].get_parameter_index(&name);
if let Some(ai) = argument_index {
for id in &group.identifiers {
id.local_index.set(ai);
id.local_type.set(Some(crate::ast::LocalType::Argument));
}
} else {
let kind = local_var_kind
.expect("local_var_kind must be set for local variables");
let lvi = u32_from_usize(sd.local_variables.len());
sd.local_variables.push(LocalVariable {
name: name.clone(),
kind,
kind: LocalVarKind::Var,
});
for id in &group.identifiers {
id.local_index.set(lvi);
id.local_type.set(Some(crate::ast::LocalType::Variable));
}
}
} else {
let kind = local_var_kind
.expect("local_var_kind must be set for local variables");
let lvi = u32_from_usize(sd.local_variables.len());
sd.local_variables.push(LocalVariable {
name: name.clone(),
kind,
});
for id in &group.identifiers {
id.local_index.set(lvi);
id.local_type.set(Some(crate::ast::LocalType::Variable));
}
}
}
}
@ -1279,10 +1279,9 @@ impl ScopeCollector {
name: Some(ref name_ident),
..
} = sd.children[i].inner
&& seen_function_names.insert(name_ident.name.clone())
{
if seen_function_names.insert(name_ident.name.clone()) {
functions_to_initialize.push(crate::ast::FunctionToInit { child_index: i });
}
functions_to_initialize.push(crate::ast::FunctionToInit { child_index: i });
}
}
}
@ -1426,10 +1425,9 @@ impl ScopeCollector {
ref is_hoisted,
..
} = child.inner
&& name.as_ref().is_some_and(|n| n.name == function.name)
{
if name.as_ref().is_some_and(|n| n.name == function.name) {
is_hoisted.set(true);
}
is_hoisted.set(true);
}
}
}