//! The compiler: a syntax tree to bytecode, one [`Code`] per function, script or eval code. //! //! Variables are looked up by name at run time through the environment chain, as ECMA-262 5.1 //! section 10 describes, so `with` and direct `eval` need no special analysis. Declarations are //! hoisted: each code lists its `var` names and function declarations, and the interpreter binds //! them when the code is entered (section 10.5). //! //! The compiler counts the operand stack depth as it emits, so `break`, `continue` and `return` //! know how many values to drop. A jump out of a `try` with a `finally` block goes through the //! block: it pushes a completion (a value and a kind number) and jumps to the block, whose //! `EndFinally` instruction goes on with the jump afterwards. Long left-nested runs of binary //! operators and member accesses, right-nested assignment chains, prefix operators, `else if` //! chains and labels are compiled with loops; every other kind of nesting is counted, and code //! nested deeper than [`MAX_DEPTH`] gives a `SyntaxError` instead of exhausting the stack. use std::collections::{HashMap, HashSet}; use std::rc::Rc; use crate::ast::{ AssignOp, Ast, BinaryOp, Expr, ExprId, ForInTarget, ForInit, FunctionId, FunctionKind, LogicalOp, PropertyValue, Stmt, StmtId, UnaryOp, UpdateOp, VarDecl, }; use crate::bytecode::{Code, CodeKind, Handler, Op, Source, fallthrough_effect, verify}; use crate::error::{SyntaxError, TOO_DEEP}; use crate::heap::Key; use crate::value::Str; /// How deeply the compiler may recurse: past the parser's default nesting limit, and shallow /// enough for a 1 MiB stack. pub(crate) const MAX_DEPTH: u32 = 600; /// Why code could not be compiled. #[derive(Debug)] pub(crate) enum CompileFail { Syntax(SyntaxError), /// The compiler made code the verifier rejects. Internal(String), } type CResult = Result; fn internal(message: impl Into) -> CResult { Err(CompileFail::Internal(message.into())) } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Label(usize); /// Where `break`, `continue` and `return` go. #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Exit { /// To the end of the jump context at this index. Break(usize), /// To the continue point of the loop at this index. Continue(usize), Return, } enum Ctx { /// A loop, a `switch` or a labeled statement. Jump { labels: Vec, is_loop: bool, plain_break: bool, brk: Label, cont: Option