use std::collections::HashMap; use std::sync::{Arc, Mutex}; use swc_common::errors::{DiagnosticBuilder, Emitter}; use swc_common::{errors::Handler, FileName, SourceMap, Spanned}; use swc_ecma_ast::EsVersion; use swc_ecma_parser::{Syntax, TsConfig}; use valuescript_vm::operations::to_i32; use crate::asm::{ Array, Builtin, Class, Definition, DefinitionContent, FnLine, Function, Instruction, Lazy, Module, Number, Object, Pointer, Register, Value, }; use crate::diagnostic::{Diagnostic, DiagnosticContainer, DiagnosticReporter}; use crate::expression_compiler::{value_from_literal, CompiledExpression, ExpressionCompiler}; use crate::function_compiler::{FunctionCompiler, Functionish}; use crate::ident::Ident; use crate::name_allocator::{ident_from_str, NameAllocator}; use crate::scope::OwnerId; use crate::scope_analysis::ScopeAnalysis; struct DiagnosticCollector { diagnostics: Arc>>, } impl Emitter for DiagnosticCollector { fn emit(&mut self, db: &DiagnosticBuilder<'_>) { if let Some(diagnostic) = Diagnostic::from_swc(db) { self.diagnostics.lock().unwrap().push(diagnostic) } } } pub fn parse(source: &str) -> (Option, Vec) { let source_map = Arc::::default(); let diagnostics_arc = Arc::new(Mutex::new(Vec::::new())); let handler = Handler::with_emitter( true, false, Box::new(DiagnosticCollector { diagnostics: diagnostics_arc.clone(), }), ); let swc_compiler = swc::Compiler::new(source_map.clone()); let file = source_map.new_source_file(FileName::Anon, source.into()); let result = swc_compiler.parse_js( file, &handler, EsVersion::Es2022, Syntax::Typescript(TsConfig::default()), swc::config::IsModule::Bool(true), None, ); let mut diagnostics = Vec::::new(); std::mem::swap(&mut diagnostics, &mut *diagnostics_arc.lock().unwrap()); (result.ok(), diagnostics) } #[derive(Default)] pub struct CompilerOutput { pub diagnostics: Vec, pub module: Module, } pub fn compile_program(program: &swc_ecma_ast::Program) -> CompilerOutput { let compiler = ModuleCompiler::compile_program(program); CompilerOutput { diagnostics: compiler.diagnostics, module: compiler.module, } } pub fn compile_module(source: &str) -> CompilerOutput { let (program_optional, mut diagnostics) = parse(source); let mut compiler_output = match program_optional { Some(program) => compile_program(&program), None => CompilerOutput::default(), }; diagnostics.append(&mut compiler_output.diagnostics); compiler_output.diagnostics = diagnostics; compiler_output } #[derive(Default)] pub struct ModuleCompiler { pub diagnostics: Vec, pub definition_allocator: NameAllocator, pub scope_analysis: ScopeAnalysis, pub constants_map: HashMap, pub module: Module, } impl DiagnosticContainer for ModuleCompiler { fn diagnostics_mut(&mut self) -> &mut Vec { &mut self.diagnostics } } impl ModuleCompiler { fn allocate_defn(&mut self, name: &str) -> Pointer { let allocated_name = self.definition_allocator.allocate(&name.to_string()); Pointer { name: allocated_name, } } fn allocate_defn_numbered(&mut self, name: &str) -> Pointer { let allocated_name = self.definition_allocator.allocate_numbered(name); Pointer { name: allocated_name, } } fn compile_program(program: &swc_ecma_ast::Program) -> Self { use swc_ecma_ast::Program::*; let module = match program { Module(module) => module, Script(script) => { let mut self_ = Self::default(); self_.error(script.span, "Scripts are not supported"); return self_; } }; let mut scope_analysis = ScopeAnalysis::run(module); let mut scope_analysis_diagnostics = Vec::::new(); std::mem::swap( &mut scope_analysis_diagnostics, &mut scope_analysis.diagnostics, ); let mut self_ = Self { scope_analysis, diagnostics: scope_analysis_diagnostics, ..Default::default() }; self_.compile_module(module); self_ } fn compile_module(&mut self, module: &swc_ecma_ast::Module) { for module_item in &module.body { self.compile_module_item(module_item); } } fn compile_module_item(&mut self, module_item: &swc_ecma_ast::ModuleItem) { use swc_ecma_ast::ModuleItem::*; match module_item { ModuleDecl(module_decl) => self.compile_module_decl(module_decl), Stmt(stmt) => self.compile_module_statement(stmt), } } fn compile_module_decl(&mut self, module_decl: &swc_ecma_ast::ModuleDecl) { use swc_ecma_ast::ModuleDecl::*; match module_decl { Import(import) => self.compile_import(import), ExportDecl(ed) => self.compile_export_decl(ed), ExportNamed(en) => self.compile_named_export(en), ExportDefaultDecl(edd) => self.compile_export_default_decl(edd), ExportDefaultExpr(ede) => { let value = match &*ede.expr { // TODO: Just do identifiers in compile_expr swc_ecma_ast::Expr::Ident(ident) => self .scope_analysis .lookup(&Ident::from_swc_ident(ident)) .map(|name| name.value.clone()), expr => self.compile_expr(expr), }; match value { Some(value) => { self.module.export_default = value; } None => { self.todo( module_decl.span(), "Failed to evaluate export default expression", ); } }; } ExportAll(_) => self.todo(module_decl.span(), "ExportAll declaration"), TsImportEquals(_) => self.not_supported(module_decl.span(), "TsImportEquals declaration"), TsExportAssignment(_) => { self.not_supported(module_decl.span(), "TsExportAssignment declaration") } TsNamespaceExport(_) => self.todo(module_decl.span(), "TsNamespaceExport declaration"), } } fn compile_module_statement(&mut self, stmt: &swc_ecma_ast::Stmt) { use swc_ecma_ast::Stmt::*; match stmt { Decl(decl) => self.compile_module_level_decl(decl), Block(block) => self.not_supported(block.span, "module level Block statement"), Empty(_) => {} Debugger(debugger) => self.not_supported(debugger.span, "module level Debugger statement"), With(with) => self.not_supported(with.span, "module level With statement"), Return(return_) => self.not_supported(return_.span, "module level Return statement"), Labeled(labeled) => self.not_supported(labeled.span, "module level Labeled statement"), Break(break_) => self.not_supported(break_.span, "module level Break statement"), Continue(continue_) => self.not_supported(continue_.span, "module level Continue statement"), If(if_) => self.not_supported(if_.span, "module level If statement"), Switch(switch) => self.not_supported(switch.span, "module level Switch statement"), Throw(throw) => self.not_supported(throw.span, "module level Throw statement"), Try(try_) => self.not_supported(try_.span, "module level Try statement"), While(while_) => self.not_supported(while_.span, "module level While statement"), DoWhile(do_while) => self.not_supported(do_while.span, "module level DoWhile statement"), For(for_) => self.not_supported(for_.span, "module level For statement"), ForIn(for_in) => self.not_supported(for_in.span, "module level ForIn statement"), ForOf(for_of) => self.not_supported(for_of.span, "module level ForOf statement"), Expr(expr) => self.not_supported(expr.span, "module level Expr statement"), }; } fn compile_module_level_decl(&mut self, decl: &swc_ecma_ast::Decl) { use swc_ecma_ast::Decl::*; match decl { Class(class) => { self.compile_class(None, Some(&class.ident), &class.class); } Fn(fn_) => self.compile_fn_decl(false, fn_), Var(var_decl) => { self.compile_var_decl(var_decl, false); } TsInterface(_) => {} TsTypeAlias(_) => {} TsEnum(ts_enum) => self.compile_enum_decl(false, ts_enum), TsModule(ts_module) => self.todo(ts_module.span, "TsModule declaration"), }; } fn compile_var_decl(&mut self, var_decl: &swc_ecma_ast::VarDecl, export: bool) { if var_decl.declare { // Uses the `declare` typescript keyword. Nothing needed to support this. return; } if var_decl.kind != swc_ecma_ast::VarDeclKind::Const { // Only `const` variables in the global area. They cannot be mutated, so might as well // insist they are `const` for clarity. self.not_supported(var_decl.span, "non-const module level variable"); } for decl in &var_decl.decls { let ident = match &decl.name { swc_ecma_ast::Pat::Ident(bi) => Some(&bi.id), _ => { self.todo(decl.name.span(), "Module level destructuring"); None } }; let init = match &decl.init { Some(_) => &decl.init, _ => { self.error(decl.init.span(), "const variable without initializer"); &None } }; if let (Some(ident), Some(init)) = (ident, init) { let value_opt = self.compile_expr(init); let value = match value_opt { Some(value) => value, None => { self.todo( init.span(), "Determine whether initializer can be statically evaluated", ); Value::String("(Static eval failed)".to_string()) } }; let pointer = match self.scope_analysis.lookup(&Ident::from_swc_ident(ident)) { Some(name) => match &name.value { Value::Pointer(p) => p.clone(), _ => { self.internal_error(ident.span(), "Expected pointer for module constant"); continue; } }, None => { self.internal_error(ident.span(), "Failed to lookup name"); continue; } }; self.constants_map.insert(pointer.clone(), value.clone()); self.module.definitions.push(Definition { pointer: pointer.clone(), content: DefinitionContent::Value(value), }); if export { self.module.export_star.properties.push(( Value::String(ident.sym.to_string()), Value::Pointer(pointer), )); } } } } fn compile_fn_decl(&mut self, export: bool, fn_: &swc_ecma_ast::FnDecl) { let fn_name = fn_.ident.sym.to_string(); let pointer = match self .scope_analysis .lookup_value(&OwnerId::Module, &Ident::from_swc_ident(&fn_.ident)) { Some(Value::Pointer(p)) => p, _ => { self.internal_error( fn_.ident.span, &format!("Pointer for {} should have been in scope", fn_name), ); return; } }; if export { self.module.export_star.properties.push(( Value::String(fn_name.clone()), Value::Pointer(pointer.clone()), )); } let mut fn_defns = self.compile_fn( pointer, Some(fn_name), Functionish::Fn(Some(fn_.ident.clone()), fn_.function.clone()), ); self.module.definitions.append(&mut fn_defns); } fn compile_enum_decl(&mut self, export: bool, ts_enum: &swc_ecma_ast::TsEnumDecl) { let pointer = match self .scope_analysis .lookup_value(&OwnerId::Module, &Ident::from_swc_ident(&ts_enum.id)) { Some(Value::Pointer(p)) => p, _ => { self.internal_error( ts_enum.id.span, &format!("Pointer for {} should have been in scope", ts_enum.id.sym), ); return; } }; if export { self.module.export_star.properties.push(( Value::String(ts_enum.id.sym.to_string()), Value::Pointer(pointer.clone()), )); } let enum_value = self.compile_enum_value(ts_enum); self.module.definitions.push(Definition { pointer, content: DefinitionContent::Value(enum_value), }); } fn compile_export_default_decl(&mut self, edd: &swc_ecma_ast::ExportDefaultDecl) { use swc_ecma_ast::DefaultDecl; match &edd.decl { DefaultDecl::Class(class) => { let pointer = self.compile_class(None, class.ident.as_ref(), &class.class); self.module.export_default = Value::Pointer(pointer); } DefaultDecl::Fn(fn_) => { let (fn_name, defn) = match &fn_.ident { Some(ident) => { let fn_name = ident.sym.to_string(); let defn = match self .scope_analysis .lookup_value(&OwnerId::Module, &Ident::from_swc_ident(ident)) { Some(Value::Pointer(p)) => p, _ => { self.internal_error( ident.span, &format!("Definition for {} should have been in scope", fn_name), ); return; } }; (Some(fn_name), defn) } None => (None, self.allocate_defn_numbered("_anon")), }; self.module.export_default = Value::Pointer(defn.clone()); let mut fn_defns = self.compile_fn( defn, fn_name, Functionish::Fn(fn_.ident.clone(), fn_.function.clone()), ); self.module.definitions.append(&mut fn_defns); } DefaultDecl::TsInterfaceDecl(_) => { // Nothing to do } } } fn compile_export_decl(&mut self, ed: &swc_ecma_ast::ExportDecl) { use swc_ecma_ast::Decl; match &ed.decl { Decl::Class(class) => { let class_name = class.ident.sym.to_string(); self.compile_class(Some(class_name), Some(&class.ident), &class.class); } Decl::Fn(fn_) => self.compile_fn_decl(true, fn_), Decl::Var(var_decl) => { self.compile_var_decl(var_decl, true); } Decl::TsInterface(_) => {} Decl::TsTypeAlias(_) => {} Decl::TsEnum(ts_enum) => self.compile_enum_decl(true, ts_enum), Decl::TsModule(ts_module) => self.todo(ts_module.span, "TsModule declaration in export"), }; } fn compile_named_export(&mut self, en: &swc_ecma_ast::NamedExport) { use swc_ecma_ast::ExportSpecifier::*; use swc_ecma_ast::ModuleExportName; if en.type_only { return; } for specifier in &en.specifiers { match specifier { Named(named) => { if named.is_type_only { continue; } let orig_name = match &named.orig { ModuleExportName::Ident(ident) => ident, ModuleExportName::Str(_) => { self.todo(named.span, "exporting a non-identifier"); continue; } }; let export_name = match &named.exported { Some(ModuleExportName::Ident(ident)) => ident.sym.to_string(), Some(ModuleExportName::Str(str_)) => { self.todo(str_.span, "exporting a non-identifier"); "_todo_export_non_ident".to_string() } None => orig_name.sym.to_string(), }; let defn = match &en.src { Some(src) => { let defn = self.allocate_defn(&export_name); self.module.definitions.push(Definition { pointer: defn.clone(), content: DefinitionContent::Lazy(Lazy { body: match orig_name.sym.to_string() == "default" { true => vec![FnLine::Instruction(Instruction::Import( Value::String(src.value.to_string()), Register::return_(), ))], false => vec![ FnLine::Instruction(Instruction::ImportStar( Value::String(src.value.to_string()), Register::return_(), )), FnLine::Instruction(Instruction::Sub( Value::Register(Register::return_()), Value::String(orig_name.sym.to_string()), Register::return_(), )), ], }, }), }); Some(defn) } None => match self .scope_analysis .lookup_value(&OwnerId::Module, &Ident::from_swc_ident(orig_name)) { Some(Value::Pointer(p)) => Some(p), lookup_result => { self.internal_error( named.orig.span(), &format!( "{} should have been a pointer, but it was {:?}, ref: {:?}", orig_name, lookup_result, self.scope_analysis.refs.get(&orig_name.span) ), ); None } }, }; if let Some(defn) = defn { if export_name == "default" { self.module.export_default = Value::Pointer(defn); } else { self .module .export_star .properties .push((Value::String(export_name), Value::Pointer(defn))); } } } Default(_) => { // It's not clear if this can actually be hit. The SWC docs suggest: // `export v from 'mod';` // but then it refuses to actually parse that. self.todo(specifier.span(), "exporting a default module export"); } Namespace(namespace) => { let namespace_name = match &namespace.name { ModuleExportName::Ident(ident) => ident.sym.to_string(), ModuleExportName::Str(_) => { self.internal_error(namespace.span, "exporting a non-identifier"); "_todo_export_non_ident".to_string() } }; let defn = self.allocate_defn(&namespace_name); let src = match &en.src { Some(src) => src.value.to_string(), None => { self.internal_error(namespace.span, "exporting a namespace without a source"); "_error_export_namespace_without_src".to_string() } }; self.module.definitions.push(Definition { pointer: defn.clone(), content: DefinitionContent::Lazy(Lazy { body: vec![FnLine::Instruction(Instruction::ImportStar( Value::String(src), Register::return_(), ))], }), }); if namespace_name == "default" { self.module.export_default = Value::Pointer(defn); } else { self .module .export_star .properties .push((Value::String(namespace_name), Value::Pointer(defn))); } } } } } fn compile_import(&mut self, import: &swc_ecma_ast::ImportDecl) { if import.type_only { return; } let import_path = import.src.value.to_string(); for specifier in &import.specifiers { use swc_ecma_ast::ImportSpecifier::*; use swc_ecma_ast::ModuleExportName; match specifier { Named(named) => { if named.is_type_only { continue; } let local_name = named.local.sym.to_string(); let external_name = match &named.imported { Some(ModuleExportName::Ident(ident)) => ident.sym.to_string(), Some(ModuleExportName::Str(str_)) => { self.todo( str_.span, "importing a module export by string (is this a real thing?)", ); "_todo_import_string".to_string() } None => local_name.clone(), }; let pointer = match self .scope_analysis .lookup_value(&OwnerId::Module, &Ident::from_swc_ident(&named.local)) { Some(Value::Pointer(p)) => p, _ => { self.internal_error( named.span, &format!("Imported name {} should have been a pointer", local_name), ); self.allocate_defn(local_name.as_str()) } }; self.module.definitions.push(Definition { pointer, content: DefinitionContent::Lazy(Lazy { body: vec![ FnLine::Instruction(Instruction::ImportStar( Value::String(import_path.clone()), Register::return_(), )), FnLine::Instruction(Instruction::Sub( Value::Register(Register::return_()), Value::String(external_name), Register::return_(), )), ], }), }); } Default(default) => { let local_name = default.local.sym.to_string(); let pointer = match self .scope_analysis .lookup_value(&OwnerId::Module, &Ident::from_swc_ident(&default.local)) { Some(Value::Pointer(p)) => p, _ => { self.internal_error( default.span, &format!("Imported name {} should have been a pointer", local_name), ); self.allocate_defn(local_name.as_str()) } }; self.module.definitions.push(Definition { pointer, content: DefinitionContent::Lazy(Lazy { body: vec![FnLine::Instruction(Instruction::Import( Value::String(import_path.clone()), Register::return_(), ))], }), }); } Namespace(namespace) => { let local_name = namespace.local.sym.to_string(); let pointer = match self .scope_analysis .lookup_value(&OwnerId::Module, &Ident::from_swc_ident(&namespace.local)) { Some(Value::Pointer(p)) => p, _ => { self.internal_error( namespace.span, &format!("Imported name {} should have been a pointer", local_name), ); self.allocate_defn(local_name.as_str()) } }; self.module.definitions.push(Definition { pointer, content: DefinitionContent::Lazy(Lazy { body: vec![FnLine::Instruction(Instruction::ImportStar( Value::String(import_path.clone()), Register::return_(), ))], }), }); } } } } fn compile_fn( &mut self, defn_pointer: Pointer, fn_name: Option, functionish: Functionish, ) -> Vec { let (defn, mut diagnostics) = FunctionCompiler::compile(self, defn_pointer, fn_name, functionish); self.diagnostics.append(&mut diagnostics); defn } fn compile_class( &mut self, export_name: Option, ident: Option<&swc_ecma_ast::Ident>, class: &swc_ecma_ast::Class, ) -> Pointer { let mut constructor: Value = Value::Void; let mut prototype: Object = Object::default(); let mut static_: Object = Object::default(); let mut dependent_definitions: Vec; let defn_name = match ident { Some(ident) => match self .scope_analysis .lookup_value(&OwnerId::Module, &Ident::from_swc_ident(ident)) { Some(Value::Pointer(p)) => p, _ => { self.internal_error( class.span, // FIXME: make class_name ident and use that span &format!("Definition for {} should have been in scope", ident.sym), ); self.allocate_defn_numbered("_scope_error") } }, None => self.allocate_defn_numbered("_anon"), }; if let Some(export_name) = export_name { self.module.export_star.properties.push(( Value::String(export_name), Value::Pointer(defn_name.clone()), )); } let mut member_initializers_fnc = FunctionCompiler::new(self, OwnerId::Span(class.span)); for class_member in &class.body { match class_member { swc_ecma_ast::ClassMember::ClassProp(class_prop) => { if class_prop.is_static { member_initializers_fnc.todo(class_prop.span, "static props"); continue; } let mut ec = ExpressionCompiler { fnc: &mut member_initializers_fnc, }; let compiled_key = ec.prop_name(&class_prop.key); let compiled_value = match &class_prop.value { None => CompiledExpression::new(Value::Undefined, vec![]), Some(expr) => ec.compile(expr, None), }; ec.fnc.push(Instruction::SubMov( compiled_key.value.clone(), compiled_value.value.clone(), Register::this(), )); ec.fnc.release_ce(compiled_key); ec.fnc.release_ce(compiled_value); } swc_ecma_ast::ClassMember::PrivateProp(private_prop) => { member_initializers_fnc.todo(private_prop.span, "private props") } _ => {} } } let mut member_initializers_assembly = Vec::::new(); member_initializers_assembly.append(&mut member_initializers_fnc.current.body); // Include any other definitions that were created by the member initializers member_initializers_fnc.process_queue(); dependent_definitions = std::mem::take(&mut member_initializers_fnc.definitions); let mut has_constructor = false; for class_member in &class.body { if let swc_ecma_ast::ClassMember::Constructor(ctor) = class_member { has_constructor = true; let ctor_defn_name = self.allocate_defn(&format!("{}_constructor", defn_name.name)); dependent_definitions.append(&mut self.compile_fn( ctor_defn_name.clone(), None, Functionish::Constructor( member_initializers_assembly.clone(), class.span, ctor.clone(), ), )); constructor = Value::Pointer(ctor_defn_name); } } if !member_initializers_assembly.is_empty() && !has_constructor { let ctor_defn_name = self.allocate_defn(&format!("{}_constructor", defn_name.name)); constructor = Value::Pointer(ctor_defn_name.clone()); dependent_definitions.push(Definition { pointer: ctor_defn_name, content: DefinitionContent::Function(Function { is_generator: false, parameters: vec![], body: member_initializers_assembly, }), }); } for class_member in &class.body { use swc_ecma_ast::ClassMember::*; match class_member { Constructor(_) => {} Method(method) => { let name = match &method.key { swc_ecma_ast::PropName::Ident(ident) => Value::String(ident.sym.to_string()), swc_ecma_ast::PropName::Computed(computed) => { let value_opt = self.compile_expr(&computed.expr); match value_opt { None => { self.todo( computed.span, "Couldn't statically evaluate computed prop name", ); continue; } Some(value) => value, } } _ => { self.todo(method.span, "Non-identifier method name"); continue; } }; let method_defn_name = self.allocate_defn(&ident_from_str(&format!("{}_{}", defn_name.name, name))); dependent_definitions.append(&mut self.compile_fn( method_defn_name.clone(), None, Functionish::Fn(None, method.function.clone()), )); let dst = match method.is_static { false => &mut prototype, true => &mut static_, }; dst .properties .push((name, Value::Pointer(method_defn_name))); } PrivateMethod(private_method) => self.todo(private_method.span, "PrivateMethod"), // Handled first because they need to be compiled before the // constructor, regardless of syntax order ClassProp(_) => {} PrivateProp(prop) => { if prop.value.is_some() { self.todo(prop.span, "class property initializers"); } } TsIndexSignature(_) => {} Empty(_) => {} StaticBlock(static_block) => { self.todo(static_block.span, "StaticBlock"); } } } self.module.definitions.push(Definition { pointer: defn_name.clone(), content: DefinitionContent::Class(Class { constructor, prototype: Value::Object(Box::new(prototype)), static_: Value::Object(Box::new(static_)), }), }); self.module.definitions.append(&mut dependent_definitions); defn_name } pub fn compile_enum_value(&mut self, ts_enum: &swc_ecma_ast::TsEnumDecl) -> Value { let mut properties = Vec::<(Value, Value)>::new(); let mut next_default_id: Option = Some(0.0); for member in &ts_enum.members { let key = match &member.id { swc_ecma_ast::TsEnumMemberId::Ident(ident) => ident.sym.to_string(), swc_ecma_ast::TsEnumMemberId::Str(str) => str.value.to_string(), }; let init_value = match &member.init { Some(init) => match self.compile_expr(init) { Some(init_value) => match init_value { Value::Number(Number(n)) => { next_default_id = Some(n + 1.0); Some(Value::Number(Number(n))) } Value::String(_) => Some(init_value), _ => None, }, None => { self.internal_error(init.span(), "Static eval failed"); None } }, None => None, }; let value = match init_value { Some(value) => value, None => { let id = match next_default_id { Some(id) => id, None => { self.error(member.span, "Missing required initializer"); 0.0 } }; let value = Value::Number(Number(id)); next_default_id = Some(id + 1.0); value } }; properties.push((Value::String(key.clone()), value.clone())); properties.push((value, Value::String(key))); } Value::Object(Box::new(Object { properties })) } pub fn compile_expr(&mut self, expr: &swc_ecma_ast::Expr) -> Option { let symbol_iterator_opt = as_symbol_iterator(expr); if symbol_iterator_opt.is_some() { return symbol_iterator_opt; } match expr { swc_ecma_ast::Expr::Lit(lit) => match value_from_literal(lit) { Ok(value) => Some(value), _ => None, }, swc_ecma_ast::Expr::Array(array) => { let mut values = Vec::::new(); for item in &array.elems { values.push(match item { Some(item) => { if item.spread.is_some() { return None; } self.compile_expr(&item.expr)? } None => Value::Void, }); } Some(Value::Array(Box::new(Array { values }))) } swc_ecma_ast::Expr::Object(object) => { let mut properties = Vec::<(Value, Value)>::new(); for prop in &object.props { let (key, value) = match prop { swc_ecma_ast::PropOrSpread::Spread(_) => return None, swc_ecma_ast::PropOrSpread::Prop(prop) => match &**prop { swc_ecma_ast::Prop::Shorthand(_) => return None, swc_ecma_ast::Prop::KeyValue(kv) => { let key = match &kv.key { swc_ecma_ast::PropName::Ident(ident) => Value::String(ident.sym.to_string()), swc_ecma_ast::PropName::Str(str) => Value::String(str.value.to_string()), swc_ecma_ast::PropName::Num(num) => Value::Number(Number(num.value)), swc_ecma_ast::PropName::Computed(computed) => { self.compile_expr(&computed.expr)? } swc_ecma_ast::PropName::BigInt(bi) => Value::BigInt(bi.value.clone()), }; let value = self.compile_expr(&kv.value)?; (key, value) } swc_ecma_ast::Prop::Assign(_) => return None, swc_ecma_ast::Prop::Getter(_) => return None, swc_ecma_ast::Prop::Setter(_) => return None, swc_ecma_ast::Prop::Method(_) => return None, }, }; properties.push((key, value)); } Some(Value::Object(Box::new(Object { properties }))) } swc_ecma_ast::Expr::This(_) => None, swc_ecma_ast::Expr::Fn(_) => None, swc_ecma_ast::Expr::Update(_) => None, swc_ecma_ast::Expr::Assign(_) => None, swc_ecma_ast::Expr::SuperProp(_) => None, swc_ecma_ast::Expr::Call(_) => None, swc_ecma_ast::Expr::New(_) => None, swc_ecma_ast::Expr::Ident(ident) => match self .scope_analysis .lookup(&Ident::from_swc_ident(ident)) .map(|name| name.value.clone()) { Some(Value::Pointer(p)) => self.constants_map.get(&p).cloned(), Some(value) => Some(value), None => None, }, swc_ecma_ast::Expr::TaggedTpl(_) => None, swc_ecma_ast::Expr::Arrow(_) => None, swc_ecma_ast::Expr::Class(_) => None, swc_ecma_ast::Expr::Yield(_) => None, swc_ecma_ast::Expr::MetaProp(_) => None, swc_ecma_ast::Expr::Await(_) => None, swc_ecma_ast::Expr::JSXMember(_) => None, swc_ecma_ast::Expr::JSXNamespacedName(_) => None, swc_ecma_ast::Expr::JSXEmpty(_) => None, swc_ecma_ast::Expr::JSXElement(_) => None, swc_ecma_ast::Expr::JSXFragment(_) => None, swc_ecma_ast::Expr::TsInstantiation(_) => None, swc_ecma_ast::Expr::PrivateName(_) => None, swc_ecma_ast::Expr::OptChain(_) => None, swc_ecma_ast::Expr::Invalid(_) => None, swc_ecma_ast::Expr::Member(_) => None, swc_ecma_ast::Expr::Cond(_) => None, swc_ecma_ast::Expr::Unary(unary) => match unary.op { swc_ecma_ast::UnaryOp::Minus => match self.compile_expr(&unary.arg)? { Value::Number(Number(x)) => Some(Value::Number(Number(-x))), Value::BigInt(bi) => Some(Value::BigInt(-bi)), _ => None, }, swc_ecma_ast::UnaryOp::Plus => match self.compile_expr(&unary.arg)? { Value::Number(Number(x)) => Some(Value::Number(Number(x))), Value::BigInt(bi) => Some(Value::BigInt(bi)), _ => None, }, swc_ecma_ast::UnaryOp::Bang => None, swc_ecma_ast::UnaryOp::Tilde => match self.compile_expr(&unary.arg)? { Value::Number(Number(x)) => Some(Value::Number(Number(!to_i32(x) as f64))), Value::BigInt(bi) => Some(Value::BigInt(!bi)), _ => None, }, swc_ecma_ast::UnaryOp::TypeOf => None, swc_ecma_ast::UnaryOp::Void => None, swc_ecma_ast::UnaryOp::Delete => None, }, swc_ecma_ast::Expr::Bin(_) => None, swc_ecma_ast::Expr::Seq(seq) => { let mut last = Value::Void; for expr in &seq.exprs { last = self.compile_expr(expr)?; } Some(last) } swc_ecma_ast::Expr::Tpl(tpl) => 'b: { let len = tpl.exprs.len(); assert_eq!(tpl.quasis.len(), len + 1); if len == 0 { break 'b Some(Value::String(tpl.quasis[0].raw.to_string())); } None // TODO } swc_ecma_ast::Expr::Paren(paren) => self.compile_expr(&paren.expr), swc_ecma_ast::Expr::TsTypeAssertion(tta) => self.compile_expr(&tta.expr), swc_ecma_ast::Expr::TsConstAssertion(tca) => self.compile_expr(&tca.expr), swc_ecma_ast::Expr::TsNonNull(tnn) => self.compile_expr(&tnn.expr), swc_ecma_ast::Expr::TsAs(ta) => self.compile_expr(&ta.expr), } } } fn as_symbol_iterator(expr: &swc_ecma_ast::Expr) -> Option { let member_expr = match expr { swc_ecma_ast::Expr::Member(member_expr) => member_expr, _ => return None, }; match &*member_expr.obj { swc_ecma_ast::Expr::Ident(ident) => { if ident.sym.to_string() != "Symbol" { return None; } } _ => return None, }; match &member_expr.prop { swc_ecma_ast::MemberProp::Ident(ident) => { if ident.sym.to_string() != "iterator" { return None; } } _ => return None, } Some(Value::Builtin(Builtin { name: "SymbolIterator".to_string(), })) }