1#![allow(clippy::too_many_lines)]
41
42use std::collections::{BTreeSet, HashMap, HashSet};
43use std::error::Error;
44use std::fmt;
45
46use bamts_bytecode::{
47 AccessorKind, BigIntLiteral, BinaryOp, Constant, ConstantId, EcmaString, EcmaStringBuilder,
48 ExceptionHandler, Function, FunctionFlags, FunctionId, Instruction, IteratorKind,
49 MAX_CONSTANTS, MAX_FUNCTIONS, MAX_INSTRUCTIONS, MAX_REGISTERS, Module, NumberBits, Pc,
50 Register, UnaryOp, Verified, VerifyError,
51};
52
53pub use crate::program::{
54 ExecutableModuleProvenance, ExecutableProgram, ProgramLowerError, ProgramLowerErrorKind,
55 ProgramLowerPhase, lower_program,
56};
57
58use crate::source::{ScriptKind, SourceId, TextRange, Utf16Pos};
59use crate::syntax::{
60 ArrayBindingElement, ArrayElement, ArrowFunction, AssignmentArrayElement, AssignmentExpression,
61 AssignmentMemberTarget, AssignmentObjectProperty, AssignmentOperator, AssignmentTarget,
62 AssignmentTargetNode, AwaitExpression, BinaryExpression, BinaryOperator, BindingPattern, Block,
63 BooleanLiteralNode, CallArgument, CallExpression, ClassDeclaration, ClassMember,
64 ConditionalExpression, DoWhileStatement, ExportDeclaration, ExportDefaultValue,
65 ExportNamedDeclaration, ExportSpecifierMode, Expr, Expression, ForBinding, ForInStatement,
66 ForInitializer, ForOfMode, ForOfStatement, ForStatement, FunctionBody, FunctionLike,
67 IdentifierNode, IfStatement, ImportBinding, ImportDeclaration, ImportSpecifierMode, Literal,
68 LogicalExpression, LogicalOperator, MemberExpression, MemberProperty, MetaProperty,
69 ModuleExportName, NewExpression, NodeKind, NumericLiteralNode, ObjectLiteral, ObjectMember,
70 ParameterNode, Pattern, PrivateIdentifierNode, PropertyModifier, PropertyName,
71 RegexLiteralNode, SourceFile, Statement, Stmt, StringLiteralNode, SwitchStatement,
72 TemplateElementNode, TemplateLiteral, TokenKind, UnaryOperator, UpdateExpression,
73 UpdateOperator, VariableDeclaration, VariableKind, WhileStatement, YieldExpression,
74};
75
76fn zero_range() -> TextRange {
79 match TextRange::new(Utf16Pos::ZERO, Utf16Pos::ZERO) {
80 Ok(range) => range,
81 Err(_) => unreachable!("Utf16Pos::ZERO is never after itself"),
82 }
83}
84
85#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
87pub struct LowerOptions {
88 pub javascript_compatibility: bool,
90}
91
92#[derive(Clone, Copy, Debug, Eq, PartialEq)]
94pub(crate) enum LoweringGoal {
95 Module,
97 ProgramModule,
99 ClassicScript,
101}
102
103const MAX_BODY_INSTRUCTIONS: usize = MAX_INSTRUCTIONS as usize - 2;
107const MAX_STRING_UNITS: usize = 1 << 20;
110
111#[derive(Clone, Debug, Eq, PartialEq)]
113pub struct LowerError {
114 pub source: SourceId,
115 pub range: TextRange,
116 pub kind: LowerErrorKind,
117}
118
119#[derive(Clone, Debug, Eq, PartialEq)]
121pub enum LowerErrorKind {
122 JavaScriptSourceNeedsCompatibility { script_kind: ScriptKind },
124 JsonSourceNotExecutable,
126 MissingSyntax { expected: NodeKind },
128 InvalidNumericLiteral,
130 InvalidBigIntLiteral,
132 InvalidRegexLiteral,
134 IllFormedMetadataString,
136 Unsupported(UnsupportedConstruct),
138 Capacity(CapacityLimit),
140 Verify(VerifyError),
143}
144
145#[derive(Clone, Copy, Debug, Eq, PartialEq)]
149pub enum UnsupportedConstruct {
150 WithStatement,
152 UsingDeclaration,
154 LabeledStatement,
156 LabeledJump,
158 DebuggerStatement,
160 EnumDeclaration,
162 NamespaceDeclaration,
164 RuntimeImportEquals,
166 RuntimeExportAll,
168 ExportAssignment,
170 DecoratedDeclaration,
172 DynamicImportExpression,
174 ImportDeclarationInScript,
176 ExportDeclarationInScript,
178 DynamicImportInScript,
180 ImportMeta,
182 EscapedIdentifier,
184 NonDecimalBigInt,
186 ReturnOutsideFunction,
188 DerivedConstructorShape,
191 ThisBeforeDerivedSuper,
193}
194
195#[derive(Clone, Copy, Debug, Eq, PartialEq)]
197pub enum CapacityLimit {
198 Registers,
199 Constants,
200 Functions,
201 Instructions,
202 StringUnits,
203 Captures,
204}
205
206impl fmt::Display for LowerError {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 write!(
209 f,
210 "lowering failed in source {} at {}..{}: {}",
211 self.source.get(),
212 self.range.start().get(),
213 self.range.end().get(),
214 self.kind
215 )
216 }
217}
218
219impl fmt::Display for LowerErrorKind {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 match self {
222 Self::JavaScriptSourceNeedsCompatibility { script_kind } => write!(
223 f,
224 "{script_kind:?} source requires LowerOptions::javascript_compatibility"
225 ),
226 Self::JsonSourceNotExecutable => f.write_str("JSON sources are not executable"),
227 Self::MissingSyntax { expected } => {
228 write!(f, "parser recovery produced a missing {expected:?}")
229 }
230 Self::InvalidNumericLiteral => f.write_str("numeric literal has no cooked value"),
231 Self::InvalidBigIntLiteral => f.write_str("bigint literal has no canonical value"),
232 Self::InvalidRegexLiteral => f.write_str("regular-expression literal is malformed"),
233 Self::IllFormedMetadataString => {
234 f.write_str("module metadata string is not well-formed UTF-16")
235 }
236 Self::Unsupported(construct) => {
237 write!(f, "unsupported runtime semantics: {construct}")
238 }
239 Self::Capacity(limit) => write!(f, "bytecode capacity exhausted: {limit}"),
240 Self::Verify(error) => write!(f, "assembled module failed verification: {error}"),
241 }
242 }
243}
244
245impl fmt::Display for UnsupportedConstruct {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 let text = match self {
248 Self::WithStatement => "`with` statement",
249 Self::UsingDeclaration => "`using` declaration",
250 Self::LabeledStatement => "labeled statement",
251 Self::LabeledJump => "labeled `break`/`continue`",
252 Self::DebuggerStatement => "`debugger` statement",
253 Self::EnumDeclaration => "runtime `enum` declaration",
254 Self::NamespaceDeclaration => "runtime `namespace` declaration",
255 Self::RuntimeImportEquals => "runtime `import =` declaration",
256 Self::RuntimeExportAll => "runtime `export *` declaration",
257 Self::ExportAssignment => "`export =` assignment",
258 Self::DecoratedDeclaration => "decorated declaration",
259 Self::DynamicImportExpression => "dynamic `import()` with a non-literal specifier",
260 Self::ImportDeclarationInScript => "`import` declaration in a classic script",
261 Self::ExportDeclarationInScript => "`export` declaration in a classic script",
262 Self::DynamicImportInScript => "dynamic `import()` in a classic script",
263 Self::ImportMeta => "`import.meta` meta property",
264 Self::EscapedIdentifier => "identifier containing escape sequences",
265 Self::NonDecimalBigInt => "non-decimal bigint literal",
266 Self::ReturnOutsideFunction => "top-level `return`",
267 Self::DerivedConstructorShape => {
268 "derived constructor without one direct `super(...)` call"
269 }
270 Self::ThisBeforeDerivedSuper => "`this` before `super(...)` in a derived constructor",
271 };
272 f.write_str(text)
273 }
274}
275
276impl fmt::Display for CapacityLimit {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278 let text = match self {
279 Self::Registers => "too many registers in one function",
280 Self::Constants => "too many pooled constants",
281 Self::Functions => "too many functions",
282 Self::Instructions => "too many instructions in one function",
283 Self::StringUnits => "string constant exceeds the deterministic pool code-unit ceiling",
284 Self::Captures => "too many captured variables in one closure",
285 };
286 f.write_str(text)
287 }
288}
289
290impl Error for LowerError {
291 fn source(&self) -> Option<&(dyn Error + 'static)> {
292 match &self.kind {
293 LowerErrorKind::Verify(error) => Some(error),
294 _ => None,
295 }
296 }
297}
298
299pub fn lower(file: &SourceFile, options: LowerOptions) -> Result<Module<Verified>, LowerError> {
310 let module = assemble(file, options)?;
311 module.verify().map_err(|error| LowerError {
312 source: file.source_id(),
313 range: file.range(),
314 kind: LowerErrorKind::Verify(error),
315 })
316}
317
318pub(crate) fn assemble(
321 file: &SourceFile,
322 options: LowerOptions,
323) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
324 assemble_with_linkage_strings(file, options, &[], LoweringGoal::Module)
325}
326
327pub(crate) fn assemble_program_module(
328 file: &SourceFile,
329 options: LowerOptions,
330 linkage_strings: &[String],
331) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
332 assemble_with_linkage_strings(file, options, linkage_strings, LoweringGoal::ProgramModule)
333}
334
335pub(crate) fn assemble_classic_script(
337 file: &SourceFile,
338 options: LowerOptions,
339) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
340 assemble_classic_script_named(file, options, "evalmachine.<anonymous>")
341}
342
343pub(crate) fn assemble_classic_script_named(
344 file: &SourceFile,
345 options: LowerOptions,
346 module_name: &str,
347) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
348 assemble_with_linkage_strings(
349 file,
350 options,
351 &[module_name.to_owned()],
352 LoweringGoal::ClassicScript,
353 )
354}
355
356fn assemble_with_linkage_strings(
357 file: &SourceFile,
358 options: LowerOptions,
359 linkage_strings: &[String],
360 goal: LoweringGoal,
361) -> Result<Module<bamts_bytecode::Unverified>, LowerError> {
362 validate_script_kind(file, options)?;
363
364 let mut builder = ModuleBuilder {
365 source: file.source_id(),
366 constants: Vec::new(),
367 functions: Vec::new(),
368 };
369 for value in linkage_strings {
370 builder.intern(Constant::String(EcmaString::from_utf8(value)), file.range())?;
371 }
372 let entry = builder.reserve_function(file.range())?;
373
374 let mut context = FunctionContext::new_top_level(file, goal);
375 let completion = if goal == LoweringGoal::ClassicScript {
376 let completion = context.alloc_register(file.range())?;
377 let undefined = context.undefined(&mut builder, file.range())?;
378 context.move_to(file.range(), completion, undefined)?;
379 context.completion = Some(completion);
380 Some(completion)
381 } else {
382 None
383 };
384 context.lower_top_level(&mut builder, file.statements())?;
385 match completion {
386 Some(value) => context.emit(file.range(), Instruction::Return { value })?,
387 None => context.emit(file.range(), Instruction::Halt)?,
388 };
389 let assembled = context.into_function(None, FunctionFlags::default());
390 builder.fill_function(entry, assembled);
391
392 let functions = builder
393 .functions
394 .into_iter()
395 .map(|slot| slot.expect("every reserved function slot is filled before assembly"))
396 .collect();
397 Ok(Module::new(builder.constants, functions, entry))
398}
399
400fn validate_script_kind(file: &SourceFile, options: LowerOptions) -> Result<(), LowerError> {
401 let kind = match file.script_kind() {
402 ScriptKind::TypeScript | ScriptKind::TypeScriptReact => return Ok(()),
403 ScriptKind::JavaScript | ScriptKind::JavaScriptReact => {
404 if options.javascript_compatibility {
405 return Ok(());
406 }
407 LowerErrorKind::JavaScriptSourceNeedsCompatibility {
408 script_kind: file.script_kind(),
409 }
410 }
411 ScriptKind::Json => LowerErrorKind::JsonSourceNotExecutable,
412 };
413 Err(LowerError {
414 source: file.source_id(),
415 range: file.range(),
416 kind,
417 })
418}
419
420struct ModuleBuilder {
422 source: SourceId,
423 constants: Vec<Constant>,
424 functions: Vec<Option<Function>>,
425}
426
427impl ModuleBuilder {
428 fn error(&self, range: TextRange, kind: LowerErrorKind) -> LowerError {
429 LowerError {
430 source: self.source,
431 range,
432 kind,
433 }
434 }
435
436 fn intern(&mut self, constant: Constant, range: TextRange) -> Result<ConstantId, LowerError> {
438 if let Constant::String(value) = &constant
439 && value.len_units() > MAX_STRING_UNITS
440 {
441 return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::StringUnits)));
442 }
443 if let Some(position) = self
444 .constants
445 .iter()
446 .position(|existing| *existing == constant)
447 {
448 return Ok(ConstantId::new(position as u32));
449 }
450 if self.constants.len() >= MAX_CONSTANTS as usize {
451 return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Constants)));
452 }
453 let id = ConstantId::new(self.constants.len() as u32);
454 self.constants.push(constant);
455 Ok(id)
456 }
457
458 fn reserve_function(&mut self, range: TextRange) -> Result<FunctionId, LowerError> {
459 if self.functions.len() >= MAX_FUNCTIONS as usize {
460 return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Functions)));
461 }
462 let id = FunctionId::new(self.functions.len() as u32);
463 self.functions.push(None);
464 Ok(id)
465 }
466
467 fn fill_function(&mut self, id: FunctionId, function: Function) {
468 self.functions[id.get() as usize] = Some(function);
469 }
470}
471
472#[derive(Clone, Copy)]
475enum Binding {
476 Local(Register),
478 Cell(Register),
480}
481
482type BindingSite = (usize, usize);
483
484#[derive(Clone, Debug, Eq, Hash, PartialEq)]
485enum BindingIdentity {
486 Function(String),
487 Lexical(BindingSite),
488}
489
490#[derive(Default)]
491struct CapturePlan {
492 captured: HashSet<BindingIdentity>,
493 runtime_cells: HashSet<BindingIdentity>,
494}
495
496impl CapturePlan {
497 fn captures(&self, name: &str, site: BindingSite, declaration_scope: DeclarationScope) -> bool {
498 self.captured
499 .contains(&binding_identity(name, site, declaration_scope))
500 }
501
502 fn requires_cell(
503 &self,
504 name: &str,
505 site: BindingSite,
506 declaration_scope: DeclarationScope,
507 ) -> bool {
508 let identity = binding_identity(name, site, declaration_scope);
509 self.captured.contains(&identity) || self.runtime_cells.contains(&identity)
510 }
511}
512
513#[derive(Clone)]
514struct ImmediateDeclaration<'a> {
515 name: String,
516 site: BindingSite,
517 range: TextRange,
518 kind: ImmediateDeclarationKind<'a>,
519}
520
521#[derive(Clone, Copy)]
522enum ImmediateDeclarationKind<'a> {
523 Lexical,
524 Function(&'a FunctionLike),
525}
526
527fn binding_identity(
528 name: &str,
529 site: BindingSite,
530 declaration_scope: DeclarationScope,
531) -> BindingIdentity {
532 match declaration_scope {
533 DeclarationScope::Function => BindingIdentity::Function(name.to_owned()),
534 DeclarationScope::Lexical | DeclarationScope::Iteration => BindingIdentity::Lexical(site),
535 }
536}
537
538fn binding_site(range: TextRange) -> BindingSite {
539 (range.start().get(), range.end().get())
540}
541
542#[derive(Clone, Copy)]
543enum DeclarationScope {
544 Function,
545 Lexical,
546 Iteration,
547}
548
549#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
551enum CaptureKey {
552 Name(String),
554 This,
556 Arguments,
558 NewTarget,
560 Parent(Register),
562}
563
564#[derive(Clone, Copy)]
566enum ArgumentsSource {
567 Own,
569 Captured(Register),
571 None,
574}
575
576struct LoopFrame {
578 breaks: Vec<Pc>,
579 continues: Vec<Pc>,
580 is_loop: bool,
581}
582
583const COMPLETION_NORMAL: i32 = 0;
585const COMPLETION_RETURN: i32 = 1;
586const COMPLETION_THROW: i32 = 2;
587const COMPLETION_BREAK: i32 = 3;
588const COMPLETION_CONTINUE: i32 = 4;
589
590struct FinallyFrame {
593 kind_reg: Register,
594 value_reg: Register,
595 pending: Vec<Pc>,
596 loop_depth: usize,
599}
600
601struct FunctionContext<'a> {
603 file: &'a SourceFile,
604 code: Vec<Instruction>,
605 registers: u32,
606 capture_count: u32,
607 parameter_count: u32,
608 scopes: Vec<HashMap<String, Binding>>,
609 predeclared_cells: HashMap<BindingIdentity, Register>,
612 capture_plan: CapturePlan,
613 loops: Vec<LoopFrame>,
614 handlers: Vec<ExceptionHandler>,
615 finally_stack: Vec<FinallyFrame>,
616 top_level: bool,
619 goal: LoweringGoal,
620 completion: Option<Register>,
622 completion_pool: Vec<Register>,
624 completion_depth: usize,
625 this_capture: Option<Register>,
628 new_target_capture: Option<Register>,
631 parent_constructor_capture: Option<Register>,
633 arguments_source: ArgumentsSource,
634}
635
636impl<'a> FunctionContext<'a> {
637 fn new_top_level(file: &'a SourceFile, goal: LoweringGoal) -> Self {
638 let capture_plan = CapturePlan::for_statements(file, file.statements());
639 Self {
640 file,
641 code: Vec::new(),
642 registers: 0,
643 capture_count: 0,
644 parameter_count: 0,
645 scopes: vec![HashMap::new()],
646 predeclared_cells: HashMap::new(),
647 capture_plan,
648 loops: Vec::new(),
649 handlers: Vec::new(),
650 finally_stack: Vec::new(),
651 top_level: true,
652 goal,
653 completion: None,
654 completion_pool: Vec::new(),
655 completion_depth: 0,
656 this_capture: None,
657 new_target_capture: None,
658 parent_constructor_capture: None,
659 arguments_source: ArgumentsSource::None,
660 }
661 }
662
663 fn into_function(self, name: Option<ConstantId>, flags: FunctionFlags) -> Function {
664 Function::new(
665 name,
666 self.capture_count,
667 self.parameter_count,
668 self.registers,
669 flags,
670 self.code,
671 self.handlers,
672 )
673 }
674
675 fn error(&self, range: TextRange, kind: LowerErrorKind) -> LowerError {
676 LowerError {
677 source: self.file.source_id(),
678 range,
679 kind,
680 }
681 }
682
683 fn unsupported(&self, range: TextRange, construct: UnsupportedConstruct) -> LowerError {
684 self.error(range, LowerErrorKind::Unsupported(construct))
685 }
686
687 fn missing(&self, range: TextRange, expected: NodeKind) -> LowerError {
688 self.error(range, LowerErrorKind::MissingSyntax { expected })
689 }
690
691 fn emit(&mut self, range: TextRange, instruction: Instruction) -> Result<Pc, LowerError> {
696 if self.code.len() >= MAX_BODY_INSTRUCTIONS {
697 return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Instructions)));
698 }
699 let pc = Pc::new(self.code.len() as u32);
700 self.code.push(instruction);
701 Ok(pc)
702 }
703
704 fn next_pc(&self) -> Pc {
705 Pc::new(self.code.len() as u32)
706 }
707
708 fn patch_jump(&mut self, at: Pc, target: Pc) {
709 match &mut self.code[at.get() as usize] {
710 Instruction::Jump { target: slot }
711 | Instruction::JumpIfTrue { target: slot, .. }
712 | Instruction::JumpIfFalse { target: slot, .. } => *slot = target,
713 other => unreachable!("patch target of non-jump instruction: {other:?}"),
714 }
715 }
716
717 fn alloc_register(&mut self, range: TextRange) -> Result<Register, LowerError> {
718 if self.registers >= MAX_REGISTERS {
719 return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Registers)));
720 }
721 let register = Register::new(self.registers);
722 self.registers += 1;
723 Ok(register)
724 }
725
726 fn load_constant(
727 &mut self,
728 builder: &mut ModuleBuilder,
729 constant: Constant,
730 range: TextRange,
731 ) -> Result<Register, LowerError> {
732 let id = builder.intern(constant, range)?;
733 let dst = self.alloc_register(range)?;
734 self.emit(range, Instruction::LoadConst { dst, constant: id })?;
735 Ok(dst)
736 }
737
738 fn undefined(
739 &mut self,
740 builder: &mut ModuleBuilder,
741 range: TextRange,
742 ) -> Result<Register, LowerError> {
743 self.load_constant(builder, Constant::Undefined, range)
744 }
745
746 fn string_reg(
749 &mut self,
750 builder: &mut ModuleBuilder,
751 value: EcmaString,
752 range: TextRange,
753 ) -> Result<Register, LowerError> {
754 self.load_constant(builder, Constant::String(value), range)
755 }
756
757 fn move_to(
758 &mut self,
759 range: TextRange,
760 dst: Register,
761 src: Register,
762 ) -> Result<(), LowerError> {
763 self.emit(range, Instruction::Move { dst, src })?;
764 Ok(())
765 }
766
767 fn lower_normalizing_statement(
768 &mut self,
769 builder: &mut ModuleBuilder,
770 range: TextRange,
771 lower: impl FnOnce(&mut Self, &mut ModuleBuilder) -> Result<(), LowerError>,
772 ) -> Result<(), LowerError> {
773 let Some(outer) = self.completion else {
774 return lower(self, builder);
775 };
776 let depth = self.completion_depth;
777 let inner = match self.completion_pool.get(depth).copied() {
778 Some(register) => register,
779 None => {
780 let register = self.alloc_register(range)?;
781 self.completion_pool.push(register);
782 register
783 }
784 };
785 let undefined = self.undefined(builder, range)?;
786 self.move_to(range, inner, undefined)?;
787 self.completion = Some(inner);
788 self.completion_depth += 1;
789 let result = lower(self, builder);
790 self.completion_depth -= 1;
791 self.completion = Some(outer);
792 result?;
793 self.move_to(range, outer, inner)
794 }
795
796 fn lower_without_completion(
797 &mut self,
798 builder: &mut ModuleBuilder,
799 lower: impl FnOnce(&mut Self, &mut ModuleBuilder) -> Result<(), LowerError>,
800 ) -> Result<(), LowerError> {
801 let completion = self.completion.take();
802 let result = lower(self, builder);
803 self.completion = completion;
804 result
805 }
806
807 fn push_scope(&mut self) {
812 self.scopes.push(HashMap::new());
813 }
814
815 fn pop_scope(&mut self) {
816 self.scopes.pop();
817 }
818
819 fn resolve(&self, name: &str) -> Option<Binding> {
820 self.scopes
821 .iter()
822 .rev()
823 .find_map(|scope| scope.get(name).copied())
824 }
825
826 fn declare(&mut self, name: String, binding: Binding, declaration_scope: DeclarationScope) {
827 let scope = match declaration_scope {
828 DeclarationScope::Function => self
829 .scopes
830 .first_mut()
831 .expect("a function context always holds its root scope"),
832 DeclarationScope::Lexical | DeclarationScope::Iteration => self
833 .scopes
834 .last_mut()
835 .expect("a function context always holds at least one scope"),
836 };
837 scope.insert(name, binding);
838 }
839
840 fn identifier_text(&self, identifier: &IdentifierNode) -> Result<String, LowerError> {
841 let token = identifier.data().token();
842 if token.is_missing() {
843 return Err(self.missing(identifier.range(), NodeKind::Identifier));
844 }
845 let Some(text) = self.file.token_text(token) else {
846 return Err(self.missing(identifier.range(), NodeKind::Identifier));
847 };
848 if text.contains('\\') {
849 return Err(
850 self.unsupported(identifier.range(), UnsupportedConstruct::EscapedIdentifier)
851 );
852 }
853 Ok(text.to_owned())
854 }
855
856 fn private_text(&self, private: &PrivateIdentifierNode) -> Result<String, LowerError> {
857 let token = private.data().token();
858 if token.is_missing() {
859 return Err(self.missing(private.range(), NodeKind::PrivateIdentifier));
860 }
861 let Some(text) = self.file.token_text(token) else {
862 return Err(self.missing(private.range(), NodeKind::PrivateIdentifier));
863 };
864 Ok(text.to_owned())
865 }
866
867 fn declare_initialized(
869 &mut self,
870 builder: &mut ModuleBuilder,
871 name: &str,
872 value: Register,
873 range: TextRange,
874 site: BindingSite,
875 declaration_scope: DeclarationScope,
876 ) -> Result<(), LowerError> {
877 if self.top_level && !matches!(declaration_scope, DeclarationScope::Iteration) {
878 let id = builder.intern(Constant::String(EcmaString::from_utf8(name)), range)?;
879 self.emit(range, Instruction::StoreGlobal { name: id, value })?;
880 return Ok(());
881 }
882 if self.capture_plan.captures(name, site, declaration_scope) {
883 let cell = self.alloc_register(range)?;
884 self.emit(range, Instruction::CreateArray { dst: cell })?;
885 self.emit(range, Instruction::ArrayPush { array: cell, value })?;
886 self.declare(name.to_owned(), Binding::Cell(cell), declaration_scope);
887 } else {
888 let home = self.alloc_register(range)?;
889 self.move_to(range, home, value)?;
890 self.declare(name.to_owned(), Binding::Local(home), declaration_scope);
891 }
892 Ok(())
893 }
894
895 fn predeclare_captured_binding(
898 &mut self,
899 name: &str,
900 range: TextRange,
901 site: BindingSite,
902 declaration_scope: DeclarationScope,
903 ) -> Result<(), LowerError> {
904 if self.top_level
905 || !self
906 .capture_plan
907 .requires_cell(name, site, declaration_scope)
908 {
909 return Ok(());
910 }
911 let identity = binding_identity(name, site, declaration_scope);
912 if self.predeclared_cells.contains_key(&identity) {
913 return Ok(());
914 }
915 if matches!(declaration_scope, DeclarationScope::Function)
916 && let Some(Binding::Cell(cell)) = self
917 .scopes
918 .first()
919 .and_then(|scope| scope.get(name).copied())
920 {
921 self.predeclared_cells.insert(identity, cell);
922 return Ok(());
923 }
924 let cell = self.alloc_register(range)?;
925 self.emit(range, Instruction::CreateCell { dst: cell })?;
926 self.declare(name.to_owned(), Binding::Cell(cell), declaration_scope);
927 self.predeclared_cells.insert(identity, cell);
928 Ok(())
929 }
930
931 fn predeclare_captured_pattern(
933 &mut self,
934 pattern: &Pattern,
935 declaration_scope: DeclarationScope,
936 ) -> Result<(), LowerError> {
937 match pattern.data() {
938 BindingPattern::Identifier(identifier) => {
939 let name = self.identifier_text(identifier)?;
940 self.predeclare_captured_binding(
941 &name,
942 identifier.range(),
943 binding_site(identifier.range()),
944 declaration_scope,
945 )
946 }
947 BindingPattern::Object(object) => {
948 for property in &object.properties {
949 self.predeclare_captured_pattern(&property.binding, declaration_scope)?;
950 }
951 Ok(())
952 }
953 BindingPattern::Array(array) => {
954 for element in &array.elements {
955 if let ArrayBindingElement::Binding(pattern) = element {
956 self.predeclare_captured_pattern(pattern, declaration_scope)?;
957 }
958 }
959 Ok(())
960 }
961 BindingPattern::Assignment(assignment) => {
962 self.predeclare_captured_pattern(&assignment.left, declaration_scope)
963 }
964 BindingPattern::Rest(rest) => {
965 self.predeclare_captured_pattern(&rest.argument, declaration_scope)
966 }
967 BindingPattern::Missing(_) => Ok(()),
968 }
969 }
970
971 fn predeclare_class_expression_binding(
974 &mut self,
975 name: &str,
976 range: TextRange,
977 site: BindingSite,
978 ) -> Result<Register, LowerError> {
979 let identity = binding_identity(name, site, DeclarationScope::Lexical);
980 if let Some(cell) = self.predeclared_cells.get(&identity).copied() {
981 return Ok(cell);
982 }
983 let cell = self.alloc_register(range)?;
984 self.emit(range, Instruction::CreateCell { dst: cell })?;
985 self.declare(
986 name.to_owned(),
987 Binding::Cell(cell),
988 DeclarationScope::Lexical,
989 );
990 self.predeclared_cells.insert(identity, cell);
991 Ok(cell)
992 }
993
994 fn cell_value(
995 &mut self,
996 builder: &mut ModuleBuilder,
997 cell: Register,
998 range: TextRange,
999 ) -> Result<Register, LowerError> {
1000 let key = self.load_constant(builder, Constant::Int32(0), range)?;
1001 let dst = self.alloc_register(range)?;
1002 self.emit(
1003 range,
1004 Instruction::GetProperty {
1005 dst,
1006 object: cell,
1007 key,
1008 },
1009 )?;
1010 Ok(dst)
1011 }
1012
1013 fn store_cell(
1014 &mut self,
1015 builder: &mut ModuleBuilder,
1016 cell: Register,
1017 value: Register,
1018 range: TextRange,
1019 ) -> Result<(), LowerError> {
1020 let key = self.load_constant(builder, Constant::Int32(0), range)?;
1021 self.emit(
1022 range,
1023 Instruction::SetProperty {
1024 object: cell,
1025 key,
1026 value,
1027 },
1028 )?;
1029 Ok(())
1030 }
1031
1032 fn rebind_iteration_cell(
1033 &mut self,
1034 builder: &mut ModuleBuilder,
1035 name: &str,
1036 range: TextRange,
1037 ) -> Result<(), LowerError> {
1038 let Some(Binding::Cell(cell)) = self.resolve(name) else {
1039 return Ok(());
1040 };
1041 let value = self.cell_value(builder, cell, range)?;
1042 self.emit(range, Instruction::CreateArray { dst: cell })?;
1043 self.emit(range, Instruction::ArrayPush { array: cell, value })?;
1044 Ok(())
1045 }
1046
1047 fn declaration_names(&self, declaration: &VariableDeclaration) -> Vec<String> {
1048 let mut names = Vec::new();
1049 for declarator in &declaration.declarations {
1050 collect_pattern_names(self.file, &declarator.data().binding, &mut names);
1051 }
1052 names
1053 }
1054
1055 fn rebind_iteration_cells(
1056 &mut self,
1057 builder: &mut ModuleBuilder,
1058 names: &[String],
1059 range: TextRange,
1060 ) -> Result<(), LowerError> {
1061 for name in names {
1062 self.rebind_iteration_cell(builder, name, range)?;
1063 }
1064 Ok(())
1065 }
1066
1067 fn store_binding(
1070 &mut self,
1071 builder: &mut ModuleBuilder,
1072 name: &str,
1073 value: Register,
1074 range: TextRange,
1075 site: BindingSite,
1076 declaration_scope: DeclarationScope,
1077 ) -> Result<(), LowerError> {
1078 let identity = binding_identity(name, site, declaration_scope);
1079 if let Some(cell) = self.predeclared_cells.get(&identity).copied() {
1080 return self.store_cell(builder, cell, value, range);
1081 }
1082 if matches!(declaration_scope, DeclarationScope::Function)
1083 && let Some(binding) = self
1084 .scopes
1085 .first()
1086 .and_then(|scope| scope.get(name).copied())
1087 {
1088 return match binding {
1089 Binding::Local(home) => self.move_to(range, home, value),
1090 Binding::Cell(cell) => self.store_cell(builder, cell, value, range),
1091 };
1092 }
1093 self.declare_initialized(builder, name, value, range, site, declaration_scope)
1094 }
1095
1096 fn hoist_vars(
1098 &mut self,
1099 builder: &mut ModuleBuilder,
1100 statements: &[Stmt],
1101 range: TextRange,
1102 ) -> Result<(), LowerError> {
1103 if self.top_level {
1104 return Ok(());
1105 }
1106 let mut names = Vec::new();
1107 collect_var_names(self.file, statements, &mut names);
1108 let mut seen = HashSet::new();
1109 for name in names {
1110 if !seen.insert(name.clone()) {
1111 continue;
1112 }
1113 if self
1114 .scopes
1115 .first()
1116 .is_some_and(|scope| scope.contains_key(&name))
1117 {
1118 continue;
1119 }
1120 let id = builder.intern(Constant::Undefined, range)?;
1121 if self
1122 .capture_plan
1123 .captures(&name, binding_site(range), DeclarationScope::Function)
1124 {
1125 let value = self.alloc_register(range)?;
1126 self.emit(
1127 range,
1128 Instruction::LoadConst {
1129 dst: value,
1130 constant: id,
1131 },
1132 )?;
1133 let cell = self.alloc_register(range)?;
1134 self.emit(range, Instruction::CreateArray { dst: cell })?;
1135 self.emit(range, Instruction::ArrayPush { array: cell, value })?;
1136 self.declare(name, Binding::Cell(cell), DeclarationScope::Function);
1137 } else {
1138 let home = self.alloc_register(range)?;
1139 self.emit(
1140 range,
1141 Instruction::LoadConst {
1142 dst: home,
1143 constant: id,
1144 },
1145 )?;
1146 self.declare(name, Binding::Local(home), DeclarationScope::Function);
1147 }
1148 }
1149 Ok(())
1150 }
1151
1152 fn read_name(
1157 &mut self,
1158 builder: &mut ModuleBuilder,
1159 name: &str,
1160 range: TextRange,
1161 ) -> Result<Register, LowerError> {
1162 if let Some(binding) = self.resolve(name) {
1163 return match binding {
1164 Binding::Local(register) => Ok(register),
1165 Binding::Cell(cell) => self.cell_value(builder, cell, range),
1166 };
1167 }
1168 if name == "arguments"
1169 && let Some(register) = self.arguments_value(builder, range)?
1170 {
1171 return Ok(register);
1172 }
1173 if name == "undefined" {
1174 return self.undefined(builder, range);
1175 }
1176 let id = builder.intern(Constant::String(EcmaString::from_utf8(name)), range)?;
1178 let dst = self.alloc_register(range)?;
1179 self.emit(range, Instruction::LoadGlobal { dst, name: id })?;
1180 Ok(dst)
1181 }
1182
1183 fn assign_name(
1186 &mut self,
1187 builder: &mut ModuleBuilder,
1188 name: &str,
1189 value: Register,
1190 range: TextRange,
1191 ) -> Result<(), LowerError> {
1192 if let Some(binding) = self.resolve(name) {
1193 return match binding {
1194 Binding::Local(home) => self.move_to(range, home, value),
1195 Binding::Cell(cell) => {
1196 let _ = self.cell_value(builder, cell, range)?;
1197 self.store_cell(builder, cell, value, range)
1198 }
1199 };
1200 }
1201 let id = builder.intern(Constant::String(EcmaString::from_utf8(name)), range)?;
1202 self.emit(range, Instruction::StoreGlobal { name: id, value })?;
1203 Ok(())
1204 }
1205
1206 fn read_name_value(
1209 &mut self,
1210 builder: &mut ModuleBuilder,
1211 name: &str,
1212 range: TextRange,
1213 ) -> Result<Register, LowerError> {
1214 self.read_name(builder, name, range)
1215 }
1216
1217 fn this_value(&mut self, range: TextRange) -> Result<Register, LowerError> {
1222 if let Some(register) = self.this_capture {
1223 return Ok(register);
1224 }
1225 let dst = self.alloc_register(range)?;
1226 self.emit(range, Instruction::LoadThis { dst })?;
1227 Ok(dst)
1228 }
1229
1230 fn new_target_value(&mut self, range: TextRange) -> Result<Register, LowerError> {
1231 if let Some(register) = self.new_target_capture {
1232 return Ok(register);
1233 }
1234 let dst = self.alloc_register(range)?;
1235 self.emit(range, Instruction::LoadNewTarget { dst })?;
1236 Ok(dst)
1237 }
1238
1239 fn arguments_value(
1242 &mut self,
1243 _builder: &mut ModuleBuilder,
1244 range: TextRange,
1245 ) -> Result<Option<Register>, LowerError> {
1246 match self.arguments_source {
1247 ArgumentsSource::Own => {
1248 let dst = self.alloc_register(range)?;
1249 self.emit(range, Instruction::LoadArguments { dst })?;
1250 Ok(Some(dst))
1251 }
1252 ArgumentsSource::Captured(register) => Ok(Some(register)),
1253 ArgumentsSource::None => Ok(None),
1254 }
1255 }
1256
1257 fn lower_top_level(
1262 &mut self,
1263 builder: &mut ModuleBuilder,
1264 statements: &[Stmt],
1265 ) -> Result<(), LowerError> {
1266 self.instantiate_declarations(builder, statements, false)?;
1267 for statement in statements {
1268 self.lower_statement(builder, statement)?;
1269 }
1270 Ok(())
1271 }
1272
1273 fn instantiate_declarations(
1274 &mut self,
1275 builder: &mut ModuleBuilder,
1276 statements: &[Stmt],
1277 switch_scope: bool,
1278 ) -> Result<(), LowerError> {
1279 let declarations = collect_immediate_declarations(self.file, statements);
1280 for declaration in &declarations {
1281 if matches!(&declaration.kind, ImmediateDeclarationKind::Lexical)
1282 && (switch_scope
1283 || self.capture_plan.requires_cell(
1284 &declaration.name,
1285 declaration.site,
1286 DeclarationScope::Lexical,
1287 ))
1288 {
1289 self.predeclare_captured_binding(
1290 &declaration.name,
1291 declaration.range,
1292 declaration.site,
1293 DeclarationScope::Lexical,
1294 )?;
1295 }
1296 }
1297 for declaration in declarations {
1298 if let ImmediateDeclarationKind::Function(function) = declaration.kind {
1299 self.instantiate_function_declaration(
1300 builder,
1301 declaration.range,
1302 &declaration.name,
1303 declaration.site,
1304 function,
1305 )?;
1306 }
1307 }
1308 Ok(())
1309 }
1310
1311 fn lower_statement(
1316 &mut self,
1317 builder: &mut ModuleBuilder,
1318 statement: &Stmt,
1319 ) -> Result<(), LowerError> {
1320 let range = statement.range();
1321 match statement.data() {
1322 Statement::Interface(_) | Statement::TypeAlias(_) | Statement::Declare(_) => Ok(()),
1323 Statement::Import(import) => self.lower_import(builder, range, import),
1324 Statement::ImportEquals(import) => {
1325 if self.goal == LoweringGoal::ClassicScript {
1326 Err(self.unsupported(range, UnsupportedConstruct::ImportDeclarationInScript))
1327 } else if import.is_type_only {
1328 Ok(())
1329 } else {
1330 Err(self.unsupported(range, UnsupportedConstruct::RuntimeImportEquals))
1331 }
1332 }
1333 Statement::Export(export) => self.lower_export(builder, range, export),
1334 Statement::Variable(declaration) => {
1335 self.lower_variable_declaration(builder, declaration)
1336 }
1337 Statement::Function(_) => Ok(()),
1338 Statement::Class(class) => self.lower_class_declaration(builder, range, class, None),
1339 Statement::Enum(_) => {
1340 Err(self.unsupported(range, UnsupportedConstruct::EnumDeclaration))
1341 }
1342 Statement::Namespace(_) => {
1343 Err(self.unsupported(range, UnsupportedConstruct::NamespaceDeclaration))
1344 }
1345 Statement::Block(block) => {
1346 self.push_scope();
1347 let result = self.lower_block(builder, block.data());
1348 self.pop_scope();
1349 result
1350 }
1351 Statement::Empty => Ok(()),
1352 Statement::Expression(expression) => {
1353 let value = self.lower_expression(builder, &expression.expression)?;
1354 if let Some(completion) = self.completion {
1355 self.move_to(range, completion, value)?;
1356 }
1357 Ok(())
1358 }
1359 Statement::If(if_statement) => {
1360 self.lower_normalizing_statement(builder, range, |this, builder| {
1361 this.lower_if(builder, if_statement)
1362 })
1363 }
1364 Statement::Switch(switch) => {
1365 self.lower_normalizing_statement(builder, range, |this, builder| {
1366 this.lower_switch(builder, range, switch)
1367 })
1368 }
1369 Statement::For(for_statement) => {
1370 self.lower_normalizing_statement(builder, range, |this, builder| {
1371 this.lower_for(builder, for_statement)
1372 })
1373 }
1374 Statement::ForIn(for_in) => {
1375 self.lower_normalizing_statement(builder, range, |this, builder| {
1376 this.lower_for_in(builder, range, for_in)
1377 })
1378 }
1379 Statement::ForOf(for_of) => {
1380 self.lower_normalizing_statement(builder, range, |this, builder| {
1381 this.lower_for_of(builder, range, for_of)
1382 })
1383 }
1384 Statement::While(while_statement) => {
1385 self.lower_normalizing_statement(builder, range, |this, builder| {
1386 this.lower_while(builder, while_statement)
1387 })
1388 }
1389 Statement::DoWhile(do_while) => {
1390 self.lower_normalizing_statement(builder, range, |this, builder| {
1391 this.lower_do_while(builder, do_while)
1392 })
1393 }
1394 Statement::Try(try_statement) => {
1395 self.lower_normalizing_statement(builder, range, |this, builder| {
1396 this.lower_try(builder, range, try_statement)
1397 })
1398 }
1399 Statement::With(_) => Err(self.unsupported(range, UnsupportedConstruct::WithStatement)),
1400 Statement::Labeled(_) => {
1401 Err(self.unsupported(range, UnsupportedConstruct::LabeledStatement))
1402 }
1403 Statement::Break(jump) => self.lower_break(builder, range, jump.label.is_some()),
1404 Statement::Continue(jump) => self.lower_continue(builder, range, jump.label.is_some()),
1405 Statement::Return(return_statement) => {
1406 if self.top_level {
1407 return Err(
1408 self.unsupported(range, UnsupportedConstruct::ReturnOutsideFunction)
1409 );
1410 }
1411 let value = match &return_statement.argument {
1412 Some(expression) => self.lower_expression(builder, expression)?,
1413 None => self.undefined(builder, range)?,
1414 };
1415 if self.route_through_finally(builder, range, COMPLETION_RETURN, Some(value))? {
1416 return Ok(());
1417 }
1418 self.emit(range, Instruction::Return { value })?;
1419 Ok(())
1420 }
1421 Statement::Throw(throw) => {
1422 let value = self.lower_expression(builder, &throw.argument)?;
1423 self.emit(range, Instruction::Throw { value })?;
1424 Ok(())
1425 }
1426 Statement::Debugger => {
1427 Err(self.unsupported(range, UnsupportedConstruct::DebuggerStatement))
1428 }
1429 Statement::Missing(missing) => Err(self.missing(range, missing.expected())),
1430 }
1431 }
1432
1433 fn lower_block(
1434 &mut self,
1435 builder: &mut ModuleBuilder,
1436 block: &Block,
1437 ) -> Result<(), LowerError> {
1438 self.instantiate_declarations(builder, &block.statements, false)?;
1439 for statement in &block.statements {
1440 self.lower_statement(builder, statement)?;
1441 }
1442 Ok(())
1443 }
1444
1445 fn lower_nested(&mut self, builder: &mut ModuleBuilder, body: &Stmt) -> Result<(), LowerError> {
1446 self.push_scope();
1447 let result = self.lower_statement(builder, body);
1448 self.pop_scope();
1449 result
1450 }
1451
1452 fn lower_if(
1453 &mut self,
1454 builder: &mut ModuleBuilder,
1455 if_statement: &IfStatement,
1456 ) -> Result<(), LowerError> {
1457 let range = if_statement.test.range();
1458 let condition = self.lower_expression(builder, &if_statement.test)?;
1459 let to_else = self.emit(
1460 range,
1461 Instruction::JumpIfFalse {
1462 condition,
1463 target: Pc::new(0),
1464 },
1465 )?;
1466 self.lower_nested(builder, &if_statement.consequent)?;
1467 match &if_statement.alternate {
1468 Some(alternate) => {
1469 let to_end = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1470 let else_pc = self.next_pc();
1471 self.patch_jump(to_else, else_pc);
1472 self.lower_nested(builder, alternate)?;
1473 let end_pc = self.next_pc();
1474 self.patch_jump(to_end, end_pc);
1475 }
1476 None => {
1477 let end_pc = self.next_pc();
1478 self.patch_jump(to_else, end_pc);
1479 }
1480 }
1481 Ok(())
1482 }
1483
1484 fn lower_while(
1485 &mut self,
1486 builder: &mut ModuleBuilder,
1487 while_statement: &WhileStatement,
1488 ) -> Result<(), LowerError> {
1489 let range = while_statement.test.range();
1490 let head = self.next_pc();
1491 let condition = self.lower_expression(builder, &while_statement.test)?;
1492 let exit_jump = self.emit(
1493 range,
1494 Instruction::JumpIfFalse {
1495 condition,
1496 target: Pc::new(0),
1497 },
1498 )?;
1499 self.loops.push(LoopFrame {
1500 breaks: Vec::new(),
1501 continues: Vec::new(),
1502 is_loop: true,
1503 });
1504 self.lower_nested(builder, &while_statement.body)?;
1505 self.emit(range, Instruction::Jump { target: head })?;
1506 let exit = self.next_pc();
1507 self.patch_jump(exit_jump, exit);
1508 let frame = self.loops.pop().expect("loop frame is balanced");
1509 for jump in frame.breaks {
1510 self.patch_jump(jump, exit);
1511 }
1512 for jump in frame.continues {
1513 self.patch_jump(jump, head);
1514 }
1515 Ok(())
1516 }
1517
1518 fn lower_do_while(
1519 &mut self,
1520 builder: &mut ModuleBuilder,
1521 do_while: &DoWhileStatement,
1522 ) -> Result<(), LowerError> {
1523 let range = do_while.test.range();
1524 let head = self.next_pc();
1525 self.loops.push(LoopFrame {
1526 breaks: Vec::new(),
1527 continues: Vec::new(),
1528 is_loop: true,
1529 });
1530 self.lower_nested(builder, &do_while.body)?;
1531 let test_pc = self.next_pc();
1532 let condition = self.lower_expression(builder, &do_while.test)?;
1533 self.emit(
1534 range,
1535 Instruction::JumpIfTrue {
1536 condition,
1537 target: head,
1538 },
1539 )?;
1540 let exit = self.next_pc();
1541 let frame = self.loops.pop().expect("loop frame is balanced");
1542 for jump in frame.breaks {
1543 self.patch_jump(jump, exit);
1544 }
1545 for jump in frame.continues {
1546 self.patch_jump(jump, test_pc);
1547 }
1548 Ok(())
1549 }
1550
1551 fn lower_for(
1552 &mut self,
1553 builder: &mut ModuleBuilder,
1554 for_statement: &ForStatement,
1555 ) -> Result<(), LowerError> {
1556 self.push_scope();
1557 let result = self.lower_for_inner(builder, for_statement);
1558 self.pop_scope();
1559 result
1560 }
1561
1562 fn lower_for_inner(
1563 &mut self,
1564 builder: &mut ModuleBuilder,
1565 for_statement: &ForStatement,
1566 ) -> Result<(), LowerError> {
1567 let per_iteration_names = match &for_statement.initializer {
1568 Some(ForInitializer::Variable(declaration))
1569 if matches!(declaration.kind, VariableKind::Let | VariableKind::Const) =>
1570 {
1571 self.declaration_names(declaration)
1572 }
1573 _ => Vec::new(),
1574 };
1575 if let Some(initializer) = &for_statement.initializer {
1576 match initializer {
1577 ForInitializer::Variable(declaration) => {
1578 if matches!(declaration.kind, VariableKind::Let | VariableKind::Const) {
1579 self.lower_iteration_variable_declaration(builder, declaration)?;
1580 } else {
1581 self.lower_variable_declaration(builder, declaration)?;
1582 }
1583 }
1584 ForInitializer::Expression(expression) => {
1585 self.lower_expression(builder, expression)?;
1586 }
1587 }
1588 }
1589 let head = self.next_pc();
1590 let exit_jump = match &for_statement.test {
1591 Some(test) => {
1592 let condition = self.lower_expression(builder, test)?;
1593 Some(self.emit(
1594 test.range(),
1595 Instruction::JumpIfFalse {
1596 condition,
1597 target: Pc::new(0),
1598 },
1599 )?)
1600 }
1601 None => None,
1602 };
1603 self.loops.push(LoopFrame {
1604 breaks: Vec::new(),
1605 continues: Vec::new(),
1606 is_loop: true,
1607 });
1608 self.lower_nested(builder, &for_statement.body)?;
1609 let update_pc = self.next_pc();
1610 self.rebind_iteration_cells(builder, &per_iteration_names, head_range(for_statement))?;
1611 if let Some(update) = &for_statement.update {
1612 self.lower_expression(builder, update)?;
1613 }
1614 self.emit(
1615 head_range(for_statement),
1616 Instruction::Jump { target: head },
1617 )?;
1618 let exit = self.next_pc();
1619 if let Some(jump) = exit_jump {
1620 self.patch_jump(jump, exit);
1621 }
1622 let frame = self.loops.pop().expect("loop frame is balanced");
1623 for jump in frame.breaks {
1624 self.patch_jump(jump, exit);
1625 }
1626 for jump in frame.continues {
1627 self.patch_jump(jump, update_pc);
1628 }
1629 Ok(())
1630 }
1631
1632 fn lower_for_in(
1634 &mut self,
1635 builder: &mut ModuleBuilder,
1636 range: TextRange,
1637 for_in: &ForInStatement,
1638 ) -> Result<(), LowerError> {
1639 let subject = self.lower_expression(builder, &for_in.object)?;
1640 self.lower_iteration(
1641 builder,
1642 range,
1643 subject,
1644 IteratorKind::Keys,
1645 &for_in.binding,
1646 &for_in.body,
1647 )
1648 }
1649
1650 fn lower_for_of(
1652 &mut self,
1653 builder: &mut ModuleBuilder,
1654 range: TextRange,
1655 for_of: &ForOfStatement,
1656 ) -> Result<(), LowerError> {
1657 let subject = self.lower_expression(builder, &for_of.iterable)?;
1658 let kind = match for_of.mode {
1659 ForOfMode::Sync => IteratorKind::Sync,
1660 ForOfMode::Async => IteratorKind::Async,
1661 };
1662 self.lower_iteration(builder, range, subject, kind, &for_of.binding, &for_of.body)
1663 }
1664
1665 fn lower_iteration(
1668 &mut self,
1669 builder: &mut ModuleBuilder,
1670 range: TextRange,
1671 subject: Register,
1672 kind: IteratorKind,
1673 binding: &ForBinding,
1674 body: &Stmt,
1675 ) -> Result<(), LowerError> {
1676 self.push_scope();
1677 match binding {
1678 ForBinding::Variable(declaration)
1679 if matches!(
1680 declaration.kind,
1681 VariableKind::Using | VariableKind::AwaitUsing
1682 ) =>
1683 {
1684 self.pop_scope();
1685 return Err(self.unsupported(range, UnsupportedConstruct::UsingDeclaration));
1686 }
1687 _ => {}
1688 }
1689 let iterator = self.alloc_register(range)?;
1690 self.emit(
1691 range,
1692 Instruction::GetIterator {
1693 dst: iterator,
1694 src: subject,
1695 kind,
1696 },
1697 )?;
1698 let done = self.alloc_register(range)?;
1699 let value = self.alloc_register(range)?;
1700 let head = self.next_pc();
1701 self.emit(
1702 range,
1703 Instruction::IteratorNext {
1704 done,
1705 value,
1706 iterator,
1707 },
1708 )?;
1709 let exit_jump = self.emit(
1710 range,
1711 Instruction::JumpIfTrue {
1712 condition: done,
1713 target: Pc::new(0),
1714 },
1715 )?;
1716 self.loops.push(LoopFrame {
1717 breaks: Vec::new(),
1718 continues: Vec::new(),
1719 is_loop: true,
1720 });
1721 self.push_scope();
1723 self.bind_for_binding(builder, binding, value, range)?;
1724 let body_result = self.lower_statement(builder, body);
1725 self.pop_scope();
1726 body_result?;
1727 self.emit(range, Instruction::Jump { target: head })?;
1728 let exit = self.next_pc();
1729 self.patch_jump(exit_jump, exit);
1730 let frame = self.loops.pop().expect("loop frame is balanced");
1731 for jump in frame.breaks {
1732 self.patch_jump(jump, exit);
1733 }
1734 for jump in frame.continues {
1735 self.patch_jump(jump, head);
1736 }
1737 self.pop_scope();
1738 Ok(())
1739 }
1740
1741 fn bind_for_binding(
1742 &mut self,
1743 builder: &mut ModuleBuilder,
1744 binding: &ForBinding,
1745 value: Register,
1746 range: TextRange,
1747 ) -> Result<(), LowerError> {
1748 match binding {
1749 ForBinding::Variable(declaration) => {
1750 let declaration_scope = match declaration.kind {
1751 VariableKind::Var => DeclarationScope::Function,
1752 VariableKind::Let | VariableKind::Const => DeclarationScope::Iteration,
1753 VariableKind::Using | VariableKind::AwaitUsing => {
1754 return Err(self.unsupported(range, UnsupportedConstruct::UsingDeclaration));
1755 }
1756 };
1757 let declarator = declaration
1758 .declarations
1759 .first()
1760 .ok_or_else(|| self.missing(range, NodeKind::VariableDeclarator))?;
1761 self.bind_pattern(
1762 builder,
1763 &declarator.data().binding,
1764 value,
1765 declaration_scope,
1766 )
1767 }
1768 ForBinding::Target(target) => self.assign_target(builder, target, value),
1769 }
1770 }
1771
1772 fn lower_break(
1773 &mut self,
1774 builder: &mut ModuleBuilder,
1775 range: TextRange,
1776 labeled: bool,
1777 ) -> Result<(), LowerError> {
1778 if labeled {
1779 return Err(self.unsupported(range, UnsupportedConstruct::LabeledJump));
1780 }
1781 if self.route_through_finally(builder, range, COMPLETION_BREAK, None)? {
1782 return Ok(());
1783 }
1784 let jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1785 let frame = self.loops.last_mut().ok_or_else(|| LowerError {
1786 source: self.file.source_id(),
1787 range,
1788 kind: LowerErrorKind::MissingSyntax {
1789 expected: NodeKind::BreakStatement,
1790 },
1791 })?;
1792 frame.breaks.push(jump);
1793 Ok(())
1794 }
1795
1796 fn lower_continue(
1797 &mut self,
1798 builder: &mut ModuleBuilder,
1799 range: TextRange,
1800 labeled: bool,
1801 ) -> Result<(), LowerError> {
1802 if labeled {
1803 return Err(self.unsupported(range, UnsupportedConstruct::LabeledJump));
1804 }
1805 if self.route_through_finally(builder, range, COMPLETION_CONTINUE, None)? {
1806 return Ok(());
1807 }
1808 let jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1809 let index = self.nearest_loop_index().ok_or_else(|| LowerError {
1810 source: self.file.source_id(),
1811 range,
1812 kind: LowerErrorKind::MissingSyntax {
1813 expected: NodeKind::ContinueStatement,
1814 },
1815 })?;
1816 self.loops[index].continues.push(jump);
1817 Ok(())
1818 }
1819
1820 fn route_through_finally(
1824 &mut self,
1825 builder: &mut ModuleBuilder,
1826 range: TextRange,
1827 kind: i32,
1828 value: Option<Register>,
1829 ) -> Result<bool, LowerError> {
1830 let Some((kind_reg, value_reg, depth)) = self
1831 .finally_stack
1832 .last()
1833 .map(|frame| (frame.kind_reg, frame.value_reg, frame.loop_depth))
1834 else {
1835 return Ok(false);
1836 };
1837 let target = match kind {
1840 COMPLETION_BREAK => {
1841 if self.loops.is_empty() {
1842 return Ok(false);
1843 }
1844 Some(self.loops.len() - 1)
1845 }
1846 COMPLETION_CONTINUE => match self.nearest_loop_index() {
1847 Some(index) => Some(index),
1848 None => return Ok(false),
1849 },
1850 _ => None,
1851 };
1852 if let Some(target) = target
1853 && target >= depth
1854 {
1855 return Ok(false);
1856 }
1857 let marker = self.load_constant(builder, Constant::Int32(kind), range)?;
1858 self.move_to(range, kind_reg, marker)?;
1859 if let Some(value) = value {
1860 self.move_to(range, value_reg, value)?;
1861 }
1862 let jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1863 self.finally_stack
1864 .last_mut()
1865 .expect("finally frame present")
1866 .pending
1867 .push(jump);
1868 Ok(true)
1869 }
1870
1871 fn nearest_loop_index(&self) -> Option<usize> {
1874 self.loops.iter().rposition(|frame| frame.is_loop)
1875 }
1876
1877 fn lower_switch(
1878 &mut self,
1879 builder: &mut ModuleBuilder,
1880 range: TextRange,
1881 switch: &SwitchStatement,
1882 ) -> Result<(), LowerError> {
1883 let discriminant = self.lower_expression(builder, &switch.discriminant)?;
1884 self.push_scope();
1885 let switch_statements = switch
1886 .cases
1887 .iter()
1888 .flat_map(|case| case.data().consequent.iter().cloned())
1889 .collect::<Vec<_>>();
1890 self.instantiate_declarations(builder, &switch_statements, true)?;
1891 self.loops.push(LoopFrame {
1892 breaks: Vec::new(),
1893 continues: Vec::new(),
1894 is_loop: false,
1895 });
1896 let mut case_jumps: Vec<Option<Pc>> = Vec::with_capacity(switch.cases.len());
1897 let mut default_index = None;
1898 for (index, case) in switch.cases.iter().enumerate() {
1899 match &case.data().test {
1900 Some(test) => {
1901 let value = self.lower_expression(builder, test)?;
1902 let matched = self.alloc_register(range)?;
1903 self.emit(
1904 range,
1905 Instruction::Binary {
1906 dst: matched,
1907 op: BinaryOp::StrictEqual,
1908 left: discriminant,
1909 right: value,
1910 },
1911 )?;
1912 let jump = self.emit(
1913 range,
1914 Instruction::JumpIfTrue {
1915 condition: matched,
1916 target: Pc::new(0),
1917 },
1918 )?;
1919 case_jumps.push(Some(jump));
1920 }
1921 None => {
1922 default_index = Some(index);
1923 case_jumps.push(None);
1924 }
1925 }
1926 }
1927 let no_match_jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1928 let mut body_starts: Vec<Pc> = Vec::with_capacity(switch.cases.len());
1929 for case in &switch.cases {
1930 let start = self.next_pc();
1931 body_starts.push(start);
1932 for statement in &case.data().consequent {
1933 self.lower_statement(builder, statement)?;
1934 }
1935 }
1936 let exit = self.next_pc();
1937 for (jump, start) in case_jumps.iter().zip(body_starts.iter()) {
1938 if let Some(jump) = jump {
1939 self.patch_jump(*jump, *start);
1940 }
1941 }
1942 match default_index {
1943 Some(index) => self.patch_jump(no_match_jump, body_starts[index]),
1944 None => self.patch_jump(no_match_jump, exit),
1945 }
1946 let frame = self.loops.pop().expect("switch break frame is balanced");
1947
1948 for jump in frame.breaks {
1949 self.patch_jump(jump, exit);
1950 }
1951 self.pop_scope();
1952 Ok(())
1953 }
1954
1955 fn lower_try(
1956 &mut self,
1957 builder: &mut ModuleBuilder,
1958 range: TextRange,
1959 try_statement: &crate::syntax::TryStatement,
1960 ) -> Result<(), LowerError> {
1961 let has_finally = try_statement
1962 .finalizer
1963 .as_ref()
1964 .is_some_and(|finalizer| !finalizer.data().statements.is_empty());
1965 if has_finally {
1966 return self.lower_try_finally(builder, range, try_statement);
1967 }
1968 let Some(handler_clause) = &try_statement.handler else {
1969 self.push_scope();
1970 let result = self.lower_block(builder, try_statement.block.data());
1971 self.pop_scope();
1972 return result;
1973 };
1974
1975 let start = self.next_pc();
1976 self.push_scope();
1977 let block_result = self.lower_block(builder, try_statement.block.data());
1978 self.pop_scope();
1979 block_result?;
1980 let end = self.next_pc();
1981 if end.get() == start.get() {
1982 return Ok(());
1983 }
1984 let over_catch = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
1985
1986 let catch_register = self.alloc_register(range)?;
1987 let handler_pc = self.next_pc();
1988 if let Some(completion) = self.completion {
1989 let undefined = self.undefined(builder, range)?;
1990 self.move_to(range, completion, undefined)?;
1991 }
1992 self.push_scope();
1993 let clause = handler_clause.data();
1994 if let Some(binding) = &clause.binding {
1995 let bind_result =
1996 self.bind_pattern(builder, binding, catch_register, DeclarationScope::Lexical);
1997 if let Err(error) = bind_result {
1998 self.pop_scope();
1999 return Err(error);
2000 }
2001 }
2002 let catch_result = self.lower_block(builder, clause.body.data());
2003 self.pop_scope();
2004 catch_result?;
2005 let after = self.next_pc();
2006 self.patch_jump(over_catch, after);
2007
2008 self.handlers.push(ExceptionHandler {
2009 start,
2010 end,
2011 handler: handler_pc,
2012 catch_register,
2013 });
2014 Ok(())
2015 }
2016
2017 fn lower_try_finally(
2021 &mut self,
2022 builder: &mut ModuleBuilder,
2023 range: TextRange,
2024 try_statement: &crate::syntax::TryStatement,
2025 ) -> Result<(), LowerError> {
2026 let finalizer = try_statement
2027 .finalizer
2028 .as_ref()
2029 .expect("lower_try_finally is only called with a non-empty finalizer");
2030 let kind_reg = self.alloc_register(range)?;
2031 let value_reg = self.alloc_register(range)?;
2032 let normal = self.load_constant(builder, Constant::Int32(COMPLETION_NORMAL), range)?;
2033 self.move_to(range, kind_reg, normal)?;
2034 let undefined = self.undefined(builder, range)?;
2035 self.move_to(range, value_reg, undefined)?;
2036 self.finally_stack.push(FinallyFrame {
2037 kind_reg,
2038 value_reg,
2039 pending: Vec::new(),
2040 loop_depth: self.loops.len(),
2041 });
2042
2043 let start = self.next_pc();
2044 self.push_scope();
2045 let body_result = self.lower_block(builder, try_statement.block.data());
2046 self.pop_scope();
2047 if let Err(error) = body_result {
2048 self.finally_stack.pop();
2049 return Err(error);
2050 }
2051 let end = self.next_pc();
2052 self.push_finally_completion(builder, range, COMPLETION_NORMAL, None)?;
2054
2055 let handler = if end.get() == start.get() {
2056 None
2057 } else {
2058 let catch_register = self.alloc_register(range)?;
2059 let handler_pc = self.next_pc();
2060 if let Some(handler_clause) = &try_statement.handler {
2061 if let Some(completion) = self.completion {
2062 let undefined = self.undefined(builder, range)?;
2063 self.move_to(range, completion, undefined)?;
2064 }
2065 self.push_scope();
2066 let clause = handler_clause.data();
2067 if let Some(binding) = &clause.binding {
2068 let bind_result = self.bind_pattern(
2069 builder,
2070 binding,
2071 catch_register,
2072 DeclarationScope::Lexical,
2073 );
2074 if let Err(error) = bind_result {
2075 self.pop_scope();
2076 self.finally_stack.pop();
2077 return Err(error);
2078 }
2079 }
2080 let catch_result = self.lower_block(builder, clause.body.data());
2081 self.pop_scope();
2082 if let Err(error) = catch_result {
2083 self.finally_stack.pop();
2084 return Err(error);
2085 }
2086 self.push_finally_completion(builder, range, COMPLETION_NORMAL, None)?;
2087 } else {
2088 self.push_finally_completion(
2090 builder,
2091 range,
2092 COMPLETION_THROW,
2093 Some(catch_register),
2094 )?;
2095 }
2096 Some((catch_register, handler_pc))
2097 };
2098
2099 let frame = self.finally_stack.pop().expect("finally frame present");
2100 let finally_pc = self.next_pc();
2101 for jump in &frame.pending {
2102 self.patch_jump(*jump, finally_pc);
2103 }
2104 if let Some((catch_register, handler_pc)) = handler {
2105 self.handlers.push(ExceptionHandler {
2106 start,
2107 end,
2108 handler: handler_pc,
2109 catch_register,
2110 });
2111 }
2112 self.push_scope();
2113 let finally_result = self.lower_without_completion(builder, |this, builder| {
2114 this.lower_block(builder, finalizer.data())
2115 });
2116 self.pop_scope();
2117 finally_result?;
2118 self.emit_finally_dispatch(builder, range, kind_reg, value_reg)
2119 }
2120
2121 fn push_finally_completion(
2124 &mut self,
2125 builder: &mut ModuleBuilder,
2126 range: TextRange,
2127 kind: i32,
2128 value: Option<Register>,
2129 ) -> Result<(), LowerError> {
2130 let (kind_reg, value_reg) = {
2131 let frame = self.finally_stack.last().expect("finally frame present");
2132 (frame.kind_reg, frame.value_reg)
2133 };
2134 let marker = self.load_constant(builder, Constant::Int32(kind), range)?;
2135 self.move_to(range, kind_reg, marker)?;
2136 if let Some(value) = value {
2137 self.move_to(range, value_reg, value)?;
2138 }
2139 let jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
2140 self.finally_stack
2141 .last_mut()
2142 .expect("finally frame present")
2143 .pending
2144 .push(jump);
2145 Ok(())
2146 }
2147
2148 fn emit_finally_dispatch(
2150 &mut self,
2151 builder: &mut ModuleBuilder,
2152 range: TextRange,
2153 kind_reg: Register,
2154 value_reg: Register,
2155 ) -> Result<(), LowerError> {
2156 let skip = self.emit_kind_guard(builder, range, kind_reg, COMPLETION_RETURN)?;
2158 self.emit(range, Instruction::Return { value: value_reg })?;
2159 let after = self.next_pc();
2160 self.patch_jump(skip, after);
2161 let skip = self.emit_kind_guard(builder, range, kind_reg, COMPLETION_THROW)?;
2163 self.emit(range, Instruction::Throw { value: value_reg })?;
2164 let after = self.next_pc();
2165 self.patch_jump(skip, after);
2166 let skip = self.emit_kind_guard(builder, range, kind_reg, COMPLETION_BREAK)?;
2168 let break_jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
2169 match self.loops.last_mut() {
2170 Some(frame) => frame.breaks.push(break_jump),
2171 None => {
2172 let target = self.next_pc();
2173 self.patch_jump(break_jump, target);
2174 }
2175 }
2176 let after = self.next_pc();
2177 self.patch_jump(skip, after);
2178 let skip = self.emit_kind_guard(builder, range, kind_reg, COMPLETION_CONTINUE)?;
2180 let continue_jump = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
2181 match self.nearest_loop_index() {
2182 Some(index) => self.loops[index].continues.push(continue_jump),
2183 None => {
2184 let target = self.next_pc();
2185 self.patch_jump(continue_jump, target);
2186 }
2187 }
2188 let after = self.next_pc();
2189 self.patch_jump(skip, after);
2190 Ok(())
2191 }
2192
2193 fn emit_kind_guard(
2196 &mut self,
2197 builder: &mut ModuleBuilder,
2198 range: TextRange,
2199 kind_reg: Register,
2200 kind: i32,
2201 ) -> Result<Pc, LowerError> {
2202 let marker = self.load_constant(builder, Constant::Int32(kind), range)?;
2203 let matched = self.alloc_register(range)?;
2204 self.emit(
2205 range,
2206 Instruction::Binary {
2207 dst: matched,
2208 op: BinaryOp::StrictEqual,
2209 left: kind_reg,
2210 right: marker,
2211 },
2212 )?;
2213 self.emit(
2214 range,
2215 Instruction::JumpIfFalse {
2216 condition: matched,
2217 target: Pc::new(0),
2218 },
2219 )
2220 }
2221
2222 fn lower_variable_declaration(
2223 &mut self,
2224 builder: &mut ModuleBuilder,
2225 declaration: &VariableDeclaration,
2226 ) -> Result<(), LowerError> {
2227 let declaration_scope = match declaration.kind {
2228 VariableKind::Var => DeclarationScope::Function,
2229 VariableKind::Let | VariableKind::Const => DeclarationScope::Lexical,
2230 VariableKind::Using | VariableKind::AwaitUsing => {
2231 let range = declaration
2232 .declarations
2233 .first()
2234 .map_or_else(zero_range, |declarator| declarator.range());
2235 return Err(self.unsupported(range, UnsupportedConstruct::UsingDeclaration));
2236 }
2237 };
2238 for declarator in &declaration.declarations {
2239 let range = declarator.range();
2240 let data = declarator.data();
2241 self.predeclare_captured_pattern(&data.binding, declaration_scope)?;
2242 let value = match &data.initializer {
2243 Some(initializer) => self.lower_expression(builder, initializer)?,
2244 None => {
2245 self.undefined(builder, range)?
2247 }
2248 };
2249 self.bind_pattern(builder, &data.binding, value, declaration_scope)?;
2250 }
2251 Ok(())
2252 }
2253
2254 fn lower_iteration_variable_declaration(
2255 &mut self,
2256 builder: &mut ModuleBuilder,
2257 declaration: &VariableDeclaration,
2258 ) -> Result<(), LowerError> {
2259 debug_assert!(matches!(
2260 declaration.kind,
2261 VariableKind::Let | VariableKind::Const
2262 ));
2263 for declarator in &declaration.declarations {
2264 let range = declarator.range();
2265 let data = declarator.data();
2266 self.predeclare_captured_pattern(&data.binding, DeclarationScope::Iteration)?;
2267 let value = match &data.initializer {
2268 Some(initializer) => self.lower_expression(builder, initializer)?,
2269 None => self.undefined(builder, range)?,
2270 };
2271 self.bind_pattern(builder, &data.binding, value, DeclarationScope::Iteration)?;
2272 }
2273 Ok(())
2274 }
2275
2276 fn instantiate_function_declaration(
2277 &mut self,
2278 builder: &mut ModuleBuilder,
2279 range: TextRange,
2280 name: &str,
2281 site: BindingSite,
2282 function: &FunctionLike,
2283 ) -> Result<(), LowerError> {
2284 if function.body.is_none() {
2285 return Ok(());
2286 }
2287 self.predeclare_captured_binding(name, range, site, DeclarationScope::Function)?;
2288 let closure = self.build_constructible_function_value(
2289 builder,
2290 range,
2291 Some(name.to_owned()),
2292 function,
2293 )?;
2294 self.store_binding(
2295 builder,
2296 name,
2297 closure,
2298 range,
2299 site,
2300 DeclarationScope::Function,
2301 )
2302 }
2303
2304 fn lower_expression(
2309 &mut self,
2310 builder: &mut ModuleBuilder,
2311 expression: &Expr,
2312 ) -> Result<Register, LowerError> {
2313 let range = expression.range();
2314 match expression.data() {
2315 Expression::Identifier(identifier) => {
2316 let name = self.identifier_text(identifier)?;
2317 self.read_name(builder, &name, range)
2318 }
2319 Expression::This => self.this_value(range),
2320 Expression::Super => {
2321 return Err(self.unsupported(range, UnsupportedConstruct::DerivedConstructorShape));
2324 }
2325 Expression::Literal(literal) => self.lower_literal(builder, range, literal),
2326 Expression::Template(template) => self.lower_template(builder, range, template),
2327 Expression::TaggedTemplate(tagged) => {
2328 self.lower_tagged_template(builder, range, tagged)
2329 }
2330 Expression::Array(array) => self.lower_array(builder, range, array),
2331 Expression::Object(object) => self.lower_object(builder, range, object),
2332 Expression::Function(function) => {
2333 self.build_constructible_function_value(builder, range, None, &function.function)
2334 }
2335 Expression::Class(class) => {
2336 let name = class
2337 .class
2338 .name
2339 .as_ref()
2340 .map(|identifier| {
2341 self.identifier_text(identifier)
2342 .map(|name| (name, binding_site(identifier.range())))
2343 })
2344 .transpose()?;
2345 self.lower_class_value(
2346 builder,
2347 range,
2348 &class.class,
2349 None,
2350 name.as_ref().map(|(name, site)| (name.as_str(), *site)),
2351 )
2352 }
2353 Expression::Arrow(arrow) => self.lower_arrow(builder, range, arrow),
2354 Expression::Call(call) => self.lower_call(builder, range, call),
2355 Expression::Member(member) => {
2356 let (_, value) = self.lower_member(builder, range, member)?;
2357 Ok(value)
2358 }
2359 Expression::New(new) => self.lower_new(builder, range, new),
2360 Expression::Await(await_expression) => {
2361 self.lower_await(builder, range, await_expression)
2362 }
2363 Expression::Yield(yield_expression) => {
2364 self.lower_yield(builder, range, yield_expression)
2365 }
2366 Expression::Unary(unary) => self.lower_unary(builder, range, unary),
2367 Expression::Update(update) => self.lower_update(builder, range, update),
2368 Expression::Binary(binary) => self.lower_binary(builder, range, binary),
2369 Expression::Logical(logical) => self.lower_logical(builder, range, logical),
2370 Expression::Conditional(conditional) => {
2371 self.lower_conditional(builder, range, conditional)
2372 }
2373 Expression::Assignment(assignment) => self.lower_assignment(builder, range, assignment),
2374 Expression::Sequence(sequence) => {
2375 let mut last = None;
2376 for expression in &sequence.expressions {
2377 last = Some(self.lower_expression(builder, expression)?);
2378 }
2379 match last {
2380 Some(register) => Ok(register),
2381 None => self.undefined(builder, range),
2382 }
2383 }
2384 Expression::Parenthesized(inner) => self.lower_expression(builder, inner),
2385 Expression::As(as_expression) => {
2386 self.lower_expression(builder, &as_expression.expression)
2387 }
2388 Expression::Satisfies(satisfies) => {
2389 self.lower_expression(builder, &satisfies.expression)
2390 }
2391 Expression::TypeAssertion(assertion) => {
2392 self.lower_expression(builder, &assertion.expression)
2393 }
2394 Expression::NonNull(non_null) => self.lower_expression(builder, &non_null.expression),
2395 Expression::Import(import) => self.lower_import_expression(builder, range, import),
2396 Expression::Meta(meta) => match meta {
2397 MetaProperty::NewTarget => self.new_target_value(range),
2398 MetaProperty::ImportMeta => {
2399 Err(self.unsupported(range, UnsupportedConstruct::ImportMeta))
2400 }
2401 },
2402 Expression::Missing(missing) => Err(self.missing(range, missing.expected())),
2403 }
2404 }
2405
2406 fn lower_unary(
2407 &mut self,
2408 builder: &mut ModuleBuilder,
2409 range: TextRange,
2410 unary: &crate::syntax::UnaryExpression,
2411 ) -> Result<Register, LowerError> {
2412 let op = match unary.operator {
2413 UnaryOperator::Void => {
2414 self.lower_expression(builder, &unary.argument)?;
2415 return self.undefined(builder, range);
2416 }
2417 UnaryOperator::Delete => return self.lower_delete(builder, range, &unary.argument),
2418 UnaryOperator::Typeof => return self.lower_typeof(builder, range, &unary.argument),
2419 UnaryOperator::Plus => UnaryOp::Plus,
2420 UnaryOperator::Minus => UnaryOp::Negate,
2421 UnaryOperator::Not => UnaryOp::LogicalNot,
2422 UnaryOperator::BitNot => UnaryOp::BitwiseNot,
2423 };
2424 let operand = self.lower_expression(builder, &unary.argument)?;
2425 let dst = self.alloc_register(range)?;
2426 self.emit(range, Instruction::Unary { dst, op, operand })?;
2427 Ok(dst)
2428 }
2429
2430 fn lower_typeof(
2433 &mut self,
2434 builder: &mut ModuleBuilder,
2435 range: TextRange,
2436 argument: &Expr,
2437 ) -> Result<Register, LowerError> {
2438 if let Expression::Identifier(identifier) = argument.data() {
2439 let name = self.identifier_text(identifier)?;
2440 let resolved = self.resolve(&name).is_some()
2441 || (name == "arguments" && !matches!(self.arguments_source, ArgumentsSource::None))
2442 || name == "undefined";
2443 if !resolved {
2444 let id = builder.intern(Constant::String(EcmaString::from_utf8(&name)), range)?;
2445 let dst = self.alloc_register(range)?;
2446 self.emit(range, Instruction::TypeOfGlobal { dst, name: id })?;
2447 return Ok(dst);
2448 }
2449 }
2450 let operand = self.lower_expression(builder, argument)?;
2451 let dst = self.alloc_register(range)?;
2452 self.emit(
2453 range,
2454 Instruction::Unary {
2455 dst,
2456 op: UnaryOp::TypeOf,
2457 operand,
2458 },
2459 )?;
2460 Ok(dst)
2461 }
2462
2463 fn lower_delete(
2464 &mut self,
2465 builder: &mut ModuleBuilder,
2466 range: TextRange,
2467 argument: &Expr,
2468 ) -> Result<Register, LowerError> {
2469 match argument.data() {
2470 Expression::Member(member) => {
2471 if member.optional {
2472 return self.lower_optional_delete(builder, range, member);
2473 }
2474 let object = self.lower_expression(builder, &member.object)?;
2475 let key = self.member_key(builder, &member.property)?;
2476 let dst = self.alloc_register(range)?;
2477 self.emit(range, Instruction::DeleteProperty { dst, object, key })?;
2478 Ok(dst)
2479 }
2480 Expression::Parenthesized(inner) => self.lower_delete(builder, range, inner),
2481 _ => self.load_constant(builder, Constant::Boolean(false), range),
2484 }
2485 }
2486
2487 fn lower_optional_delete(
2488 &mut self,
2489 builder: &mut ModuleBuilder,
2490 range: TextRange,
2491 member: &MemberExpression,
2492 ) -> Result<Register, LowerError> {
2493 let result = self.alloc_register(range)?;
2494 let truthy = self.load_constant(builder, Constant::Boolean(true), range)?;
2495 self.move_to(range, result, truthy)?;
2496 let object = self.lower_expression(builder, &member.object)?;
2497 let is_nullish = self.compute_nullish(builder, range, object)?;
2498 let skip = self.emit(
2499 range,
2500 Instruction::JumpIfTrue {
2501 condition: is_nullish,
2502 target: Pc::new(0),
2503 },
2504 )?;
2505 let key = self.member_key(builder, &member.property)?;
2506 let deleted = self.alloc_register(range)?;
2507 self.emit(
2508 range,
2509 Instruction::DeleteProperty {
2510 dst: deleted,
2511 object,
2512 key,
2513 },
2514 )?;
2515 self.move_to(range, result, deleted)?;
2516 let end = self.next_pc();
2517 self.patch_jump(skip, end);
2518 Ok(result)
2519 }
2520
2521 fn lower_binary(
2522 &mut self,
2523 builder: &mut ModuleBuilder,
2524 range: TextRange,
2525 binary: &BinaryExpression,
2526 ) -> Result<Register, LowerError> {
2527 let op = map_binary_operator(binary.operator);
2528 let left = self.lower_expression(builder, &binary.left)?;
2529 let right = self.lower_expression(builder, &binary.right)?;
2530 let dst = self.alloc_register(range)?;
2531 self.emit(
2532 range,
2533 Instruction::Binary {
2534 dst,
2535 op,
2536 left,
2537 right,
2538 },
2539 )?;
2540 Ok(dst)
2541 }
2542
2543 fn lower_logical(
2544 &mut self,
2545 builder: &mut ModuleBuilder,
2546 range: TextRange,
2547 logical: &LogicalExpression,
2548 ) -> Result<Register, LowerError> {
2549 let result = self.alloc_register(range)?;
2550 let left = self.lower_expression(builder, &logical.left)?;
2551 self.move_to(range, result, left)?;
2552 let short_circuit = self.branch_on_short_circuit(builder, range, logical.operator, left)?;
2553 let right = self.lower_expression(builder, &logical.right)?;
2554 self.move_to(range, result, right)?;
2555 let end = self.next_pc();
2556 self.patch_jump(short_circuit, end);
2557 Ok(result)
2558 }
2559
2560 fn branch_on_short_circuit(
2563 &mut self,
2564 builder: &mut ModuleBuilder,
2565 range: TextRange,
2566 operator: LogicalOperator,
2567 left: Register,
2568 ) -> Result<Pc, LowerError> {
2569 match operator {
2570 LogicalOperator::And => self.emit(
2571 range,
2572 Instruction::JumpIfFalse {
2573 condition: left,
2574 target: Pc::new(0),
2575 },
2576 ),
2577 LogicalOperator::Or => self.emit(
2578 range,
2579 Instruction::JumpIfTrue {
2580 condition: left,
2581 target: Pc::new(0),
2582 },
2583 ),
2584 LogicalOperator::Nullish => {
2585 let is_nullish = self.compute_nullish(builder, range, left)?;
2586 self.emit(
2587 range,
2588 Instruction::JumpIfFalse {
2589 condition: is_nullish,
2590 target: Pc::new(0),
2591 },
2592 )
2593 }
2594 }
2595 }
2596
2597 fn compute_nullish(
2599 &mut self,
2600 builder: &mut ModuleBuilder,
2601 range: TextRange,
2602 value: Register,
2603 ) -> Result<Register, LowerError> {
2604 let null = self.load_constant(builder, Constant::Null, range)?;
2605 let is_null = self.alloc_register(range)?;
2606 self.emit(
2607 range,
2608 Instruction::Binary {
2609 dst: is_null,
2610 op: BinaryOp::Equal,
2611 left: value,
2612 right: null,
2613 },
2614 )?;
2615 Ok(is_null)
2616 }
2617
2618 fn lower_conditional(
2619 &mut self,
2620 builder: &mut ModuleBuilder,
2621 range: TextRange,
2622 conditional: &ConditionalExpression,
2623 ) -> Result<Register, LowerError> {
2624 let result = self.alloc_register(range)?;
2625 let condition = self.lower_expression(builder, &conditional.test)?;
2626 let to_alternate = self.emit(
2627 range,
2628 Instruction::JumpIfFalse {
2629 condition,
2630 target: Pc::new(0),
2631 },
2632 )?;
2633 let consequent = self.lower_expression(builder, &conditional.consequent)?;
2634 self.move_to(range, result, consequent)?;
2635 let to_end = self.emit(range, Instruction::Jump { target: Pc::new(0) })?;
2636 let alternate_pc = self.next_pc();
2637 self.patch_jump(to_alternate, alternate_pc);
2638 let alternate = self.lower_expression(builder, &conditional.alternate)?;
2639 self.move_to(range, result, alternate)?;
2640 let end = self.next_pc();
2641 self.patch_jump(to_end, end);
2642 Ok(result)
2643 }
2644
2645 fn lower_assignment(
2650 &mut self,
2651 builder: &mut ModuleBuilder,
2652 range: TextRange,
2653 assignment: &AssignmentExpression,
2654 ) -> Result<Register, LowerError> {
2655 match assignment.left.data() {
2656 AssignmentTarget::Identifier(identifier) => {
2657 let name = self.identifier_text(identifier)?;
2658 self.lower_identifier_assignment(builder, range, &name, assignment)
2659 }
2660 AssignmentTarget::Member(member) => {
2661 self.lower_member_assignment(builder, range, member, assignment)
2662 }
2663 AssignmentTarget::Object(_) | AssignmentTarget::Array(_) => {
2664 if compound_operator(assignment.operator).is_some() {
2666 return Err(
2667 self.missing(assignment.left.range(), NodeKind::AssignmentExpression)
2668 );
2669 }
2670 let value = self.lower_expression(builder, &assignment.right)?;
2671 self.assign_target(builder, &assignment.left, value)?;
2672 Ok(value)
2673 }
2674 AssignmentTarget::Missing(missing) => {
2675 Err(self.missing(assignment.left.range(), missing.expected()))
2676 }
2677 }
2678 }
2679
2680 fn lower_identifier_assignment(
2681 &mut self,
2682 builder: &mut ModuleBuilder,
2683 range: TextRange,
2684 name: &str,
2685 assignment: &AssignmentExpression,
2686 ) -> Result<Register, LowerError> {
2687 match compound_operator(assignment.operator) {
2688 None => {
2689 let value = self.lower_expression(builder, &assignment.right)?;
2690 self.assign_name(builder, name, value, range)?;
2691 Ok(value)
2692 }
2693 Some(CompoundOp::Arithmetic(op)) => {
2694 let current = self.read_name_value(builder, name, range)?;
2695 let right = self.lower_expression(builder, &assignment.right)?;
2696 let result = self.alloc_register(range)?;
2697 self.emit(
2698 range,
2699 Instruction::Binary {
2700 dst: result,
2701 op,
2702 left: current,
2703 right,
2704 },
2705 )?;
2706 self.assign_name(builder, name, result, range)?;
2707 Ok(result)
2708 }
2709 Some(CompoundOp::Logical(op)) => {
2710 let result = self.alloc_register(range)?;
2711 let current = self.read_name_value(builder, name, range)?;
2712 self.move_to(range, result, current)?;
2713 let skip = self.branch_on_short_circuit(builder, range, op, current)?;
2714 let value = self.lower_expression(builder, &assignment.right)?;
2715 self.assign_name(builder, name, value, range)?;
2716 self.move_to(range, result, value)?;
2717 let end = self.next_pc();
2718 self.patch_jump(skip, end);
2719 Ok(result)
2720 }
2721 }
2722 }
2723
2724 fn lower_member_assignment(
2725 &mut self,
2726 builder: &mut ModuleBuilder,
2727 range: TextRange,
2728 member: &AssignmentMemberTarget,
2729 assignment: &AssignmentExpression,
2730 ) -> Result<Register, LowerError> {
2731 let object = self.lower_expression(builder, &member.object)?;
2732 let key = self.member_key(builder, &member.property)?;
2733 match compound_operator(assignment.operator) {
2734 None => {
2735 let value = self.lower_expression(builder, &assignment.right)?;
2736 self.emit(range, Instruction::SetProperty { object, key, value })?;
2737 Ok(value)
2738 }
2739 Some(CompoundOp::Arithmetic(op)) => {
2740 let current = self.alloc_register(range)?;
2741 self.emit(
2742 range,
2743 Instruction::GetProperty {
2744 dst: current,
2745 object,
2746 key,
2747 },
2748 )?;
2749 let right = self.lower_expression(builder, &assignment.right)?;
2750 let result = self.alloc_register(range)?;
2751 self.emit(
2752 range,
2753 Instruction::Binary {
2754 dst: result,
2755 op,
2756 left: current,
2757 right,
2758 },
2759 )?;
2760 self.emit(
2761 range,
2762 Instruction::SetProperty {
2763 object,
2764 key,
2765 value: result,
2766 },
2767 )?;
2768 Ok(result)
2769 }
2770 Some(CompoundOp::Logical(op)) => {
2771 let result = self.alloc_register(range)?;
2772 let current = self.alloc_register(range)?;
2773 self.emit(
2774 range,
2775 Instruction::GetProperty {
2776 dst: current,
2777 object,
2778 key,
2779 },
2780 )?;
2781 self.move_to(range, result, current)?;
2782 let skip = self.branch_on_short_circuit(builder, range, op, current)?;
2783 let value = self.lower_expression(builder, &assignment.right)?;
2784 self.emit(range, Instruction::SetProperty { object, key, value })?;
2785 self.move_to(range, result, value)?;
2786 let end = self.next_pc();
2787 self.patch_jump(skip, end);
2788 Ok(result)
2789 }
2790 }
2791 }
2792
2793 fn lower_update(
2794 &mut self,
2795 builder: &mut ModuleBuilder,
2796 range: TextRange,
2797 update: &UpdateExpression,
2798 ) -> Result<Register, LowerError> {
2799 let op = match update.operator {
2800 UpdateOperator::Increment => BinaryOp::Add,
2801 UpdateOperator::Decrement => BinaryOp::Subtract,
2802 };
2803 match update.argument.data() {
2804 AssignmentTarget::Identifier(identifier) => {
2805 let name = self.identifier_text(identifier)?;
2806 let current = self.read_name_value(builder, &name, range)?;
2807 let old = self.alloc_register(range)?;
2808 self.move_to(range, old, current)?;
2809 let one = self.load_constant(builder, Constant::Int32(1), range)?;
2810 let updated = self.alloc_register(range)?;
2811 self.emit(
2812 range,
2813 Instruction::Binary {
2814 dst: updated,
2815 op,
2816 left: old,
2817 right: one,
2818 },
2819 )?;
2820 self.assign_name(builder, &name, updated, range)?;
2821 Ok(if update.prefix { updated } else { old })
2822 }
2823 AssignmentTarget::Member(member) => {
2824 let object = self.lower_expression(builder, &member.object)?;
2825 let key = self.member_key(builder, &member.property)?;
2826 let old = self.alloc_register(range)?;
2827 self.emit(
2828 range,
2829 Instruction::GetProperty {
2830 dst: old,
2831 object,
2832 key,
2833 },
2834 )?;
2835 let one = self.load_constant(builder, Constant::Int32(1), range)?;
2836 let updated = self.alloc_register(range)?;
2837 self.emit(
2838 range,
2839 Instruction::Binary {
2840 dst: updated,
2841 op,
2842 left: old,
2843 right: one,
2844 },
2845 )?;
2846 self.emit(
2847 range,
2848 Instruction::SetProperty {
2849 object,
2850 key,
2851 value: updated,
2852 },
2853 )?;
2854 Ok(if update.prefix { updated } else { old })
2855 }
2856 AssignmentTarget::Object(_) | AssignmentTarget::Array(_) => {
2857 Err(self.missing(update.argument.range(), NodeKind::UpdateExpression))
2858 }
2859 AssignmentTarget::Missing(missing) => {
2860 Err(self.missing(update.argument.range(), missing.expected()))
2861 }
2862 }
2863 }
2864
2865 fn lower_await(
2867 &mut self,
2868 builder: &mut ModuleBuilder,
2869 range: TextRange,
2870 await_expression: &AwaitExpression,
2871 ) -> Result<Register, LowerError> {
2872 let src = self.lower_expression(builder, &await_expression.argument)?;
2873 self.emit_suspend(range, src)
2874 }
2875
2876 fn lower_yield(
2879 &mut self,
2880 builder: &mut ModuleBuilder,
2881 range: TextRange,
2882 yield_expression: &YieldExpression,
2883 ) -> Result<Register, LowerError> {
2884 if yield_expression.delegate {
2885 return self.lower_yield_delegate(builder, range, yield_expression);
2886 }
2887 let src = match &yield_expression.argument {
2888 Some(expression) => self.lower_expression(builder, expression)?,
2889 None => self.undefined(builder, range)?,
2890 };
2891 self.emit_suspend(range, src)
2892 }
2893
2894 fn lower_yield_delegate(
2897 &mut self,
2898 builder: &mut ModuleBuilder,
2899 range: TextRange,
2900 yield_expression: &YieldExpression,
2901 ) -> Result<Register, LowerError> {
2902 let subject = match &yield_expression.argument {
2903 Some(expression) => self.lower_expression(builder, expression)?,
2904 None => self.undefined(builder, range)?,
2905 };
2906 let iterator = self.alloc_register(range)?;
2907 self.emit(
2908 range,
2909 Instruction::GetIterator {
2910 dst: iterator,
2911 src: subject,
2912 kind: IteratorKind::Sync,
2913 },
2914 )?;
2915 let done = self.alloc_register(range)?;
2916 let value = self.alloc_register(range)?;
2917 let result = self.alloc_register(range)?;
2918 let undefined = self.undefined(builder, range)?;
2919 self.move_to(range, result, undefined)?;
2920 let head = self.next_pc();
2921 self.emit(
2922 range,
2923 Instruction::IteratorNext {
2924 done,
2925 value,
2926 iterator,
2927 },
2928 )?;
2929 let exit_jump = self.emit(
2930 range,
2931 Instruction::JumpIfTrue {
2932 condition: done,
2933 target: Pc::new(0),
2934 },
2935 )?;
2936 let resumed = self.emit_suspend(range, value)?;
2937 self.move_to(range, result, resumed)?;
2938 self.emit(range, Instruction::Jump { target: head })?;
2939 let exit = self.next_pc();
2940 self.patch_jump(exit_jump, exit);
2941 self.move_to(range, result, value)?;
2943 Ok(result)
2944 }
2945
2946 fn emit_suspend(&mut self, range: TextRange, src: Register) -> Result<Register, LowerError> {
2949 let dst = self.alloc_register(range)?;
2950 let resume = Pc::new(self.code.len() as u32 + 1);
2951 self.emit(range, Instruction::Suspend { dst, src, resume })?;
2952 Ok(dst)
2953 }
2954
2955 fn lower_member(
2963 &mut self,
2964 builder: &mut ModuleBuilder,
2965 range: TextRange,
2966 member: &MemberExpression,
2967 ) -> Result<(Register, Register), LowerError> {
2968 if member.optional {
2969 let value = self.lower_optional_chain(builder, range, member)?;
2970 let object = self.undefined(builder, range)?;
2973 return Ok((object, value));
2974 }
2975 let object = self.lower_expression(builder, &member.object)?;
2976 let key = self.member_key(builder, &member.property)?;
2977 let dst = self.alloc_register(range)?;
2978 self.emit(range, Instruction::GetProperty { dst, object, key })?;
2979 Ok((object, dst))
2980 }
2981
2982 fn lower_optional_chain(
2985 &mut self,
2986 builder: &mut ModuleBuilder,
2987 range: TextRange,
2988 member: &MemberExpression,
2989 ) -> Result<Register, LowerError> {
2990 let result = self.alloc_register(range)?;
2991 let undefined = self.undefined(builder, range)?;
2992 self.move_to(range, result, undefined)?;
2993 let object = self.lower_expression(builder, &member.object)?;
2994 let is_nullish = self.compute_nullish(builder, range, object)?;
2995 let skip = self.emit(
2996 range,
2997 Instruction::JumpIfTrue {
2998 condition: is_nullish,
2999 target: Pc::new(0),
3000 },
3001 )?;
3002 let key = self.member_key(builder, &member.property)?;
3003 let value = self.alloc_register(range)?;
3004 self.emit(
3005 range,
3006 Instruction::GetProperty {
3007 dst: value,
3008 object,
3009 key,
3010 },
3011 )?;
3012 self.move_to(range, result, value)?;
3013 let end = self.next_pc();
3014 self.patch_jump(skip, end);
3015 Ok(result)
3016 }
3017
3018 fn lower_call(
3019 &mut self,
3020 builder: &mut ModuleBuilder,
3021 range: TextRange,
3022 call: &CallExpression,
3023 ) -> Result<Register, LowerError> {
3024 if let Expression::Member(member) = call.callee.data()
3025 && member.optional
3026 {
3027 return self.lower_optional_member_call(builder, range, call, member);
3028 }
3029 if call.optional {
3030 return self.lower_optional_call(builder, range, call);
3031 }
3032 let (callee, this_value) = self.lower_callee(builder, range, &call.callee)?;
3033 let arguments = self.build_arguments(builder, range, &call.arguments)?;
3034 let dst = self.alloc_register(range)?;
3035 self.emit(
3036 range,
3037 Instruction::Call {
3038 dst,
3039 callee,
3040 this_value,
3041 arguments,
3042 },
3043 )?;
3044 Ok(dst)
3045 }
3046
3047 fn lower_optional_member_call(
3048 &mut self,
3049 builder: &mut ModuleBuilder,
3050 range: TextRange,
3051 call: &CallExpression,
3052 member: &MemberExpression,
3053 ) -> Result<Register, LowerError> {
3054 let result = self.alloc_register(range)?;
3055 let undefined = self.undefined(builder, range)?;
3056 self.move_to(range, result, undefined)?;
3057
3058 let object = self.lower_expression(builder, &member.object)?;
3059 let object_is_nullish = self.compute_nullish(builder, range, object)?;
3060 let object_skip = self.emit(
3061 range,
3062 Instruction::JumpIfTrue {
3063 condition: object_is_nullish,
3064 target: Pc::new(0),
3065 },
3066 )?;
3067
3068 let key = self.member_key(builder, &member.property)?;
3069 let callee = self.alloc_register(range)?;
3070 self.emit(
3071 range,
3072 Instruction::GetProperty {
3073 dst: callee,
3074 object,
3075 key,
3076 },
3077 )?;
3078
3079 let callee_skip = if call.optional {
3080 let callee_is_nullish = self.compute_nullish(builder, range, callee)?;
3081 Some(self.emit(
3082 range,
3083 Instruction::JumpIfTrue {
3084 condition: callee_is_nullish,
3085 target: Pc::new(0),
3086 },
3087 )?)
3088 } else {
3089 None
3090 };
3091
3092 let arguments = self.build_arguments(builder, range, &call.arguments)?;
3093 let value = self.alloc_register(range)?;
3094 self.emit(
3095 range,
3096 Instruction::Call {
3097 dst: value,
3098 callee,
3099 this_value: object,
3100 arguments,
3101 },
3102 )?;
3103 self.move_to(range, result, value)?;
3104
3105 let end = self.next_pc();
3106 self.patch_jump(object_skip, end);
3107 if let Some(callee_skip) = callee_skip {
3108 self.patch_jump(callee_skip, end);
3109 }
3110 Ok(result)
3111 }
3112
3113 fn lower_callee(
3116 &mut self,
3117 builder: &mut ModuleBuilder,
3118 range: TextRange,
3119 callee: &Expr,
3120 ) -> Result<(Register, Register), LowerError> {
3121 match callee.data() {
3122 Expression::Member(member) if !member.optional => {
3123 if matches!(member.object.data(), Expression::Super) {
3124 let this_value = self.this_value(range)?;
3126 let object = this_value;
3127 let key = self.member_key(builder, &member.property)?;
3128 let value = self.alloc_register(range)?;
3129 self.emit(
3130 range,
3131 Instruction::GetProperty {
3132 dst: value,
3133 object,
3134 key,
3135 },
3136 )?;
3137 return Ok((value, this_value));
3138 }
3139 let (object, value) = self.lower_member(builder, callee.range(), member)?;
3140 Ok((value, object))
3141 }
3142 Expression::Super => {
3143 let this_value = self.this_value(range)?;
3149 Ok((this_value, this_value))
3150 }
3151 _ => {
3152 let callee = self.lower_expression(builder, callee)?;
3153 let this_value = self.undefined(builder, range)?;
3154 Ok((callee, this_value))
3155 }
3156 }
3157 }
3158
3159 fn lower_optional_call(
3160 &mut self,
3161 builder: &mut ModuleBuilder,
3162 range: TextRange,
3163 call: &CallExpression,
3164 ) -> Result<Register, LowerError> {
3165 let result = self.alloc_register(range)?;
3166 let undefined = self.undefined(builder, range)?;
3167 self.move_to(range, result, undefined)?;
3168 let (callee, this_value) = self.lower_callee(builder, range, &call.callee)?;
3169 let is_nullish = self.compute_nullish(builder, range, callee)?;
3170 let skip = self.emit(
3171 range,
3172 Instruction::JumpIfTrue {
3173 condition: is_nullish,
3174 target: Pc::new(0),
3175 },
3176 )?;
3177 let arguments = self.build_arguments(builder, range, &call.arguments)?;
3178 let value = self.alloc_register(range)?;
3179 self.emit(
3180 range,
3181 Instruction::Call {
3182 dst: value,
3183 callee,
3184 this_value,
3185 arguments,
3186 },
3187 )?;
3188 self.move_to(range, result, value)?;
3189 let end = self.next_pc();
3190 self.patch_jump(skip, end);
3191 Ok(result)
3192 }
3193
3194 fn lower_new(
3195 &mut self,
3196 builder: &mut ModuleBuilder,
3197 range: TextRange,
3198 new: &NewExpression,
3199 ) -> Result<Register, LowerError> {
3200 let callee = self.lower_expression(builder, &new.callee)?;
3201 let arguments = self.build_arguments(builder, range, &new.arguments)?;
3202 let dst = self.alloc_register(range)?;
3203 self.emit(
3204 range,
3205 Instruction::Construct {
3206 dst,
3207 callee,
3208 arguments,
3209 },
3210 )?;
3211 Ok(dst)
3212 }
3213
3214 fn build_arguments(
3216 &mut self,
3217 builder: &mut ModuleBuilder,
3218 range: TextRange,
3219 arguments: &[CallArgument],
3220 ) -> Result<Register, LowerError> {
3221 let array = self.alloc_register(range)?;
3222 self.emit(range, Instruction::CreateArray { dst: array })?;
3223 for argument in arguments {
3224 match argument {
3225 CallArgument::Expression(expression) => {
3226 let value = self.lower_expression(builder, expression)?;
3227 self.emit(range, Instruction::ArrayPush { array, value })?;
3228 }
3229 CallArgument::Spread(spread) => {
3230 let iterable = self.lower_expression(builder, &spread.argument)?;
3231 self.emit(range, Instruction::ArrayExtend { array, iterable })?;
3232 }
3233 CallArgument::Missing(missing) => {
3234 return Err(self.error(
3235 zero_range(),
3236 LowerErrorKind::MissingSyntax {
3237 expected: missing.expected(),
3238 },
3239 ));
3240 }
3241 }
3242 }
3243 Ok(array)
3244 }
3245
3246 fn lower_array(
3251 &mut self,
3252 builder: &mut ModuleBuilder,
3253 range: TextRange,
3254 array: &crate::syntax::ArrayLiteral,
3255 ) -> Result<Register, LowerError> {
3256 let dst = self.alloc_register(range)?;
3257 self.emit(range, Instruction::CreateArray { dst })?;
3258 for element in &array.elements {
3259 match element {
3260 ArrayElement::Expression(expression) => {
3261 let value = self.lower_expression(builder, expression)?;
3262 self.emit(range, Instruction::ArrayPush { array: dst, value })?;
3263 }
3264 ArrayElement::Spread(spread) => {
3265 let iterable = self.lower_expression(builder, &spread.argument)?;
3266 self.emit(
3267 range,
3268 Instruction::ArrayExtend {
3269 array: dst,
3270 iterable,
3271 },
3272 )?;
3273 }
3274 ArrayElement::Elision => {
3275 let hole = self.undefined(builder, range)?;
3276 self.emit(
3277 range,
3278 Instruction::ArrayPush {
3279 array: dst,
3280 value: hole,
3281 },
3282 )?;
3283 }
3284 ArrayElement::Missing(missing) => {
3285 return Err(self.missing(range, missing.expected()));
3286 }
3287 }
3288 }
3289 Ok(dst)
3290 }
3291
3292 fn lower_object(
3293 &mut self,
3294 builder: &mut ModuleBuilder,
3295 range: TextRange,
3296 object: &ObjectLiteral,
3297 ) -> Result<Register, LowerError> {
3298 let dst = self.alloc_register(range)?;
3299 self.emit(range, Instruction::CreateObject { dst })?;
3300 for member in &object.members {
3301 match member.data() {
3302 ObjectMember::Property(property) => {
3303 let key = self.property_key(builder, &property.name)?;
3304 let value = self.lower_expression(builder, &property.value)?;
3305 self.install_property(
3306 builder,
3307 member.range(),
3308 dst,
3309 key,
3310 value,
3311 property.modifier,
3312 )?;
3313 }
3314 ObjectMember::Method(method) => {
3315 let key = self.property_key(builder, &method.name)?;
3316 let value =
3317 self.build_function_value(builder, member.range(), None, &method.function)?;
3318 self.install_property(
3319 builder,
3320 member.range(),
3321 dst,
3322 key,
3323 value,
3324 method.modifier,
3325 )?;
3326 }
3327 ObjectMember::Spread(spread) => {
3328 let source = self.lower_expression(builder, &spread.argument)?;
3329 self.emit(
3330 member.range(),
3331 Instruction::ObjectSpread {
3332 target: dst,
3333 source,
3334 },
3335 )?;
3336 }
3337 ObjectMember::Missing(missing) => {
3338 return Err(self.missing(member.range(), missing.expected()));
3339 }
3340 }
3341 }
3342 Ok(dst)
3343 }
3344
3345 fn install_property(
3347 &mut self,
3348 _builder: &mut ModuleBuilder,
3349 range: TextRange,
3350 object: Register,
3351 key: Register,
3352 value: Register,
3353 modifier: PropertyModifier,
3354 ) -> Result<(), LowerError> {
3355 match modifier {
3356 PropertyModifier::None => {
3357 self.emit(range, Instruction::SetProperty { object, key, value })?;
3358 }
3359 PropertyModifier::Get => {
3360 self.emit(
3361 range,
3362 Instruction::DefineAccessor {
3363 object,
3364 key,
3365 accessor: value,
3366 kind: AccessorKind::Getter,
3367 },
3368 )?;
3369 }
3370 PropertyModifier::Set => {
3371 self.emit(
3372 range,
3373 Instruction::DefineAccessor {
3374 object,
3375 key,
3376 accessor: value,
3377 kind: AccessorKind::Setter,
3378 },
3379 )?;
3380 }
3381 }
3382 Ok(())
3383 }
3384
3385 fn lower_literal(
3390 &mut self,
3391 builder: &mut ModuleBuilder,
3392 range: TextRange,
3393 literal: &Literal,
3394 ) -> Result<Register, LowerError> {
3395 match literal {
3396 Literal::Number(number) => self.lower_numeric_literal(builder, number),
3397 Literal::String(string) => {
3398 let value = self.string_literal_value(string)?;
3399 self.string_reg(builder, value, range)
3400 }
3401 Literal::Boolean(boolean) => {
3402 let value = self.boolean_literal_value(boolean)?;
3403 self.load_constant(builder, Constant::Boolean(value), range)
3404 }
3405 Literal::Null(_) => self.load_constant(builder, Constant::Null, range),
3406 Literal::BigInt(_) => self.lower_bigint_literal(builder, range, literal),
3407 Literal::Regex(regex) => self.lower_regex_literal(builder, range, regex),
3408 }
3409 }
3410
3411 fn lower_template(
3412 &mut self,
3413 builder: &mut ModuleBuilder,
3414 range: TextRange,
3415 template: &TemplateLiteral,
3416 ) -> Result<Register, LowerError> {
3417 let cooked = self.cooked_template_parts(template)?;
3420 let first = cooked.first().cloned().unwrap_or_default();
3421 let mut acc = self.string_reg(builder, first, range)?;
3422 for (index, expression) in template.expressions.iter().enumerate() {
3423 let value = self.lower_expression(builder, expression)?;
3424 let joined = self.alloc_register(range)?;
3425 self.emit(
3426 range,
3427 Instruction::Binary {
3428 dst: joined,
3429 op: BinaryOp::Add,
3430 left: acc,
3431 right: value,
3432 },
3433 )?;
3434 acc = joined;
3435 let chunk = cooked.get(index + 1).cloned().unwrap_or_default();
3436 let chunk_reg = self.string_reg(builder, chunk, range)?;
3437 let joined = self.alloc_register(range)?;
3438 self.emit(
3439 range,
3440 Instruction::Binary {
3441 dst: joined,
3442 op: BinaryOp::Add,
3443 left: acc,
3444 right: chunk_reg,
3445 },
3446 )?;
3447 acc = joined;
3448 }
3449 Ok(acc)
3450 }
3451
3452 fn lower_tagged_template(
3455 &mut self,
3456 builder: &mut ModuleBuilder,
3457 range: TextRange,
3458 tagged: &crate::syntax::TaggedTemplateExpression,
3459 ) -> Result<Register, LowerError> {
3460 let (callee, this_value) = self.lower_callee(builder, range, &tagged.tag)?;
3461 let cooked = self.cooked_template_parts(&tagged.template)?;
3462 let raw = self.raw_template_parts(&tagged.template)?;
3463 let strings = self.alloc_register(range)?;
3465 self.emit(range, Instruction::CreateArray { dst: strings })?;
3466 for part in &cooked {
3467 let value = self.string_reg(builder, part.clone(), range)?;
3468 self.emit(
3469 range,
3470 Instruction::ArrayPush {
3471 array: strings,
3472 value,
3473 },
3474 )?;
3475 }
3476 let raw_array = self.alloc_register(range)?;
3477 self.emit(range, Instruction::CreateArray { dst: raw_array })?;
3478 for part in &raw {
3479 let value = self.string_reg(builder, part.clone(), range)?;
3480 self.emit(
3481 range,
3482 Instruction::ArrayPush {
3483 array: raw_array,
3484 value,
3485 },
3486 )?;
3487 }
3488 let raw_key = self.string_reg(builder, EcmaString::from_utf8("raw"), range)?;
3489 self.emit(
3490 range,
3491 Instruction::SetProperty {
3492 object: strings,
3493 key: raw_key,
3494 value: raw_array,
3495 },
3496 )?;
3497 let arguments = self.alloc_register(range)?;
3498 self.emit(range, Instruction::CreateArray { dst: arguments })?;
3499 self.emit(
3500 range,
3501 Instruction::ArrayPush {
3502 array: arguments,
3503 value: strings,
3504 },
3505 )?;
3506 for expression in &tagged.template.expressions {
3507 let value = self.lower_expression(builder, expression)?;
3508 self.emit(
3509 range,
3510 Instruction::ArrayPush {
3511 array: arguments,
3512 value,
3513 },
3514 )?;
3515 }
3516 let dst = self.alloc_register(range)?;
3517 self.emit(
3518 range,
3519 Instruction::Call {
3520 dst,
3521 callee,
3522 this_value,
3523 arguments,
3524 },
3525 )?;
3526 Ok(dst)
3527 }
3528
3529 fn cooked_template_parts(
3530 &self,
3531 template: &TemplateLiteral,
3532 ) -> Result<Vec<EcmaString>, LowerError> {
3533 template
3534 .elements
3535 .iter()
3536 .map(|element| self.template_element_text(element, true))
3537 .collect()
3538 }
3539
3540 fn raw_template_parts(
3541 &self,
3542 template: &TemplateLiteral,
3543 ) -> Result<Vec<EcmaString>, LowerError> {
3544 template
3545 .elements
3546 .iter()
3547 .map(|element| self.template_element_text(element, false))
3548 .collect()
3549 }
3550
3551 fn template_element_text(
3554 &self,
3555 element: &TemplateElementNode,
3556 cook: bool,
3557 ) -> Result<EcmaString, LowerError> {
3558 let token = element.data().token();
3559 if token.is_missing() {
3560 return Ok(EcmaString::default());
3561 }
3562 let Some(text) = self.file.token_text(token) else {
3563 return Ok(EcmaString::default());
3564 };
3565 let interior = trim_template_delimiters(text, token.kind());
3566 if cook {
3567 Ok(cook_escapes(interior))
3568 } else {
3569 Ok(EcmaString::from_utf8(interior))
3570 }
3571 }
3572
3573 fn lower_regex_literal(
3574 &mut self,
3575 builder: &mut ModuleBuilder,
3576 range: TextRange,
3577 regex: &RegexLiteralNode,
3578 ) -> Result<Register, LowerError> {
3579 let token = regex.data().token();
3580 if token.is_missing() {
3581 return Err(self.missing(range, NodeKind::RegexLiteral));
3582 }
3583 let lexeme = self
3584 .file
3585 .token_text(token)
3586 .ok_or_else(|| self.error(range, LowerErrorKind::InvalidRegexLiteral))?;
3587 let (pattern, flags) = split_regex(lexeme)
3588 .ok_or_else(|| self.error(range, LowerErrorKind::InvalidRegexLiteral))?;
3589 let pattern_id =
3590 builder.intern(Constant::String(EcmaString::from_utf8(&pattern)), range)?;
3591 let flags_id = builder.intern(Constant::String(EcmaString::from_utf8(&flags)), range)?;
3592 let dst = self.alloc_register(range)?;
3593 self.emit(
3594 range,
3595 Instruction::CreateRegExp {
3596 dst,
3597 pattern: pattern_id,
3598 flags: flags_id,
3599 },
3600 )?;
3601 Ok(dst)
3602 }
3603
3604 fn lower_bigint_literal(
3605 &mut self,
3606 builder: &mut ModuleBuilder,
3607 range: TextRange,
3608 literal: &Literal,
3609 ) -> Result<Register, LowerError> {
3610 let Literal::BigInt(node) = literal else {
3611 unreachable!("lower_bigint_literal only handles bigint literals");
3612 };
3613 let token = node.data().token();
3614 if token.is_missing() {
3615 return Err(self.missing(range, NodeKind::BigIntLiteral));
3616 }
3617 let lexeme = self
3618 .file
3619 .token_text(token)
3620 .ok_or_else(|| self.error(range, LowerErrorKind::InvalidBigIntLiteral))?;
3621 let canonical = canonical_bigint_text(lexeme)
3622 .ok_or_else(|| self.unsupported(range, UnsupportedConstruct::NonDecimalBigInt))?;
3623 let value = BigIntLiteral::new(canonical)
3624 .ok_or_else(|| self.error(range, LowerErrorKind::InvalidBigIntLiteral))?;
3625 self.load_constant(builder, Constant::BigInt(value), range)
3626 }
3627
3628 fn lower_numeric_literal(
3629 &mut self,
3630 builder: &mut ModuleBuilder,
3631 number: &NumericLiteralNode,
3632 ) -> Result<Register, LowerError> {
3633 let range = number.range();
3634 let token = number.data().token();
3635 if token.is_missing() {
3636 return Err(self.missing(range, NodeKind::NumericLiteral));
3637 }
3638 let lexeme = self
3639 .file
3640 .token_text(token)
3641 .filter(|text| !text.is_empty())
3642 .ok_or_else(|| self.error(range, LowerErrorKind::InvalidNumericLiteral))?;
3643 let value = cook_number(lexeme)
3644 .ok_or_else(|| self.error(range, LowerErrorKind::InvalidNumericLiteral))?;
3645 self.load_constant(builder, number_constant(value), range)
3646 }
3647
3648 fn string_literal_value(&self, string: &StringLiteralNode) -> Result<EcmaString, LowerError> {
3649 let range = string.range();
3650 let token = string.data().token();
3651 let missing = || self.missing(range, NodeKind::StringLiteral);
3652 if token.is_missing() {
3653 return Err(missing());
3654 }
3655 let text = self.file.token_text(token).ok_or_else(missing)?;
3656 if text.len() < 2 {
3657 return Err(missing());
3658 }
3659 let interior = &text[1..text.len() - 1];
3660 Ok(cook_escapes(interior))
3661 }
3662
3663 fn boolean_literal_value(&self, boolean: &BooleanLiteralNode) -> Result<bool, LowerError> {
3664 let token = boolean.data().token();
3665 match token.kind() {
3666 TokenKind::KwTrue if !token.is_missing() => Ok(true),
3667 TokenKind::KwFalse if !token.is_missing() => Ok(false),
3668 _ => Err(self.missing(boolean.range(), NodeKind::BooleanLiteral)),
3669 }
3670 }
3671
3672 fn member_key(
3679 &mut self,
3680 builder: &mut ModuleBuilder,
3681 property: &MemberProperty,
3682 ) -> Result<Register, LowerError> {
3683 match property {
3684 MemberProperty::Named(identifier) => {
3685 let name = self.identifier_text(identifier)?;
3686 self.string_reg(builder, EcmaString::from_utf8(&name), identifier.range())
3687 }
3688 MemberProperty::Computed(expression) => self.lower_expression(builder, expression),
3689 MemberProperty::Private(private) => {
3690 let name = self.private_text(private)?;
3691 self.read_name(builder, &name, private.range())
3692 }
3693 }
3694 }
3695
3696 fn property_key(
3698 &mut self,
3699 builder: &mut ModuleBuilder,
3700 name: &PropertyName,
3701 ) -> Result<Register, LowerError> {
3702 match name {
3703 PropertyName::Identifier(identifier) => {
3704 let text = self.identifier_text(identifier)?;
3705 self.string_reg(builder, EcmaString::from_utf8(&text), identifier.range())
3706 }
3707 PropertyName::String(string) => {
3708 let value = self.string_literal_value(string)?;
3709 self.string_reg(builder, value, string.range())
3710 }
3711 PropertyName::Number(number) => {
3712 let key = numeric_key_text(self, number)?;
3713 self.string_reg(builder, EcmaString::from_utf8(&key), number.range())
3714 }
3715 PropertyName::Computed(expression) => self.lower_expression(builder, expression),
3716 PropertyName::Private(private) => {
3717 let name = self.private_text(private)?;
3718 self.read_name(builder, &name, private.range())
3719 }
3720 PropertyName::Missing(missing) => Err(self.error(
3721 zero_range(),
3722 LowerErrorKind::MissingSyntax {
3723 expected: missing.expected(),
3724 },
3725 )),
3726 }
3727 }
3728
3729 fn lower_import(
3734 &mut self,
3735 builder: &mut ModuleBuilder,
3736 range: TextRange,
3737 import: &ImportDeclaration,
3738 ) -> Result<(), LowerError> {
3739 if self.goal == LoweringGoal::ClassicScript {
3740 return Err(self.unsupported(range, UnsupportedConstruct::ImportDeclarationInScript));
3741 }
3742 if import.type_only || self.goal == LoweringGoal::ProgramModule {
3743 return Ok(());
3744 }
3745 let specifier = self.string_literal_value(&import.source)?;
3746 let specifier_id = builder.intern(Constant::String(specifier), range)?;
3747 let module = self.alloc_register(range)?;
3748 self.emit(
3749 range,
3750 Instruction::Import {
3751 dst: module,
3752 specifier: specifier_id,
3753 },
3754 )?;
3755 let Some(clause) = &import.clause else {
3756 return Ok(());
3757 };
3758 if let Some(default) = &clause.default {
3759 let name = self.identifier_text(default)?;
3760 let value = self.get_named(builder, range, module, "default")?;
3761 self.store_binding(
3762 builder,
3763 &name,
3764 value,
3765 range,
3766 binding_site(default.range()),
3767 DeclarationScope::Function,
3768 )?;
3769 }
3770 match &clause.binding {
3771 Some(ImportBinding::Namespace(identifier)) => {
3772 let name = self.identifier_text(identifier)?;
3773 self.store_binding(
3774 builder,
3775 &name,
3776 module,
3777 range,
3778 binding_site(identifier.range()),
3779 DeclarationScope::Function,
3780 )?;
3781 }
3782 Some(ImportBinding::Named(specifiers)) => {
3783 for specifier in specifiers {
3784 let data = specifier.data();
3785 if matches!(data.mode, ImportSpecifierMode::TypeOnly) {
3786 continue;
3787 }
3788 let local = self.identifier_text(&data.local)?;
3789 let imported = self.module_export_name(&data.imported)?;
3790 let value = self.get_named(builder, range, module, &imported)?;
3791 self.store_binding(
3792 builder,
3793 &local,
3794 value,
3795 range,
3796 binding_site(data.local.range()),
3797 DeclarationScope::Function,
3798 )?;
3799 }
3800 }
3801 None => {}
3802 }
3803 Ok(())
3804 }
3805
3806 fn lower_import_expression(
3807 &mut self,
3808 builder: &mut ModuleBuilder,
3809 range: TextRange,
3810 import: &crate::syntax::ImportExpression,
3811 ) -> Result<Register, LowerError> {
3812 if self.goal == LoweringGoal::ClassicScript {
3813 return Err(self.unsupported(range, UnsupportedConstruct::DynamicImportInScript));
3814 }
3815 if let Expression::Literal(Literal::String(string)) = import.source.data() {
3816 let specifier = self.string_literal_value(string)?;
3817 let specifier_id = builder.intern(Constant::String(specifier), range)?;
3818 let dst = self.alloc_register(range)?;
3819 self.emit(
3820 range,
3821 Instruction::Import {
3822 dst,
3823 specifier: specifier_id,
3824 },
3825 )?;
3826 Ok(dst)
3827 } else {
3828 Err(self.unsupported(range, UnsupportedConstruct::DynamicImportExpression))
3829 }
3830 }
3831
3832 fn get_named(
3833 &mut self,
3834 builder: &mut ModuleBuilder,
3835 range: TextRange,
3836 object: Register,
3837 name: &str,
3838 ) -> Result<Register, LowerError> {
3839 let key = self.string_reg(builder, EcmaString::from_utf8(name), range)?;
3840 let dst = self.alloc_register(range)?;
3841 self.emit(range, Instruction::GetProperty { dst, object, key })?;
3842 Ok(dst)
3843 }
3844
3845 fn export_binding(
3847 &mut self,
3848 builder: &mut ModuleBuilder,
3849 range: TextRange,
3850 local: &str,
3851 exported: &str,
3852 ) -> Result<(), LowerError> {
3853 if self.goal == LoweringGoal::ProgramModule {
3854 return Ok(());
3855 }
3856 let src = self.read_name(builder, local, range)?;
3857 let name = builder.intern(Constant::String(EcmaString::from_utf8(exported)), range)?;
3858 self.emit(range, Instruction::Export { name, src })?;
3859 Ok(())
3860 }
3861
3862 fn export_value(
3863 &mut self,
3864 builder: &mut ModuleBuilder,
3865 range: TextRange,
3866 exported: &str,
3867 src: Register,
3868 ) -> Result<(), LowerError> {
3869 if self.goal == LoweringGoal::ProgramModule {
3870 debug_assert_eq!(exported, "default");
3871 return self.store_binding(
3872 builder,
3873 "*default*",
3874 src,
3875 range,
3876 binding_site(range),
3877 DeclarationScope::Lexical,
3878 );
3879 }
3880 let name = builder.intern(Constant::String(EcmaString::from_utf8(exported)), range)?;
3881 self.emit(range, Instruction::Export { name, src })?;
3882 Ok(())
3883 }
3884
3885 fn lower_export(
3886 &mut self,
3887 builder: &mut ModuleBuilder,
3888 range: TextRange,
3889 export: &ExportDeclaration,
3890 ) -> Result<(), LowerError> {
3891 if self.goal == LoweringGoal::ClassicScript {
3892 return Err(self.unsupported(range, UnsupportedConstruct::ExportDeclarationInScript));
3893 }
3894
3895 match export {
3896 ExportDeclaration::Named(ExportNamedDeclaration::Declaration(statement)) => {
3897 self.lower_statement(builder, statement)?;
3898 for name in declared_names(self.file, statement) {
3899 self.export_binding(builder, range, &name, &name)?;
3900 }
3901 Ok(())
3902 }
3903 ExportDeclaration::Named(ExportNamedDeclaration::Specifiers {
3904 type_only,
3905 specifiers,
3906 source,
3907 ..
3908 }) => {
3909 if *type_only || self.goal == LoweringGoal::ProgramModule {
3910 return Ok(());
3911 }
3912 if let Some(source) = source {
3913 let specifier = self.string_literal_value(source)?;
3915 let specifier_id = builder.intern(Constant::String(specifier), range)?;
3916 let module = self.alloc_register(range)?;
3917 self.emit(
3918 range,
3919 Instruction::Import {
3920 dst: module,
3921 specifier: specifier_id,
3922 },
3923 )?;
3924 for specifier in specifiers {
3925 let data = specifier.data();
3926 if matches!(data.mode, ExportSpecifierMode::TypeOnly) {
3927 continue;
3928 }
3929 let local = self.module_export_name(&data.local)?;
3930 let exported = self.module_export_name(&data.exported)?;
3931 let value = self.get_named(builder, range, module, &local)?;
3932 self.export_value(builder, range, &exported, value)?;
3933 }
3934 return Ok(());
3935 }
3936 for specifier in specifiers {
3937 let data = specifier.data();
3938 if matches!(data.mode, ExportSpecifierMode::TypeOnly) {
3939 continue;
3940 }
3941 let local = self.module_export_name(&data.local)?;
3942 let exported = self.module_export_name(&data.exported)?;
3943 self.export_binding(builder, range, &local, &exported)?;
3944 }
3945 Ok(())
3946 }
3947 ExportDeclaration::All(all) => {
3948 if all.type_only || self.goal == LoweringGoal::ProgramModule {
3949 Ok(())
3950 } else {
3951 Err(self.unsupported(range, UnsupportedConstruct::RuntimeExportAll))
3952 }
3953 }
3954 ExportDeclaration::Default(default) => match &default.value {
3955 ExportDefaultValue::Expression(expression) => {
3956 let value = self.lower_expression(builder, expression)?;
3957 self.export_value(builder, range, "default", value)
3958 }
3959 ExportDefaultValue::Function(function) => {
3960 if function.body.is_none() {
3961 return Ok(());
3962 }
3963 if let Some(identifier) = &function.name {
3964 let name = self.identifier_text(identifier)?;
3965 let closure = self.read_name(builder, &name, range)?;
3966 self.export_value(builder, range, "default", closure)
3967 } else {
3968 let closure = self
3969 .build_constructible_function_value(builder, range, None, function)?;
3970 self.export_value(builder, range, "default", closure)
3971 }
3972 }
3973 ExportDefaultValue::Class(class) => {
3974 let value = self.lower_class_value(builder, range, class, None, None)?;
3975 if let Some(identifier) = &class.name {
3976 let name = self.identifier_text(identifier)?;
3977 self.store_binding(
3978 builder,
3979 &name,
3980 value,
3981 range,
3982 binding_site(identifier.range()),
3983 DeclarationScope::Lexical,
3984 )?;
3985 }
3986 self.export_value(builder, range, "default", value)
3987 }
3988 ExportDefaultValue::Missing(missing) => {
3989 Err(self.missing(range, missing.expected()))
3990 }
3991 },
3992 ExportDeclaration::Assignment(_) => {
3993 Err(self.unsupported(range, UnsupportedConstruct::ExportAssignment))
3994 }
3995 }
3996 }
3997
3998 fn module_export_name(&self, name: &ModuleExportName) -> Result<String, LowerError> {
3999 match name {
4000 ModuleExportName::Identifier(identifier) => self.identifier_text(identifier),
4001 ModuleExportName::String(string) => self
4002 .string_literal_value(string)?
4003 .to_utf8_strict()
4004 .map_err(|_| self.error(string.range(), LowerErrorKind::IllFormedMetadataString)),
4005 ModuleExportName::Missing(missing) => Err(self.error(
4006 zero_range(),
4007 LowerErrorKind::MissingSyntax {
4008 expected: missing.expected(),
4009 },
4010 )),
4011 }
4012 }
4013
4014 fn bind_pattern(
4020 &mut self,
4021 builder: &mut ModuleBuilder,
4022 pattern: &Pattern,
4023 value: Register,
4024 declaration_scope: DeclarationScope,
4025 ) -> Result<(), LowerError> {
4026 let range = pattern.range();
4027 match pattern.data() {
4028 BindingPattern::Identifier(identifier) => {
4029 let name = self.identifier_text(identifier)?;
4030 self.store_binding(
4031 builder,
4032 &name,
4033 value,
4034 range,
4035 binding_site(identifier.range()),
4036 declaration_scope,
4037 )
4038 }
4039 BindingPattern::Object(object) => {
4040 let mut taken: Vec<Register> = Vec::new();
4041 for property in &object.properties {
4042 if let BindingPattern::Rest(rest) = property.binding.data() {
4043 let rest_value = self.rest_object(builder, range, value, &taken)?;
4044 self.bind_pattern(builder, &rest.argument, rest_value, declaration_scope)?;
4045 continue;
4046 }
4047 let key = self.property_key(builder, &property.name)?;
4048 taken.push(key);
4049 let element = self.alloc_register(range)?;
4050 self.emit(
4051 range,
4052 Instruction::GetProperty {
4053 dst: element,
4054 object: value,
4055 key,
4056 },
4057 )?;
4058 let element = match &property.initializer {
4059 Some(default) => self.apply_default(builder, range, element, default)?,
4060 None => element,
4061 };
4062 self.bind_pattern(builder, &property.binding, element, declaration_scope)?;
4063 }
4064 Ok(())
4065 }
4066 BindingPattern::Array(array) => {
4067 let iterator = self.alloc_register(range)?;
4068 self.emit(
4069 range,
4070 Instruction::GetIterator {
4071 dst: iterator,
4072 src: value,
4073 kind: IteratorKind::Sync,
4074 },
4075 )?;
4076 for element in &array.elements {
4077 match element {
4078 ArrayBindingElement::Elision => {
4079 self.iterator_step_discard(range, iterator)?;
4080 }
4081 ArrayBindingElement::Binding(inner) => {
4082 if let BindingPattern::Rest(rest) = inner.data() {
4083 let rest_value = self.rest_array(builder, range, iterator)?;
4084 self.bind_pattern(
4085 builder,
4086 &rest.argument,
4087 rest_value,
4088 declaration_scope,
4089 )?;
4090 } else {
4091 let (element_value, default) = self.destructure_element(inner);
4092 let value = self.iterator_step_value(builder, range, iterator)?;
4093 let value = match default {
4094 Some(default) => {
4095 self.apply_default(builder, range, value, default)?
4096 }
4097 None => value,
4098 };
4099 let _ = element_value;
4100 self.bind_pattern(builder, inner, value, declaration_scope)?;
4101 }
4102 }
4103 ArrayBindingElement::Missing(missing) => {
4104 return Err(self.missing(range, missing.expected()));
4105 }
4106 }
4107 }
4108 Ok(())
4109 }
4110 BindingPattern::Assignment(assignment) => {
4111 let value = self.apply_default(builder, range, value, &assignment.right)?;
4112 self.bind_pattern(builder, &assignment.left, value, declaration_scope)
4113 }
4114 BindingPattern::Rest(rest) => {
4115 self.bind_pattern(builder, &rest.argument, value, declaration_scope)
4117 }
4118 BindingPattern::Missing(missing) => Err(self.missing(range, missing.expected())),
4119 }
4120 }
4121
4122 fn destructure_element<'p>(&self, pattern: &'p Pattern) -> (&'p Pattern, Option<&'p Expr>) {
4124 if let BindingPattern::Assignment(assignment) = pattern.data() {
4125 (&assignment.left, Some(&assignment.right))
4126 } else {
4127 (pattern, None)
4128 }
4129 }
4130
4131 fn apply_default(
4133 &mut self,
4134 builder: &mut ModuleBuilder,
4135 range: TextRange,
4136 value: Register,
4137 default: &Expr,
4138 ) -> Result<Register, LowerError> {
4139 let result = self.alloc_register(range)?;
4140 self.move_to(range, result, value)?;
4141 let undefined = self.undefined(builder, range)?;
4142 let is_undefined = self.alloc_register(range)?;
4143 self.emit(
4144 range,
4145 Instruction::Binary {
4146 dst: is_undefined,
4147 op: BinaryOp::StrictEqual,
4148 left: value,
4149 right: undefined,
4150 },
4151 )?;
4152 let skip = self.emit(
4153 range,
4154 Instruction::JumpIfFalse {
4155 condition: is_undefined,
4156 target: Pc::new(0),
4157 },
4158 )?;
4159 let default_value = self.lower_expression(builder, default)?;
4160 self.move_to(range, result, default_value)?;
4161 let end = self.next_pc();
4162 self.patch_jump(skip, end);
4163 Ok(result)
4164 }
4165
4166 fn iterator_step_discard(
4168 &mut self,
4169 range: TextRange,
4170 iterator: Register,
4171 ) -> Result<(), LowerError> {
4172 let done = self.alloc_register(range)?;
4173 let value = self.alloc_register(range)?;
4174 self.emit(
4175 range,
4176 Instruction::IteratorNext {
4177 done,
4178 value,
4179 iterator,
4180 },
4181 )?;
4182 Ok(())
4183 }
4184
4185 fn iterator_step_value(
4188 &mut self,
4189 builder: &mut ModuleBuilder,
4190 range: TextRange,
4191 iterator: Register,
4192 ) -> Result<Register, LowerError> {
4193 let done = self.alloc_register(range)?;
4194 let value = self.alloc_register(range)?;
4195 let result = self.alloc_register(range)?;
4196 let undefined = self.undefined(builder, range)?;
4197 self.move_to(range, result, undefined)?;
4198 self.emit(
4199 range,
4200 Instruction::IteratorNext {
4201 done,
4202 value,
4203 iterator,
4204 },
4205 )?;
4206 let skip = self.emit(
4207 range,
4208 Instruction::JumpIfTrue {
4209 condition: done,
4210 target: Pc::new(0),
4211 },
4212 )?;
4213 self.move_to(range, result, value)?;
4214 let end = self.next_pc();
4215 self.patch_jump(skip, end);
4216 Ok(result)
4217 }
4218
4219 fn rest_array(
4221 &mut self,
4222 _builder: &mut ModuleBuilder,
4223 range: TextRange,
4224 iterator: Register,
4225 ) -> Result<Register, LowerError> {
4226 let array = self.alloc_register(range)?;
4227 self.emit(range, Instruction::CreateArray { dst: array })?;
4228 let done = self.alloc_register(range)?;
4229 let value = self.alloc_register(range)?;
4230 let head = self.next_pc();
4231 self.emit(
4232 range,
4233 Instruction::IteratorNext {
4234 done,
4235 value,
4236 iterator,
4237 },
4238 )?;
4239 let exit = self.emit(
4240 range,
4241 Instruction::JumpIfTrue {
4242 condition: done,
4243 target: Pc::new(0),
4244 },
4245 )?;
4246 self.emit(range, Instruction::ArrayPush { array, value })?;
4247 self.emit(range, Instruction::Jump { target: head })?;
4248 let exit_pc = self.next_pc();
4249 self.patch_jump(exit, exit_pc);
4250 Ok(array)
4251 }
4252
4253 fn rest_object(
4255 &mut self,
4256 _builder: &mut ModuleBuilder,
4257 range: TextRange,
4258 object: Register,
4259 taken: &[Register],
4260 ) -> Result<Register, LowerError> {
4261 let rest = self.alloc_register(range)?;
4262 self.emit(range, Instruction::CreateObject { dst: rest })?;
4263 self.emit(
4264 range,
4265 Instruction::ObjectSpread {
4266 target: rest,
4267 source: object,
4268 },
4269 )?;
4270 for key in taken {
4271 let discarded = self.alloc_register(range)?;
4272 self.emit(
4273 range,
4274 Instruction::DeleteProperty {
4275 dst: discarded,
4276 object: rest,
4277 key: *key,
4278 },
4279 )?;
4280 }
4281 Ok(rest)
4282 }
4283
4284 fn assign_target(
4287 &mut self,
4288 builder: &mut ModuleBuilder,
4289 target: &AssignmentTargetNode,
4290 value: Register,
4291 ) -> Result<(), LowerError> {
4292 let range = target.range();
4293 match target.data() {
4294 AssignmentTarget::Identifier(identifier) => {
4295 let name = self.identifier_text(identifier)?;
4296 self.assign_name(builder, &name, value, range)
4297 }
4298 AssignmentTarget::Member(member) => {
4299 let object = self.lower_expression(builder, &member.object)?;
4300 let key = self.member_key(builder, &member.property)?;
4301 self.emit(range, Instruction::SetProperty { object, key, value })?;
4302 Ok(())
4303 }
4304 AssignmentTarget::Object(object) => {
4305 let mut taken: Vec<Register> = Vec::new();
4306 for property in &object.properties {
4307 self.assign_object_property(builder, range, value, property, &mut taken)?;
4308 }
4309 Ok(())
4310 }
4311 AssignmentTarget::Array(array) => {
4312 let iterator = self.alloc_register(range)?;
4313 self.emit(
4314 range,
4315 Instruction::GetIterator {
4316 dst: iterator,
4317 src: value,
4318 kind: IteratorKind::Sync,
4319 },
4320 )?;
4321 for element in &array.elements {
4322 match element {
4323 AssignmentArrayElement::Elision => {
4324 self.iterator_step_discard(range, iterator)?;
4325 }
4326 AssignmentArrayElement::Target(inner) => {
4327 let element = self.iterator_step_value(builder, range, iterator)?;
4328 self.assign_target(builder, inner, element)?;
4329 }
4330 AssignmentArrayElement::Missing(missing) => {
4331 return Err(self.missing(range, missing.expected()));
4332 }
4333 }
4334 }
4335 Ok(())
4336 }
4337 AssignmentTarget::Missing(missing) => Err(self.missing(range, missing.expected())),
4338 }
4339 }
4340
4341 fn assign_object_property(
4342 &mut self,
4343 builder: &mut ModuleBuilder,
4344 range: TextRange,
4345 source: Register,
4346 property: &AssignmentObjectProperty,
4347 taken: &mut Vec<Register>,
4348 ) -> Result<(), LowerError> {
4349 let key = self.property_key(builder, &property.name)?;
4350 taken.push(key);
4351 let element = self.alloc_register(range)?;
4352 self.emit(
4353 range,
4354 Instruction::GetProperty {
4355 dst: element,
4356 object: source,
4357 key,
4358 },
4359 )?;
4360 let element = match &property.initializer {
4361 Some(default) => self.apply_default(builder, range, element, default)?,
4362 None => element,
4363 };
4364 self.assign_target(builder, &property.target, element)
4365 }
4366
4367 fn lower_arrow(
4372 &mut self,
4373 builder: &mut ModuleBuilder,
4374 range: TextRange,
4375 arrow: &ArrowFunction,
4376 ) -> Result<Register, LowerError> {
4377 let flags = FunctionFlags {
4378 is_async: arrow.is_async,
4379 is_generator: false,
4380 };
4381 let captures =
4382 self.compute_captures(&arrow.parameters, ArrowBody::Arrow(&arrow.body), true);
4383 let id = builder.reserve_function(range)?;
4384 self.build_function_into(
4385 builder,
4386 id,
4387 range,
4388 None,
4389 &arrow.parameters,
4390 ArrowBody::Arrow(&arrow.body),
4391 flags,
4392 &captures,
4393 true,
4394 )?;
4395 self.materialize_closure(builder, range, id, &captures)
4396 }
4397
4398 fn build_constructible_function_value(
4399 &mut self,
4400 builder: &mut ModuleBuilder,
4401 range: TextRange,
4402 name: Option<String>,
4403 function: &FunctionLike,
4404 ) -> Result<Register, LowerError> {
4405 let closure = self.build_function_value(builder, range, name, function)?;
4406 if !function.is_async && !function.is_generator {
4407 let prototype = self.alloc_register(range)?;
4408 self.emit(range, Instruction::CreateObject { dst: prototype })?;
4409 let constructor_key =
4410 self.string_reg(builder, EcmaString::from_utf8("constructor"), range)?;
4411 self.emit(
4412 range,
4413 Instruction::SetProperty {
4414 object: prototype,
4415 key: constructor_key,
4416 value: closure,
4417 },
4418 )?;
4419 let prototype_key =
4420 self.string_reg(builder, EcmaString::from_utf8("prototype"), range)?;
4421 self.emit(
4422 range,
4423 Instruction::SetProperty {
4424 object: closure,
4425 key: prototype_key,
4426 value: prototype,
4427 },
4428 )?;
4429 }
4430 Ok(closure)
4431 }
4432
4433 fn build_function_value(
4435 &mut self,
4436 builder: &mut ModuleBuilder,
4437 range: TextRange,
4438 name: Option<String>,
4439 function: &FunctionLike,
4440 ) -> Result<Register, LowerError> {
4441 if let Some(decorator) = function.decorators.first() {
4442 return Err(self.unsupported(
4443 decorator.range(),
4444 UnsupportedConstruct::DecoratedDeclaration,
4445 ));
4446 }
4447 let flags = FunctionFlags {
4448 is_async: function.is_async,
4449 is_generator: function.is_generator,
4450 };
4451 let body = function
4452 .body
4453 .as_ref()
4454 .ok_or_else(|| self.missing(range, NodeKind::BlockStatement))?;
4455 let captures =
4456 self.compute_captures(&function.parameters, ArrowBody::Function(body), false);
4457 let id = builder.reserve_function(range)?;
4458 self.build_function_into(
4459 builder,
4460 id,
4461 range,
4462 name,
4463 &function.parameters,
4464 ArrowBody::Function(body),
4465 flags,
4466 &captures,
4467 false,
4468 )?;
4469 self.materialize_closure(builder, range, id, &captures)
4470 }
4471
4472 #[allow(clippy::too_many_arguments)]
4474 fn build_function_into(
4475 &mut self,
4476 builder: &mut ModuleBuilder,
4477 id: FunctionId,
4478 range: TextRange,
4479 name: Option<String>,
4480 parameters: &[ParameterNode],
4481 body: ArrowBody<'_>,
4482 flags: FunctionFlags,
4483 captures: &[CaptureKey],
4484 is_arrow: bool,
4485 ) -> Result<(), LowerError> {
4486 let capture_plan = CapturePlan::for_function(self.file, parameters, body);
4487 let mut inner = FunctionContext {
4488 file: self.file,
4489 code: Vec::new(),
4490 registers: 0,
4491 capture_count: 0,
4492 parameter_count: 0,
4493 scopes: vec![HashMap::new()],
4494 predeclared_cells: HashMap::new(),
4495 capture_plan,
4496 loops: Vec::new(),
4497 handlers: Vec::new(),
4498 finally_stack: Vec::new(),
4499 top_level: false,
4500 goal: self.goal,
4501 completion: None,
4502 completion_pool: Vec::new(),
4503 completion_depth: 0,
4504 this_capture: None,
4505 new_target_capture: None,
4506 parent_constructor_capture: None,
4507 arguments_source: if is_arrow {
4508 ArgumentsSource::None
4509 } else {
4510 ArgumentsSource::Own
4511 },
4512 };
4513 for capture in captures {
4515 let register = inner.alloc_register(range)?;
4516 inner.capture_count += 1;
4517 match capture {
4518 CaptureKey::Name(name) => {
4519 inner.declare(
4520 name.clone(),
4521 Binding::Cell(register),
4522 DeclarationScope::Function,
4523 );
4524 }
4525 CaptureKey::This => inner.this_capture = Some(register),
4526 CaptureKey::Arguments => {
4527 inner.arguments_source = ArgumentsSource::Captured(register);
4528 }
4529 CaptureKey::NewTarget => inner.new_target_capture = Some(register),
4530 CaptureKey::Parent(_) => inner.parent_constructor_capture = Some(register),
4531 }
4532 }
4533 inner.bind_parameters(builder, parameters, range)?;
4537 if let ArrowBody::Function(FunctionBody::Block(block))
4538 | ArrowBody::Arrow(FunctionBody::Block(block)) = body
4539 {
4540 inner.hoist_vars(builder, &block.data().statements, range)?;
4541 }
4542 match body {
4543 ArrowBody::Function(FunctionBody::Block(block)) => {
4544 inner.lower_block(builder, block.data())?;
4545 inner.emit_return_undefined(builder, range)?;
4546 }
4547 ArrowBody::Arrow(FunctionBody::Block(block)) => {
4548 inner.lower_block(builder, block.data())?;
4549 inner.emit_return_undefined(builder, range)?;
4550 }
4551 ArrowBody::Arrow(FunctionBody::Expression(expression)) => {
4552 let value = inner.lower_expression(builder, expression)?;
4553 inner.emit(range, Instruction::Return { value })?;
4554 }
4555 ArrowBody::Function(FunctionBody::Expression(_)) => {
4556 return Err(self.missing(range, NodeKind::BlockStatement));
4557 }
4558 ArrowBody::Function(FunctionBody::Missing(missing))
4559 | ArrowBody::Arrow(FunctionBody::Missing(missing)) => {
4560 return Err(self.missing(range, missing.expected()));
4561 }
4562 }
4563 let name_constant = match name {
4564 Some(name) => {
4565 Some(builder.intern(Constant::String(EcmaString::from_utf8(&name)), range)?)
4566 }
4567 None => None,
4568 };
4569 let assembled = inner.into_function(name_constant, flags);
4570 builder.fill_function(id, assembled);
4571 Ok(())
4572 }
4573
4574 fn bind_parameters(
4577 &mut self,
4578 builder: &mut ModuleBuilder,
4579 parameters: &[ParameterNode],
4580 range: TextRange,
4581 ) -> Result<(), LowerError> {
4582 let rest_index = parameters.iter().position(|parameter| {
4584 matches!(parameter.data().binding.data(), BindingPattern::Rest(_))
4585 });
4586 let fixed = rest_index.unwrap_or(parameters.len());
4587 let mut slots = Vec::with_capacity(fixed);
4589 for _ in 0..fixed {
4590 let register = self.alloc_register(range)?;
4591 self.parameter_count += 1;
4592 slots.push(register);
4593 }
4594 let mut undefined_seed = None;
4597 for (index, parameter) in parameters.iter().enumerate() {
4598 let mut names = Vec::new();
4599 collect_pattern_names(self.file, ¶meter.data().binding, &mut names);
4600 for name in names {
4601 if !self.capture_plan.captures(
4602 &name,
4603 binding_site(parameter.range()),
4604 DeclarationScope::Function,
4605 ) || self
4606 .scopes
4607 .first()
4608 .is_some_and(|scope| scope.contains_key(&name))
4609 {
4610 continue;
4611 }
4612 let seed = if index < fixed
4613 && matches!(
4614 parameter.data().binding.data(),
4615 BindingPattern::Identifier(identifier)
4616 if identifier_name(self.file, identifier).as_deref() == Some(&name)
4617 ) {
4618 slots[index]
4619 } else if let Some(seed) = undefined_seed {
4620 seed
4621 } else {
4622 let seed = self.undefined(builder, parameter.range())?;
4623 undefined_seed = Some(seed);
4624 seed
4625 };
4626 let cell = self.alloc_register(parameter.range())?;
4627 self.emit(parameter.range(), Instruction::CreateArray { dst: cell })?;
4628 self.emit(
4629 parameter.range(),
4630 Instruction::ArrayPush {
4631 array: cell,
4632 value: seed,
4633 },
4634 )?;
4635 self.declare(name, Binding::Cell(cell), DeclarationScope::Function);
4636 }
4637 }
4638 for (index, parameter) in parameters.iter().take(fixed).enumerate() {
4639 let data = parameter.data();
4640 let slot = slots[index];
4641 let value = match &data.initializer {
4642 Some(default) => self.apply_default(builder, parameter.range(), slot, default)?,
4643 None => slot,
4644 };
4645 self.bind_pattern(builder, &data.binding, value, DeclarationScope::Function)?;
4646 }
4647 if let Some(rest_index) = rest_index {
4648 let parameter = ¶meters[rest_index];
4649 let rest_argument = match parameter.data().binding.data() {
4650 BindingPattern::Rest(rest) => &rest.argument,
4651 _ => unreachable!("rest_index points at a rest binding"),
4652 };
4653 let rest = self.collect_rest_parameter(builder, range, fixed as u32)?;
4654 self.bind_pattern(builder, rest_argument, rest, DeclarationScope::Function)?;
4655 }
4656 Ok(())
4657 }
4658
4659 fn collect_rest_parameter(
4661 &mut self,
4662 builder: &mut ModuleBuilder,
4663 range: TextRange,
4664 fixed: u32,
4665 ) -> Result<Register, LowerError> {
4666 let arguments = self.alloc_register(range)?;
4673 self.emit(range, Instruction::LoadArguments { dst: arguments })?;
4674 let iterator = self.alloc_register(range)?;
4675 self.emit(
4676 range,
4677 Instruction::GetIterator {
4678 dst: iterator,
4679 src: arguments,
4680 kind: IteratorKind::Sync,
4681 },
4682 )?;
4683 for _ in 0..fixed {
4684 self.iterator_step_discard(range, iterator)?;
4685 }
4686 self.rest_array(builder, range, iterator)
4687 }
4688
4689 fn emit_return_undefined(
4690 &mut self,
4691 builder: &mut ModuleBuilder,
4692 range: TextRange,
4693 ) -> Result<(), LowerError> {
4694 let value = self.undefined(builder, range)?;
4695 self.emit(range, Instruction::Return { value })?;
4696 Ok(())
4697 }
4698
4699 fn materialize_closure(
4702 &mut self,
4703 builder: &mut ModuleBuilder,
4704 range: TextRange,
4705 id: FunctionId,
4706 captures: &[CaptureKey],
4707 ) -> Result<Register, LowerError> {
4708 if captures.len() > MAX_REGISTERS as usize {
4709 return Err(self.error(range, LowerErrorKind::Capacity(CapacityLimit::Captures)));
4710 }
4711 let array = self.alloc_register(range)?;
4712 self.emit(range, Instruction::CreateArray { dst: array })?;
4713 for capture in captures {
4714 let value = self.capture_value(builder, range, capture)?;
4715 self.emit(range, Instruction::ArrayPush { array, value })?;
4716 }
4717 let dst = self.alloc_register(range)?;
4718 self.emit(
4719 range,
4720 Instruction::CreateClosure {
4721 dst,
4722 function: id,
4723 captures: array,
4724 },
4725 )?;
4726 Ok(dst)
4727 }
4728
4729 fn capture_value(
4730 &mut self,
4731 builder: &mut ModuleBuilder,
4732 range: TextRange,
4733 capture: &CaptureKey,
4734 ) -> Result<Register, LowerError> {
4735 match capture {
4736 CaptureKey::Name(name) => match self.resolve(name) {
4737 Some(Binding::Cell(cell)) => Ok(cell),
4738 Some(Binding::Local(_)) => {
4739 panic!("capture plan resolved named capture `{name}` to Local")
4740 }
4741 None => Err(self.error(
4742 range,
4743 LowerErrorKind::MissingSyntax {
4744 expected: NodeKind::Identifier,
4745 },
4746 )),
4747 },
4748 CaptureKey::This => self.this_value(range),
4749 CaptureKey::Arguments => match self.arguments_value(builder, range)? {
4750 Some(register) => Ok(register),
4751 None => self.undefined(builder, range),
4752 },
4753 CaptureKey::NewTarget => self.new_target_value(range),
4754 CaptureKey::Parent(parent) => Ok(*parent),
4755 }
4756 }
4757
4758 fn compute_captures(
4762 &self,
4763 parameters: &[ParameterNode],
4764 body: ArrowBody<'_>,
4765 is_arrow: bool,
4766 ) -> Vec<CaptureKey> {
4767 let mut scanner = FreeVarScanner::new(self.file);
4768 scanner.scan_function(parameters, body, is_arrow);
4769 let mut captures = Vec::new();
4770 for name in &scanner.free {
4771 if self.resolve(name).is_some() {
4772 captures.push(CaptureKey::Name(name.clone()));
4773 }
4774 }
4775 if is_arrow {
4776 if scanner.uses_this {
4777 captures.push(CaptureKey::This);
4778 }
4779 if scanner.uses_arguments {
4780 captures.push(CaptureKey::Arguments);
4781 }
4782 if scanner.uses_new_target {
4783 captures.push(CaptureKey::NewTarget);
4784 }
4785 }
4786 captures
4787 }
4788
4789 fn lower_class_declaration(
4794 &mut self,
4795 builder: &mut ModuleBuilder,
4796 range: TextRange,
4797 class: &ClassDeclaration,
4798 forced_name: Option<&str>,
4799 ) -> Result<(), LowerError> {
4800 let name = match forced_name {
4801 Some(name) => Some(name.to_owned()),
4802 None => match &class.name {
4803 Some(identifier) => Some(self.identifier_text(identifier)?),
4804 None => None,
4805 },
4806 };
4807 self.lower_class_value(builder, range, class, name.as_deref(), None)?;
4808 Ok(())
4809 }
4810
4811 fn lower_class_value(
4814 &mut self,
4815 builder: &mut ModuleBuilder,
4816 range: TextRange,
4817 class: &ClassDeclaration,
4818 declaration_name: Option<&str>,
4819 expression_name: Option<(&str, BindingSite)>,
4820 ) -> Result<Register, LowerError> {
4821 if let Some(decorator) = class.decorators.first() {
4822 return Err(self.unsupported(
4823 decorator.range(),
4824 UnsupportedConstruct::DecoratedDeclaration,
4825 ));
4826 }
4827 let expression_cell = if let Some((name, site)) = expression_name {
4828 self.push_scope();
4829 Some(self.predeclare_class_expression_binding(name, range, site)?)
4830 } else {
4831 None
4832 };
4833 let parent = match &class.extends {
4836 Some(heritage) => Some(self.lower_expression(builder, &heritage.expression)?),
4837 None => None,
4838 };
4839 let declaration_target = if let Some(name) = declaration_name {
4840 let site = class
4841 .name
4842 .as_ref()
4843 .map_or(binding_site(range), |identifier| {
4844 binding_site(identifier.range())
4845 });
4846 self.predeclare_captured_binding(name, range, site, DeclarationScope::Lexical)?;
4847 let identity = binding_identity(name, site, DeclarationScope::Lexical);
4848 if self.top_level {
4849 None
4850 } else if let Some(cell) = self.predeclared_cells.get(&identity).copied() {
4851 Some(Binding::Cell(cell))
4852 } else {
4853 let home = self.alloc_register(range)?;
4854 self.declare(
4855 name.to_owned(),
4856 Binding::Local(home),
4857 DeclarationScope::Lexical,
4858 );
4859 Some(Binding::Local(home))
4860 }
4861 } else {
4862 None
4863 };
4864 if expression_cell.is_none() {
4865 self.push_scope();
4866 }
4867 self.create_private_names(builder, range, class)?;
4868 let constructor = self.find_constructor(class);
4869 let ctor = self.build_constructor(builder, range, class, constructor, parent)?;
4870 let prototype = self.alloc_register(range)?;
4871 self.emit(range, Instruction::CreateObject { dst: prototype })?;
4872 if let Some(parent) = parent {
4873 let parent_prototype = self.get_named(builder, range, parent, "prototype")?;
4874 self.emit(
4875 range,
4876 Instruction::SetPrototype {
4877 object: prototype,
4878 prototype: parent_prototype,
4879 },
4880 )?;
4881 self.emit(
4882 range,
4883 Instruction::SetPrototype {
4884 object: ctor,
4885 prototype: parent,
4886 },
4887 )?;
4888 }
4889 let prototype_key = self.string_reg(builder, EcmaString::from_utf8("prototype"), range)?;
4890 self.emit(
4891 range,
4892 Instruction::SetProperty {
4893 object: ctor,
4894 key: prototype_key,
4895 value: prototype,
4896 },
4897 )?;
4898 if let Some(cell) = expression_cell {
4899 self.store_cell(builder, cell, ctor, range)?;
4900 }
4901 if let Some(name) = declaration_name {
4902 match declaration_target {
4903 Some(Binding::Local(home)) => self.move_to(range, home, ctor)?,
4904 Some(Binding::Cell(cell)) => self.store_cell(builder, cell, ctor, range)?,
4905 None => {
4906 debug_assert!(self.top_level);
4907 let id =
4908 builder.intern(Constant::String(EcmaString::from_utf8(name)), range)?;
4909 self.emit(
4910 range,
4911 Instruction::StoreGlobal {
4912 name: id,
4913 value: ctor,
4914 },
4915 )?;
4916 }
4917 }
4918 }
4919 for member in &class.members {
4920 self.lower_class_member(builder, ctor, prototype, member)?;
4921 }
4922 self.pop_scope();
4923 Ok(ctor)
4924 }
4925
4926 fn find_constructor<'c>(
4927 &self,
4928 class: &'c ClassDeclaration,
4929 ) -> Option<&'c crate::syntax::ConstructorDeclaration> {
4930 class.members.iter().find_map(|member| match member.data() {
4931 ClassMember::Constructor(constructor) => Some(constructor),
4932 _ => None,
4933 })
4934 }
4935
4936 fn create_private_names(
4939 &mut self,
4940 builder: &mut ModuleBuilder,
4941 range: TextRange,
4942 class: &ClassDeclaration,
4943 ) -> Result<(), LowerError> {
4944 let mut seen = HashSet::new();
4945 for member in &class.members {
4946 let name = match member.data() {
4947 ClassMember::Method(method) => &method.name,
4948 ClassMember::Property(property) => &property.name,
4949 ClassMember::AutoAccessor(accessor) => &accessor.name,
4950 _ => continue,
4951 };
4952 if let PropertyName::Private(private) = name {
4953 let text = self.private_text(private)?;
4954 if !seen.insert(text.clone()) {
4955 continue;
4956 }
4957 let description =
4958 builder.intern(Constant::String(EcmaString::from_utf8(&text)), range)?;
4959 let value = self.alloc_register(range)?;
4960 self.emit(
4961 range,
4962 Instruction::CreatePrivateName {
4963 dst: value,
4964 description,
4965 },
4966 )?;
4967 if self.capture_plan.captures(
4968 &text,
4969 binding_site(private.range()),
4970 DeclarationScope::Lexical,
4971 ) {
4972 let cell = self.alloc_register(range)?;
4973 self.emit(range, Instruction::CreateArray { dst: cell })?;
4974 self.emit(range, Instruction::ArrayPush { array: cell, value })?;
4975 self.declare(text, Binding::Cell(cell), DeclarationScope::Lexical);
4976 } else {
4977 self.declare(text, Binding::Local(value), DeclarationScope::Lexical);
4978 }
4979 }
4980 }
4981 Ok(())
4982 }
4983
4984 fn build_constructor(
4985 &mut self,
4986 builder: &mut ModuleBuilder,
4987 range: TextRange,
4988 class: &ClassDeclaration,
4989 constructor: Option<&crate::syntax::ConstructorDeclaration>,
4990 parent: Option<Register>,
4991 ) -> Result<Register, LowerError> {
4992 let fields: Vec<&crate::syntax::ClassProperty> = class
4994 .members
4995 .iter()
4996 .filter_map(|member| match member.data() {
4997 ClassMember::Property(property)
4998 if !property.modifiers.is_static
4999 && !property.modifiers.is_abstract
5000 && !property.modifiers.is_declare =>
5001 {
5002 Some(property)
5003 }
5004 _ => None,
5005 })
5006 .collect();
5007 let (parameters, body_block): (&[ParameterNode], Option<&Block>) = match constructor {
5008 Some(constructor) => (&constructor.parameters, Some(constructor.body.data())),
5009 None => (&[], None),
5010 };
5011 let captures = self.compute_constructor_captures(parameters, body_block, &fields, parent);
5012 let id = builder.reserve_function(range)?;
5013 self.build_constructor_into(
5014 builder,
5015 id,
5016 range,
5017 parameters,
5018 body_block,
5019 &fields,
5020 &captures,
5021 parent.is_some(),
5022 )?;
5023 self.materialize_closure(builder, range, id, &captures)
5024 }
5025
5026 fn compute_constructor_captures(
5027 &self,
5028 parameters: &[ParameterNode],
5029 body: Option<&Block>,
5030 fields: &[&crate::syntax::ClassProperty],
5031 parent: Option<Register>,
5032 ) -> Vec<CaptureKey> {
5033 let mut scanner = FreeVarScanner::new(self.file);
5034 scanner.preseed_parameters(parameters);
5035 if let Some(block) = body {
5036 scanner.preseed_vars(&block.statements);
5037 }
5038 scanner.scan_parameter_initializers(parameters);
5039 if let Some(block) = body {
5040 for statement in &block.statements {
5041 scanner.scan_statement(statement);
5042 }
5043 }
5044 for field in fields {
5045 scanner.scan_property_name(&field.name);
5046 if let Some(initializer) = &field.initializer {
5047 scanner.scan_expression(initializer);
5048 }
5049 }
5050 let mut captures = Vec::new();
5051 for name in &scanner.free {
5052 if self.resolve(name).is_some() {
5053 captures.push(CaptureKey::Name(name.clone()));
5054 }
5055 }
5056 if let Some(parent) = parent {
5057 captures.push(CaptureKey::Parent(parent));
5058 }
5059 captures
5060 }
5061
5062 fn derived_super_index(&self, body: &Block) -> Result<usize, LowerError> {
5063 let mut direct = None;
5064 for (index, statement) in body.statements.iter().enumerate() {
5065 if let Statement::Expression(expression) = statement.data()
5066 && let Expression::Call(call) = expression.expression.data()
5067 && !call.optional
5068 && matches!(call.callee.data(), Expression::Super)
5069 {
5070 if direct.replace(index).is_some() {
5071 return Err(self.unsupported(
5072 statement.range(),
5073 UnsupportedConstruct::DerivedConstructorShape,
5074 ));
5075 }
5076 }
5077 }
5078 let Some(index) = direct else {
5079 return Err(self.unsupported(
5080 body.statements
5081 .first()
5082 .map_or_else(zero_range, |statement| statement.range()),
5083 UnsupportedConstruct::DerivedConstructorShape,
5084 ));
5085 };
5086 let first = body
5087 .statements
5088 .first()
5089 .expect("direct super requires a statement");
5090 let last = body
5091 .statements
5092 .last()
5093 .expect("direct super requires a statement");
5094 let super_count = self
5095 .file
5096 .tokens()
5097 .iter()
5098 .filter(|token| {
5099 token.kind() == TokenKind::KwSuper
5100 && token.range().start() >= first.range().start()
5101 && token.range().end() <= last.range().end()
5102 })
5103 .count();
5104 if super_count != 1 {
5105 return Err(self.unsupported(
5106 body.statements[index].range(),
5107 UnsupportedConstruct::DerivedConstructorShape,
5108 ));
5109 }
5110 let super_statement = body.statements[index].range();
5111 if self.file.tokens().iter().any(|token| {
5112 token.kind() == TokenKind::KwThis
5113 && token.range().start() >= first.range().start()
5114 && token.range().end() <= super_statement.end()
5115 }) {
5116 return Err(self.unsupported(
5117 super_statement,
5118 UnsupportedConstruct::ThisBeforeDerivedSuper,
5119 ));
5120 }
5121 Ok(index)
5122 }
5123
5124 fn initialize_instance_fields(
5125 &mut self,
5126 builder: &mut ModuleBuilder,
5127 range: TextRange,
5128 fields: &[&crate::syntax::ClassProperty],
5129 ) -> Result<(), LowerError> {
5130 for field in fields {
5131 let this_value = self.this_value(range)?;
5132 let key = self.property_key(builder, &field.name)?;
5133 let value = match &field.initializer {
5134 Some(initializer) => self.lower_expression(builder, initializer)?,
5135 None => self.undefined(builder, range)?,
5136 };
5137 self.emit(
5138 range,
5139 Instruction::SetProperty {
5140 object: this_value,
5141 key,
5142 value,
5143 },
5144 )?;
5145 }
5146 Ok(())
5147 }
5148
5149 fn lower_derived_super(
5150 &mut self,
5151 builder: &mut ModuleBuilder,
5152 range: TextRange,
5153 call: &CallExpression,
5154 ) -> Result<(), LowerError> {
5155 let parent = self.parent_constructor_capture.ok_or_else(|| {
5156 self.unsupported(range, UnsupportedConstruct::DerivedConstructorShape)
5157 })?;
5158 let this_value = self.this_value(range)?;
5159 let arguments = self.build_arguments(builder, range, &call.arguments)?;
5160 let dst = self.alloc_register(range)?;
5161 self.emit(
5162 range,
5163 Instruction::Call {
5164 dst,
5165 callee: parent,
5166 this_value,
5167 arguments,
5168 },
5169 )?;
5170 Ok(())
5171 }
5172
5173 fn lower_implicit_derived_super(
5174 &mut self,
5175 builder: &mut ModuleBuilder,
5176 range: TextRange,
5177 ) -> Result<(), LowerError> {
5178 let parent = self.parent_constructor_capture.ok_or_else(|| {
5179 self.unsupported(range, UnsupportedConstruct::DerivedConstructorShape)
5180 })?;
5181 let this_value = self.this_value(range)?;
5182 let arguments = self.arguments_value(builder, range)?.ok_or_else(|| {
5183 self.unsupported(range, UnsupportedConstruct::DerivedConstructorShape)
5184 })?;
5185 let call_arguments = self.alloc_register(range)?;
5186 self.emit(
5187 range,
5188 Instruction::CreateArray {
5189 dst: call_arguments,
5190 },
5191 )?;
5192 self.emit(
5193 range,
5194 Instruction::ArrayExtend {
5195 array: call_arguments,
5196 iterable: arguments,
5197 },
5198 )?;
5199 let dst = self.alloc_register(range)?;
5200 self.emit(
5201 range,
5202 Instruction::Call {
5203 dst,
5204 callee: parent,
5205 this_value,
5206 arguments: call_arguments,
5207 },
5208 )?;
5209 Ok(())
5210 }
5211
5212 #[allow(clippy::too_many_arguments)]
5213 fn build_constructor_into(
5214 &mut self,
5215 builder: &mut ModuleBuilder,
5216 id: FunctionId,
5217 range: TextRange,
5218 parameters: &[ParameterNode],
5219 body: Option<&Block>,
5220 fields: &[&crate::syntax::ClassProperty],
5221 captures: &[CaptureKey],
5222 derived: bool,
5223 ) -> Result<(), LowerError> {
5224 let capture_plan = CapturePlan::for_constructor(self.file, parameters, body, fields);
5225 let mut inner = FunctionContext {
5226 file: self.file,
5227 code: Vec::new(),
5228 registers: 0,
5229 capture_count: 0,
5230 parameter_count: 0,
5231 scopes: vec![HashMap::new()],
5232 predeclared_cells: HashMap::new(),
5233 capture_plan,
5234 loops: Vec::new(),
5235 handlers: Vec::new(),
5236 finally_stack: Vec::new(),
5237 top_level: false,
5238 goal: self.goal,
5239 completion: None,
5240 completion_pool: Vec::new(),
5241 completion_depth: 0,
5242 this_capture: None,
5243 new_target_capture: None,
5244 parent_constructor_capture: None,
5245 arguments_source: ArgumentsSource::Own,
5246 };
5247 for capture in captures {
5248 let register = inner.alloc_register(range)?;
5249 inner.capture_count += 1;
5250 match capture {
5251 CaptureKey::Name(name) => {
5252 inner.declare(
5253 name.clone(),
5254 Binding::Cell(register),
5255 DeclarationScope::Function,
5256 );
5257 }
5258 CaptureKey::Parent(_) => inner.parent_constructor_capture = Some(register),
5259 CaptureKey::This | CaptureKey::Arguments | CaptureKey::NewTarget => {
5260 unreachable!("constructors do not capture arrow-only bindings")
5261 }
5262 }
5263 }
5264 inner.bind_parameters(builder, parameters, range)?;
5265 if let Some(block) = body {
5266 inner.hoist_vars(builder, &block.statements, range)?;
5267 }
5268 if derived {
5269 if let Some(block) = body {
5270 let super_index = inner.derived_super_index(block)?;
5271 inner.push_scope();
5272 for statement in &block.statements[..super_index] {
5273 inner.lower_statement(builder, statement)?;
5274 }
5275 let Statement::Expression(expression) = block.statements[super_index].data() else {
5276 unreachable!("derived_super_index selects an expression statement");
5277 };
5278 let Expression::Call(call) = expression.expression.data() else {
5279 unreachable!("derived_super_index selects a call expression");
5280 };
5281 inner.lower_derived_super(builder, block.statements[super_index].range(), call)?;
5282 let body_scope = inner.scopes.pop().expect("constructor block scope exists");
5283 inner.initialize_instance_fields(builder, range, fields)?;
5284 inner.scopes.push(body_scope);
5285 for statement in &block.statements[super_index + 1..] {
5286 inner.lower_statement(builder, statement)?;
5287 }
5288 inner.pop_scope();
5289 } else {
5290 inner.lower_implicit_derived_super(builder, range)?;
5291 inner.initialize_instance_fields(builder, range, fields)?;
5292 }
5293 } else {
5294 inner.initialize_instance_fields(builder, range, fields)?;
5295 if let Some(block) = body {
5296 inner.lower_block(builder, block)?;
5297 }
5298 }
5299 inner.emit_return_undefined(builder, range)?;
5300 let assembled = inner.into_function(None, FunctionFlags::default());
5301 builder.fill_function(id, assembled);
5302 Ok(())
5303 }
5304
5305 fn lower_class_member(
5306 &mut self,
5307 builder: &mut ModuleBuilder,
5308 ctor: Register,
5309 prototype: Register,
5310 member: &crate::syntax::ClassMemberNode,
5311 ) -> Result<(), LowerError> {
5312 let range = member.range();
5313 match member.data() {
5314 ClassMember::Constructor(_) => Ok(()),
5315 ClassMember::Method(method) => {
5316 if method.function.body.is_none() {
5317 return Ok(());
5319 }
5320 let target = if method.modifiers.is_static {
5321 ctor
5322 } else {
5323 prototype
5324 };
5325 let key = self.property_key(builder, &method.name)?;
5326 let value = self.build_function_value(builder, range, None, &method.function)?;
5327 self.install_property(builder, range, target, key, value, method.modifier)
5328 }
5329 ClassMember::Property(property) => {
5330 if property.modifiers.is_abstract || property.modifiers.is_declare {
5331 return Ok(());
5333 }
5334 if property.modifiers.is_static {
5335 let key = self.property_key(builder, &property.name)?;
5337 let value = match &property.initializer {
5338 Some(initializer) => self.lower_expression(builder, initializer)?,
5339 None => self.undefined(builder, range)?,
5340 };
5341 self.emit(
5342 range,
5343 Instruction::SetProperty {
5344 object: ctor,
5345 key,
5346 value,
5347 },
5348 )?;
5349 }
5350 Ok(())
5352 }
5353 ClassMember::AutoAccessor(accessor) => {
5354 if accessor.modifiers.is_abstract || accessor.modifiers.is_declare {
5355 return Ok(());
5356 }
5357 let target = if accessor.modifiers.is_static {
5360 ctor
5361 } else {
5362 prototype
5363 };
5364 let key = self.property_key(builder, &accessor.name)?;
5365 let value = match &accessor.initializer {
5366 Some(initializer) => self.lower_expression(builder, initializer)?,
5367 None => self.undefined(builder, range)?,
5368 };
5369 self.emit(
5370 range,
5371 Instruction::SetProperty {
5372 object: target,
5373 key,
5374 value,
5375 },
5376 )?;
5377 Ok(())
5378 }
5379 ClassMember::StaticBlock(block) => {
5380 self.push_scope();
5383 let result = self.lower_block(builder, block.data());
5384 self.pop_scope();
5385 result
5386 }
5387 ClassMember::IndexSignature(_) => Ok(()),
5388 ClassMember::Missing(missing) => Err(self.missing(range, missing.expected())),
5389 }
5390 }
5391}
5392
5393#[derive(Clone, Copy)]
5395enum ArrowBody<'a> {
5396 Function(&'a FunctionBody),
5397 Arrow(&'a FunctionBody),
5398}
5399
5400#[derive(Clone)]
5401struct ScannedBinding {
5402 identity: BindingIdentity,
5403 owner_depth: u32,
5404}
5405
5406struct FreeVarScanner<'a> {
5409 file: &'a SourceFile,
5410 bound: Vec<HashMap<String, ScannedBinding>>,
5411 function_roots: Vec<usize>,
5412 free: BTreeSet<String>,
5413 captured: HashSet<BindingIdentity>,
5414 runtime_cells: HashSet<BindingIdentity>,
5415 initialized: HashSet<BindingIdentity>,
5416 uses_this: bool,
5417 uses_arguments: bool,
5418 uses_new_target: bool,
5419 fn_boundary: u32,
5420 function_depth: u32,
5421}
5422
5423impl CapturePlan {
5424 fn for_statements(file: &SourceFile, statements: &[Stmt]) -> Self {
5425 let mut scanner = FreeVarScanner::new(file);
5426 scanner.preseed_vars(statements);
5427 scanner.predeclare_immediate(statements, false);
5428 for statement in statements {
5429 scanner.scan_statement(statement);
5430 }
5431 Self {
5432 captured: scanner.captured,
5433 runtime_cells: scanner.runtime_cells,
5434 }
5435 }
5436
5437 fn for_function(file: &SourceFile, parameters: &[ParameterNode], body: ArrowBody<'_>) -> Self {
5438 let mut scanner = FreeVarScanner::new(file);
5439 scanner.scan_function(parameters, body, false);
5440 Self {
5441 captured: scanner.captured,
5442 runtime_cells: scanner.runtime_cells,
5443 }
5444 }
5445
5446 fn for_constructor(
5447 file: &SourceFile,
5448 parameters: &[ParameterNode],
5449 body: Option<&Block>,
5450 fields: &[&crate::syntax::ClassProperty],
5451 ) -> Self {
5452 let mut scanner = FreeVarScanner::new(file);
5453 scanner.preseed_parameters(parameters);
5454 if let Some(block) = body {
5455 scanner.preseed_vars(&block.statements);
5456 scanner.predeclare_immediate(&block.statements, false);
5457 }
5458 scanner.scan_parameter_initializers(parameters);
5459 if let Some(block) = body {
5460 for statement in &block.statements {
5461 scanner.scan_statement(statement);
5462 }
5463 }
5464 for field in fields {
5465 scanner.scan_property_name(&field.name);
5466 if let Some(initializer) = &field.initializer {
5467 scanner.scan_expression(initializer);
5468 }
5469 }
5470 Self {
5471 captured: scanner.captured,
5472 runtime_cells: scanner.runtime_cells,
5473 }
5474 }
5475}
5476
5477impl<'a> FreeVarScanner<'a> {
5478 fn new(file: &'a SourceFile) -> Self {
5479 Self {
5480 file,
5481 bound: vec![HashMap::new()],
5482 function_roots: vec![0],
5483 free: BTreeSet::new(),
5484 captured: HashSet::new(),
5485 runtime_cells: HashSet::new(),
5486 initialized: HashSet::new(),
5487 uses_this: false,
5488 uses_arguments: false,
5489 uses_new_target: false,
5490 fn_boundary: 0,
5491 function_depth: 0,
5492 }
5493 }
5494
5495 fn scan_function(
5496 &mut self,
5497 parameters: &[ParameterNode],
5498 body: ArrowBody<'_>,
5499 _is_arrow: bool,
5500 ) {
5501 self.preseed_parameters(parameters);
5502 if let ArrowBody::Function(FunctionBody::Block(block))
5503 | ArrowBody::Arrow(FunctionBody::Block(block)) = body
5504 {
5505 self.preseed_vars(&block.data().statements);
5506 self.predeclare_immediate(&block.data().statements, false);
5507 }
5508 self.scan_parameter_initializers(parameters);
5509 match body {
5510 ArrowBody::Function(FunctionBody::Block(block))
5511 | ArrowBody::Arrow(FunctionBody::Block(block)) => {
5512 for statement in &block.data().statements {
5513 self.scan_statement(statement);
5514 }
5515 }
5516 ArrowBody::Arrow(FunctionBody::Expression(expression))
5517 | ArrowBody::Function(FunctionBody::Expression(expression)) => {
5518 self.scan_expression(expression);
5519 }
5520 _ => {}
5521 }
5522 }
5523
5524 fn push(&mut self) {
5525 self.bound.push(HashMap::new());
5526 }
5527
5528 fn pop(&mut self) {
5529 self.bound.pop();
5530 }
5531
5532 fn bind_function(&mut self, name: String) {
5533 let root = *self
5534 .function_roots
5535 .last()
5536 .expect("scanner always has a function root");
5537 self.bound[root]
5538 .entry(name.clone())
5539 .or_insert(ScannedBinding {
5540 identity: BindingIdentity::Function(name),
5541 owner_depth: self.function_depth,
5542 });
5543 }
5544
5545 fn bind_lexical(&mut self, name: String, range: TextRange) {
5546 if let Some(scope) = self.bound.last_mut() {
5547 scope.insert(
5548 name,
5549 ScannedBinding {
5550 identity: BindingIdentity::Lexical(binding_site(range)),
5551 owner_depth: self.function_depth,
5552 },
5553 );
5554 }
5555 }
5556
5557 fn resolve_binding(&self, name: &str) -> Option<&ScannedBinding> {
5558 self.bound.iter().rev().find_map(|scope| scope.get(name))
5559 }
5560
5561 fn scan_property_name(&mut self, name: &PropertyName) {
5562 match name {
5563 PropertyName::Computed(expression) => self.scan_expression(expression),
5564 PropertyName::Private(private) => {
5565 if let Some(text) = private_name(self.file, private) {
5566 self.use_name(&text);
5567 }
5568 }
5569 _ => {}
5570 }
5571 }
5572
5573 fn use_name(&mut self, name: &str) {
5574 if name == "arguments" {
5575 if self.fn_boundary == 0 {
5576 self.uses_arguments = true;
5577 }
5578 return;
5579 }
5580 if let Some(binding) = self.resolve_binding(name).cloned() {
5581 if binding.owner_depth == 0 && !self.initialized.contains(&binding.identity) {
5582 self.runtime_cells.insert(binding.identity.clone());
5583 }
5584 if self.function_depth > binding.owner_depth && binding.owner_depth == 0 {
5585 self.captured.insert(binding.identity);
5586 }
5587 } else {
5588 self.free.insert(name.to_owned());
5589 }
5590 }
5591
5592 fn predeclare_immediate(&mut self, statements: &[Stmt], switch_scope: bool) {
5593 for declaration in collect_immediate_declarations(self.file, statements) {
5594 match declaration.kind {
5595 ImmediateDeclarationKind::Function(_) => {
5596 let identity = BindingIdentity::Function(declaration.name.clone());
5597 self.bind_function(declaration.name);
5598 self.initialized.insert(identity);
5599 }
5600 ImmediateDeclarationKind::Lexical => {
5601 let identity = BindingIdentity::Lexical(declaration.site);
5602 self.bind_lexical(declaration.name, declaration.range);
5603 if switch_scope {
5604 self.runtime_cells.insert(identity);
5605 }
5606 }
5607 }
5608 }
5609 }
5610
5611 fn initialize_pattern(&mut self, pattern: &Pattern, declaration_scope: DeclarationScope) {
5612 let mut names = Vec::new();
5613 collect_pattern_names(self.file, pattern, &mut names);
5614 for name in names {
5615 if let Some(binding) = self.resolve_binding(&name)
5616 && (matches!(declaration_scope, DeclarationScope::Function)
5617 || binding.owner_depth == self.function_depth)
5618 {
5619 self.initialized.insert(binding.identity.clone());
5620 }
5621 }
5622 }
5623
5624 fn preseed_parameters(&mut self, parameters: &[ParameterNode]) {
5625 for parameter in parameters {
5626 let mut names = Vec::new();
5627 collect_pattern_names(self.file, ¶meter.data().binding, &mut names);
5628 for name in names {
5629 self.bind_function(name.clone());
5630 self.initialized.insert(BindingIdentity::Function(name));
5631 }
5632 }
5633 }
5634
5635 fn preseed_vars(&mut self, statements: &[Stmt]) {
5636 let mut names = Vec::new();
5637 collect_var_names(self.file, statements, &mut names);
5638 for name in names {
5639 self.bind_function(name.clone());
5640 self.initialized.insert(BindingIdentity::Function(name));
5641 }
5642 }
5643
5644 fn scan_parameter_initializers(&mut self, parameters: &[ParameterNode]) {
5645 for parameter in parameters {
5646 let data = parameter.data();
5647 if let Some(initializer) = &data.initializer {
5648 self.scan_expression(initializer);
5649 }
5650 self.scan_pattern_effects(&data.binding);
5651 }
5652 }
5653
5654 fn scan_pattern_effects(&mut self, pattern: &Pattern) {
5655 match pattern.data() {
5656 BindingPattern::Identifier(_) | BindingPattern::Missing(_) => {}
5657 BindingPattern::Object(object) => {
5658 for property in &object.properties {
5659 if let PropertyName::Computed(expression) = &property.name {
5660 self.scan_expression(expression);
5661 }
5662 if let Some(initializer) = &property.initializer {
5663 self.scan_expression(initializer);
5664 }
5665 self.scan_pattern_effects(&property.binding);
5666 }
5667 }
5668 BindingPattern::Array(array) => {
5669 for element in &array.elements {
5670 if let ArrayBindingElement::Binding(inner) = element {
5671 self.scan_pattern_effects(inner);
5672 }
5673 }
5674 }
5675 BindingPattern::Rest(rest) => self.scan_pattern_effects(&rest.argument),
5676 BindingPattern::Assignment(assignment) => {
5677 self.scan_expression(&assignment.right);
5678 self.scan_pattern_effects(&assignment.left);
5679 }
5680 }
5681 }
5682
5683 fn bind_pattern(&mut self, pattern: &Pattern, declaration_scope: DeclarationScope) {
5684 match pattern.data() {
5685 BindingPattern::Identifier(identifier) => {
5686 if let Some(text) = identifier_name(self.file, identifier) {
5687 match declaration_scope {
5688 DeclarationScope::Function => self.bind_function(text),
5689 DeclarationScope::Lexical | DeclarationScope::Iteration => {
5690 self.bind_lexical(text, identifier.range());
5691 }
5692 }
5693 }
5694 }
5695 BindingPattern::Object(object) => {
5696 for property in &object.properties {
5697 self.bind_pattern(&property.binding, declaration_scope);
5698 }
5699 }
5700 BindingPattern::Array(array) => {
5701 for element in &array.elements {
5702 if let ArrayBindingElement::Binding(inner) = element {
5703 self.bind_pattern(inner, declaration_scope);
5704 }
5705 }
5706 }
5707 BindingPattern::Rest(rest) => self.bind_pattern(&rest.argument, declaration_scope),
5708 BindingPattern::Assignment(assignment) => {
5709 self.bind_pattern(&assignment.left, declaration_scope);
5710 }
5711 BindingPattern::Missing(_) => {}
5712 }
5713 }
5714
5715 fn scan_statement(&mut self, statement: &Stmt) {
5716 match statement.data() {
5717 Statement::Variable(declaration) => {
5718 let scope = match declaration.kind {
5719 VariableKind::Var => DeclarationScope::Function,
5720 _ => DeclarationScope::Lexical,
5721 };
5722 for declarator in &declaration.declarations {
5723 if let Some(initializer) = &declarator.data().initializer {
5724 self.scan_expression(initializer);
5725 }
5726 self.scan_pattern_effects(&declarator.data().binding);
5727 self.initialize_pattern(&declarator.data().binding, scope);
5728 }
5729 }
5730 Statement::Function(declaration) => {
5731 self.scan_function_like(&declaration.function);
5732 }
5733 Statement::Class(class) => {
5734 self.scan_class_heritage(class);
5735 if let Some(name) = &class.name
5736 && let Some(text) = identifier_name(self.file, name)
5737 && let Some(binding) = self.resolve_binding(&text)
5738 {
5739 self.initialized.insert(binding.identity.clone());
5740 }
5741 self.scan_class(class);
5742 }
5743 Statement::Expression(expression) => self.scan_expression(&expression.expression),
5744 Statement::Return(statement) => {
5745 if let Some(argument) = &statement.argument {
5746 self.scan_expression(argument);
5747 }
5748 }
5749 Statement::Throw(statement) => self.scan_expression(&statement.argument),
5750 Statement::If(statement) => {
5751 self.scan_expression(&statement.test);
5752 self.scan_statement(&statement.consequent);
5753 if let Some(alternate) = &statement.alternate {
5754 self.scan_statement(alternate);
5755 }
5756 }
5757 Statement::Block(block) => {
5758 self.push();
5759 self.predeclare_immediate(&block.data().statements, false);
5760 for statement in &block.data().statements {
5761 self.scan_statement(statement);
5762 }
5763 self.pop();
5764 }
5765 Statement::While(statement) => {
5766 self.scan_expression(&statement.test);
5767 self.scan_statement(&statement.body);
5768 }
5769 Statement::DoWhile(statement) => {
5770 self.scan_statement(&statement.body);
5771 self.scan_expression(&statement.test);
5772 }
5773 Statement::For(statement) => {
5774 self.push();
5775 if let Some(initializer) = &statement.initializer {
5776 match initializer {
5777 ForInitializer::Variable(declaration) => {
5778 let scope = match declaration.kind {
5779 VariableKind::Var => DeclarationScope::Function,
5780 _ => DeclarationScope::Lexical,
5781 };
5782 for declarator in &declaration.declarations {
5783 self.bind_pattern(&declarator.data().binding, scope);
5784 if let Some(init) = &declarator.data().initializer {
5785 self.scan_expression(init);
5786 }
5787 self.scan_pattern_effects(&declarator.data().binding);
5788 self.initialize_pattern(&declarator.data().binding, scope);
5789 }
5790 }
5791 ForInitializer::Expression(expression) => self.scan_expression(expression),
5792 }
5793 }
5794 if let Some(test) = &statement.test {
5795 self.scan_expression(test);
5796 }
5797 if let Some(update) = &statement.update {
5798 self.scan_expression(update);
5799 }
5800 self.scan_statement(&statement.body);
5801 self.pop();
5802 }
5803 Statement::ForIn(statement) => {
5804 self.push();
5805 self.scan_expression(&statement.object);
5806 self.scan_for_binding(&statement.binding);
5807 self.scan_statement(&statement.body);
5808 self.pop();
5809 }
5810 Statement::ForOf(statement) => {
5811 self.push();
5812 self.scan_expression(&statement.iterable);
5813 self.scan_for_binding(&statement.binding);
5814 self.scan_statement(&statement.body);
5815 self.pop();
5816 }
5817 Statement::Switch(statement) => {
5818 self.scan_expression(&statement.discriminant);
5819 self.push();
5820 let statements = statement
5821 .cases
5822 .iter()
5823 .flat_map(|case| case.data().consequent.iter().cloned())
5824 .collect::<Vec<_>>();
5825 self.predeclare_immediate(&statements, true);
5826 for case in &statement.cases {
5827 if let Some(test) = &case.data().test {
5828 self.scan_expression(test);
5829 }
5830 for statement in &case.data().consequent {
5831 self.scan_statement(statement);
5832 }
5833 }
5834 self.pop();
5835 }
5836 Statement::Try(statement) => {
5837 self.push();
5838 self.predeclare_immediate(&statement.block.data().statements, false);
5839 for statement in &statement.block.data().statements {
5840 self.scan_statement(statement);
5841 }
5842 self.pop();
5843 if let Some(handler) = &statement.handler {
5844 self.push();
5845 if let Some(binding) = &handler.data().binding {
5846 self.bind_pattern(binding, DeclarationScope::Lexical);
5847 self.initialize_pattern(binding, DeclarationScope::Lexical);
5848 }
5849 self.predeclare_immediate(&handler.data().body.data().statements, false);
5850 for statement in &handler.data().body.data().statements {
5851 self.scan_statement(statement);
5852 }
5853 self.pop();
5854 }
5855 if let Some(finalizer) = &statement.finalizer {
5856 self.push();
5857 self.predeclare_immediate(&finalizer.data().statements, false);
5858 for statement in &finalizer.data().statements {
5859 self.scan_statement(statement);
5860 }
5861 self.pop();
5862 }
5863 }
5864 Statement::Labeled(statement) => self.scan_statement(&statement.body),
5865 Statement::Export(ExportDeclaration::Named(ExportNamedDeclaration::Declaration(
5866 statement,
5867 ))) => self.scan_statement(statement),
5868 Statement::Export(ExportDeclaration::Default(default)) => match &default.value {
5869 ExportDefaultValue::Expression(expression) => self.scan_expression(expression),
5870 ExportDefaultValue::Function(function) => {
5871 if function.body.is_some()
5872 && let Some(name) = &function.name
5873 && let Some(text) = identifier_name(self.file, name)
5874 {
5875 self.bind_function(text);
5876 }
5877 self.scan_function_like(function);
5878 }
5879 ExportDefaultValue::Class(class) => {
5880 self.scan_class_heritage(class);
5881 if let Some(name) = &class.name
5882 && let Some(text) = identifier_name(self.file, name)
5883 {
5884 self.bind_lexical(text, name.range());
5885 }
5886 self.scan_class(class);
5887 }
5888 ExportDefaultValue::Missing(_) => {}
5889 },
5890 _ => {}
5891 }
5892 }
5893
5894 fn scan_for_binding(&mut self, binding: &ForBinding) {
5895 match binding {
5896 ForBinding::Variable(declaration) => {
5897 let scope = match declaration.kind {
5898 VariableKind::Var => DeclarationScope::Function,
5899 _ => DeclarationScope::Iteration,
5900 };
5901 for declarator in &declaration.declarations {
5902 self.bind_pattern(&declarator.data().binding, scope);
5903 self.scan_pattern_effects(&declarator.data().binding);
5904 }
5905 }
5906 ForBinding::Target(target) => self.scan_assignment_target(target),
5907 }
5908 }
5909
5910 fn scan_function_like(&mut self, function: &FunctionLike) {
5911 self.fn_boundary += 1;
5912 self.function_depth += 1;
5913 self.push();
5914 self.function_roots.push(self.bound.len() - 1);
5915 self.preseed_parameters(&function.parameters);
5916 if let Some(FunctionBody::Block(block)) = &function.body {
5917 self.preseed_vars(&block.data().statements);
5918 self.predeclare_immediate(&block.data().statements, false);
5919 }
5920 self.scan_parameter_initializers(&function.parameters);
5921 if let Some(body) = &function.body {
5922 match body {
5923 FunctionBody::Block(block) => {
5924 for statement in &block.data().statements {
5925 self.scan_statement(statement);
5926 }
5927 }
5928 FunctionBody::Expression(expression) => self.scan_expression(expression),
5929 FunctionBody::Missing(_) => {}
5930 }
5931 }
5932 self.function_roots.pop();
5933 self.pop();
5934 self.function_depth -= 1;
5935 self.fn_boundary -= 1;
5936 }
5937
5938 fn scan_arrow(&mut self, arrow: &ArrowFunction) {
5939 self.function_depth += 1;
5940 self.push();
5941 self.function_roots.push(self.bound.len() - 1);
5942 self.preseed_parameters(&arrow.parameters);
5943 if let FunctionBody::Block(block) = &arrow.body {
5944 self.preseed_vars(&block.data().statements);
5945 self.predeclare_immediate(&block.data().statements, false);
5946 }
5947 self.scan_parameter_initializers(&arrow.parameters);
5948 match &arrow.body {
5949 FunctionBody::Block(block) => {
5950 for statement in &block.data().statements {
5951 self.scan_statement(statement);
5952 }
5953 }
5954 FunctionBody::Expression(expression) => self.scan_expression(expression),
5955 FunctionBody::Missing(_) => {}
5956 }
5957 self.function_roots.pop();
5958 self.pop();
5959 self.function_depth -= 1;
5960 }
5961
5962 fn scan_class_heritage(&mut self, class: &ClassDeclaration) {
5963 if let Some(heritage) = &class.extends {
5964 self.scan_expression(&heritage.expression);
5965 }
5966 }
5967
5968 fn scan_class(&mut self, class: &ClassDeclaration) {
5969 self.push();
5970 let mut seen_private = HashSet::new();
5971 for member in &class.members {
5972 let name = match member.data() {
5973 ClassMember::Method(method) => &method.name,
5974 ClassMember::Property(property) => &property.name,
5975 ClassMember::AutoAccessor(accessor) => &accessor.name,
5976 _ => continue,
5977 };
5978 if let PropertyName::Private(private) = name
5979 && let Some(text) = private_name(self.file, private)
5980 && seen_private.insert(text.clone())
5981 {
5982 self.bind_lexical(text, private.range());
5983 }
5984 }
5985 let constructor = class.members.iter().find_map(|member| match member.data() {
5986 ClassMember::Constructor(constructor) => Some(constructor),
5987 _ => None,
5988 });
5989 let parameters = constructor
5990 .map(|constructor| constructor.parameters.as_slice())
5991 .unwrap_or(&[]);
5992 self.fn_boundary += 1;
5993 self.function_depth += 1;
5994 self.push();
5995 self.function_roots.push(self.bound.len() - 1);
5996 self.preseed_parameters(parameters);
5997 if let Some(constructor) = constructor {
5998 self.preseed_vars(&constructor.body.data().statements);
5999 self.predeclare_immediate(&constructor.body.data().statements, false);
6000 }
6001 self.scan_parameter_initializers(parameters);
6002 if let Some(constructor) = constructor {
6003 for statement in &constructor.body.data().statements {
6004 self.scan_statement(statement);
6005 }
6006 }
6007 for member in &class.members {
6008 if let ClassMember::Property(property) = member.data()
6009 && !property.modifiers.is_static
6010 && !property.modifiers.is_abstract
6011 && !property.modifiers.is_declare
6012 {
6013 self.scan_property_name(&property.name);
6014 if let Some(initializer) = &property.initializer {
6015 self.scan_expression(initializer);
6016 }
6017 }
6018 }
6019 self.function_roots.pop();
6020 self.pop();
6021 self.function_depth -= 1;
6022 self.fn_boundary -= 1;
6023
6024 for member in &class.members {
6025 match member.data() {
6026 ClassMember::Constructor(_) => {}
6027 ClassMember::Method(method) => {
6028 if let PropertyName::Computed(expression) = &method.name {
6029 self.scan_expression(expression);
6030 }
6031 self.scan_function_like(&method.function);
6032 }
6033 ClassMember::Property(property) if property.modifiers.is_static => {
6034 self.scan_property_name(&property.name);
6035 if let Some(initializer) = &property.initializer {
6036 self.scan_expression(initializer);
6037 }
6038 }
6039 ClassMember::AutoAccessor(accessor) => {
6040 self.scan_property_name(&accessor.name);
6041 if let Some(initializer) = &accessor.initializer {
6042 self.scan_expression(initializer);
6043 }
6044 }
6045 ClassMember::StaticBlock(block) => {
6046 self.push();
6047 self.predeclare_immediate(&block.data().statements, false);
6048 for statement in &block.data().statements {
6049 self.scan_statement(statement);
6050 }
6051 self.pop();
6052 }
6053 _ => {}
6054 }
6055 }
6056 self.pop();
6057 }
6058
6059 fn scan_expression(&mut self, expression: &Expr) {
6060 match expression.data() {
6061 Expression::Identifier(identifier) => {
6062 if let Some(name) = identifier_name(self.file, identifier) {
6063 self.use_name(&name);
6064 }
6065 }
6066 Expression::This => {
6067 if self.fn_boundary == 0 {
6068 self.uses_this = true;
6069 }
6070 }
6071 Expression::Super => {}
6072 Expression::Meta(MetaProperty::NewTarget) => {
6073 if self.fn_boundary == 0 {
6074 self.uses_new_target = true;
6075 }
6076 }
6077 Expression::Meta(MetaProperty::ImportMeta) => {}
6078 Expression::Literal(_) => {}
6079 Expression::Template(template) => {
6080 for expression in &template.expressions {
6081 self.scan_expression(expression);
6082 }
6083 }
6084 Expression::TaggedTemplate(tagged) => {
6085 self.scan_expression(&tagged.tag);
6086 for expression in &tagged.template.expressions {
6087 self.scan_expression(expression);
6088 }
6089 }
6090 Expression::Array(array) => {
6091 for element in &array.elements {
6092 match element {
6093 ArrayElement::Expression(expression) => self.scan_expression(expression),
6094 ArrayElement::Spread(spread) => self.scan_expression(&spread.argument),
6095 _ => {}
6096 }
6097 }
6098 }
6099 Expression::Object(object) => {
6100 for member in &object.members {
6101 match member.data() {
6102 ObjectMember::Property(property) => {
6103 if let PropertyName::Computed(key) = &property.name {
6104 self.scan_expression(key);
6105 }
6106 self.scan_expression(&property.value);
6107 }
6108 ObjectMember::Method(method) => {
6109 if let PropertyName::Computed(key) = &method.name {
6110 self.scan_expression(key);
6111 }
6112 self.scan_function_like(&method.function);
6113 }
6114 ObjectMember::Spread(spread) => self.scan_expression(&spread.argument),
6115 ObjectMember::Missing(_) => {}
6116 }
6117 }
6118 }
6119 Expression::Function(function) => self.scan_function_like(&function.function),
6120 Expression::Class(class) => {
6121 self.scan_class_heritage(&class.class);
6122 self.scan_class(&class.class);
6123 }
6124 Expression::Arrow(arrow) => self.scan_arrow(arrow),
6125 Expression::Call(call) => {
6126 self.scan_expression(&call.callee);
6127 for argument in &call.arguments {
6128 match argument {
6129 CallArgument::Expression(expression) => self.scan_expression(expression),
6130 CallArgument::Spread(spread) => self.scan_expression(&spread.argument),
6131 CallArgument::Missing(_) => {}
6132 }
6133 }
6134 }
6135 Expression::New(new) => {
6136 self.scan_expression(&new.callee);
6137 for argument in &new.arguments {
6138 match argument {
6139 CallArgument::Expression(expression) => self.scan_expression(expression),
6140 CallArgument::Spread(spread) => self.scan_expression(&spread.argument),
6141 CallArgument::Missing(_) => {}
6142 }
6143 }
6144 }
6145 Expression::Member(member) => {
6146 self.scan_expression(&member.object);
6147 if let MemberProperty::Computed(expression) = &member.property {
6148 self.scan_expression(expression);
6149 }
6150 if let MemberProperty::Private(private) = &member.property
6151 && let Some(name) = private_name(self.file, private)
6152 {
6153 self.use_name(&name);
6154 }
6155 }
6156 Expression::Await(await_expression) => self.scan_expression(&await_expression.argument),
6157 Expression::Yield(yield_expression) => {
6158 if let Some(argument) = &yield_expression.argument {
6159 self.scan_expression(argument);
6160 }
6161 }
6162 Expression::Unary(unary) => self.scan_expression(&unary.argument),
6163 Expression::Update(update) => self.scan_assignment_target(&update.argument),
6164 Expression::Binary(binary) => {
6165 self.scan_expression(&binary.left);
6166 self.scan_expression(&binary.right);
6167 }
6168 Expression::Logical(logical) => {
6169 self.scan_expression(&logical.left);
6170 self.scan_expression(&logical.right);
6171 }
6172 Expression::Conditional(conditional) => {
6173 self.scan_expression(&conditional.test);
6174 self.scan_expression(&conditional.consequent);
6175 self.scan_expression(&conditional.alternate);
6176 }
6177 Expression::Assignment(assignment) => {
6178 self.scan_expression(&assignment.right);
6179 self.scan_assignment_target(&assignment.left);
6180 }
6181 Expression::Sequence(sequence) => {
6182 for expression in &sequence.expressions {
6183 self.scan_expression(expression);
6184 }
6185 }
6186 Expression::Parenthesized(inner)
6187 | Expression::NonNull(crate::syntax::NonNullExpression { expression: inner }) => {
6188 self.scan_expression(inner);
6189 }
6190 Expression::As(expression) => self.scan_expression(&expression.expression),
6191 Expression::Satisfies(expression) => self.scan_expression(&expression.expression),
6192 Expression::TypeAssertion(expression) => self.scan_expression(&expression.expression),
6193 Expression::Import(import) => {
6194 self.scan_expression(&import.source);
6195 if let Some(options) = &import.options {
6196 self.scan_expression(options);
6197 }
6198 }
6199 Expression::Missing(_) => {}
6200 }
6201 }
6202
6203 fn scan_assignment_target(&mut self, target: &AssignmentTargetNode) {
6204 match target.data() {
6205 AssignmentTarget::Identifier(identifier) => {
6206 if let Some(name) = identifier_name(self.file, identifier) {
6207 self.use_name(&name);
6208 }
6209 }
6210 AssignmentTarget::Member(member) => {
6211 self.scan_expression(&member.object);
6212 if let MemberProperty::Computed(expression) = &member.property {
6213 self.scan_expression(expression);
6214 }
6215 if let MemberProperty::Private(private) = &member.property
6216 && let Some(name) = private_name(self.file, private)
6217 {
6218 self.use_name(&name);
6219 }
6220 }
6221 AssignmentTarget::Object(object) => {
6222 for property in &object.properties {
6223 if let PropertyName::Computed(key) = &property.name {
6224 self.scan_expression(key);
6225 }
6226 if let Some(initializer) = &property.initializer {
6227 self.scan_expression(initializer);
6228 }
6229 self.scan_assignment_target(&property.target);
6230 }
6231 }
6232 AssignmentTarget::Array(array) => {
6233 for element in &array.elements {
6234 if let AssignmentArrayElement::Target(inner) = element {
6235 self.scan_assignment_target(inner);
6236 }
6237 }
6238 }
6239 AssignmentTarget::Missing(_) => {}
6240 }
6241 }
6242}
6243
6244fn head_range(for_statement: &ForStatement) -> TextRange {
6246 for_statement
6247 .test
6248 .as_ref()
6249 .map_or_else(zero_range, |test| test.range())
6250}
6251
6252fn collect_immediate_declarations<'a>(
6253 file: &SourceFile,
6254 statements: &'a [Stmt],
6255) -> Vec<ImmediateDeclaration<'a>> {
6256 let mut declarations = Vec::new();
6257 for statement in statements {
6258 collect_immediate_declaration(file, statement, &mut declarations);
6259 }
6260 declarations
6261}
6262
6263fn collect_immediate_declaration<'a>(
6264 file: &SourceFile,
6265 statement: &'a Stmt,
6266 declarations: &mut Vec<ImmediateDeclaration<'a>>,
6267) {
6268 match statement.data() {
6269 Statement::Variable(declaration)
6270 if matches!(declaration.kind, VariableKind::Let | VariableKind::Const) =>
6271 {
6272 for declarator in &declaration.declarations {
6273 collect_pattern_declarations(file, &declarator.data().binding, declarations);
6274 }
6275 }
6276 Statement::Function(declaration) => {
6277 let function = &declaration.function;
6278 if function.body.is_some()
6279 && let Some(identifier) = &function.name
6280 && let Some(name) = identifier_name(file, identifier)
6281 {
6282 declarations.push(ImmediateDeclaration {
6283 name,
6284 site: binding_site(identifier.range()),
6285 range: statement.range(),
6286 kind: ImmediateDeclarationKind::Function(function),
6287 });
6288 }
6289 }
6290 Statement::Class(class) => {
6291 if let Some(identifier) = &class.name
6292 && let Some(name) = identifier_name(file, identifier)
6293 {
6294 declarations.push(ImmediateDeclaration {
6295 name,
6296 site: binding_site(identifier.range()),
6297 range: identifier.range(),
6298 kind: ImmediateDeclarationKind::Lexical,
6299 });
6300 }
6301 }
6302 Statement::Export(ExportDeclaration::Named(ExportNamedDeclaration::Declaration(
6303 declaration,
6304 ))) => collect_immediate_declaration(file, declaration, declarations),
6305 Statement::Export(ExportDeclaration::Default(default)) => match &default.value {
6306 ExportDefaultValue::Function(function) => {
6307 if function.body.is_some()
6308 && let Some(identifier) = &function.name
6309 && let Some(name) = identifier_name(file, identifier)
6310 {
6311 declarations.push(ImmediateDeclaration {
6312 name,
6313 site: binding_site(identifier.range()),
6314 range: statement.range(),
6315 kind: ImmediateDeclarationKind::Function(function),
6316 });
6317 }
6318 }
6319 ExportDefaultValue::Class(class) => {
6320 if let Some(identifier) = &class.name
6321 && let Some(name) = identifier_name(file, identifier)
6322 {
6323 declarations.push(ImmediateDeclaration {
6324 name,
6325 site: binding_site(identifier.range()),
6326 range: identifier.range(),
6327 kind: ImmediateDeclarationKind::Lexical,
6328 });
6329 }
6330 }
6331 _ => {}
6332 },
6333 _ => {}
6334 }
6335}
6336
6337fn collect_pattern_declarations<'a>(
6338 file: &SourceFile,
6339 pattern: &'a Pattern,
6340 declarations: &mut Vec<ImmediateDeclaration<'a>>,
6341) {
6342 match pattern.data() {
6343 BindingPattern::Identifier(identifier) => {
6344 if let Some(name) = identifier_name(file, identifier) {
6345 declarations.push(ImmediateDeclaration {
6346 name,
6347 site: binding_site(identifier.range()),
6348 range: identifier.range(),
6349 kind: ImmediateDeclarationKind::Lexical,
6350 });
6351 }
6352 }
6353 BindingPattern::Object(object) => {
6354 for property in &object.properties {
6355 collect_pattern_declarations(file, &property.binding, declarations);
6356 }
6357 }
6358 BindingPattern::Array(array) => {
6359 for element in &array.elements {
6360 if let ArrayBindingElement::Binding(binding) = element {
6361 collect_pattern_declarations(file, binding, declarations);
6362 }
6363 }
6364 }
6365 BindingPattern::Assignment(assignment) => {
6366 collect_pattern_declarations(file, &assignment.left, declarations);
6367 }
6368 BindingPattern::Rest(rest) => {
6369 collect_pattern_declarations(file, &rest.argument, declarations);
6370 }
6371 BindingPattern::Missing(_) => {}
6372 }
6373}
6374
6375pub(crate) fn declared_names(file: &SourceFile, statement: &Stmt) -> Vec<String> {
6377 let mut names = Vec::new();
6378 match statement.data() {
6379 Statement::Variable(declaration) => {
6380 for declarator in &declaration.declarations {
6381 collect_pattern_names(file, &declarator.data().binding, &mut names);
6382 }
6383 }
6384 Statement::Function(declaration) => {
6385 if let Some(name) = &declaration.function.name
6386 && let Some(text) = identifier_name(file, name)
6387 {
6388 names.push(text);
6389 }
6390 }
6391 Statement::Class(class) => {
6392 if let Some(name) = &class.name
6393 && let Some(text) = identifier_name(file, name)
6394 {
6395 names.push(text);
6396 }
6397 }
6398 _ => {}
6399 }
6400 names
6401}
6402
6403pub(crate) fn collect_var_names(file: &SourceFile, statements: &[Stmt], names: &mut Vec<String>) {
6406 for statement in statements {
6407 collect_var_names_stmt(file, statement, names);
6408 }
6409}
6410
6411fn collect_var_names_stmt(file: &SourceFile, statement: &Stmt, names: &mut Vec<String>) {
6412 match statement.data() {
6413 Statement::Variable(declaration) if matches!(declaration.kind, VariableKind::Var) => {
6414 for declarator in &declaration.declarations {
6415 collect_pattern_names(file, &declarator.data().binding, names);
6416 }
6417 }
6418 Statement::Block(block) => collect_var_names(file, &block.data().statements, names),
6419 Statement::If(statement) => {
6420 collect_var_names_stmt(file, &statement.consequent, names);
6421 if let Some(alternate) = &statement.alternate {
6422 collect_var_names_stmt(file, alternate, names);
6423 }
6424 }
6425 Statement::For(statement) => {
6426 if let Some(ForInitializer::Variable(declaration)) = &statement.initializer
6427 && matches!(declaration.kind, VariableKind::Var)
6428 {
6429 for declarator in &declaration.declarations {
6430 collect_pattern_names(file, &declarator.data().binding, names);
6431 }
6432 }
6433 collect_var_names_stmt(file, &statement.body, names);
6434 }
6435 Statement::ForIn(statement) => {
6436 collect_for_binding_var(file, &statement.binding, names);
6437 collect_var_names_stmt(file, &statement.body, names);
6438 }
6439 Statement::ForOf(statement) => {
6440 collect_for_binding_var(file, &statement.binding, names);
6441 collect_var_names_stmt(file, &statement.body, names);
6442 }
6443 Statement::While(statement) => collect_var_names_stmt(file, &statement.body, names),
6444 Statement::DoWhile(statement) => collect_var_names_stmt(file, &statement.body, names),
6445 Statement::Switch(statement) => {
6446 for case in &statement.cases {
6447 for statement in &case.data().consequent {
6448 collect_var_names_stmt(file, statement, names);
6449 }
6450 }
6451 }
6452 Statement::Try(statement) => {
6453 for statement in &statement.block.data().statements {
6454 collect_var_names_stmt(file, statement, names);
6455 }
6456 if let Some(handler) = &statement.handler {
6457 for statement in &handler.data().body.data().statements {
6458 collect_var_names_stmt(file, statement, names);
6459 }
6460 }
6461 if let Some(finalizer) = &statement.finalizer {
6462 for statement in &finalizer.data().statements {
6463 collect_var_names_stmt(file, statement, names);
6464 }
6465 }
6466 }
6467 Statement::Labeled(statement) => collect_var_names_stmt(file, &statement.body, names),
6468 Statement::Export(ExportDeclaration::Named(ExportNamedDeclaration::Declaration(
6469 statement,
6470 ))) => collect_var_names_stmt(file, statement, names),
6471 _ => {}
6472 }
6473}
6474
6475fn collect_for_binding_var(file: &SourceFile, binding: &ForBinding, names: &mut Vec<String>) {
6476 if let ForBinding::Variable(declaration) = binding
6477 && matches!(declaration.kind, VariableKind::Var)
6478 {
6479 for declarator in &declaration.declarations {
6480 collect_pattern_names(file, &declarator.data().binding, names);
6481 }
6482 }
6483}
6484
6485pub(crate) fn collect_pattern_names(file: &SourceFile, pattern: &Pattern, names: &mut Vec<String>) {
6486 match pattern.data() {
6487 BindingPattern::Identifier(identifier) => {
6488 if let Some(text) = identifier_name(file, identifier) {
6489 names.push(text);
6490 }
6491 }
6492 BindingPattern::Object(object) => {
6493 for property in &object.properties {
6494 collect_pattern_names(file, &property.binding, names);
6495 }
6496 }
6497 BindingPattern::Array(array) => {
6498 for element in &array.elements {
6499 if let ArrayBindingElement::Binding(inner) = element {
6500 collect_pattern_names(file, inner, names);
6501 }
6502 }
6503 }
6504 BindingPattern::Rest(rest) => collect_pattern_names(file, &rest.argument, names),
6505 BindingPattern::Assignment(assignment) => {
6506 collect_pattern_names(file, &assignment.left, names);
6507 }
6508 BindingPattern::Missing(_) => {}
6509 }
6510}
6511
6512fn identifier_name(file: &SourceFile, identifier: &IdentifierNode) -> Option<String> {
6514 let token = identifier.data().token();
6515 if token.is_missing() {
6516 return None;
6517 }
6518 file.token_text(token).map(str::to_owned)
6519}
6520
6521fn private_name(file: &SourceFile, private: &PrivateIdentifierNode) -> Option<String> {
6524 let token = private.data().token();
6525 if token.is_missing() {
6526 return None;
6527 }
6528 file.token_text(token).map(str::to_owned)
6529}
6530
6531fn map_binary_operator(operator: BinaryOperator) -> BinaryOp {
6533 match operator {
6534 BinaryOperator::Add => BinaryOp::Add,
6535 BinaryOperator::Subtract => BinaryOp::Subtract,
6536 BinaryOperator::Multiply => BinaryOp::Multiply,
6537 BinaryOperator::Divide => BinaryOp::Divide,
6538 BinaryOperator::Remainder => BinaryOp::Remainder,
6539 BinaryOperator::Exponentiate => BinaryOp::Exponent,
6540 BinaryOperator::BitAnd => BinaryOp::BitAnd,
6541 BinaryOperator::BitOr => BinaryOp::BitOr,
6542 BinaryOperator::BitXor => BinaryOp::BitXor,
6543 BinaryOperator::LeftShift => BinaryOp::ShiftLeft,
6544 BinaryOperator::SignedRightShift => BinaryOp::ShiftRight,
6545 BinaryOperator::UnsignedRightShift => BinaryOp::UnsignedShiftRight,
6546 BinaryOperator::Equal => BinaryOp::Equal,
6547 BinaryOperator::NotEqual => BinaryOp::NotEqual,
6548 BinaryOperator::StrictEqual => BinaryOp::StrictEqual,
6549 BinaryOperator::StrictNotEqual => BinaryOp::StrictNotEqual,
6550 BinaryOperator::LessThan => BinaryOp::LessThan,
6551 BinaryOperator::LessThanOrEqual => BinaryOp::LessThanOrEqual,
6552 BinaryOperator::GreaterThan => BinaryOp::GreaterThan,
6553 BinaryOperator::GreaterThanOrEqual => BinaryOp::GreaterThanOrEqual,
6554 BinaryOperator::Instanceof => BinaryOp::InstanceOf,
6555 BinaryOperator::In => BinaryOp::In,
6556 }
6557}
6558
6559enum CompoundOp {
6561 Arithmetic(BinaryOp),
6562 Logical(LogicalOperator),
6563}
6564
6565fn compound_operator(operator: AssignmentOperator) -> Option<CompoundOp> {
6567 let op = match operator {
6568 AssignmentOperator::Assign => return None,
6569 AssignmentOperator::AddAssign => BinaryOp::Add,
6570 AssignmentOperator::SubtractAssign => BinaryOp::Subtract,
6571 AssignmentOperator::MultiplyAssign => BinaryOp::Multiply,
6572 AssignmentOperator::DivideAssign => BinaryOp::Divide,
6573 AssignmentOperator::RemainderAssign => BinaryOp::Remainder,
6574 AssignmentOperator::ExponentiateAssign => BinaryOp::Exponent,
6575 AssignmentOperator::LeftShiftAssign => BinaryOp::ShiftLeft,
6576 AssignmentOperator::SignedRightShiftAssign => BinaryOp::ShiftRight,
6577 AssignmentOperator::UnsignedRightShiftAssign => BinaryOp::UnsignedShiftRight,
6578 AssignmentOperator::BitAndAssign => BinaryOp::BitAnd,
6579 AssignmentOperator::BitOrAssign => BinaryOp::BitOr,
6580 AssignmentOperator::BitXorAssign => BinaryOp::BitXor,
6581 AssignmentOperator::LogicalAndAssign => {
6582 return Some(CompoundOp::Logical(LogicalOperator::And));
6583 }
6584 AssignmentOperator::LogicalOrAssign => {
6585 return Some(CompoundOp::Logical(LogicalOperator::Or));
6586 }
6587 AssignmentOperator::NullishAssign => {
6588 return Some(CompoundOp::Logical(LogicalOperator::Nullish));
6589 }
6590 };
6591 Some(CompoundOp::Arithmetic(op))
6592}
6593
6594fn numeric_key_text(
6596 context: &FunctionContext<'_>,
6597 number: &NumericLiteralNode,
6598) -> Result<String, LowerError> {
6599 let range = number.range();
6600 let token = number.data().token();
6601 if token.is_missing() {
6602 return Err(context.missing(range, NodeKind::NumericLiteral));
6603 }
6604 let lexeme = context
6605 .file
6606 .token_text(token)
6607 .filter(|text| !text.is_empty())
6608 .ok_or_else(|| context.error(range, LowerErrorKind::InvalidNumericLiteral))?;
6609 let value = cook_number(lexeme)
6610 .ok_or_else(|| context.error(range, LowerErrorKind::InvalidNumericLiteral))?;
6611 if value.fract() == 0.0 && value.is_finite() && (0.0..=9_007_199_254_740_991.0).contains(&value)
6612 {
6613 Ok(format!("{}", value as u64))
6614 } else {
6615 Ok(format_number_key(value))
6617 }
6618}
6619
6620fn format_number_key(value: f64) -> String {
6621 if value == 0.0 {
6622 "0".to_owned()
6623 } else {
6624 let text = format!("{value}");
6625 text
6626 }
6627}
6628
6629fn trim_template_delimiters(text: &str, kind: TokenKind) -> &str {
6631 let (head, tail): (usize, usize) = match kind {
6632 TokenKind::NoSubstitutionTemplate => (1, 1),
6634 TokenKind::TemplateHead => (1, 2),
6636 TokenKind::TemplateMiddle => (1, 2),
6638 TokenKind::TemplateTail => (1, 1),
6640 _ => (0, 0),
6641 };
6642 let bytes = text.len();
6643 if bytes < head + tail {
6644 return "";
6645 }
6646 &text[head..bytes - tail]
6647}
6648
6649fn cook_escapes(input: &str) -> EcmaString {
6652 if !input.contains('\\') {
6653 return EcmaString::from_utf8(input);
6654 }
6655 let mut output = EcmaStringBuilder::with_capacity(input.encode_utf16().count());
6656 let mut chars = input.chars().peekable();
6657 while let Some(ch) = chars.next() {
6658 if ch != '\\' {
6659 output
6660 .push_code_point(u32::from(ch))
6661 .expect("a Rust char is a Unicode scalar");
6662 continue;
6663 }
6664 let Some(escape) = chars.next() else {
6665 output.push_unit(b'\\'.into());
6666 break;
6667 };
6668 match escape {
6669 'n' => output.push_unit(b'\n'.into()),
6670 't' => output.push_unit(b'\t'.into()),
6671 'r' => output.push_unit(b'\r'.into()),
6672 'b' => output.push_unit(0x0008),
6673 'f' => output.push_unit(0x000C),
6674 'v' => output.push_unit(0x000B),
6675 '0' if !chars.peek().is_some_and(|c| c.is_ascii_digit()) => output.push_unit(0),
6676 '\n' => {}
6677 '\r' => {
6678 if chars.peek() == Some(&'\n') {
6679 chars.next();
6680 }
6681 }
6682 'x' => {
6683 let hi = chars.next();
6684 let lo = chars.next();
6685 if let (Some(hi), Some(lo)) = (hi, lo)
6686 && let (Some(h), Some(l)) = (hi.to_digit(16), lo.to_digit(16))
6687 {
6688 output.push_unit((h * 16 + l) as u16);
6689 } else {
6690 output.push_unit(b'x'.into());
6691 }
6692 }
6693 'u' => cook_unicode_escape(&mut chars, &mut output),
6694 other => output
6695 .push_code_point(u32::from(other))
6696 .expect("a Rust char is a Unicode scalar"),
6697 }
6698 }
6699 output.finish()
6700}
6701
6702fn cook_unicode_escape(
6703 chars: &mut std::iter::Peekable<std::str::Chars<'_>>,
6704 output: &mut EcmaStringBuilder,
6705) {
6706 if chars.peek() == Some(&'{') {
6707 chars.next();
6708 let mut value = 0u32;
6709 let mut any = false;
6710 while let Some(&c) = chars.peek() {
6711 if c == '}' {
6712 chars.next();
6713 break;
6714 }
6715 let Some(digit) = c.to_digit(16) else { break };
6716 value = value.saturating_mul(16).saturating_add(digit);
6717 any = true;
6718 chars.next();
6719 }
6720 if any && value <= 0x10_FFFF {
6721 output
6722 .push_code_point(value)
6723 .expect("a bounded code point is representable");
6724 }
6725 return;
6726 }
6727 let mut value = 0u16;
6728 let mut count = 0;
6729 while count < 4 {
6730 let Some(&c) = chars.peek() else { break };
6731 let Some(digit) = c.to_digit(16) else { break };
6732 value = value * 16 + digit as u16;
6733 chars.next();
6734 count += 1;
6735 }
6736 if count == 4 {
6737 output.push_unit(value);
6738 } else {
6739 output.push_unit(b'u'.into());
6740 }
6741}
6742
6743fn split_regex(lexeme: &str) -> Option<(String, String)> {
6745 let lexeme = lexeme.strip_prefix('/')?;
6746 let last_slash = lexeme.rfind('/')?;
6747 let pattern = &lexeme[..last_slash];
6748 let flags = &lexeme[last_slash + 1..];
6749 Some((pattern.to_owned(), flags.to_owned()))
6750}
6751
6752fn canonical_bigint_text(lexeme: &str) -> Option<String> {
6754 let digits = lexeme.strip_suffix('n')?;
6755 if digits.is_empty() {
6756 return None;
6757 }
6758 if digits.len() >= 2 {
6759 let prefix = &digits[..2];
6760 if matches!(prefix, "0x" | "0X" | "0o" | "0O" | "0b" | "0B") {
6761 return None;
6762 }
6763 }
6764 let cleaned: String = digits.chars().filter(|c| *c != '_').collect();
6765 if cleaned.is_empty() || !cleaned.chars().all(|c| c.is_ascii_digit()) {
6766 return None;
6767 }
6768 let trimmed = cleaned.trim_start_matches('0');
6769 if trimmed.is_empty() {
6770 Some("0".to_owned())
6771 } else {
6772 Some(trimmed.to_owned())
6773 }
6774}
6775
6776fn cook_number(lexeme: &str) -> Option<f64> {
6778 let cleaned: String = lexeme.chars().filter(|c| *c != '_').collect();
6779 if let Some(rest) = cleaned
6780 .strip_prefix("0x")
6781 .or_else(|| cleaned.strip_prefix("0X"))
6782 {
6783 return radix_value(rest, 16);
6784 }
6785 if let Some(rest) = cleaned
6786 .strip_prefix("0o")
6787 .or_else(|| cleaned.strip_prefix("0O"))
6788 {
6789 return radix_value(rest, 8);
6790 }
6791 if let Some(rest) = cleaned
6792 .strip_prefix("0b")
6793 .or_else(|| cleaned.strip_prefix("0B"))
6794 {
6795 return radix_value(rest, 2);
6796 }
6797 cleaned.parse::<f64>().ok()
6798}
6799
6800fn radix_value(digits: &str, radix: u32) -> Option<f64> {
6801 if digits.is_empty() {
6802 return None;
6803 }
6804 let mut value = 0.0_f64;
6805 for ch in digits.chars() {
6806 let digit = ch.to_digit(radix)?;
6807 value = value * f64::from(radix) + f64::from(digit);
6808 }
6809 Some(value)
6810}
6811
6812fn number_constant(value: f64) -> Constant {
6814 if value.fract() == 0.0
6815 && value.is_finite()
6816 && (f64::from(i32::MIN)..=f64::from(i32::MAX)).contains(&value)
6817 && !(value == 0.0 && value.is_sign_negative())
6818 {
6819 Constant::Int32(value as i32)
6820 } else {
6821 Constant::Number(NumberBits::from_f64(value))
6822 }
6823}
6824
6825#[cfg(test)]
6826mod tests {
6827 use std::collections::BTreeSet;
6828 use std::fs;
6829 use std::path::{Path, PathBuf};
6830 use std::sync::Arc;
6831
6832 use super::{LowerErrorKind, LowerOptions, UnsupportedConstruct, cook_escapes, lower};
6833 use crate::parser::parse;
6834 use crate::scanner::scan;
6835 use crate::source::{ScriptKind, SourceId, SourceText};
6836
6837 fn repository_root() -> PathBuf {
6838 Path::new(env!("CARGO_MANIFEST_DIR"))
6839 .join("../..")
6840 .canonicalize()
6841 .expect("repository root is readable")
6842 }
6843
6844 fn script_kind(path: &str) -> ScriptKind {
6845 if path.ends_with(".d.ts")
6846 || path.ends_with(".ts")
6847 || path.ends_with(".mts")
6848 || path.ends_with(".cts")
6849 {
6850 ScriptKind::TypeScript
6851 } else if path.ends_with(".tsx") {
6852 ScriptKind::TypeScriptReact
6853 } else if path.ends_with(".js") || path.ends_with(".mjs") || path.ends_with(".cjs") {
6854 ScriptKind::JavaScript
6855 } else if path.ends_with(".jsx") {
6856 ScriptKind::JavaScriptReact
6857 } else {
6858 panic!("declared corpus source has unsupported extension: {path}");
6859 }
6860 }
6861
6862 fn declared_corpus_sources(root: &Path) -> Vec<String> {
6866 let manifest = fs::read_to_string(root.join("corpus/manifest.toml"))
6867 .expect("corpus manifest is readable");
6868 let mut sources = BTreeSet::new();
6869 for line in manifest.lines() {
6870 if let Some(value) = quoted_value(line, "entrypoint") {
6871 sources.insert(value);
6872 }
6873 }
6874 let specs = root.join("corpus/specs");
6875 for entry in fs::read_dir(&specs).expect("corpus specs directory is readable") {
6876 let path = entry.expect("spec directory entry").path();
6877 if path.extension().and_then(|extension| extension.to_str()) != Some("toml") {
6878 continue;
6879 }
6880 let text = fs::read_to_string(&path).expect("corpus spec is UTF-8");
6881 let start = text
6882 .find("source_files")
6883 .unwrap_or_else(|| panic!("{} has no source_files", path.display()));
6884 let array = &text[start..];
6885 let open = array
6886 .find('[')
6887 .expect("source_files has an opening bracket");
6888 let close = array[open + 1..]
6889 .find(']')
6890 .map(|index| open + 1 + index)
6891 .expect("source_files has a closing bracket");
6892 let contents = &array[open + 1..close];
6893 for item in contents.split(',') {
6894 let item = item.trim();
6895 if item.is_empty() {
6896 continue;
6897 }
6898 let value = item
6899 .strip_prefix('"')
6900 .and_then(|item| item.strip_suffix('"'))
6901 .unwrap_or_else(|| {
6902 panic!(
6903 "{} has malformed source_files item `{item}`",
6904 path.display()
6905 )
6906 });
6907 sources.insert(value.to_owned());
6908 }
6909 }
6910 assert_eq!(
6911 sources.len(),
6912 63,
6913 "the checked corpus contract is 63 sources"
6914 );
6915 sources.into_iter().collect()
6916 }
6917
6918 fn quoted_value(line: &str, key: &str) -> Option<String> {
6919 let line = line.trim();
6920 let value = line
6921 .strip_prefix(key)?
6922 .trim_start()
6923 .strip_prefix('=')?
6924 .trim();
6925 Some(value.strip_prefix('"')?.strip_suffix('"')?.to_owned())
6926 }
6927
6928 #[test]
6929 fn cooking_preserves_lone_surrogate_units() {
6930 assert_eq!(cook_escapes("\\uD800").as_units(), [0xD800]);
6931 assert_eq!(cook_escapes("\\uD83D\\uDE03").as_units(), [0xD83D, 0xDE03]);
6932 assert_eq!(cook_escapes("\\u{1F603}").as_units(), [0xD83D, 0xDE03]);
6933 }
6934
6935 #[test]
6936 fn all_declared_corpus_sources_lower_to_verified_modules() {
6937 let root = repository_root();
6938 let sources = declared_corpus_sources(&root);
6939 let mut failures = Vec::new();
6940 for (index, relative) in sources.iter().enumerate() {
6941 let path = root.join(relative);
6942 let text = fs::read_to_string(&path)
6943 .unwrap_or_else(|error| panic!("{} is unreadable: {error}", path.display()));
6944 let source = Arc::new(SourceText::new(text));
6945 let scanned = scan(SourceId::new(index as u32), script_kind(relative), source);
6946 let parsed = parse(scanned);
6947 match lower(
6948 parsed.product(),
6949 LowerOptions {
6950 javascript_compatibility: true,
6951 },
6952 ) {
6953 Ok(module) => {
6954 assert!(
6955 module.certificate(module.entry()).is_some(),
6956 "{relative}: entry is verified"
6957 );
6958 }
6959 Err(error) => failures.push(format!("{relative}: {error}")),
6960 }
6961 }
6962 assert!(
6963 failures.is_empty(),
6964 "{}/{} declared corpus sources failed lowering:\n{}",
6965 failures.len(),
6966 sources.len(),
6967 failures.join("\n")
6968 );
6969 }
6970
6971 use bamts_bytecode::{
6972 BinaryOp, Constant, DecodeLimits, Instruction, Module, Register, Verified, decode_verified,
6973 };
6974
6975 fn lower_js(src: &str) -> Module<Verified> {
6976 let source = Arc::new(SourceText::new(src.to_owned()));
6977 let scanned = scan(SourceId::new(0), ScriptKind::TypeScript, source);
6978 let parsed = parse(scanned);
6979 lower(
6980 parsed.product(),
6981 LowerOptions {
6982 javascript_compatibility: true,
6983 },
6984 )
6985 .expect("snippet lowers to a verified module")
6986 }
6987
6988 #[test]
6989 fn lowering_preserves_lone_surrogate_escapes() {
6990 let module = lower_js("const lone = '\\uD800'; const face = '\\u{1F603}';");
6991 let strings: Vec<_> = module
6992 .constants()
6993 .iter()
6994 .filter_map(|constant| match constant {
6995 Constant::String(value) => Some(value.as_units()),
6996 _ => None,
6997 })
6998 .collect();
6999 assert!(strings.iter().any(|units| *units == [0xD800]));
7000 assert!(strings.iter().any(|units| *units == [0xD83D, 0xDE03]));
7001 }
7002
7003 fn any_instruction(
7004 module: &Module<Verified>,
7005 predicate: impl Fn(&Instruction) -> bool,
7006 ) -> bool {
7007 module
7008 .functions()
7009 .iter()
7010 .flat_map(|function| function.code())
7011 .any(predicate)
7012 }
7013
7014 fn max_capture_count(module: &Module<Verified>) -> u32 {
7015 module
7016 .functions()
7017 .iter()
7018 .map(|function| function.capture_count())
7019 .max()
7020 .unwrap_or(0)
7021 }
7022
7023 fn created_cell_used_as_capture(code: &[Instruction]) -> Option<Register> {
7024 code.iter().find_map(|instruction| {
7025 let Instruction::ArrayPush { array, value } = instruction else {
7026 return None;
7027 };
7028 if array == value {
7029 return None;
7030 }
7031 code.iter()
7032 .any(|candidate| {
7033 matches!(
7034 candidate,
7035 Instruction::CreateArray { dst } | Instruction::CreateCell { dst }
7036 if dst == value
7037 )
7038 })
7039 .then_some(*value)
7040 })
7041 }
7042
7043 fn cell_capture_pushes(code: &[Instruction], cell: Register) -> usize {
7044 code.iter()
7045 .filter(|instruction| {
7046 matches!(instruction, Instruction::ArrayPush { value, .. } if *value == cell)
7047 })
7048 .count()
7049 }
7050
7051 fn cell_allocations(code: &[Instruction], cell: Register) -> usize {
7052 code.iter()
7053 .filter(|instruction| {
7054 matches!(
7055 instruction,
7056 Instruction::CreateArray { dst } | Instruction::CreateCell { dst }
7057 if *dst == cell
7058 )
7059 })
7060 .count()
7061 }
7062
7063 fn dormant_promotion_cell_blocks(code: &[Instruction]) -> usize {
7064 code.iter()
7065 .enumerate()
7066 .filter(|(entry, instruction)| {
7067 let Instruction::CreateArray { dst } = instruction else {
7068 return false;
7069 };
7070 matches!(
7071 code.get((*entry).saturating_sub(1)),
7072 Some(Instruction::Jump { target }) if target.get() as usize == *entry + 2
7073 ) && matches!(
7074 code.get(*entry + 1),
7075 Some(Instruction::ArrayPush { array, value }) if array == dst && value == dst
7076 ) && !code.iter().any(|instruction| {
7077 matches!(
7078 instruction,
7079 Instruction::Jump { target }
7080 | Instruction::JumpIfTrue { target, .. }
7081 | Instruction::JumpIfFalse { target, .. }
7082 if target.get() as usize == *entry
7083 )
7084 })
7085 })
7086 .count()
7087 }
7088
7089 #[test]
7090 fn outer_write_is_observed_by_a_captured_read() {
7091 let module = lower_js(
7092 "function outer() { let value = 0; const read = () => value; value = 1; return read; }",
7093 );
7094 let code = module
7095 .functions()
7096 .iter()
7097 .map(|function| function.code())
7098 .find(|code| {
7099 created_cell_used_as_capture(code).is_some()
7100 && code
7101 .iter()
7102 .any(|instruction| matches!(instruction, Instruction::SetProperty { .. }))
7103 })
7104 .expect("outer function owns the promoted binding");
7105 let cell = created_cell_used_as_capture(code).expect("cell is captured");
7106 assert!(code.iter().any(
7107 |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell)
7108 ));
7109 assert!(module.functions().iter().any(|function| {
7110 function.capture_count() == 1
7111 && function.code().iter().any(
7112 |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == Register::new(0))
7113 )
7114 }));
7115 }
7116
7117 #[test]
7118 fn inner_write_is_observed_by_the_outer_read() {
7119 let module = lower_js(
7120 "function outer() { let value = 0; const write = () => { value = 1; }; write(); return value; }",
7121 );
7122 assert!(module.functions().iter().any(|function| {
7123 function.capture_count() == 1
7124 && function.code().iter().any(
7125 |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == Register::new(0))
7126 )
7127 }));
7128 assert!(module.functions().iter().any(|function| {
7129 let code = function.code();
7130 created_cell_used_as_capture(code).is_some_and(|cell| {
7131 code.iter().any(
7132 |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == cell)
7133 )
7134 })
7135 }));
7136 }
7137
7138 #[test]
7139 fn assigned_local_cell_dominates_a_later_conditional_closure() {
7140 let module = lower_js(
7141 "function outer(flag) { let value = 0; value = 1; let read; if (flag) read = () => value; return value; }",
7142 );
7143 let code = module
7144 .functions()
7145 .iter()
7146 .map(|function| function.code())
7147 .find(|code| created_cell_used_as_capture(code).is_some())
7148 .expect("outer function owns the captured cell");
7149 let cell = created_cell_used_as_capture(code).expect("cell is captured");
7150 let allocation = code
7151 .iter()
7152 .position(
7153 |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7154 )
7155 .expect("cell allocation is present");
7156 let conditional = code
7157 .iter()
7158 .position(|instruction| matches!(instruction, Instruction::JumpIfFalse { .. }))
7159 .expect("if statement branches");
7160 assert!(allocation < conditional);
7161 assert!(code[..conditional].iter().any(
7162 |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell)
7163 ));
7164 }
7165
7166 #[test]
7167 fn assigned_parameter_uses_one_cell_before_later_method_capture() {
7168 let module = lower_js(
7169 "function mitt(all) { all = all || new Map(); return { read() { return all; } }; }",
7170 );
7171 let code = module
7172 .functions()
7173 .iter()
7174 .map(|function| function.code())
7175 .find(|code| created_cell_used_as_capture(code).is_some())
7176 .expect("mitt function owns the parameter cell");
7177 let cell = created_cell_used_as_capture(code).expect("parameter cell is captured");
7178 let allocation = code
7179 .iter()
7180 .position(
7181 |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7182 )
7183 .expect("parameter cell is allocated");
7184 let assignment = code
7185 .iter()
7186 .position(
7187 |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell),
7188 )
7189 .expect("parameter assignment stores through its cell");
7190 let closure = code
7191 .iter()
7192 .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7193 .expect("method closure is created");
7194 assert!(allocation < assignment && assignment < closure);
7195 assert_eq!(cell_allocations(code, cell), 1);
7196 }
7197
7198 #[test]
7199 fn captured_parameter_cells_precede_default_initializer_closures() {
7200 for source in [
7201 "function f(a = () => a) { return a; }",
7202 "function f(make = () => later, later = 1) { return make; }",
7203 "function f(make = () => rest, ...rest) { return make; }",
7204 "function f({ value = () => value } = {}) { return value; }",
7205 ] {
7206 let module = lower_js(source);
7207 let owner = module
7208 .functions()
7209 .iter()
7210 .find(|function| created_cell_used_as_capture(function.code()).is_some())
7211 .expect("parameter owner materializes a captured default closure");
7212 let code = owner.code();
7213 let cell = created_cell_used_as_capture(code).expect("parameter cell is captured");
7214 let allocation = code
7215 .iter()
7216 .position(
7217 |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7218 )
7219 .expect("parameter cell is allocated");
7220 let closure = code
7221 .iter()
7222 .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7223 .expect("default initializer creates a closure");
7224 assert!(allocation < closure, "{source}");
7225 assert_eq!(cell_allocations(code, cell), 1, "{source}");
7226 }
7227 }
7228
7229 #[test]
7230 fn declaration_owned_closures_capture_their_predeclared_cells() {
7231 for source in [
7232 "function outer() { const f = () => f; return f; }",
7233 "function outer() { function f() { return f; } return f; }",
7234 "function outer() { class C { self() { return C; } } return C; }",
7235 "function outer() { const { f = () => f } = {}; return f; }",
7236 "function outer() { const f = () => f; { const f = 1; } return f; }",
7237 ] {
7238 let module = lower_js(source);
7239 let owner = module
7240 .functions()
7241 .iter()
7242 .find(|function| created_cell_used_as_capture(function.code()).is_some())
7243 .expect("declaration owner materializes the captured cell");
7244 let code = owner.code();
7245 let cell = created_cell_used_as_capture(code).expect("cell is captured");
7246 let allocation = code
7247 .iter()
7248 .position(
7249 |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7250 )
7251 .expect("captured cell is allocated");
7252 let closure = code
7253 .iter()
7254 .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7255 .expect("declaration value creates a closure");
7256 assert!(allocation < closure, "{source}");
7257 assert_eq!(cell_allocations(code, cell), 1, "{source}");
7258 assert!(
7259 code[closure..].iter().any(
7260 |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell)
7261 ),
7262 "the declaration stores its final value into the same cell: {source}"
7263 );
7264 }
7265 }
7266
7267 #[test]
7268 fn safe_uncaptured_lexical_stays_in_a_register() {
7269 let module = lower_js("function outer() { const value = 1; return value; }");
7270 assert!(module.functions().iter().all(|function| {
7271 !function
7272 .code()
7273 .iter()
7274 .any(|instruction| matches!(instruction, Instruction::CreateCell { .. }))
7275 }));
7276 }
7277
7278 #[test]
7279 fn captured_later_lexical_is_predeclared_once() {
7280 let module = lower_js(
7281 "function outer() { const read = () => later; const later = 1; return read; }",
7282 );
7283 let owner = module
7284 .functions()
7285 .iter()
7286 .find(|function| {
7287 function
7288 .code()
7289 .iter()
7290 .any(|instruction| matches!(instruction, Instruction::CreateCell { .. }))
7291 })
7292 .expect("owner predeclares the later lexical cell");
7293 assert_eq!(
7294 owner
7295 .code()
7296 .iter()
7297 .filter(|instruction| matches!(instruction, Instruction::CreateCell { .. }))
7298 .count(),
7299 1
7300 );
7301 let cell = created_cell_used_as_capture(owner.code()).expect("closure captures the cell");
7302 assert_eq!(cell_allocations(owner.code(), cell), 1);
7303 }
7304
7305 #[test]
7306 fn early_closure_read_uses_one_predeclared_cell_then_initializes_it() {
7307 let module = lower_js(
7308 "function outer() { const read = () => later; read(); let later = 1; return read(); }",
7309 );
7310 let code = module
7311 .functions()
7312 .iter()
7313 .map(|function| function.code())
7314 .find(|code| created_cell_used_as_capture(code).is_some())
7315 .expect("outer owns the captured later binding");
7316 let cell = created_cell_used_as_capture(code).expect("later binding is a cell");
7317 let create = code
7318 .iter()
7319 .position(|instruction| matches!(instruction, Instruction::CreateCell { dst } if *dst == cell))
7320 .expect("cell is seeded at scope entry");
7321 let closure = code
7322 .iter()
7323 .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7324 .expect("reader closure is instantiated");
7325 let initialize = code
7326 .iter()
7327 .position(|instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell))
7328 .expect("declaration initializes the cell");
7329 assert!(create < closure && closure < initialize);
7330 assert_eq!(cell_allocations(code, cell), 1);
7331 }
7332
7333 #[test]
7334 fn function_declaration_is_instantiated_once_before_executable_statements() {
7335 let module = lower_js(
7336 "function outer() { return declaredLater(); function declaredLater() { return 2; } }",
7337 );
7338 let code = module
7339 .functions()
7340 .iter()
7341 .map(|function| function.code())
7342 .find(|code| {
7343 code.iter()
7344 .any(|instruction| matches!(instruction, Instruction::Call { .. }))
7345 })
7346 .expect("outer function calls its declaration");
7347 let closure = code
7348 .iter()
7349 .position(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7350 .expect("function declaration is instantiated");
7351 let call = code
7352 .iter()
7353 .position(|instruction| matches!(instruction, Instruction::Call { .. }))
7354 .expect("call is emitted");
7355 assert!(closure < call);
7356 assert_eq!(
7357 code.iter()
7358 .filter(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7359 .count(),
7360 1
7361 );
7362 }
7363
7364 #[test]
7365 fn class_heritage_reads_its_own_uninitialized_cell() {
7366 let module = lower_js("function outer() { class C extends C {} return C; }");
7367 let code = module
7368 .functions()
7369 .iter()
7370 .map(|function| function.code())
7371 .find(|code| {
7372 code.iter()
7373 .any(|instruction| matches!(instruction, Instruction::CreateCell { .. }))
7374 })
7375 .expect("class owner predeclares its binding");
7376 let cell = code
7377 .iter()
7378 .find_map(|instruction| match instruction {
7379 Instruction::CreateCell { dst } => Some(*dst),
7380 _ => None,
7381 })
7382 .expect("class cell exists");
7383 let create = code
7384 .iter()
7385 .position(|instruction| matches!(instruction, Instruction::CreateCell { dst } if *dst == cell))
7386 .expect("class cell allocation");
7387 let heritage_read = code
7388 .iter()
7389 .position(|instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == cell))
7390 .expect("heritage reads through the class cell");
7391 let initialize = code
7392 .iter()
7393 .rposition(|instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == cell))
7394 .expect("class declaration initializes the same cell");
7395 assert!(create < heritage_read && heritage_read < initialize);
7396 }
7397
7398 #[test]
7399 fn same_name_shadow_does_not_overbox_uncaptured_binding() {
7400 let module =
7401 lower_js("function outer() { let value = 1; { let value = 2; } return () => value; }");
7402 let code = module
7403 .functions()
7404 .iter()
7405 .map(|function| function.code())
7406 .find(|code| created_cell_used_as_capture(code).is_some())
7407 .expect("outer binding is captured");
7408 assert_eq!(
7409 code.iter()
7410 .filter(|instruction| matches!(
7411 instruction,
7412 Instruction::CreateArray { .. } | Instruction::CreateCell { .. }
7413 ))
7414 .count(),
7415 2,
7416 "only the captured binding cell and closure capture array allocate"
7417 );
7418 assert_eq!(dormant_promotion_cell_blocks(code), 0);
7419 }
7420
7421 #[test]
7422 fn sibling_getter_and_setter_capture_the_same_cell() {
7423 let module = lower_js(
7424 "function outer() { let value = 0; const get = () => value; const set = (next) => { value = next; }; return [get, set]; }",
7425 );
7426 assert!(module.functions().iter().any(|function| {
7427 let code = function.code();
7428 created_cell_used_as_capture(code)
7429 .is_some_and(|cell| cell_capture_pushes(code, cell) == 2)
7430 }));
7431 }
7432
7433 #[test]
7434 fn transitive_capture_passes_an_existing_cell_without_wrapping_it() {
7435 let module = lower_js("function outer() { let value = 1; return () => () => value; }");
7436 let middle = module
7437 .functions()
7438 .iter()
7439 .find(|function| {
7440 function.capture_count() == 1
7441 && function
7442 .code()
7443 .iter()
7444 .any(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7445 })
7446 .expect("middle closure materializes the inner closure");
7447 assert_eq!(
7448 middle
7449 .code()
7450 .iter()
7451 .filter(|instruction| matches!(instruction, Instruction::CreateArray { .. }))
7452 .count(),
7453 1,
7454 "only the capture array is allocated; capture register zero is already a cell"
7455 );
7456 assert!(middle.code().iter().any(
7457 |instruction| matches!(instruction, Instruction::ArrayPush { value, .. } if *value == Register::new(0))
7458 ));
7459 assert!(module.functions().iter().any(|function| {
7460 function.capture_count() == 1
7461 && !function
7462 .code()
7463 .iter()
7464 .any(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7465 && function.code().iter().any(
7466 |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == Register::new(0))
7467 )
7468 }));
7469 }
7470
7471 #[test]
7472 fn capture_of_capture_reads_and_reexports_the_same_cell() {
7473 let module = lower_js(
7474 "function outer() { let value = 1; return () => { value; return () => value; }; }",
7475 );
7476 let middle = module
7477 .functions()
7478 .iter()
7479 .find(|function| {
7480 function.capture_count() == 1
7481 && function
7482 .code()
7483 .iter()
7484 .any(|instruction| matches!(instruction, Instruction::CreateClosure { .. }))
7485 })
7486 .expect("middle closure reads and reexports the capture");
7487 assert!(middle.code().iter().any(
7488 |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == Register::new(0))
7489 ));
7490 assert_eq!(
7491 middle
7492 .code()
7493 .iter()
7494 .filter(|instruction| matches!(instruction, Instruction::CreateArray { .. }))
7495 .count(),
7496 1
7497 );
7498 }
7499
7500 #[test]
7501 fn logical_assignment_reads_and_writes_cell_contents() {
7502 let module = lower_js(
7503 "function outer() { let optional = false; return (term) => { optional ||= term; }; }",
7504 );
7505 assert!(module.functions().iter().any(|function| {
7506 function.capture_count() == 1
7507 && function.code().iter().any(
7508 |instruction| matches!(instruction, Instruction::GetProperty { object, .. } if *object == Register::new(0))
7509 )
7510 && function.code().iter().any(
7511 |instruction| matches!(instruction, Instruction::SetProperty { object, .. } if *object == Register::new(0))
7512 )
7513 }));
7514 }
7515
7516 #[test]
7517 fn classic_for_let_rebinds_but_for_var_reuses_its_cell() {
7518 let lexical = lower_js(
7519 "function outer() { const reads = []; for (let index = 0; index < 2; index++) reads.push(() => index); return reads; }",
7520 );
7521 let shared = lower_js(
7522 "function outer() { const reads = []; for (var index = 0; index < 2; index++) reads.push(() => index); return reads; }",
7523 );
7524 let lexical_allocations = lexical
7525 .functions()
7526 .iter()
7527 .find_map(|function| {
7528 let code = function.code();
7529 let cell = created_cell_used_as_capture(code)?;
7530 Some(cell_allocations(code, cell))
7531 })
7532 .expect("lexical loop captures its binding cell");
7533 let shared_allocations = shared
7534 .functions()
7535 .iter()
7536 .find_map(|function| {
7537 let code = function.code();
7538 let cell = created_cell_used_as_capture(code)?;
7539 Some(cell_allocations(code, cell))
7540 })
7541 .expect("var loop captures its binding cell");
7542 assert_eq!(lexical_allocations, 2, "let copies into a fresh cell");
7543 assert_eq!(shared_allocations, 1, "var retains one function cell");
7544 for module in [&lexical, &shared] {
7545 let code = module
7546 .functions()
7547 .iter()
7548 .map(|function| function.code())
7549 .find(|code| created_cell_used_as_capture(code).is_some())
7550 .expect("captured loop owns a cell");
7551 assert_eq!(
7552 dormant_promotion_cell_blocks(code),
7553 0,
7554 "captured loops contain no late-promotion scaffold"
7555 );
7556 }
7557 }
7558
7559 #[test]
7560 fn uncaptured_ordinary_and_loop_locals_emit_no_cell_scaffolds() {
7561 let ordinary = lower_js("function outer(value) { let copy = value; return copy; }");
7562 let classic =
7563 lower_js("function outer(limit) { for (let index = 0; index < limit; index++) {} }");
7564 let iterator = lower_js("function outer(values) { for (let value of values) {} }");
7565
7566 for (label, module) in [
7567 ("ordinary", &ordinary),
7568 ("classic", &classic),
7569 ("iterator", &iterator),
7570 ] {
7571 let entry = module.entry().get() as usize;
7572 let code = module
7573 .functions()
7574 .iter()
7575 .enumerate()
7576 .find(|(index, _)| *index != entry)
7577 .map(|(_, function)| function.code())
7578 .expect("snippet has one declared function");
7579 assert_eq!(dormant_promotion_cell_blocks(code), 0);
7580 assert!(
7581 !code
7582 .iter()
7583 .any(|instruction| matches!(instruction, Instruction::CreateArray { .. })),
7584 "{label} local storage allocates no cell"
7585 );
7586 assert!(
7587 !code
7588 .iter()
7589 .any(|instruction| matches!(instruction, Instruction::ArrayPush { .. })),
7590 "{label} local storage initializes no cell"
7591 );
7592 }
7593 }
7594
7595 #[test]
7596 fn iterator_let_allocates_inside_the_loop_but_var_allocates_before_it() {
7597 fn allocation_and_step(module: &Module<Verified>) -> (usize, usize) {
7598 module
7599 .functions()
7600 .iter()
7601 .find_map(|function| {
7602 let code = function.code();
7603 let cell = created_cell_used_as_capture(code)?;
7604 let allocation = code.iter().position(
7605 |instruction| matches!(instruction, Instruction::CreateArray { dst } | Instruction::CreateCell { dst } if *dst == cell),
7606 )?;
7607 let step = code
7608 .iter()
7609 .position(|instruction| matches!(instruction, Instruction::IteratorNext { .. }))?;
7610 Some((allocation, step))
7611 })
7612 .expect("iterator loop captures its declaration")
7613 }
7614
7615 let lexical = lower_js(
7616 "function outer(values) { const reads = []; for (let value of values) reads.push(() => value); return reads; }",
7617 );
7618 let shared = lower_js(
7619 "function outer(values) { const reads = []; for (var value of values) reads.push(() => value); return reads; }",
7620 );
7621 let (lexical_allocation, lexical_step) = allocation_and_step(&lexical);
7622 let (shared_allocation, shared_step) = allocation_and_step(&shared);
7623 assert!(
7624 lexical_step < lexical_allocation,
7625 "let creates a new cell after each iterator step"
7626 );
7627 assert!(
7628 shared_allocation < shared_step,
7629 "var creates one cell before iterator stepping begins"
7630 );
7631 }
7632
7633 #[test]
7634 fn computed_member_access_uses_a_register_key() {
7635 let module = lower_js("const o: any = {}; const k = \"a\"; const v = o[k];");
7636 assert!(any_instruction(&module, |i| matches!(
7637 i,
7638 Instruction::GetProperty { .. }
7639 )));
7640 }
7641
7642 #[test]
7643 fn spread_call_builds_an_arguments_array_with_extend() {
7644 let module = lower_js("declare const f: any; const xs = [1]; f(...xs);");
7645 assert!(any_instruction(&module, |i| matches!(
7646 i,
7647 Instruction::ArrayExtend { .. }
7648 )));
7649 assert!(any_instruction(&module, |i| matches!(
7650 i,
7651 Instruction::Call { .. }
7652 )));
7653 }
7654
7655 #[test]
7656 fn nonempty_array_pushes_elements() {
7657 let module = lower_js("const a = [1, 2, 3];");
7658 assert!(any_instruction(&module, |i| matches!(
7659 i,
7660 Instruction::ArrayPush { .. }
7661 )));
7662 }
7663
7664 #[test]
7665 fn closure_capturing_a_local_emits_a_nonempty_capture() {
7666 let module = lower_js("function outer() { const x = 1; return () => x; }");
7667 assert!(any_instruction(&module, |i| matches!(
7668 i,
7669 Instruction::CreateClosure { .. }
7670 )));
7671 assert!(max_capture_count(&module) >= 1, "the arrow captures `x`");
7672 }
7673
7674 #[test]
7675 fn ordinary_function_declarations_materialize_own_prototypes() {
7676 let module = lower_js("function Base() {}");
7677 let code = module.functions()[0].code();
7678 let constants = module.constants();
7679
7680 let key_name = |register: Register| -> String {
7685 let id = code
7686 .iter()
7687 .find_map(|instruction| match instruction {
7688 Instruction::LoadConst { dst, constant } if *dst == register => Some(*constant),
7689 _ => None,
7690 })
7691 .unwrap_or_else(|| panic!("no LoadConst defines key register {register:?}"));
7692 match &constants[id.get() as usize] {
7693 Constant::String(value) => value
7694 .to_utf8_strict()
7695 .expect("compiler-interned property key is well-formed UTF-16"),
7696 other => panic!("expected a string constant for the key, got {other:?}"),
7697 }
7698 };
7699
7700 let (closure_index, closure) = code
7701 .iter()
7702 .enumerate()
7703 .find_map(|(index, instruction)| match instruction {
7704 Instruction::CreateClosure { dst, .. } => Some((index, *dst)),
7705 _ => None,
7706 })
7707 .expect("function declaration materializes a closure");
7708
7709 let (prototype_index, prototype) = code[closure_index + 1..]
7710 .iter()
7711 .enumerate()
7712 .find_map(|(offset, instruction)| match instruction {
7713 Instruction::CreateObject { dst } => Some((closure_index + 1 + offset, *dst)),
7714 _ => None,
7715 })
7716 .expect("ordinary function gets an own prototype object");
7717
7718 let constructor_key = code[prototype_index + 1..]
7721 .iter()
7722 .find_map(|instruction| match instruction {
7723 Instruction::SetProperty { object, key, value }
7724 if *object == prototype && *value == closure =>
7725 {
7726 Some(*key)
7727 }
7728 _ => None,
7729 })
7730 .expect("prototype.constructor is assigned the closure");
7731 assert_eq!(
7732 key_name(constructor_key),
7733 "constructor",
7734 "the reverse link is stored under the key \"constructor\""
7735 );
7736
7737 let prototype_key = code[closure_index + 1..]
7740 .iter()
7741 .find_map(|instruction| match instruction {
7742 Instruction::SetProperty { object, key, value }
7743 if *object == closure && *value == prototype =>
7744 {
7745 Some(*key)
7746 }
7747 _ => None,
7748 })
7749 .expect("closure.prototype is assigned the prototype");
7750 assert_eq!(
7751 key_name(prototype_key),
7752 "prototype",
7753 "the forward link is stored under the key \"prototype\""
7754 );
7755 }
7756
7757 #[test]
7758 fn template_literal_lowers_to_string_concatenation() {
7759 let module = lower_js("const a = 1; const s = `x${a}y`;");
7760 assert!(any_instruction(&module, |i| matches!(
7761 i,
7762 Instruction::Binary {
7763 op: bamts_bytecode::BinaryOp::Add,
7764 ..
7765 }
7766 )));
7767 }
7768
7769 #[test]
7770 fn for_of_lowers_to_the_iterator_protocol() {
7771 let module = lower_js("for (const v of [1, 2]) { globalThis; }");
7772 assert!(any_instruction(&module, |i| matches!(
7773 i,
7774 Instruction::GetIterator { .. }
7775 )));
7776 assert!(any_instruction(&module, |i| matches!(
7777 i,
7778 Instruction::IteratorNext { .. }
7779 )));
7780 }
7781
7782 #[test]
7783 fn object_destructuring_reads_named_properties() {
7784 let module = lower_js("declare const obj: any; const { a, b } = obj;");
7785 assert!(any_instruction(&module, |i| matches!(
7786 i,
7787 Instruction::GetProperty { .. }
7788 )));
7789 }
7790
7791 #[test]
7792 fn regex_literal_lowers_to_create_regexp() {
7793 let module = lower_js("const r = /ab+c/gi;");
7794 assert!(any_instruction(&module, |i| matches!(
7795 i,
7796 Instruction::CreateRegExp { .. }
7797 )));
7798 }
7799
7800 #[test]
7801 fn class_with_extends_builds_prototype_chain() {
7802 let module = lower_js("class B {} class C extends B { m() { return 1; } }");
7803 assert!(any_instruction(&module, |i| matches!(
7804 i,
7805 Instruction::SetPrototype { .. }
7806 )));
7807 assert!(any_instruction(&module, |i| matches!(
7808 i,
7809 Instruction::CreateClosure { .. }
7810 )));
7811 }
7812
7813 #[test]
7814 fn derived_constructor_places_fields_between_super_and_trailing_body() {
7815 let module = lower_js(
7816 "class Base {} class Derived extends Base { field = 1; constructor() { before(); super(); after(); } }",
7817 );
7818 let constructor = module
7819 .functions()
7820 .iter()
7821 .find_map(|function| {
7822 let calls: Vec<_> = function
7823 .code()
7824 .iter()
7825 .enumerate()
7826 .filter_map(|(index, instruction)| {
7827 matches!(instruction, Instruction::Call { .. }).then_some(index)
7828 })
7829 .collect();
7830 (calls.len() == 3).then_some((function, calls))
7831 })
7832 .expect("derived constructor contains before, super, and after calls");
7833 let field = constructor
7834 .0
7835 .code()
7836 .iter()
7837 .enumerate()
7838 .find_map(|(index, instruction)| {
7839 matches!(instruction, Instruction::SetProperty { .. }).then_some(index)
7840 })
7841 .expect("derived field is initialized");
7842 assert!(constructor.1[1] < field && field < constructor.1[2]);
7843 }
7844
7845 #[test]
7846 fn implicit_derived_constructor_forwards_arguments_before_fields() {
7847 let module = lower_js("class Base {} class Derived extends Base { field = 1; }");
7848 let constructor = module
7849 .functions()
7850 .iter()
7851 .find(|function| {
7852 function
7853 .code()
7854 .iter()
7855 .any(|instruction| matches!(instruction, Instruction::ArrayExtend { .. }))
7856 })
7857 .expect("implicit derived constructor extends its arguments array");
7858 let call = constructor
7859 .code()
7860 .iter()
7861 .position(|instruction| matches!(instruction, Instruction::Call { .. }))
7862 .expect("implicit derived constructor calls its parent");
7863 let field = constructor
7864 .code()
7865 .iter()
7866 .position(|instruction| matches!(instruction, Instruction::SetProperty { .. }))
7867 .expect("implicit derived constructor initializes fields");
7868 assert!(call < field);
7869 }
7870
7871 #[test]
7872 fn unsupported_derived_super_shapes_fail_lowering() {
7873 for source in [
7874 "class Base {} class Derived extends Base { constructor() {} }",
7875 "class Base {} class Derived extends Base { constructor() { super(); super(); } }",
7876 "class Base {} class Derived extends Base { constructor() { if (flag) super(); } }",
7877 "class Base {} class Derived extends Base { constructor() { this.x = 1; super(); } }",
7878 ] {
7879 let source = Arc::new(SourceText::new(source));
7880 let scanned = scan(SourceId::new(0), ScriptKind::TypeScript, source);
7881 let parsed = parse(scanned);
7882 let error = lower(
7883 parsed.product(),
7884 LowerOptions {
7885 javascript_compatibility: true,
7886 },
7887 )
7888 .expect_err("unsupported derived constructor shape fails lowering");
7889 assert!(matches!(
7890 error.kind,
7891 LowerErrorKind::Unsupported(
7892 UnsupportedConstruct::DerivedConstructorShape
7893 | UnsupportedConstruct::ThisBeforeDerivedSuper
7894 )
7895 ));
7896 }
7897 }
7898
7899 #[test]
7900 fn private_field_creates_a_private_name() {
7901 let module = lower_js("class C { #x = 1; read() { return this.#x; } }");
7902 assert!(any_instruction(&module, |i| matches!(
7903 i,
7904 Instruction::CreatePrivateName { .. }
7905 )));
7906 assert!(any_instruction(&module, |i| matches!(
7907 i,
7908 Instruction::LoadThis { .. }
7909 )));
7910 }
7911
7912 #[test]
7913 fn generator_sets_the_flag_and_suspends() {
7914 let module = lower_js("function* g() { yield 1; yield 2; }");
7915 assert!(
7916 module
7917 .functions()
7918 .iter()
7919 .any(|function| function.flags().is_generator)
7920 );
7921 assert!(any_instruction(&module, |i| matches!(
7922 i,
7923 Instruction::Suspend { .. }
7924 )));
7925 }
7926
7927 #[test]
7928 fn async_await_suspends() {
7929 let module = lower_js("async function f(p: any) { return await p; }");
7930 assert!(
7931 module
7932 .functions()
7933 .iter()
7934 .any(|function| function.flags().is_async)
7935 );
7936 assert!(any_instruction(&module, |i| matches!(
7937 i,
7938 Instruction::Suspend { .. }
7939 )));
7940 }
7941
7942 #[test]
7943 fn free_names_load_from_the_environment() {
7944 let module = lower_js("const keys = Object.keys({});");
7945 assert!(any_instruction(&module, |i| matches!(
7946 i,
7947 Instruction::LoadGlobal { .. }
7948 )));
7949 }
7950
7951 #[test]
7952 fn try_finally_routes_completions_through_the_finalizer() {
7953 let module = lower_js(
7954 "function f(x: any) { while (x) { try { return 1; } finally { x = 0; } } return 2; }",
7955 );
7956 assert!(
7958 module
7959 .functions()
7960 .iter()
7961 .any(|function| !function.handlers().is_empty())
7962 );
7963 assert_round_trips(&module);
7964 }
7965
7966 #[test]
7967 fn optional_member_call_skips_arguments_and_preserves_receiver() {
7968 let key_is_method = |code: &[Instruction], constants: &[Constant], register: Register| {
7969 let constant = code
7970 .iter()
7971 .find_map(|instruction| match instruction {
7972 Instruction::LoadConst { dst, constant } if *dst == register => Some(*constant),
7973 _ => None,
7974 })
7975 .unwrap_or_else(|| panic!("no LoadConst defines property key {register:?}"));
7976 match &constants[constant.get() as usize] {
7977 Constant::String(value) => {
7978 value.to_utf8_strict().is_ok_and(|value| value == "method")
7979 }
7980 _ => false,
7981 }
7982 };
7983
7984 let assert_optional_call = |src: &str, expect_method_jump: bool| {
7985 let module = lower_js(src);
7986 let code = module.functions()[0].code();
7987 let constants = module.constants();
7988
7989 let (member_index, callee, object) = code
7990 .iter()
7991 .enumerate()
7992 .find_map(|(index, instruction)| match instruction {
7993 Instruction::GetProperty { dst, object, key }
7994 if key_is_method(code, constants, *key) =>
7995 {
7996 Some((index, *dst, *object))
7997 }
7998 _ => None,
7999 })
8000 .expect("optional member call loads its method");
8001
8002 let (call_index, call_dst, this_value) = code
8003 .iter()
8004 .enumerate()
8005 .find_map(|(index, instruction)| match instruction {
8006 Instruction::Call {
8007 dst,
8008 callee: call_callee,
8009 this_value,
8010 ..
8011 } if *call_callee == callee => Some((index, *dst, *this_value)),
8012 _ => None,
8013 })
8014 .expect("optional member call invokes the loaded method");
8015 assert_eq!(
8016 this_value, object,
8017 "the call keeps the member object as this"
8018 );
8019
8020 let result_move_index = code[call_index + 1..]
8021 .iter()
8022 .enumerate()
8023 .find_map(|(offset, instruction)| match instruction {
8024 Instruction::Move { src, .. } if *src == call_dst => {
8025 Some(call_index + 1 + offset)
8026 }
8027 _ => None,
8028 })
8029 .expect("the call result is moved into a shared result register");
8030 let merge = result_move_index + 1;
8031
8032 let (object_jump_index, object_jump_target) = code[..member_index]
8033 .iter()
8034 .enumerate()
8035 .rev()
8036 .find_map(|(index, instruction)| match instruction {
8037 Instruction::JumpIfTrue { condition, target }
8038 if code[..index].iter().any(|prior| {
8039 matches!(
8040 prior,
8041 Instruction::Binary { dst, op: BinaryOp::Equal, left, .. }
8042 if *dst == *condition && *left == object
8043 )
8044 }) =>
8045 {
8046 Some((index, *target))
8047 }
8048 _ => None,
8049 })
8050 .expect("optional object test branches before the member read");
8051 assert!(
8052 code[object_jump_index + 1..call_index]
8053 .iter()
8054 .any(|instruction| matches!(instruction, Instruction::Call { .. })),
8055 "the argument side effect is emitted before the member call"
8056 );
8057 assert_eq!(
8058 object_jump_target.get() as usize,
8059 merge,
8060 "the object-nullish branch jumps to the instruction after the result assignment"
8061 );
8062
8063 if expect_method_jump {
8064 let method_jump_target = code[member_index + 1..call_index]
8065 .iter()
8066 .enumerate()
8067 .rev()
8068 .find_map(|(offset, instruction)| {
8069 let index = member_index + 1 + offset;
8070 match instruction {
8071 Instruction::JumpIfTrue { condition, target }
8072 if code[member_index + 1..index].iter().any(|prior| {
8073 matches!(
8074 prior,
8075 Instruction::Binary { dst, op: BinaryOp::Equal, left, .. }
8076 if *dst == *condition && *left == callee
8077 )
8078 }) =>
8079 {
8080 Some(*target)
8081 }
8082 _ => None,
8083 }
8084 })
8085 .expect("optional method test branches before the call");
8086 assert_eq!(
8087 method_jump_target.get() as usize,
8088 merge,
8089 "the method-nullish branch jumps to the same merge point"
8090 );
8091 }
8092 };
8093
8094 assert_optional_call(
8095 "declare const obj: any; declare function side_effect(): number; obj?.method(side_effect());",
8096 false,
8097 );
8098 assert_optional_call(
8099 "declare const obj: any; declare function side_effect(): number; obj?.method?.(side_effect());",
8100 true,
8101 );
8102 }
8103
8104 #[test]
8105 fn arrow_rest_loads_the_activations_own_arguments() {
8106 let module = lower_js("const collect = (...options) => options; collect(1, 2);");
8111 let entry = module.entry().get() as usize;
8112 let arrow = module
8113 .functions()
8114 .iter()
8115 .enumerate()
8116 .find(|(index, _)| *index != entry)
8117 .map(|(_, function)| function)
8118 .expect("the arrow is the sole non-entry function");
8119 let code = arrow.code();
8120 assert!(
8121 code.iter()
8122 .any(|instruction| matches!(instruction, Instruction::LoadArguments { .. })),
8123 "the arrow body loads its own activation arguments for rest"
8124 );
8125 assert!(
8126 code.iter()
8127 .any(|instruction| matches!(instruction, Instruction::GetIterator { .. })),
8128 "rest collection iterates the loaded arguments"
8129 );
8130 assert!(
8131 code.iter()
8132 .any(|instruction| matches!(instruction, Instruction::ArrayPush { .. })),
8133 "rest collection pushes into the rest array"
8134 );
8135 assert_round_trips(&module);
8136 }
8137
8138 #[test]
8139 fn regular_function_rest_with_fixed_parameters_loads_arguments() {
8140 let module = lower_js("function f(a, ...rest) { return rest; }");
8143 let entry = module.entry().get() as usize;
8144 let function = module
8145 .functions()
8146 .iter()
8147 .enumerate()
8148 .find(|(index, _)| *index != entry)
8149 .map(|(_, function)| function)
8150 .expect("the regular function is the sole non-entry function");
8151 let code = function.code();
8152 assert!(
8153 code.iter()
8154 .any(|instruction| matches!(instruction, Instruction::LoadArguments { .. })),
8155 "the function loads its own arguments for rest"
8156 );
8157 assert!(
8158 code.iter()
8159 .any(|instruction| matches!(instruction, Instruction::GetIterator { .. })),
8160 "rest collection iterates the loaded arguments"
8161 );
8162 assert!(
8163 code.iter()
8164 .any(|instruction| matches!(instruction, Instruction::IteratorNext { .. })),
8165 "the fixed parameter is discarded by stepping the iterator"
8166 );
8167 assert_round_trips(&module);
8168 }
8169
8170 #[test]
8171 fn arrow_lexical_arguments_is_captured_not_loaded() {
8172 let module = lower_js("function outer() { const read = () => arguments; return read(); }");
8177 let arrow = module
8178 .functions()
8179 .iter()
8180 .find(|function| function.capture_count() >= 1)
8181 .expect("the arrow captures `arguments` from outer");
8182 assert!(
8183 !arrow
8184 .code()
8185 .iter()
8186 .any(|instruction| matches!(instruction, Instruction::LoadArguments { .. })),
8187 "the arrow reads `arguments` from its capture, not its own activation"
8188 );
8189 assert_round_trips(&module);
8190 }
8191
8192 fn assert_round_trips(module: &Module<Verified>) {
8193 let bytes = module.encode();
8194 decode_verified(&bytes, &DecodeLimits::default())
8195 .expect("a verified module re-decodes and re-verifies");
8196 }
8197}