LibJs/Rust: Migrate to edition 2024
This commit is contained in:
parent
0c6133176b
commit
bbb6121df4
13 changed files with 3662 additions and 3480 deletions
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "libjs_rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue