use std::cell::Cell; use std::collections::HashMap; use crate::emit_ctx::{LabelCtx, ScopeCtx}; use crate::symbol_table::{Binding, SymbolTable}; use oxide_bytecode::module::CompiledModule; use oxide_bytecode::module::UpvalueCapture; use oxide_bytecode::opcode::{self, OpCode}; pub use crate::hash::{compiled_module_hash, structural_hash}; use crate::symbol_table::ScopeKind; pub use oxide_bytecode::module::Constant; pub use oxide_parser::VariableDeclarationKind; pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement, UnaryOperator}; pub struct Compiler; pub(crate) fn is_int_literal(value: f64) -> bool { value.fract() == 0.0 && value >= i32::MIN as f64 && value <= i32::MAX as f64 } pub(crate) fn is_side_effect_free(expr: &Expression) -> bool { let mut stack = vec![expr]; while let Some(expr) = stack.pop() { match expr { Expression::NumericLiteral(_) | Expression::StringLiteral(_) | Expression::BooleanLiteral(_) | Expression::NullLiteral(_) | Expression::Identifier(_) | Expression::RegExpLiteral(_) | Expression::ThisExpression(_) => {} Expression::ParenthesizedExpression(p) => stack.push(&p.expression), Expression::BinaryExpression(bin) => { stack.push(&bin.left); stack.push(&bin.right); } Expression::UnaryExpression(un) if !matches!(un.operator, UnaryOperator::Delete) => { stack.push(&un.argument); } _ => return false, } } true } const BUILTIN_GLOBALS: &[&str] = &[ "NaN", "undefined", "Infinity", "globalThis", "Object", "Array", "String", "Number", "Boolean", "Function", "Error", "TypeError", "ReferenceError", "RangeError", "SyntaxError", "URIError", "EvalError", "Math", "JSON", "Promise", "Date", "Set", "Map", "RegExp", "Symbol", "parseInt", "parseFloat", "isNaN", "isFinite", "Proxy", "WeakMap", "WeakSet", "WeakRef", "FinalizationRegistry", "Atomics", "SharedArrayBuffer", "ArrayBuffer", "DataView", "Iterator", "BigInt", "Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Array", "Int32Array", "Uint32Array", "Float32Array", "Float64Array", "BigInt64Array", "BigUint64Array", "Reflect", "escape", "unescape", "encodeURI", "decodeURI", "encodeURIComponent", "decodeURIComponent", ]; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[allow(dead_code)] pub(crate) enum Label { IfElse(u32), IfEnd(u32), WhileStart(u32), WhileEnd(u32), ForStart(u32), ForUpdate(u32), ForEnd(u32), TernaryEnd(u32), TernaryElse(u32), DoWhileStart(u32), DoWhileEnd(u32), ForInStart(u32), ForInEnd(u32), ForOfStart(u32), ForOfEnd(u32), SwitchEnd(u32), SwitchCase(u32, u32), CatchBody(u32), FinallyBody(u32), TryEnd(u32), LabeledEnd(u32), } pub(crate) struct JumpFixup { pub(crate) pc: usize, pub(crate) label: Label, pub(crate) opcode: OpCode, pub(crate) rd: u8, } pub(crate) struct FieldBuffer { pub(crate) bytecode: Vec, pub(crate) labels: Vec<(Label, usize)>, pub(crate) fixups: Vec, } /// A labeled-statement scope active during emission. `break label` targets /// `break_label`; `continue label` targets `continue_label` (only set when the /// labeled statement directly wraps an iteration statement). #[derive(Debug, Clone)] pub(crate) struct LabelScope { pub(crate) name: String, pub(crate) break_label: Label, pub(crate) continue_label: Option