diff --git a/Cargo.lock b/Cargo.lock index e3d11c50..594a8433 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -437,6 +437,7 @@ version = "0.3.0" dependencies = [ "ahash", "bincode", + "dash_proc_macro", "dash_regex", "derive_more", "either", @@ -625,6 +626,7 @@ dependencies = [ "dash_proc_macro", "dash_regex", "dash_typed_cfg", + "hashbrown 0.14.0", "if_chain", "rustc-hash", "smallvec", diff --git a/cli/src/cmd/eval.rs b/cli/src/cmd/eval.rs index cad29612..e13f94a5 100644 --- a/cli/src/cmd/eval.rs +++ b/cli/src/cmd/eval.rs @@ -17,10 +17,10 @@ pub fn eval(args: &ArgMatches) -> anyhow::Result<()> { match scope.eval(source, opt) { Ok(value) => println!("{}", format_value(value.root(&mut scope), &mut scope).unwrap()), - Err((EvalError::Exception(value), _)) => { + Err(EvalError::Exception(value)) => { println!("{}", format_value(value.root(&mut scope), &mut scope).unwrap()) } - Err((EvalError::Middle(errs), _)) => println!("{}", errs.formattable(source, true)), + Err(EvalError::Middle(errs)) => println!("{}", errs.formattable(source, true)), }; scope.process_async_tasks(); diff --git a/cli/src/cmd/repl.rs b/cli/src/cmd/repl.rs index 6e23415a..99b512c8 100644 --- a/cli/src/cmd/repl.rs +++ b/cli/src/cmd/repl.rs @@ -21,10 +21,10 @@ pub fn repl() -> anyhow::Result<()> { match scope.eval(&input, OptLevel::Aggressive) { Ok(value) => println!("{}", format_value(value.root(&mut scope), &mut scope).unwrap()), - Err((EvalError::Exception(value), _)) => { + Err(EvalError::Exception(value)) => { println!("{}", format_value(value.root(&mut scope), &mut scope).unwrap()) } - Err((EvalError::Middle(errs), _)) => println!("{}", errs.formattable(&input, true)), + Err(EvalError::Middle(errs)) => println!("{}", errs.formattable(&input, true)), } scope.process_async_tasks(); diff --git a/cli/src/cmd/run.rs b/cli/src/cmd/run.rs index 6f9a91c1..d87864f8 100644 --- a/cli/src/cmd/run.rs +++ b/cli/src/cmd/run.rs @@ -63,8 +63,8 @@ async fn inner(source: String, opt: OptLevel, quiet: bool, initial_gc_threshold: let mut scope = rt.vm_mut().scope(); let value = match scope.eval(&source, opt) { Ok(val) => val.root(&mut scope), - Err((EvalError::Exception(val), _)) => val.root(&mut scope), - Err((EvalError::Middle(errs), _)) => { + Err(EvalError::Exception(val)) => val.root(&mut scope), + Err(EvalError::Middle(errs)) => { println!("{}", errs.formattable(&source, true)); return Ok(()); } diff --git a/crates/dash_compiler/src/instruction.rs b/crates/dash_compiler/src/instruction.rs index c0699a94..70de025e 100755 --- a/crates/dash_compiler/src/instruction.rs +++ b/crates/dash_compiler/src/instruction.rs @@ -1,9 +1,9 @@ use std::convert::TryInto; -use std::rc::Rc; use dash_middle::compiler::constant::{Constant, LimitExceededError}; use dash_middle::compiler::instruction::{AssignKind, Instruction, IntrinsicOperation}; use dash_middle::compiler::{FunctionCallMetadata, ObjectMemberKind as CompilerObjectMemberKind, StaticImportKind}; +use dash_middle::interner::Symbol; use dash_middle::parser::error::Error; use dash_middle::parser::expr::ObjectMemberKind; use dash_middle::sourcemap::Span; @@ -96,13 +96,13 @@ impl<'cx, 'interner> InstructionBuilder<'cx, 'interner> { compile_local_load_into(&mut self.current_function_mut().buf, index, is_extern); } - pub fn build_global_load(&mut self, ident: Rc) -> Result<(), LimitExceededError> { + pub fn build_global_load(&mut self, ident: Symbol) -> Result<(), LimitExceededError> { let id = self.current_function_mut().cp.add(Constant::Identifier(ident))?; self.write_wide_instr(Instruction::LdGlobal, Instruction::LdGlobalW, id); Ok(()) } - pub fn build_global_store(&mut self, kind: AssignKind, ident: Rc) -> Result<(), LimitExceededError> { + pub fn build_global_store(&mut self, kind: AssignKind, ident: Symbol) -> Result<(), LimitExceededError> { let id = self.current_function_mut().cp.add(Constant::Identifier(ident))?; self.write_wide_instr(Instruction::StoreGlobal, Instruction::StoreGlobalW, id); self.write(kind as u8); @@ -178,7 +178,7 @@ impl<'cx, 'interner> InstructionBuilder<'cx, 'interner> { self.build_jmp_header(label, is_local_label); } - pub fn build_static_prop_access(&mut self, ident: Rc, preserve_this: bool) -> Result<(), LimitExceededError> { + pub fn build_static_prop_access(&mut self, ident: Symbol, preserve_this: bool) -> Result<(), LimitExceededError> { let id = self.current_function_mut().cp.add(Constant::Identifier(ident))?; self.write_wide_instr(Instruction::StaticPropAccess, Instruction::StaticPropAccessW, id); self.write(preserve_this.into()); @@ -191,7 +191,7 @@ impl<'cx, 'interner> InstructionBuilder<'cx, 'interner> { self.write(preserve_this.into()); } - pub fn build_static_prop_assign(&mut self, kind: AssignKind, ident: Rc) -> Result<(), LimitExceededError> { + pub fn build_static_prop_assign(&mut self, kind: AssignKind, ident: Symbol) -> Result<(), LimitExceededError> { let id = self.current_function_mut().cp.add(Constant::Identifier(ident))?; self.write_instr(Instruction::StaticPropAssign); self.write(kind as u8); @@ -220,7 +220,7 @@ impl<'cx, 'interner> InstructionBuilder<'cx, 'interner> { fn compile_object_member_kind( ib: &mut InstructionBuilder, span: Span, // TODO: this should not be the span of the obj literal but the member kind - name: Rc, + name: Symbol, kind_id: u8, ) -> Result<(), Error> { let id = ib @@ -239,12 +239,10 @@ impl<'cx, 'interner> InstructionBuilder<'cx, 'interner> { let kind_id = CompilerObjectMemberKind::from(&member) as u8; match member { ObjectMemberKind::Dynamic(..) => self.write(kind_id), - ObjectMemberKind::Static(name) => { - compile_object_member_kind(self, span, self.interner.resolve(name).clone(), kind_id)? - } + ObjectMemberKind::Static(name) => compile_object_member_kind(self, span, name, kind_id)?, ObjectMemberKind::Spread => self.write(kind_id), ObjectMemberKind::Getter(name) | ObjectMemberKind::Setter(name) => { - compile_object_member_kind(self, span, self.interner.resolve(name).clone(), kind_id)? + compile_object_member_kind(self, span, name, kind_id)? } } } diff --git a/crates/dash_compiler/src/lib.rs b/crates/dash_compiler/src/lib.rs index 0dc60b7c..3596eaeb 100644 --- a/crates/dash_compiler/src/lib.rs +++ b/crates/dash_compiler/src/lib.rs @@ -331,12 +331,12 @@ impl<'interner> FunctionCompiler<'interner> { let mut ib = InstructionBuilder::new(self); let for_of_iter_id = ib .current_scope_mut() - .add_local(sym::FOR_OF_ITER, VariableDeclarationKind::Unnameable, None) + .add_local(sym::for_of_iter, VariableDeclarationKind::Unnameable, None) .map_err(|_| Error::LocalLimitExceeded(expr.span))?; let for_of_gen_step_id = ib .current_scope_mut() - .add_local(sym::FOR_OF_GEN_STEP, VariableDeclarationKind::Unnameable, None) + .add_local(sym::for_of_gen_step, VariableDeclarationKind::Unnameable, None) .map_err(|_| Error::LocalLimitExceeded(expr.span))?; ib.accept_expr(expr)?; @@ -376,7 +376,7 @@ impl<'interner> FunctionCompiler<'interner> { }, Expr { span: Span::COMPILER_GENERATED, - kind: ExprKind::identifier(sym::VALUE), + kind: ExprKind::identifier(sym::value), }, ), }), @@ -423,7 +423,7 @@ impl<'interner> FunctionCompiler<'interner> { }, Expr { span: Span::COMPILER_GENERATED, - kind: ExprKind::identifier(sym::NEXT), + kind: ExprKind::identifier(sym::next), }, ), }, @@ -436,7 +436,7 @@ impl<'interner> FunctionCompiler<'interner> { }, Expr { span: Span::COMPILER_GENERATED, - kind: ExprKind::identifier(sym::DONE), + kind: ExprKind::identifier(sym::done), }, ), }, @@ -671,7 +671,7 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { } fn visit_literal_expression(&mut self, span: Span, expr: LiteralExpr) -> Result<(), Error> { - let constant = Constant::from_literal(self.interner, &expr); + let constant = Constant::from_literal(&expr); InstructionBuilder::new(self) .build_constant(constant) .map_err(|_| Error::ConstantPoolLimitExceeded(span))?; @@ -682,15 +682,15 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { let mut ib = InstructionBuilder::new(self); match ident { - sym::THIS => ib.build_this(), - sym::SUPER => ib.build_super(), - sym::GLOBAL_THIS => ib.build_global(), - sym::INFINITY => ib.build_infinity(), - sym::NAN => ib.build_nan(), + sym::this => ib.build_this(), + sym::super_ => ib.build_super(), + sym::globalThis => ib.build_global(), + sym::Infinity => ib.build_infinity(), + sym::NaN => ib.build_nan(), ident => match ib.find_local(ident) { Some((index, _, is_extern)) => ib.build_local_load(index, is_extern), _ => ib - .build_global_load(ib.interner.resolve(ident).clone()) + .build_global_load(ident) .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, }, }; @@ -717,7 +717,6 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { false, ) => { ib.accept_expr(*target)?; - let ident = ib.interner.resolve(ident).clone(); let id = ib .current_function_mut() .cp @@ -733,7 +732,6 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { }, ExprKind::Literal(LiteralExpr::Identifier(ident)) => { ib.build_global(); - let ident = ib.interner.resolve(ident).clone(); let id = ib .current_function_mut() .cp @@ -821,7 +819,6 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { .current_scope_mut() .add_local(name, binding.kind, None) .map_err(|_| Error::LocalLimitExceeded(span))?; - let res_name = ib.interner.resolve(name).clone(); let var_id = ib .current_function_mut() @@ -831,7 +828,7 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { let ident_id = ib .current_function_mut() .cp - .add(Constant::Identifier(res_name)) + .add(Constant::Identifier(name)) .map_err(|_| Error::ConstantPoolLimitExceeded(span))?; ib.writew(var_id); ib.writew(ident_id); @@ -1042,7 +1039,6 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { macro_rules! assign { ($kind:expr) => {{ ib.accept_expr(*right)?; - let ident = ib.interner.resolve(ident).clone(); ib.build_global_store($kind, ident) .map_err(|_| Error::ConstantPoolLimitExceeded(span))?; }}; @@ -1072,8 +1068,7 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { macro_rules! staticassign { ($ident:expr, $kind:expr) => {{ ib.accept_expr(*right)?; - let ident = ib.interner.resolve($ident).clone(); - ib.build_static_prop_assign($kind, ident) + ib.build_static_prop_assign($kind, $ident) .map_err(|_| Error::ConstantPoolLimitExceeded(span))?; }}; } @@ -1251,33 +1246,33 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { } match (target, property) { - (sym::MATH, sym::EXP) => emit_spec!(InstructionBuilder::build_exp), - (sym::MATH, sym::LOG2) => emit_spec!(InstructionBuilder::build_log2), - (sym::MATH, sym::EXPM1) => emit_spec!(InstructionBuilder::build_expm1), - (sym::MATH, sym::CBRT) => emit_spec!(InstructionBuilder::build_cbrt), - (sym::MATH, sym::CLZ32) => emit_spec!(InstructionBuilder::build_clz32), - (sym::MATH, sym::ATANH) => emit_spec!(InstructionBuilder::build_atanh), - (sym::MATH, sym::ATAN2) => emit_spec!(InstructionBuilder::build_atanh2), - (sym::MATH, sym::ROUND) => emit_spec!(InstructionBuilder::build_round), - (sym::MATH, sym::ACOSH) => emit_spec!(InstructionBuilder::build_acosh), - (sym::MATH, sym::ABS) => emit_spec!(InstructionBuilder::build_abs), - (sym::MATH, sym::SINH) => emit_spec!(InstructionBuilder::build_sinh), - (sym::MATH, sym::SIN) => emit_spec!(InstructionBuilder::build_sin), - (sym::MATH, sym::CEIL) => emit_spec!(InstructionBuilder::build_ceil), - (sym::MATH, sym::TAN) => emit_spec!(InstructionBuilder::build_tan), - (sym::MATH, sym::TRUNC) => emit_spec!(InstructionBuilder::build_trunc), - (sym::MATH, sym::ASINH) => emit_spec!(InstructionBuilder::build_asinh), - (sym::MATH, sym::LOG10) => emit_spec!(InstructionBuilder::build_log10), - (sym::MATH, sym::ASIN) => emit_spec!(InstructionBuilder::build_asin), - (sym::MATH, sym::RANDOM) => emit_spec!(InstructionBuilder::build_random), - (sym::MATH, sym::LOG1P) => emit_spec!(InstructionBuilder::build_log1p), - (sym::MATH, sym::SQRT) => emit_spec!(InstructionBuilder::build_sqrt), - (sym::MATH, sym::ATAN) => emit_spec!(InstructionBuilder::build_atan), - (sym::MATH, sym::LOG) => emit_spec!(InstructionBuilder::build_log), - (sym::MATH, sym::FLOOR) => emit_spec!(InstructionBuilder::build_floor), - (sym::MATH, sym::COSH) => emit_spec!(InstructionBuilder::build_cosh), - (sym::MATH, sym::ACOS) => emit_spec!(InstructionBuilder::build_acos), - (sym::MATH, sym::COS) => emit_spec!(InstructionBuilder::build_cos), + (sym::Math, sym::exp) => emit_spec!(InstructionBuilder::build_exp), + (sym::Math, sym::log2) => emit_spec!(InstructionBuilder::build_log2), + (sym::Math, sym::expm1) => emit_spec!(InstructionBuilder::build_expm1), + (sym::Math, sym::cbrt) => emit_spec!(InstructionBuilder::build_cbrt), + (sym::Math, sym::clz32) => emit_spec!(InstructionBuilder::build_clz32), + (sym::Math, sym::atanh) => emit_spec!(InstructionBuilder::build_atanh), + (sym::Math, sym::atan2) => emit_spec!(InstructionBuilder::build_atanh2), + (sym::Math, sym::round) => emit_spec!(InstructionBuilder::build_round), + (sym::Math, sym::acosh) => emit_spec!(InstructionBuilder::build_acosh), + (sym::Math, sym::abs) => emit_spec!(InstructionBuilder::build_abs), + (sym::Math, sym::sinh) => emit_spec!(InstructionBuilder::build_sinh), + (sym::Math, sym::sin) => emit_spec!(InstructionBuilder::build_sin), + (sym::Math, sym::ceil) => emit_spec!(InstructionBuilder::build_ceil), + (sym::Math, sym::tan) => emit_spec!(InstructionBuilder::build_tan), + (sym::Math, sym::trunc) => emit_spec!(InstructionBuilder::build_trunc), + (sym::Math, sym::asinh) => emit_spec!(InstructionBuilder::build_asinh), + (sym::Math, sym::log10) => emit_spec!(InstructionBuilder::build_log10), + (sym::Math, sym::asin) => emit_spec!(InstructionBuilder::build_asin), + (sym::Math, sym::random) => emit_spec!(InstructionBuilder::build_random), + (sym::Math, sym::log1p) => emit_spec!(InstructionBuilder::build_log1p), + (sym::Math, sym::sqrt) => emit_spec!(InstructionBuilder::build_sqrt), + (sym::Math, sym::atan) => emit_spec!(InstructionBuilder::build_atan), + (sym::Math, sym::log) => emit_spec!(InstructionBuilder::build_log), + (sym::Math, sym::floor) => emit_spec!(InstructionBuilder::build_floor), + (sym::Math, sym::cosh) => emit_spec!(InstructionBuilder::build_cosh), + (sym::Math, sym::acos) => emit_spec!(InstructionBuilder::build_acos), + (sym::Math, sym::cos) => emit_spec!(InstructionBuilder::build_cos), _ => {} } } @@ -1369,7 +1364,6 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { }, false, ) => { - let ident = ib.interner.resolve(ident).clone(); ib.build_static_prop_access(ident, preserve_this) .map_err(|_| Error::ConstantPoolLimitExceeded(span))?; } @@ -1414,8 +1408,6 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { _ => unreachable!("Token never emitted"), } } else { - let ident = ib.interner.resolve(ident).clone(); - match tt { TokenType::Increment => ib .build_global_store(AssignKind::PostfixIncrement, ident) @@ -1437,18 +1429,15 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { span, }, false, - ) => { - let ident = ib.interner.resolve(ident).clone(); - match tt { - TokenType::Increment => ib - .build_static_prop_assign(AssignKind::PostfixIncrement, ident) - .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, - TokenType::Decrement => ib - .build_static_prop_assign(AssignKind::PostfixDecrement, ident) - .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, - _ => unreachable!("Token never emitted"), - } - } + ) => match tt { + TokenType::Increment => ib + .build_static_prop_assign(AssignKind::PostfixIncrement, ident) + .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, + TokenType::Decrement => ib + .build_static_prop_assign(AssignKind::PostfixDecrement, ident) + .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, + _ => unreachable!("Token never emitted"), + }, (prop, true) => { ib.accept_expr(prop)?; match tt { @@ -1490,8 +1479,6 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { _ => unreachable!("Token never emitted"), } } else { - let ident = ib.interner.resolve(ident).clone(); - match tt { TokenType::Increment => ib .build_global_store(AssignKind::PrefixIncrement, ident) @@ -1513,18 +1500,15 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { span, }, false, - ) => { - let ident = ib.interner.resolve(ident).clone(); - match tt { - TokenType::Increment => ib - .build_static_prop_assign(AssignKind::PrefixIncrement, ident) - .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, - TokenType::Decrement => ib - .build_static_prop_assign(AssignKind::PrefixDecrement, ident) - .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, - _ => unreachable!("Token never emitted"), - } - } + ) => match tt { + TokenType::Increment => ib + .build_static_prop_assign(AssignKind::PrefixIncrement, ident) + .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, + TokenType::Decrement => ib + .build_static_prop_assign(AssignKind::PrefixDecrement, ident) + .map_err(|_| Error::ConstantPoolLimitExceeded(span))?, + _ => unreachable!("Token never emitted"), + }, (prop, true) => { ib.accept_expr(prop)?; match tt { @@ -1611,7 +1595,7 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { buffer: Buffer(Cell::new(cmp.buf.into())), constants: cmp.cp.into_vec().into(), locals, - name: name.map(|sym| ib.interner.resolve(sym).clone()), + name, ty, params: match arguments.last() { Some((Parameter::Spread(..), ..)) => arguments.len() - 1, @@ -1801,8 +1785,6 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { ) .map_err(|_| Error::LocalLimitExceeded(span))?; - let path = ib.interner.resolve(path).clone(); - let path_id = ib .current_function_mut() .cp @@ -1836,12 +1818,10 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { let mut it = Vec::with_capacity(names.len()); for name in names.iter().copied() { - let res_name = ib.interner.resolve(name).clone(); - let ident_id = ib .current_function_mut() .cp - .add(Constant::Identifier(res_name)) + .add(Constant::Identifier(name)) .map_err(|_| Error::ConstantPoolLimitExceeded(span))?; match ib.find_local(name) { @@ -1944,7 +1924,7 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { let constructor = class.members.iter().find_map(|member| { if let ClassMemberKind::Method(method) = &member.kind { - if method.name == Some(sym::CONSTRUCTOR) { + if method.name == Some(sym::constructor) { return Some(method.clone()); } } @@ -1959,7 +1939,7 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { .map_err(|_| Error::LocalLimitExceeded(span))?, None => ib .current_scope_mut() - .add_local(sym::DESUGARED_CLASS, VariableDeclarationKind::Unnameable, None) + .add_local(sym::DesugaredClass, VariableDeclarationKind::Unnameable, None) .map_err(|_| Error::LocalLimitExceeded(span))?, }; @@ -2031,7 +2011,7 @@ impl<'interner> Visitor> for FunctionCompiler<'interner> { load_class_binding.clone(), Expr { span: Span::COMPILER_GENERATED, - kind: ExprKind::identifier(sym::PROTOTYPE), + kind: ExprKind::identifier(sym::prototype), }, ), }, diff --git a/crates/dash_compiler/src/transformations.rs b/crates/dash_compiler/src/transformations.rs index c00c2c4a..7900ed11 100644 --- a/crates/dash_compiler/src/transformations.rs +++ b/crates/dash_compiler/src/transformations.rs @@ -56,7 +56,7 @@ pub fn insert_initializer_in_constructor(class: &Class, statements: &mut Vec FunctionDecompiler<'buf> { for fun in functions { let out = fun.buffer.with(|buffer| { - FunctionDecompiler::new( - buffer, - &fun.constants, - &format!("{}::{}", self.name, fun.name.as_deref().unwrap_or("")), - ) - .run() + FunctionDecompiler::new(buffer, &fun.constants, &format!("{}::{:?}", self.name, fun.name)).run() })?; self.out.push('\n'); self.out.push_str(&out); @@ -440,7 +435,7 @@ impl fmt::Display for DisplayConstant<'_> { Constant::String(s) => write!(f, "\"{s}\""), Constant::Boolean(b) => write!(f, "{b}"), Constant::Identifier(ident) => write!(f, "{ident}"), - Constant::Function(fun) => write!(f, "", fun.name.as_deref().unwrap_or("")), + Constant::Function(fun) => write!(f, "", fun.name), Constant::Null => f.write_str("null"), Constant::Undefined => f.write_str("undefined"), Constant::Regex(_, _, source) => write!(f, "{source}"), diff --git a/crates/dash_dlloader/src/lib.rs b/crates/dash_dlloader/src/lib.rs index 18490b79..fddb3e68 100644 --- a/crates/dash_dlloader/src/lib.rs +++ b/crates/dash_dlloader/src/lib.rs @@ -8,6 +8,7 @@ use dash_vm::value::function::native::CallContext; use dash_vm::value::function::{Function, FunctionKind}; use dash_vm::value::object::{NamedObject, Object, PropertyValue}; use dash_vm::value::ops::conversions::ValueConversion; +use dash_vm::value::string::JsString; use dash_vm::value::Value; use libloading::Library; @@ -17,19 +18,16 @@ type InitFunction = unsafe extern "C" fn(*mut CallContext, *mut Result Result, Value> { - if path != "@std/dlloader" { + fn import(&self, sc: &mut LocalScope, _: StaticImportKind, path: JsString) -> Result, Value> { + if path.res(sc) != "@std/dlloader" { return Ok(None); } let object = NamedObject::new(sc); - let load_sync = Function::new(sc, Some("load".into()), FunctionKind::Native(load_sync)); + let load = sc.intern("load"); + let load_sync = Function::new(sc, Some(load.into()), FunctionKind::Native(load_sync)); let load_sync = sc.register(load_sync); - object.set_property( - sc, - "load".into(), - PropertyValue::static_default(Value::Object(load_sync)), - )?; + object.set_property(sc, load.into(), PropertyValue::static_default(Value::Object(load_sync)))?; Ok(Some(Value::Object(sc.register(object)))) } @@ -54,10 +52,10 @@ pub fn load_sync(mut cx: CallContext) -> Result { None => throw!(cx.scope, ReferenceError, "Missing path to dynamic library"), }; - let path = ValueConversion::to_string(path, cx.scope)?; + let path = ValueConversion::to_js_string(path, cx.scope)?; unsafe { - let lib = match Library::new(path.as_ref()) { + let lib = match Library::new(path.res(cx.scope)) { // TODO: Currently we (intentionally) leak all dlopen'd handles, because we don't know exactly when we should close it Ok(lib) => ManuallyDrop::new(lib), Err(err) => throw!(cx.scope, Error, "{}", err), diff --git a/crates/dash_lexer/src/lib.rs b/crates/dash_lexer/src/lib.rs index 8fb20f6c..2d581be6 100644 --- a/crates/dash_lexer/src/lib.rs +++ b/crates/dash_lexer/src/lib.rs @@ -441,7 +441,7 @@ impl<'a, 'interner> Lexer<'a, 'interner> { self.advance(); // identifier reading requires one character to be read self.read_identifier_raw() } else { - sym::EMPTY + sym::empty }; self.create_contextified_token(TokenType::RegexLiteral { diff --git a/crates/dash_middle/Cargo.toml b/crates/dash_middle/Cargo.toml index 155129b5..ed0ee864 100644 --- a/crates/dash_middle/Cargo.toml +++ b/crates/dash_middle/Cargo.toml @@ -23,3 +23,4 @@ hashbrown = "0.14.0" rustc-hash = "1.1.0" memchr = "2.5.0" owo-colors = "3.5.0" +dash_proc_macro = { path = "../dash_proc_macro" } diff --git a/crates/dash_middle/src/compiler/constant.rs b/crates/dash_middle/src/compiler/constant.rs index 615b53e6..2adef0fd 100755 --- a/crates/dash_middle/src/compiler/constant.rs +++ b/crates/dash_middle/src/compiler/constant.rs @@ -7,7 +7,7 @@ use std::rc::Rc; #[cfg(feature = "format")] use serde::{Deserialize, Serialize}; -use crate::interner::StringInterner; +use crate::interner::Symbol; use crate::parser::expr::LiteralExpr; use crate::parser::statement::FunctionKind; @@ -69,7 +69,7 @@ impl Clone for Buffer { #[cfg_attr(feature = "format", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] pub struct Function { - pub name: Option>, + pub name: Option, pub buffer: Buffer, pub ty: FunctionKind, pub locals: usize, @@ -100,11 +100,11 @@ impl Function { #[derive(Debug, Clone)] pub enum Constant { Number(f64), - String(Rc), - Identifier(Rc), + String(Symbol), + Identifier(Symbol), Boolean(bool), Function(Rc), - Regex(dash_regex::ParsedRegex, dash_regex::Flags, Rc), + Regex(dash_regex::ParsedRegex, dash_regex::Flags, Symbol), Null, Undefined, } @@ -117,16 +117,16 @@ impl Constant { } } - pub fn as_string(&self) -> Option<&Rc> { + pub fn as_string(&self) -> Option { match self { - Constant::String(s) => Some(s), + Constant::String(s) => Some(*s), _ => None, } } - pub fn as_identifier(&self) -> Option<&Rc> { + pub fn as_identifier(&self) -> Option { match self { - Constant::Identifier(s) => Some(s), + Constant::Identifier(s) => Some(*s), _ => None, } } @@ -138,17 +138,15 @@ impl Constant { } } - pub fn from_literal(interner: &StringInterner, expr: &LiteralExpr) -> Self { + pub fn from_literal(expr: &LiteralExpr) -> Self { match expr { LiteralExpr::Number(n) => Self::Number(*n), - LiteralExpr::Identifier(s) => Self::Identifier(interner.resolve(*s).clone()), - LiteralExpr::String(s) => Self::String(interner.resolve(*s).clone()), + LiteralExpr::Identifier(s) => Self::Identifier(*s), + LiteralExpr::String(s) => Self::String(*s), LiteralExpr::Boolean(b) => Self::Boolean(*b), LiteralExpr::Null => Self::Null, LiteralExpr::Undefined => Self::Undefined, - LiteralExpr::Regex(regex, flags, source) => { - Self::Regex(regex.clone(), *flags, interner.resolve(*source).clone()) - } + LiteralExpr::Regex(regex, flags, source) => Self::Regex(regex.clone(), *flags, *source), } } } diff --git a/crates/dash_middle/src/interner.rs b/crates/dash_middle/src/interner.rs index d4463094..cfd22883 100644 --- a/crates/dash_middle/src/interner.rs +++ b/crates/dash_middle/src/interner.rs @@ -1,3 +1,4 @@ +use std::cell::Cell; use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::rc::Rc; use std::{borrow, fmt}; @@ -5,195 +6,271 @@ use std::{borrow, fmt}; use hashbrown::hash_map::RawEntryMut; use rustc_hash::FxHasher; -pub mod sym { - use super::Symbol; +#[cfg(feature = "format")] +use serde::{Deserialize, Serialize}; - pub const IF: Symbol = Symbol(0); - pub const ELSE: Symbol = Symbol(1); - pub const FUNCTION: Symbol = Symbol(2); - pub const VAR: Symbol = Symbol(3); - pub const LET: Symbol = Symbol(4); - pub const CONST: Symbol = Symbol(5); - pub const RETURN: Symbol = Symbol(6); - pub const THROW: Symbol = Symbol(7); - pub const TRY: Symbol = Symbol(8); - pub const CATCH: Symbol = Symbol(9); - pub const FINALLY: Symbol = Symbol(10); - pub const TRUE_LIT: Symbol = Symbol(11); - pub const FALSE_LIT: Symbol = Symbol(12); - pub const NULL_LIT: Symbol = Symbol(13); - pub const UNDEFINED_LIT: Symbol = Symbol(14); - pub const YIELD: Symbol = Symbol(15); - pub const NEW: Symbol = Symbol(16); - pub const FOR: Symbol = Symbol(17); - pub const DO: Symbol = Symbol(18); - pub const WHILE: Symbol = Symbol(19); - pub const IN: Symbol = Symbol(20); - pub const INSTANCEOF: Symbol = Symbol(21); - pub const ASYNC: Symbol = Symbol(22); - pub const AWAIT: Symbol = Symbol(23); - pub const DELETE: Symbol = Symbol(24); - pub const VOID: Symbol = Symbol(25); - pub const TYPEOF: Symbol = Symbol(26); - pub const CONTINUE: Symbol = Symbol(27); - pub const BREAK: Symbol = Symbol(28); - pub const IMPORT: Symbol = Symbol(29); - pub const EXPORT: Symbol = Symbol(30); - pub const DEFAULT: Symbol = Symbol(31); - pub const DEBUGGER: Symbol = Symbol(32); - pub const OF: Symbol = Symbol(33); - pub const CLASS: Symbol = Symbol(34); - pub const EXTENDS: Symbol = Symbol(35); - pub const STATIC: Symbol = Symbol(36); - pub const SWITCH: Symbol = Symbol(37); - pub const CASE: Symbol = Symbol(38); - pub const GET: Symbol = Symbol(39); - pub const SET: Symbol = Symbol(40); +pub mod sym { + #![allow(non_upper_case_globals)] - pub const PREINTERNED: &[(&str, Symbol); 84] = &[ - ("if", IF), - ("else", ELSE), - ("function", FUNCTION), - ("var", VAR), - ("let", LET), - ("const", CONST), - ("return", RETURN), - ("throw", THROW), - ("try", TRY), - ("catch", CATCH), - ("finally", FINALLY), - ("true", TRUE_LIT), - ("false", FALSE_LIT), - ("null", NULL_LIT), - ("undefined", UNDEFINED_LIT), - ("yield", YIELD), - ("new", NEW), - ("for", FOR), - ("do", DO), - ("while", WHILE), - ("in", IN), - ("instanceof", INSTANCEOF), - ("async", ASYNC), - ("await", AWAIT), - ("delete", DELETE), - ("void", VOID), - ("typeof", TYPEOF), - ("continue", CONTINUE), - ("break", BREAK), - ("import", IMPORT), - ("export", EXPORT), - ("default", DEFAULT), - ("debugger", DEBUGGER), - ("of", OF), - ("class", CLASS), - ("extends", EXTENDS), - ("static", STATIC), - ("switch", SWITCH), - ("case", CASE), - ("get", GET), - ("set", SET), - ("$", DOLLAR), - ("", EMPTY), - ("constructor", CONSTRUCTOR), - ("this", THIS), - ("for_of_iter", FOR_OF_ITER), - ("for_of_gen_step", FOR_OF_GEN_STEP), - ("value", VALUE), - ("done", DONE), - ("next", NEXT), - ("super", SUPER), - ("globalThis", GLOBAL_THIS), - ("Infinity", INFINITY), - ("NaN", NAN), - ("Math", MATH), - ("exp", EXP), - ("log2", LOG2), - ("expm1", EXPM1), - ("cbrt", CBRT), - ("clz32", CLZ32), - ("atanh", ATANH), - ("atan2", ATAN2), - ("round", ROUND), - ("acosh", ACOSH), - ("abs", ABS), - ("sinh", SINH), - ("sin", SIN), - ("ceil", CEIL), - ("tan", TAN), - ("trunc", TRUNC), - ("asinh", ASINH), - ("log10", LOG10), - ("asin", ASIN), - ("random", RANDOM), - ("log1p", LOG1P), - ("sqrt", SQRT), - ("atan", ATAN), - ("log", LOG), - ("floor", FLOOR), - ("cosh", COSH), - ("acos", ACOS), - ("cos", COS), - ("DesugaredClass", DESUGARED_CLASS), - ("prototype", PROTOTYPE), - ]; + use super::Symbol; - // ⚠️⚠️⚠️⚠️ Update these constants when adding a keyword. - // We rely on the fact that the keywords are contiguous in the symbol table, - // making it very easy and cheap to check if a symbol is a keyword. - // TODO: automate this with a proc macro or sorts. - pub const KEYWORD_START: Symbol = IF; - pub const KEYWORD_END: Symbol = SET; + dash_proc_macro::define_symbols! { + [ + // Keywords, defined separately so that they can be contiguous in the symbol table + // and we can easily check if a symbol is a keyword with a simple range check. + Keywords { + if_: "if", + else_: "else", + function, + var, + let_: "let", + const_: "const", + return_: "return", + throw, + try_: "try", + catch, + finally, + true_: "true", + false_: "false", + null, + undefined, + yield_: "yield", + new, + for_: "for", + do_: "do", + while_: "while", + in_: "in", + instanceof, + async_: "async", + await_: "await", + delete, + void, + typeof_: "typeof", + continue_: "continue", + break_: "break", + import, + export, + default, + debugger, + of, + class, + extends, + static_: "static", + switch, + case, + get, + set + }, + // Other preinterned symbols that can be referred to statically + Symbols { + dollar: "$", + empty: "", + constructor, + this, + for_of_iter, + for_of_gen_step, + value, + done, + next, + super_: "super", + globalThis, + Infinity, + NaN, + Math, + exp, + log2, + expm1, + cbrt, + clz32, + atanh, + atan2, + round, + acosh, + abs, + sinh, + sin, + ceil, + tan, + trunc, + asinh, + log10, + asin, + random, + log1p, + sqrt, + atan, + log, + floor, + cosh, + acos, + cos, + DesugaredClass, + prototype, + name, + length, + message, + stack, + Error, + toString, + valueOf, + object, + boolean, + number, + bigint, + string, + symbol, + comma: ",", + writable, + enumerable, + configurable, + __proto__, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError, + AggregateError, + Function, + bind, + call, + create, + keys, + getOwnPropertyDescriptor, + getOwnPropertyDescriptors, + defineProperty, + entries, + assign, + Object, + hasOwnProperty, + tanh, + max, + min, + PI, + isFinite, + isNaN, + isSafeInteger, + Number, + toFixed, + Boolean, + fromCharCode, + String, + charAt, + charCodeAt, + concat, + endsWith, + startsWith, + includes, + indexOf, + lastIndexOf, + padEnd, + padStart, + repeat, + replace, + replaceAll, + split, + toLowerCase, + toUpperCase, + big, + blink, + bold, + fixed, + italics, + strike, + sub, + sup, + fontcolor, + fontsize, + link, + trim, + trimStart, + trimEnd, + substr, + substring, + from, + isArray, + Array, + join, + values, + at, + every, + some, + fill, + filter, + reduce, + find, + findIndex, + flat, + forEach, + map, + pop, + push, + reverse, + shift, + sort, + unshift, + slice, + asyncIterator, + hasInstance, + iterator, + match_: "match", + matchAll, + search, + species, + toPrimitive, + toStringTag, + unscopables, + JsSymbol: "Symbol", + ArrayBuffer, + byteLength, + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + resolve, + reject, + Promise, + then, + Set, + add, + has, + clear, + size, + Map, + RegExp, + test, + exec, + now, + Date, + parse, + parseFloat, + parseInt, + console, + JSON, + isConcatSpreadable, + zero: "0", + one: "1", + } + ] + } +} - // Other non-keyword preinterned symbols - pub const DOLLAR: Symbol = Symbol(KEYWORD_END.0 + 1); - pub const EMPTY: Symbol = Symbol(KEYWORD_END.0 + 2); - pub const CONSTRUCTOR: Symbol = Symbol(KEYWORD_END.0 + 3); - pub const THIS: Symbol = Symbol(KEYWORD_END.0 + 4); - pub const FOR_OF_ITER: Symbol = Symbol(KEYWORD_END.0 + 5); - pub const FOR_OF_GEN_STEP: Symbol = Symbol(KEYWORD_END.0 + 6); - pub const VALUE: Symbol = Symbol(KEYWORD_END.0 + 7); - pub const DONE: Symbol = Symbol(KEYWORD_END.0 + 8); - pub const NEXT: Symbol = Symbol(KEYWORD_END.0 + 9); - pub const SUPER: Symbol = Symbol(KEYWORD_END.0 + 10); - pub const GLOBAL_THIS: Symbol = Symbol(KEYWORD_END.0 + 11); - pub const INFINITY: Symbol = Symbol(KEYWORD_END.0 + 12); - pub const NAN: Symbol = Symbol(KEYWORD_END.0 + 13); - pub const MATH: Symbol = Symbol(KEYWORD_END.0 + 14); - pub const EXP: Symbol = Symbol(KEYWORD_END.0 + 15); - pub const LOG2: Symbol = Symbol(KEYWORD_END.0 + 16); - pub const EXPM1: Symbol = Symbol(KEYWORD_END.0 + 17); - pub const CBRT: Symbol = Symbol(KEYWORD_END.0 + 18); - pub const CLZ32: Symbol = Symbol(KEYWORD_END.0 + 19); - pub const ATANH: Symbol = Symbol(KEYWORD_END.0 + 20); - pub const ATAN2: Symbol = Symbol(KEYWORD_END.0 + 21); - pub const ROUND: Symbol = Symbol(KEYWORD_END.0 + 22); - pub const ACOSH: Symbol = Symbol(KEYWORD_END.0 + 23); - pub const ABS: Symbol = Symbol(KEYWORD_END.0 + 24); - pub const SINH: Symbol = Symbol(KEYWORD_END.0 + 25); - pub const SIN: Symbol = Symbol(KEYWORD_END.0 + 26); - pub const CEIL: Symbol = Symbol(KEYWORD_END.0 + 27); - pub const TAN: Symbol = Symbol(KEYWORD_END.0 + 28); - pub const TRUNC: Symbol = Symbol(KEYWORD_END.0 + 29); - pub const ASINH: Symbol = Symbol(KEYWORD_END.0 + 30); - pub const LOG10: Symbol = Symbol(KEYWORD_END.0 + 31); - pub const ASIN: Symbol = Symbol(KEYWORD_END.0 + 32); - pub const RANDOM: Symbol = Symbol(KEYWORD_END.0 + 33); - pub const LOG1P: Symbol = Symbol(KEYWORD_END.0 + 34); - pub const SQRT: Symbol = Symbol(KEYWORD_END.0 + 35); - pub const ATAN: Symbol = Symbol(KEYWORD_END.0 + 36); - pub const LOG: Symbol = Symbol(KEYWORD_END.0 + 37); - pub const FLOOR: Symbol = Symbol(KEYWORD_END.0 + 38); - pub const COSH: Symbol = Symbol(KEYWORD_END.0 + 39); - pub const ACOS: Symbol = Symbol(KEYWORD_END.0 + 40); - pub const COS: Symbol = Symbol(KEYWORD_END.0 + 41); - pub const DESUGARED_CLASS: Symbol = Symbol(KEYWORD_END.0 + 42); - pub const PROTOTYPE: Symbol = Symbol(KEYWORD_END.0 + 43); +#[derive(Debug)] +struct StringData { + visited: Cell, + value: Rc, } #[derive(Default, Debug)] pub struct StringInterner { - store: Vec>, + store: Vec>, mapping: hashbrown::HashMap, RawSymbol, BuildHasherDefault>, + /// List of free indices in the storage + free: Vec, } fn fxhash(s: &str) -> u64 { @@ -212,16 +289,25 @@ impl StringInterner { let s: Rc = Rc::from(*s); debug_assert!(store.len() == index.0 as usize); mapping.insert(s.clone(), index.0); - store.push(s); + store.push(Some(StringData { + visited: Cell::new(false), + value: s, + })); } - Self { store, mapping } + Self { + store, + mapping, + free: Vec::new(), + } } - pub fn resolve(&self, symbol: Symbol) -> &Rc { - &self.store[symbol.0 as usize] + pub fn resolve(&self, symbol: Symbol) -> &str { + self.store[symbol.0 as usize].as_ref().unwrap().value.as_ref() } + // TODO: perf improvement idea: use interior mutability and allow calling with just a `&self` + // would save a bunch of useless clones pub fn intern(&mut self, value: impl borrow::Borrow) -> Symbol { let value = value.borrow(); let hash = fxhash(value); @@ -229,11 +315,61 @@ impl StringInterner { match self.mapping.raw_entry_mut().from_hash(hash, |k| &**k == value) { RawEntryMut::Occupied(entry) => Symbol(*entry.get()), RawEntryMut::Vacant(entry) => { - let id = self.store.len() as RawSymbol; - let value: Rc = Rc::from(value); - self.store.push(Rc::clone(&value)); - entry.insert_hashed_nocheck(hash, value, id); - Symbol(id) + if let Some(id) = self.free.pop() { + let value: Rc = Rc::from(value); + self.store[id as usize] = Some(StringData { + value: Rc::clone(&value), + visited: Cell::new(false), + }); + entry.insert_hashed_nocheck(hash, value, id); + Symbol(id) + } else { + let id = self.store.len() as RawSymbol; + let value: Rc = Rc::from(value); + self.store.push(Some(StringData { + value: Rc::clone(&value), + visited: Cell::new(false), + })); + entry.insert_hashed_nocheck(hash, value, id); + Symbol(id) + } + } + } + } + + pub fn intern_usize(&mut self, val: usize) -> Symbol { + // for now this just calls `intern`, but we might want to specialize this + let string = val.to_string(); + self.intern(string.as_ref()) + } + + pub fn intern_isize(&mut self, val: isize) -> Symbol { + // for now this just calls `intern`, but we might want to specialize this + let string = val.to_string(); + self.intern(string.as_ref()) + } + + pub fn intern_char(&mut self, val: char) -> Symbol { + let mut buf = [0; 4]; + self.intern(val.encode_utf8(&mut buf)) + } + + pub fn mark(&self, sym: Symbol) { + self.store[sym.0 as usize].as_ref().unwrap().visited.set(true); + } + + /// You must mark all reachable symbols before calling this. + /// It won't cause undefined behavior if you don't (hence not unsafe), but it can lead to oddities such as panics. + pub fn sweep(&mut self) { + for i in 0..self.store.len() { + if let Some(data) = self.store[i].as_ref() { + if !data.visited.get() { + self.mapping.remove(&data.value); + self.store[i] = None; + self.free.push(i as RawSymbol); + } else { + data.visited.set(false); + } } } } @@ -242,6 +378,7 @@ impl StringInterner { type RawSymbol = u32; #[derive(Debug, Eq, Hash, PartialEq, Clone, Copy)] +#[cfg_attr(feature = "format", derive(Serialize, Deserialize))] pub struct Symbol(RawSymbol); impl fmt::Display for Symbol { @@ -255,9 +392,14 @@ impl fmt::Display for Symbol { } impl Symbol { + /// This should only be used if you *really* need to. Prefer `Symbol`s directly wherever possible. + pub fn raw(self) -> u32 { + self.0 + } + pub fn is_keyword(self) -> bool { #![allow(clippy::absurd_extreme_comparisons)] - self.0 >= sym::KEYWORD_START.0 && self.0 <= sym::KEYWORD_END.0 + self.0 >= sym::KEYWORD_START && self.0 <= sym::KEYWORD_END } } diff --git a/crates/dash_middle/src/lexer/token.rs b/crates/dash_middle/src/lexer/token.rs index abc8f4b5..c4857fa8 100644 --- a/crates/dash_middle/src/lexer/token.rs +++ b/crates/dash_middle/src/lexer/token.rs @@ -2,7 +2,6 @@ use std::fmt; use crate::interner::{sym, Symbol}; use crate::sourcemap::Span; -use dash_regex::flags::Flags; use derive_more::Display; /// The type of a token @@ -395,7 +394,7 @@ impl TokenType { pub fn as_identifier(&self) -> Option { match self { Self::Identifier(sym) => Some(*sym), - Self::Dollar => Some(sym::DOLLAR), + Self::Dollar => Some(sym::dollar), _ => None, } } @@ -403,47 +402,47 @@ impl TokenType { /// This is the reverse operation of `as_token(Symol)` pub fn as_reserved_keyword(&self) -> Option { match self { - Self::If => Some(sym::IF), - Self::Else => Some(sym::ELSE), - Self::Function => Some(sym::FUNCTION), - Self::Var => Some(sym::VAR), - Self::Let => Some(sym::LET), - Self::Const => Some(sym::CONST), - Self::Return => Some(sym::RETURN), - Self::Throw => Some(sym::THROW), - Self::Try => Some(sym::TRY), - Self::Catch => Some(sym::CATCH), - Self::Finally => Some(sym::FINALLY), - Self::TrueLit => Some(sym::TRUE_LIT), - Self::FalseLit => Some(sym::FALSE_LIT), - Self::NullLit => Some(sym::NULL_LIT), - Self::UndefinedLit => Some(sym::UNDEFINED_LIT), - Self::Yield => Some(sym::YIELD), - Self::New => Some(sym::NEW), - Self::For => Some(sym::FOR), - Self::Do => Some(sym::DO), - Self::While => Some(sym::WHILE), - Self::In => Some(sym::IN), - Self::Instanceof => Some(sym::INSTANCEOF), - Self::Async => Some(sym::ASYNC), - Self::Await => Some(sym::AWAIT), - Self::Delete => Some(sym::DELETE), - Self::Void => Some(sym::VOID), - Self::Typeof => Some(sym::TYPEOF), - Self::Continue => Some(sym::CONTINUE), - Self::Break => Some(sym::BREAK), - Self::Import => Some(sym::IMPORT), - Self::Export => Some(sym::EXPORT), - Self::Default => Some(sym::DEFAULT), - Self::Debugger => Some(sym::DEBUGGER), - Self::Of => Some(sym::OF), - Self::Class => Some(sym::CLASS), - Self::Extends => Some(sym::EXTENDS), - Self::Static => Some(sym::STATIC), - Self::Switch => Some(sym::SWITCH), - Self::Case => Some(sym::CASE), - Self::Get => Some(sym::GET), - Self::Set => Some(sym::SET), + Self::If => Some(sym::if_), + Self::Else => Some(sym::else_), + Self::Function => Some(sym::function), + Self::Var => Some(sym::var), + Self::Let => Some(sym::let_), + Self::Const => Some(sym::const_), + Self::Return => Some(sym::return_), + Self::Throw => Some(sym::throw), + Self::Try => Some(sym::try_), + Self::Catch => Some(sym::catch), + Self::Finally => Some(sym::finally), + Self::TrueLit => Some(sym::true_), + Self::FalseLit => Some(sym::false_), + Self::NullLit => Some(sym::null), + Self::UndefinedLit => Some(sym::undefined), + Self::Yield => Some(sym::yield_), + Self::New => Some(sym::new), + Self::For => Some(sym::for_), + Self::Do => Some(sym::do_), + Self::While => Some(sym::while_), + Self::In => Some(sym::in_), + Self::Instanceof => Some(sym::instanceof), + Self::Async => Some(sym::async_), + Self::Await => Some(sym::await_), + Self::Delete => Some(sym::delete), + Self::Void => Some(sym::void), + Self::Typeof => Some(sym::typeof_), + Self::Continue => Some(sym::continue_), + Self::Break => Some(sym::break_), + Self::Import => Some(sym::import), + Self::Export => Some(sym::export), + Self::Default => Some(sym::default), + Self::Debugger => Some(sym::debugger), + Self::Of => Some(sym::of), + Self::Class => Some(sym::class), + Self::Extends => Some(sym::extends), + Self::Static => Some(sym::static_), + Self::Switch => Some(sym::switch), + Self::Case => Some(sym::case), + Self::Get => Some(sym::get), + Self::Set => Some(sym::set), _ => None, } } @@ -462,14 +461,14 @@ impl TokenType { /// Returns a "dummy" identifier. /// Should only be used in `ErrorKind`s. - pub const DUMMY_IDENTIFIER: Self = Self::Identifier(sym::EMPTY); + pub const DUMMY_IDENTIFIER: Self = Self::Identifier(sym::empty); /// Returns a "dummy" string. - pub const DUMMY_STRING: Self = Self::String(sym::EMPTY); + pub const DUMMY_STRING: Self = Self::String(sym::empty); /// Returns a "dummy" template literal. /// Should only be used in `ErrorKind`s. - pub const DUMMY_TEMPLATE_LITERAL: Self = Self::TemplateLiteral(sym::EMPTY); + pub const DUMMY_TEMPLATE_LITERAL: Self = Self::TemplateLiteral(sym::empty); pub fn fmt_for_expected_tys(&self) -> impl fmt::Display + '_ { struct DisplayExpectedTys<'a>(&'a TokenType); @@ -489,47 +488,47 @@ impl TokenType { pub fn as_token(s: Symbol) -> TokenType { match s { - sym::IF => TokenType::If, - sym::ELSE => TokenType::Else, - sym::FUNCTION => TokenType::Function, - sym::VAR => TokenType::Var, - sym::LET => TokenType::Let, - sym::CONST => TokenType::Const, - sym::RETURN => TokenType::Return, - sym::THROW => TokenType::Throw, - sym::TRY => TokenType::Try, - sym::CATCH => TokenType::Catch, - sym::FINALLY => TokenType::Finally, - sym::TRUE_LIT => TokenType::TrueLit, - sym::FALSE_LIT => TokenType::FalseLit, - sym::NULL_LIT => TokenType::NullLit, - sym::UNDEFINED_LIT => TokenType::UndefinedLit, - sym::YIELD => TokenType::Yield, - sym::NEW => TokenType::New, - sym::FOR => TokenType::For, - sym::DO => TokenType::Do, - sym::WHILE => TokenType::While, - sym::IN => TokenType::In, - sym::INSTANCEOF => TokenType::Instanceof, - sym::ASYNC => TokenType::Async, - sym::AWAIT => TokenType::Await, - sym::DELETE => TokenType::Delete, - sym::VOID => TokenType::Void, - sym::TYPEOF => TokenType::Typeof, - sym::CONTINUE => TokenType::Continue, - sym::BREAK => TokenType::Break, - sym::IMPORT => TokenType::Import, - sym::EXPORT => TokenType::Export, - sym::DEFAULT => TokenType::Default, - sym::DEBUGGER => TokenType::Debugger, - sym::OF => TokenType::Of, - sym::CLASS => TokenType::Class, - sym::EXTENDS => TokenType::Extends, - sym::STATIC => TokenType::Static, - sym::SWITCH => TokenType::Switch, - sym::CASE => TokenType::Case, - sym::GET => TokenType::Get, - sym::SET => TokenType::Set, + sym::if_ => TokenType::If, + sym::else_ => TokenType::Else, + sym::function => TokenType::Function, + sym::var => TokenType::Var, + sym::let_ => TokenType::Let, + sym::const_ => TokenType::Const, + sym::return_ => TokenType::Return, + sym::throw => TokenType::Throw, + sym::try_ => TokenType::Try, + sym::catch => TokenType::Catch, + sym::finally => TokenType::Finally, + sym::true_ => TokenType::TrueLit, + sym::false_ => TokenType::FalseLit, + sym::null => TokenType::NullLit, + sym::undefined => TokenType::UndefinedLit, + sym::yield_ => TokenType::Yield, + sym::new => TokenType::New, + sym::for_ => TokenType::For, + sym::do_ => TokenType::Do, + sym::while_ => TokenType::While, + sym::in_ => TokenType::In, + sym::instanceof => TokenType::Instanceof, + sym::async_ => TokenType::Async, + sym::await_ => TokenType::Await, + sym::delete => TokenType::Delete, + sym::void => TokenType::Void, + sym::typeof_ => TokenType::Typeof, + sym::continue_ => TokenType::Continue, + sym::break_ => TokenType::Break, + sym::import => TokenType::Import, + sym::export => TokenType::Export, + sym::default => TokenType::Default, + sym::debugger => TokenType::Debugger, + sym::of => TokenType::Of, + sym::class => TokenType::Class, + sym::extends => TokenType::Extends, + sym::static_ => TokenType::Static, + sym::switch => TokenType::Switch, + sym::case => TokenType::Case, + sym::get => TokenType::Get, + sym::set => TokenType::Set, _ => TokenType::Identifier(s), } } diff --git a/crates/dash_middle/src/parser/expr.rs b/crates/dash_middle/src/parser/expr.rs index c1568b55..a6dff4c7 100644 --- a/crates/dash_middle/src/parser/expr.rs +++ b/crates/dash_middle/src/parser/expr.rs @@ -517,7 +517,7 @@ impl LiteralExpr { Self::Boolean(b) => Some(*b), Self::Identifier(_) => None, Self::Number(n) => Some(*n != 0.0), - Self::String(s) => Some(*s != sym::EMPTY), + Self::String(s) => Some(*s != sym::empty), Self::Null => Some(false), Self::Undefined => Some(false), Self::Regex(..) => Some(true), diff --git a/crates/dash_middle/src/parser/statement.rs b/crates/dash_middle/src/parser/statement.rs index c851c65d..2b09a5a4 100644 --- a/crates/dash_middle/src/parser/statement.rs +++ b/crates/dash_middle/src/parser/statement.rs @@ -180,7 +180,7 @@ impl ImportKind { /// A catch statement #[derive(Debug, Clone, Display)] -#[display(fmt = "catch ({}) {{ {} }}", "ident.unwrap_or(sym::EMPTY)", "body")] +#[display(fmt = "catch ({}) {{ {} }}", "ident.unwrap_or(sym::empty)", "body")] pub struct Catch { /// The body of a catch statement pub body: Box, @@ -803,7 +803,7 @@ pub struct Class { impl fmt::Display for Class { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "class {}", self.name.unwrap_or(sym::EMPTY))?; + write!(f, "class {}", self.name.unwrap_or(sym::empty))?; if let Some(extends) = &self.extends { write!(f, " extends {extends}")?; @@ -858,7 +858,7 @@ impl ClassMember { } match &self.kind { - ClassMemberKind::Method(m) if m.name == Some(sym::CONSTRUCTOR) => Some(m), + ClassMemberKind::Method(m) if m.name == Some(sym::constructor) => Some(m), _ => None, } } diff --git a/crates/dash_node_impl/src/lib.rs b/crates/dash_node_impl/src/lib.rs index 35b222d3..f7dde80f 100644 --- a/crates/dash_node_impl/src/lib.rs +++ b/crates/dash_node_impl/src/lib.rs @@ -3,7 +3,6 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use anyhow::{anyhow, bail, Context}; -use dash_middle::interner::StringInterner; use dash_middle::parser::error::IntoFormattableErrors; use dash_optimizer::OptLevel; use dash_proc_macro::Trace; @@ -53,7 +52,7 @@ async fn run_inner_fallible(path: &str, opt: OptLevel, initial_gc_threshold: Opt execute_node_module(scope, path, path, &entry, opt, global_state, Rc::new(package_state)).map_err( |err| match err { - (EvalError::Middle(errs), _, entry) => anyhow!("{}", errs.formattable(&entry, true)), + (EvalError::Middle(errs), entry) => anyhow!("{}", errs.formattable(&entry, true)), (EvalError::Exception(err), ..) => anyhow!("{}", format_value(err.root(scope), scope).unwrap()), }, )?; @@ -80,7 +79,7 @@ fn execute_node_module( opt: OptLevel, global_state: Rc, package: Rc, -) -> Result, String)> { +) -> Result { let exports = Value::Object(scope.register(NamedObject::new(scope))); let module = Value::Object(scope.register(NamedObject::new(scope))); let require = Value::Object(scope.register(RequireFunction { @@ -89,8 +88,9 @@ fn execute_node_module( package, object: NamedObject::new(scope), })); + let key = scope.intern("exports"); module - .set_property(scope, "exports".into(), PropertyValue::static_default(exports.clone())) + .set_property(scope, key.into(), PropertyValue::static_default(exports.clone())) .unwrap(); global_state @@ -104,11 +104,11 @@ fn execute_node_module( let fun = match scope.eval(&code, opt) { Ok(v) => v.root(scope), - Err((err, intern)) => return Err((err, Some(intern), code)), + Err(err) => return Err((err, code)), }; fun.apply(scope, Value::undefined(), vec![exports, module.clone(), require]) - .map_err(|err| (EvalError::Exception(err.into()), None, code))?; + .map_err(|err| (EvalError::Exception(err.into()), code))?; Ok(module) } @@ -161,10 +161,12 @@ impl Object for RequireFunction { let Some(Value::String(arg)) = args.first() else { throw!(scope, Error, "require() expects a string argument"); }; + let exports = scope.intern("exports"); + let arg = arg.res(scope); let is_path = matches!(arg.chars().next(), Some('.' | '/' | '~')); if is_path { - let canonicalized_path = match self.current_dir.join(&**arg).canonicalize() { + let canonicalized_path = match self.current_dir.join(arg).canonicalize() { Ok(v) => v, Err(err) => throw!(scope, Error, err.to_string()), }; @@ -198,15 +200,15 @@ impl Object for RequireFunction { ) { Ok(v) => v, Err((EvalError::Exception(value), ..)) => return Err(value), - Err((EvalError::Middle(errs), _, source)) => { + Err((EvalError::Middle(errs), source)) => { throw!(scope, SyntaxError, "{}", errs.formattable(&source, true)) } }; - module.get_property(scope, "exports".into()) + module.get_property(scope, exports.into()) } else { // Resolve dependency - let dir_path = self.state.node_modules_dir.join(&**arg); + let dir_path = self.state.node_modules_dir.join(arg); let package_state = process_package_json(&dir_path).unwrap(); let file_path = dir_path.join(&package_state.metadata.main); let source = std::fs::read_to_string(&file_path).unwrap(); @@ -222,12 +224,12 @@ impl Object for RequireFunction { ) { Ok(v) => v, Err((EvalError::Exception(value), ..)) => return Err(value), - Err((EvalError::Middle(errs), _, source)) => { + Err((EvalError::Middle(errs), source)) => { throw!(scope, SyntaxError, "{}", errs.formattable(&source, true)) } }; - module.get_property(scope, "exports".into()) + module.get_property(scope, exports.into()) } } } diff --git a/crates/dash_parser/src/expr.rs b/crates/dash_parser/src/expr.rs index c3c4b29d..d3d01a58 100644 --- a/crates/dash_parser/src/expr.rs +++ b/crates/dash_parser/src/expr.rs @@ -458,7 +458,7 @@ impl<'a, 'interner> Parser<'a, 'interner> { .. }) ) { - ObjectMemberKind::Static(sym::GET) + ObjectMemberKind::Static(sym::get) } else { ObjectMemberKind::Getter(self.expect_identifier_or_reserved_kw(true)?) } @@ -471,7 +471,7 @@ impl<'a, 'interner> Parser<'a, 'interner> { .. }) ) { - ObjectMemberKind::Static(sym::SET) + ObjectMemberKind::Static(sym::set) } else { ObjectMemberKind::Setter(self.expect_identifier_or_reserved_kw(true)?) } diff --git a/crates/dash_proc_macro/Cargo.toml b/crates/dash_proc_macro/Cargo.toml index 53e5584c..c9125571 100644 --- a/crates/dash_proc_macro/Cargo.toml +++ b/crates/dash_proc_macro/Cargo.toml @@ -10,7 +10,7 @@ proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -syn = "1.0" +syn = { version = "1.0", features = ["parsing", "extra-traits", "full"] } quote = "1.0" proc-macro-crate = "1.1.3" proc-macro2 = "1.0.50" diff --git a/crates/dash_proc_macro/src/define_symbols.rs b/crates/dash_proc_macro/src/define_symbols.rs new file mode 100644 index 00000000..f6b63fbf --- /dev/null +++ b/crates/dash_proc_macro/src/define_symbols.rs @@ -0,0 +1,106 @@ +use std::collections::HashSet; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{Expr, ExprLit, Lit, Member}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum Kind { + Keyword = 0, + Symbol = 1, +} + +pub fn define_symbols_impl(tt: TokenStream) -> TokenStream { + let Expr::Array(arr) = syn::parse_macro_input!(tt as syn::Expr) else { + panic!("must be an array"); + }; + + let mut symbols = Vec::new(); + + for expr in arr.elems { + if let Expr::Struct(strukt) = expr { + let kind = match strukt.path.segments.last().unwrap().ident.to_string().as_ref() { + "Keywords" => Kind::Keyword, + "Symbols" => Kind::Symbol, + other => panic!("unknown struct: {}", other), + }; + + for field in strukt.fields { + let Member::Named(name) = field.member else { + todo!("must be a named member") + }; + + if let Expr::Lit(ExprLit { + lit: Lit::Str(string), .. + }) = field.expr + { + // alias, e.g. `true_: "true"` + symbols.push((kind, name, string.value())); + } else { + let v = name.to_string(); + symbols.push((kind, name, v)); + } + } + } else { + panic!("must be a struct") + } + } + + let mut rust_idents = HashSet::new(); + let mut js_idents = HashSet::new(); + for (_, rust_sym, js_sym) in &symbols { + if !rust_idents.insert(rust_sym) { + panic!("duplicate rust ident: {}", rust_sym); + } + if !js_idents.insert(js_sym) { + panic!("duplicate js ident: {}", js_sym); + } + } + + symbols.sort_by(|a, b| a.0.cmp(&b.0)); + + // defines all the const symbols + let consts = symbols + .iter() + .enumerate() + .map(|(index, (_, rust_sym, _))| { + let index = index as u32; + quote! { + pub const #rust_sym: Symbol = Symbol(#index); + } + }) + .collect::(); + + // defines the symbol array + let preinterned_array = symbols + .iter() + .map(|(_, rust_sym, js_sym)| { + quote! { + (#js_sym, #rust_sym), + } + }) + .collect::(); + + let keyword_start = symbols.iter().position(|&(k, ..)| k == Kind::Symbol).unwrap(); + let (keyword_end, ..) = symbols + .iter() + .enumerate() + .filter(|(_, &(k, ..))| k == Kind::Symbol) + .last() + .unwrap(); + + let keyword_start = keyword_start as u32; + let keyword_end = keyword_end as u32; + + quote! { + #consts + + pub const KEYWORD_START: u32 = #keyword_start; + pub const KEYWORD_END: u32 = #keyword_end; + + pub const PREINTERNED: &[(&str, Symbol)] = &[ + #preinterned_array + ]; + } + .into() +} diff --git a/crates/dash_proc_macro/src/lib.rs b/crates/dash_proc_macro/src/lib.rs index 108b258d..df6ea23d 100644 --- a/crates/dash_proc_macro/src/lib.rs +++ b/crates/dash_proc_macro/src/lib.rs @@ -1,49 +1,14 @@ use proc_macro::TokenStream; -use proc_macro2::Span; -use proc_macro_crate::{crate_name, FoundCrate}; -use quote::quote; -use syn::{Data, DataStruct, Fields, Ident}; -macro_rules! error { - ($msg:expr) => { - return quote! { compile_error!($msg); }.into() - }; -} +mod define_symbols; +mod trace; #[proc_macro_derive(Trace)] pub fn trace(tt: TokenStream) -> TokenStream { - let input = syn::parse_macro_input!(tt as syn::DeriveInput); - let ident = input.ident; - - let generics = &input.generics; - let fields = match input.data { - Data::Struct(DataStruct { - fields: Fields::Named(ref fields), - .. - }) => fields - .named - .iter() - .map(|x| x.ident.as_ref().unwrap()) - .map(|x| quote! { self.#x.trace(); }), - _ => error!("#[derive(Trace)] can only be used on structs"), - }; - - let found_crate = match crate_name("dash_vm").unwrap() { - FoundCrate::Itself => quote!(crate::gc::trace::Trace), - FoundCrate::Name(crate_name) => { - let ident = Ident::new(&crate_name, Span::call_site()); - quote!(::#ident::gc::trace::Trace) - } - }; - - let generics_names = generics.type_params().map(|x| &x.ident); - let expanded = quote! { - unsafe impl #generics #found_crate for #ident <#(#generics_names),*> { - fn trace(&self) { - #(#fields)* - } - } - }; + trace::trace_impl(tt) +} - expanded.into() +#[proc_macro] +pub fn define_symbols(tt: TokenStream) -> TokenStream { + define_symbols::define_symbols_impl(tt) } diff --git a/crates/dash_proc_macro/src/trace.rs b/crates/dash_proc_macro/src/trace.rs new file mode 100644 index 00000000..3ef4418b --- /dev/null +++ b/crates/dash_proc_macro/src/trace.rs @@ -0,0 +1,52 @@ +use proc_macro::TokenStream; +use proc_macro2::Span; +use proc_macro_crate::{crate_name, FoundCrate}; +use quote::quote; +use syn::{Data, DataStruct, Fields, Ident}; + +macro_rules! error { + ($msg:expr) => { + return quote! { compile_error!($msg); }.into() + }; +} + +pub fn trace_impl(tt: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(tt as syn::DeriveInput); + let ident = input.ident; + + let generics = &input.generics; + let fields = match input.data { + Data::Struct(DataStruct { + fields: Fields::Named(ref fields), + .. + }) => fields + .named + .iter() + .map(|x| x.ident.as_ref().unwrap()) + .map(|x| quote! { self.#x.trace(cx); }), + _ => error!("#[derive(Trace)] can only be used on structs"), + }; + + let (trace_trait, trace_ctxt) = match crate_name("dash_vm").unwrap() { + FoundCrate::Itself => (quote!(crate::gc::trace::Trace), quote!(crate::gc::trace::TraceCtxt<'_>)), + FoundCrate::Name(crate_name) => { + let ident = Ident::new(&crate_name, Span::call_site()); + ( + quote!(::#ident::gc::trace::Trace), + quote!(::#ident::gc::trace::TraceCtxt<'_>), + ) + } + }; + + let generics_names = generics.type_params().map(|x| &x.ident); + let expanded = quote! { + unsafe impl #generics #trace_trait for #ident <#(#generics_names),*> { + #[allow(unused_variables)] + fn trace(&self, cx: &mut #trace_ctxt) { + #(#fields)* + } + } + }; + + expanded.into() +} diff --git a/crates/dash_rt/src/lib.rs b/crates/dash_rt/src/lib.rs index 27d641ec..0ca15510 100755 --- a/crates/dash_rt/src/lib.rs +++ b/crates/dash_rt/src/lib.rs @@ -1,10 +1,8 @@ use std::cell::OnceCell; use std::future::Future; -use std::rc::Rc; use dash_compiler::FunctionCompiler; use dash_middle::compiler::CompileResult; -use dash_middle::interner::StringInterner; use dash_vm::frame::{Exports, Frame}; use dash_vm::gc::persistent::Persistent; use dash_vm::localscope::LocalScope; @@ -67,7 +65,7 @@ where Ok(Value::Object(promise)) } -pub fn format_value(value: Value, scope: &mut LocalScope) -> Result, Value> { +pub fn format_value<'s>(value: Value, scope: &'s mut LocalScope) -> Result<&'s str, Value> { thread_local! { // Cache bytecode so we can avoid recompiling it every time // We can be even smarter if we need to -- cache the whole value at callsite @@ -78,7 +76,7 @@ pub fn format_value(value: Value, scope: &mut LocalScope) -> Result, Val let inspect = tls.get_or_init(|| { FunctionCompiler::compile_str( // TODO: can reuse a string interner if worth it - &mut StringInterner::new(), + &mut scope.interner, include_str!("../js/inspect.js"), Default::default(), ) @@ -100,8 +98,8 @@ pub fn format_value(value: Value, scope: &mut LocalScope) -> Result, Val .apply(scope, Value::undefined(), vec![value]) .unwrap() .root(scope) - .to_string(scope) + .to_js_string(scope) .unwrap(); - Ok(result) + Ok(result.res(scope)) } diff --git a/crates/dash_rt/src/module.rs b/crates/dash_rt/src/module.rs index 9f267ca1..90f5ab96 100644 --- a/crates/dash_rt/src/module.rs +++ b/crates/dash_rt/src/module.rs @@ -2,19 +2,20 @@ use std::fmt::Debug; use dash_middle::compiler::StaticImportKind; use dash_vm::localscope::LocalScope; +use dash_vm::value::string::JsString; use dash_vm::value::Value; #[derive(Debug)] pub struct NoopModule; impl ModuleLoader for NoopModule { - fn import(&self, _: &mut LocalScope, _: StaticImportKind, _: &str) -> Result, Value> { + fn import(&self, _: &mut LocalScope, _: StaticImportKind, _: JsString) -> Result, Value> { Ok(None) } } pub trait ModuleLoader: Debug { - fn import(&self, sc: &mut LocalScope, import_ty: StaticImportKind, path: &str) -> Result, Value>; + fn import(&self, sc: &mut LocalScope, import_ty: StaticImportKind, path: JsString) -> Result, Value>; fn or(self, other: M) -> Or where @@ -31,7 +32,7 @@ pub struct Or { } impl ModuleLoader for Or { - fn import(&self, sc: &mut LocalScope, import_ty: StaticImportKind, path: &str) -> Result, Value> { + fn import(&self, sc: &mut LocalScope, import_ty: StaticImportKind, path: JsString) -> Result, Value> { let m1 = self.m1.import(sc, import_ty, path)?; if m1.is_some() { return Ok(m1); diff --git a/crates/dash_rt/src/runtime.rs b/crates/dash_rt/src/runtime.rs index 4db91a0a..f00916e5 100644 --- a/crates/dash_rt/src/runtime.rs +++ b/crates/dash_rt/src/runtime.rs @@ -2,10 +2,10 @@ use std::fmt::Debug; use std::time::SystemTime; use dash_middle::compiler::StaticImportKind; -use dash_middle::interner::StringInterner; use dash_optimizer::OptLevel; use dash_vm::eval::EvalError; use dash_vm::params::VmParams; +use dash_vm::value::string::JsString; use dash_vm::value::{Unrooted, Value}; use dash_vm::{throw, Vm}; use tokio::sync::mpsc; @@ -56,7 +56,7 @@ impl Runtime { State::from_vm(&self.vm).set_root_module(module_manager); } - pub fn eval(&mut self, code: &str, opt: OptLevel) -> Result { + pub fn eval(&mut self, code: &str, opt: OptLevel) -> Result { self.vm.eval(code, opt) } @@ -103,7 +103,7 @@ fn time_callback(_: &mut Vm) -> Result { .as_millis() as u64) } -fn import_callback(vm: &mut Vm, import_ty: StaticImportKind, path: &str) -> Result { +fn import_callback(vm: &mut Vm, import_ty: StaticImportKind, path: JsString) -> Result { let mut sc = vm.scope(); let root = State::from_vm(&sc).root_module().clone(); @@ -117,5 +117,6 @@ fn import_callback(vm: &mut Vm, import_ty: StaticImportKind, path: &str) -> Resu } // If it got here, the module was not found - throw!(sc, RangeError, "Module not found: {}", path) + let path = path.res(&mut sc).to_owned(); + throw!(&mut sc, RangeError, "Module not found: {}", path) } diff --git a/crates/dash_rt_fetch/src/lib.rs b/crates/dash_rt_fetch/src/lib.rs index 4247653f..712ee01d 100644 --- a/crates/dash_rt_fetch/src/lib.rs +++ b/crates/dash_rt_fetch/src/lib.rs @@ -4,13 +4,14 @@ use dash_rt::event::EventMessage; use dash_rt::module::ModuleLoader; use dash_rt::state::State; use dash_vm::gc::persistent::Persistent; -use dash_vm::gc::trace::Trace; +use dash_vm::gc::trace::{Trace, TraceCtxt}; use dash_vm::localscope::LocalScope; use dash_vm::value::error::Error; use dash_vm::value::function::native::CallContext; use dash_vm::value::function::{Function, FunctionKind}; -use dash_vm::value::object::{NamedObject, Object, PropertyKey, PropertyValue}; +use dash_vm::value::object::{NamedObject, Object, PropertyValue}; use dash_vm::value::promise::Promise; +use dash_vm::value::string::JsString; use dash_vm::value::Value; use dash_vm::{delegate, throw, PromiseAction, Vm}; use once_cell::sync::Lazy; @@ -20,12 +21,18 @@ use reqwest::{Client, Method}; pub struct FetchModule; impl ModuleLoader for FetchModule { - fn import(&self, sc: &mut LocalScope, _import_ty: StaticImportKind, path: &str) -> Result, Value> { - if path != "@std/fetch" { + fn import( + &self, + sc: &mut LocalScope, + _import_ty: StaticImportKind, + path: JsString, + ) -> Result, Value> { + if path.res(sc) != "@std/fetch" { return Ok(None); } - let fun = Function::new(sc, Some("fetch".into()), FunctionKind::Native(fetch)); + let name = sc.intern("fetch"); + let fun = Function::new(sc, Some(name.into()), FunctionKind::Native(fetch)); let fun = sc.register(fun); Ok(Some(Value::Object(fun))) @@ -36,7 +43,7 @@ static REQWEST: Lazy = Lazy::new(Client::new); fn fetch(cx: CallContext) -> Result { let url = match cx.args.first() { - Some(Value::String(url)) => url.to_string(), + Some(Value::String(url)) => url.res(cx.scope).to_owned(), _ => throw!(cx.scope, TypeError, "Expected a string as the first argument"), }; @@ -70,20 +77,17 @@ fn fetch(cx: CallContext) -> Result { let (req, action) = match req { Ok(resp) => { let obj = HttpResponse::new(resp, &sc); - let text_fun = Function::new(&sc, Some("text".into()), FunctionKind::Native(http_response_text)); + let text = sc.intern("text"); + let text_fun = Function::new(&sc, Some(text.into()), FunctionKind::Native(http_response_text)); let text_fun = Value::Object(sc.register(text_fun)); - obj.set_property( - &mut sc, - PropertyKey::String("text".into()), - PropertyValue::static_default(text_fun), - ) - .unwrap(); + obj.set_property(&mut sc, text.into(), PropertyValue::static_default(text_fun)) + .unwrap(); (Value::Object(sc.register(obj)), PromiseAction::Resolve) } Err(err) => { - let err = Error::new(&sc, err.to_string()); + let err = Error::new(&mut sc, err.to_string()); (Value::Object(sc.register(err)), PromiseAction::Reject) } }; @@ -136,11 +140,11 @@ fn http_response_text(cx: CallContext) -> Result { let (value, action) = match text { Ok(text) => { - let text = Value::String(text.into()); + let text = Value::String(sc.intern(text.as_ref()).into()); (text, PromiseAction::Resolve) } Err(err) => { - let err = Error::new(&sc, err.to_string()); + let err = Error::new(&mut sc, err.to_string()); let err = Value::Object(sc.register(err)); (err, PromiseAction::Reject) } @@ -170,8 +174,8 @@ impl HttpResponse { } unsafe impl Trace for HttpResponse { - fn trace(&self) { - self.obj.trace(); + fn trace(&self, cx: &mut TraceCtxt<'_>) { + self.obj.trace(cx); } } diff --git a/crates/dash_rt_fs/src/lib.rs b/crates/dash_rt_fs/src/lib.rs index e2366cda..88ec9e42 100644 --- a/crates/dash_rt_fs/src/lib.rs +++ b/crates/dash_rt_fs/src/lib.rs @@ -1,6 +1,7 @@ use dash_middle::compiler::StaticImportKind; use dash_rt::module::ModuleLoader; use dash_vm::localscope::LocalScope; +use dash_vm::value::string::JsString; use dash_vm::value::Value; pub mod promises; @@ -10,8 +11,13 @@ pub mod sync; pub struct FsModule; impl ModuleLoader for FsModule { - fn import(&self, sc: &mut LocalScope, _import_ty: StaticImportKind, path: &str) -> Result, Value> { - match path { + fn import( + &self, + sc: &mut LocalScope, + _import_ty: StaticImportKind, + path: JsString, + ) -> Result, Value> { + match path.res(sc) { "@std/fs" => promises::init_module(sc).map(Some), "@std/fssync" => sync::init_module(sc).map(Some), _ => Ok(None), diff --git a/crates/dash_rt_fs/src/promises.rs b/crates/dash_rt_fs/src/promises.rs index c012b451..0c9b54bf 100644 --- a/crates/dash_rt_fs/src/promises.rs +++ b/crates/dash_rt_fs/src/promises.rs @@ -3,18 +3,19 @@ use dash_vm::localscope::LocalScope; use dash_vm::value::error::Error; use dash_vm::value::function::native::CallContext; use dash_vm::value::function::{Function, FunctionKind}; -use dash_vm::value::object::{NamedObject, Object, PropertyKey, PropertyValue}; +use dash_vm::value::object::{NamedObject, Object, PropertyValue}; use dash_vm::value::ops::conversions::ValueConversion; use dash_vm::value::{Value, ValueContext}; pub fn init_module(sc: &mut LocalScope) -> Result { - let read_file_value = Function::new(sc, Some("readFile".into()), FunctionKind::Native(read_file)); + let name = sc.intern("readFile"); + let read_file_value = Function::new(sc, Some(name.into()), FunctionKind::Native(read_file)); let read_file_value = sc.register(read_file_value); let module = NamedObject::new(sc); module.set_property( sc, - PropertyKey::String("readFile".into()), + name.into(), PropertyValue::static_default(Value::Object(read_file_value)), )?; @@ -22,11 +23,16 @@ pub fn init_module(sc: &mut LocalScope) -> Result { } fn read_file(cx: CallContext) -> Result { - let path = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - let path = ToString::to_string(&path); + let path = cx + .args + .first() + .unwrap_or_undefined() + .to_js_string(cx.scope)? + .res(cx.scope) + .to_owned(); wrap_async(cx, tokio::fs::read_to_string(path), |sc, res| match res { - Ok(s) => Ok(Value::String(s.into())), + Ok(s) => Ok(Value::String(sc.intern(s.as_ref()).into())), Err(e) => { let err = Error::new(sc, e.to_string()); Err(Value::Object(sc.register(err))) diff --git a/crates/dash_rt_fs/src/sync.rs b/crates/dash_rt_fs/src/sync.rs index bfe835e3..781f40fb 100644 --- a/crates/dash_rt_fs/src/sync.rs +++ b/crates/dash_rt_fs/src/sync.rs @@ -2,18 +2,19 @@ use dash_vm::localscope::LocalScope; use dash_vm::value::error::Error; use dash_vm::value::function::native::CallContext; use dash_vm::value::function::{Function, FunctionKind}; -use dash_vm::value::object::{NamedObject, Object, PropertyKey, PropertyValue}; +use dash_vm::value::object::{NamedObject, Object, PropertyValue}; use dash_vm::value::ops::conversions::ValueConversion; use dash_vm::value::{Value, ValueContext}; pub fn init_module(sc: &mut LocalScope) -> Result { - let read_file_value = Function::new(sc, Some("readFile".into()), FunctionKind::Native(read_file)); + let name = sc.intern("readFile"); + let read_file_value = Function::new(sc, Some(name.into()), FunctionKind::Native(read_file)); let read_file_value = sc.register(read_file_value); let module = NamedObject::new(sc); module.set_property( sc, - PropertyKey::String("readFile".into()), + name.into(), PropertyValue::static_default(Value::Object(read_file_value)), )?; @@ -21,11 +22,16 @@ pub fn init_module(sc: &mut LocalScope) -> Result { } fn read_file(cx: CallContext) -> Result { - let path = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - let path = ToString::to_string(&path); + let path = cx + .args + .first() + .unwrap_or_undefined() + .to_js_string(cx.scope)? + .res(cx.scope) + .to_owned(); match std::fs::read_to_string(path) { - Ok(s) => Ok(Value::String(s.into())), + Ok(s) => Ok(Value::String(cx.scope.intern(s.as_ref()).into())), Err(err) => { let err = Error::new(cx.scope, err.to_string()); Err(Value::Object(cx.scope.register(err))) diff --git a/crates/dash_rt_http/src/lib.rs b/crates/dash_rt_http/src/lib.rs index e792d0e7..497fbb88 100644 --- a/crates/dash_rt_http/src/lib.rs +++ b/crates/dash_rt_http/src/lib.rs @@ -8,13 +8,14 @@ use dash_rt::event::EventMessage; use dash_rt::module::ModuleLoader; use dash_rt::state::State; use dash_vm::gc::persistent::Persistent; -use dash_vm::gc::trace::Trace; +use dash_vm::gc::trace::{Trace, TraceCtxt}; use dash_vm::localscope::LocalScope; use dash_vm::value::function::native::CallContext; use dash_vm::value::function::{Function, FunctionKind}; use dash_vm::value::object::{NamedObject, Object, PropertyValue}; use dash_vm::value::ops::conversions::ValueConversion; use dash_vm::value::root_ext::RootErrExt; +use dash_vm::value::string::JsString; use dash_vm::value::{Value, ValueContext}; use dash_vm::{delegate, throw}; use hyper::Body; @@ -25,15 +26,21 @@ use tokio::sync::oneshot::Sender; pub struct HttpModule; impl ModuleLoader for HttpModule { - fn import(&self, sc: &mut LocalScope, _import_ty: StaticImportKind, path: &str) -> Result, Value> { - if path != "@std/http" { + fn import( + &self, + sc: &mut LocalScope, + _import_ty: StaticImportKind, + path: JsString, + ) -> Result, Value> { + if path.res(sc) != "@std/http" { return Ok(None); } let module = NamedObject::new(sc); let listen = Function::new(sc, None, FunctionKind::Native(listen)); let listen = sc.register(listen); - module.set_property(sc, "listen".into(), PropertyValue::static_default(listen.into()))?; + let key = sc.intern("listen"); + module.set_property(sc, key.into(), PropertyValue::static_default(listen.into()))?; let module = sc.register(module); Ok(Some(module.into())) @@ -83,16 +90,17 @@ pub fn listen(cx: CallContext) -> Result { let cb = cb.get(); let ctx = HttpContext::new(&mut scope, req_tx); - let fun = Function::new(&scope, Some("respond".into()), FunctionKind::Native(ctx_respond)); + let name = scope.intern("respond"); + let fun = Function::new(&scope, Some(name.into()), FunctionKind::Native(ctx_respond)); let fun = scope.register(fun); - ctx.set_property(&mut scope, "respond".into(), PropertyValue::static_default(fun.into())) + ctx.set_property(&mut scope, name.into(), PropertyValue::static_default(fun.into())) .unwrap(); let ctx = Value::Object(scope.register(ctx)); if let Err(err) = cb.apply(&mut scope, Value::undefined(), vec![ctx]).root_err(&mut scope) { - match err.to_string(&mut scope) { - Ok(err) => eprintln!("Unhandled exception in HTTP handler! {err}"), + match err.to_js_string(&mut scope) { + Ok(err) => eprintln!("Unhandled exception in HTTP handler! {}", err.res(&scope)), Err(..) => eprintln!("Unhandled exception in exception toString method in HTTP handler!"), } } @@ -127,8 +135,8 @@ struct HttpContext { } unsafe impl Trace for HttpContext { - fn trace(&self) { - self.obj.trace(); + fn trace(&self, cx: &mut TraceCtxt<'_>) { + self.obj.trace(cx); } } @@ -167,9 +175,9 @@ fn ctx_respond(cx: CallContext) -> Result { None => throw!(cx.scope, Error, "Cannot respond twice"), }; - let message = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let message = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; - if sender.send(Body::from(ToString::to_string(&message))).is_err() { + if sender.send(Body::from(message.res(cx.scope).to_owned())).is_err() { eprintln!("Failed to respond to HTTP event."); } diff --git a/crates/dash_rt_net/src/lib.rs b/crates/dash_rt_net/src/lib.rs index acca5084..521d0db4 100644 --- a/crates/dash_rt_net/src/lib.rs +++ b/crates/dash_rt_net/src/lib.rs @@ -2,6 +2,7 @@ use dash_middle::compiler::StaticImportKind; use dash_rt::module::ModuleLoader; use dash_vm::localscope::LocalScope; use dash_vm::value::object::{NamedObject, Object, PropertyValue}; +use dash_vm::value::string::JsString; use dash_vm::value::Value; use crate::listener::TcpListenerConstructor; @@ -12,16 +13,22 @@ mod listener; pub struct NetModule; impl ModuleLoader for NetModule { - fn import(&self, sc: &mut LocalScope, _import_ty: StaticImportKind, path: &str) -> Result, Value> { - if path != "@std/net" { + fn import( + &self, + sc: &mut LocalScope, + _import_ty: StaticImportKind, + path: JsString, + ) -> Result, Value> { + if path.res(sc) != "@std/net" { return Ok(None); } let exports = NamedObject::new(sc); let tcplistener = sc.register(TcpListenerConstructor {}); + let name = sc.intern("TcpListener"); exports.set_property( sc, - "TcpListener".into(), + name.into(), PropertyValue::static_default(Value::Object(tcplistener)), )?; diff --git a/crates/dash_rt_net/src/listener/mod.rs b/crates/dash_rt_net/src/listener/mod.rs index 0efbba95..e807f134 100644 --- a/crates/dash_rt_net/src/listener/mod.rs +++ b/crates/dash_rt_net/src/listener/mod.rs @@ -5,7 +5,7 @@ use dash_rt::event::EventMessage; use dash_rt::state::State; use dash_rt::wrap_async; use dash_vm::gc::persistent::Persistent; -use dash_vm::gc::trace::Trace; +use dash_vm::gc::trace::{Trace, TraceCtxt}; use dash_vm::localscope::LocalScope; use dash_vm::value::arraybuffer::ArrayBuffer; use dash_vm::value::function::native::CallContext; @@ -34,7 +34,7 @@ impl Object for TcpListenerConstructor { fn set_property( &self, _sc: &mut dash_vm::localscope::LocalScope, - _key: dash_vm::value::object::PropertyKey<'static>, + _key: dash_vm::value::object::PropertyKey, _value: dash_vm::value::object::PropertyValue, ) -> Result<(), dash_vm::value::Value> { Ok(()) @@ -87,7 +87,7 @@ impl Object for TcpListenerConstructor { "TcpListener requires the first argument be the address" ); }; - let value = String::from(value.to_string(scope)?.as_ref()); + let value = String::from(value.to_js_string(scope)?.res(scope)); let (tx, mut rx) = mpsc::channel(1); let state = State::from_vm(scope); @@ -142,7 +142,7 @@ impl Object for TcpListenerConstructor { self } - fn own_keys(&self) -> Result, dash_vm::value::Value> { + fn own_keys(&self, _: &mut LocalScope<'_>) -> Result, dash_vm::value::Value> { Ok(Vec::new()) } } @@ -159,22 +159,19 @@ struct TcpListenerHandle { // SAFETY: all fields are recursively traced, enforced via pattern matching unsafe impl Trace for TcpListenerHandle { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { let Self { object, sender: _ } = self; - object.trace(); + object.trace(cx); } } impl TcpListenerHandle { pub fn new(sender: mpsc::Sender, sc: &mut LocalScope) -> Result { let object = NamedObject::new(sc); - let accept_fn = Function::new(sc, Some("accept".into()), FunctionKind::Native(tcplistener_accept)); + let name = sc.intern("accept"); + let accept_fn = Function::new(sc, Some(name.into()), FunctionKind::Native(tcplistener_accept)); let accept_fn = sc.register(accept_fn); - object.set_property( - sc, - "accept".into(), - PropertyValue::static_default(Value::Object(accept_fn)), - )?; + object.set_property(sc, name.into(), PropertyValue::static_default(Value::Object(accept_fn)))?; Ok(Self { object, sender }) } } @@ -232,18 +229,20 @@ impl TcpStreamHandle { reader_tx: mpsc::UnboundedSender>>, ) -> Result { let object = NamedObject::new(scope); - let write_fn = Function::new(scope, Some("write".into()), FunctionKind::Native(tcpstream_write)); + let name = scope.intern("write"); + let write_fn = Function::new(scope, Some(name.into()), FunctionKind::Native(tcpstream_write)); let write_fn = scope.register(write_fn); object.set_property( scope, - "write".into(), + name.into(), PropertyValue::static_default(Value::Object(write_fn)), )?; - let read_fn = Function::new(scope, Some("read".into()), FunctionKind::Native(tcpstream_read)); + let name = scope.intern("read"); + let read_fn = Function::new(scope, Some(name.into()), FunctionKind::Native(tcpstream_read)); let read_fn = scope.register(read_fn); object.set_property( scope, - "read".into(), + name.into(), PropertyValue::static_default(Value::Object(read_fn)), )?; Ok(Self { @@ -255,13 +254,13 @@ impl TcpStreamHandle { } unsafe impl Trace for TcpStreamHandle { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { let Self { object, writer_tx: _, reader_tx: _, } = self; - object.trace(); + object.trace(cx); } } diff --git a/crates/dash_rt_script_modules/src/lib.rs b/crates/dash_rt_script_modules/src/lib.rs index 66af9fb3..cccc0eaf 100644 --- a/crates/dash_rt_script_modules/src/lib.rs +++ b/crates/dash_rt_script_modules/src/lib.rs @@ -3,6 +3,7 @@ use std::cell::RefCell; use dash_middle::compiler::StaticImportKind; use dash_rt::module::ModuleLoader; use dash_vm::localscope::LocalScope; +use dash_vm::value::string::JsString; use dash_vm::value::{Root, Value}; use dash_vm::{throw, Vm}; use indexmap::IndexSet; @@ -13,14 +14,14 @@ pub struct ScriptModule { } impl ScriptModule { - pub fn add_import(&self, sc: &mut LocalScope, name: &str) -> Result<(), Value> { + pub fn add_import(&self, sc: &mut LocalScope, name: String) -> Result { let mut stack = self.import_stack.borrow_mut(); - if stack.contains(name) { + if stack.contains(&name) { throw!(sc, Error, "import cycle detected: {}", name); } stack.insert(name.to_string()); - Ok(()) + Ok(name) } pub fn pop_import(&self) { @@ -29,8 +30,8 @@ impl ScriptModule { } impl ModuleLoader for ScriptModule { - fn import(&self, sc: &mut LocalScope, import_ty: StaticImportKind, path: &str) -> Result, Value> { - self.add_import(sc, path)?; + fn import(&self, sc: &mut LocalScope, import_ty: StaticImportKind, path: JsString) -> Result, Value> { + let path = self.add_import(sc, path.res(sc).to_owned())?; let contents = match std::fs::read_to_string(path) { Ok(c) => c, diff --git a/crates/dash_rt_timers/src/lib.rs b/crates/dash_rt_timers/src/lib.rs index f7780779..651b0707 100644 --- a/crates/dash_rt_timers/src/lib.rs +++ b/crates/dash_rt_timers/src/lib.rs @@ -13,20 +13,27 @@ use dash_vm::value::function::native::CallContext; use dash_vm::value::function::{Function, FunctionKind}; use dash_vm::value::object::{NamedObject, Object, PropertyValue}; use dash_vm::value::ops::conversions::ValueConversion; +use dash_vm::value::string::JsString; use dash_vm::value::Value; #[derive(Debug)] pub struct TimersModule; impl ModuleLoader for TimersModule { - fn import(&self, sc: &mut LocalScope, _import_ty: StaticImportKind, path: &str) -> Result, Value> { - if path == "@std/timers" { + fn import( + &self, + sc: &mut LocalScope, + _import_ty: StaticImportKind, + path: JsString, + ) -> Result, Value> { + if path.res(sc) == "@std/timers" { let obj = NamedObject::new(sc); - let set_timeout = Function::new(sc, Some("setTimeout".into()), FunctionKind::Native(set_timeout)); + let name = sc.intern("setTimeout"); + let set_timeout = Function::new(sc, Some(name.into()), FunctionKind::Native(set_timeout)); let set_timeout = Value::Object(sc.register(set_timeout)); - obj.set_property(sc, "setTimeout".into(), PropertyValue::static_default(set_timeout))?; + obj.set_property(sc, name.into(), PropertyValue::static_default(set_timeout))?; Ok(Some(Value::Object(sc.register(obj)))) } else { diff --git a/crates/dash_vm/Cargo.toml b/crates/dash_vm/Cargo.toml index 9ea7222f..b3fdd51f 100644 --- a/crates/dash_vm/Cargo.toml +++ b/crates/dash_vm/Cargo.toml @@ -24,6 +24,7 @@ bitflags = "1.3.2" smallvec = { version = "1.9.0", features = ["const_generics"] } ahash = "0.8.3" rustc-hash = "1.1.0" +hashbrown = "0.14.0" if_chain = "1.0.2" [dev-dependencies] diff --git a/crates/dash_vm/src/dispatch.rs b/crates/dash_vm/src/dispatch.rs index eeb7bc56..7304bedd 100755 --- a/crates/dash_vm/src/dispatch.rs +++ b/crates/dash_vm/src/dispatch.rs @@ -2,12 +2,12 @@ use dash_log::warn; use std::ops::{Deref, DerefMut}; -use std::rc::Rc; use std::vec::Drain; use crate::frame::Frame; use crate::gc::handle::Handle; use crate::localscope::LocalScope; +use crate::value::string::JsString; use crate::value::{ExternalValue, Root, Unrooted}; use super::value::Value; @@ -159,18 +159,18 @@ impl<'sc, 'vm> DispatchContext<'sc, 'vm> { self.active_frame().function.constants[index].clone() } - pub fn identifier_constant(&self, index: usize) -> Rc { + pub fn identifier_constant(&self, index: usize) -> JsString { self.constant(index) .as_identifier() - .cloned() .expect("Bytecode attempted to reference invalid identifier constant") + .into() } - pub fn string_constant(&self, index: usize) -> Rc { + pub fn string_constant(&self, index: usize) -> JsString { self.constant(index) .as_string() - .cloned() .expect("Bytecode attempted to reference invalid string constant") + .into() } pub fn number_constant(&self, index: usize) -> f64 { @@ -196,9 +196,9 @@ impl<'sc, 'vm> DerefMut for DispatchContext<'sc, 'vm> { mod handlers { use dash_middle::compiler::instruction::{AssignKind, IntrinsicOperation}; use dash_middle::compiler::{ArrayMemberKind, FunctionCallMetadata, ObjectMemberKind, StaticImportKind}; + use dash_middle::interner::sym; use if_chain::if_chain; use smallvec::SmallVec; - use std::borrow::Cow; use std::ops::{Add, Div, Mul, Rem, Sub}; use crate::frame::{Frame, FrameState, TryBlock}; @@ -349,7 +349,7 @@ mod handlers { pub fn not<'sc, 'vm>(mut cx: DispatchContext<'sc, 'vm>) -> Result, Unrooted> { let value = cx.pop_stack_rooted(); - let result = value.not(); + let result = value.not(cx.scope); cx.stack.push(result); Ok(None) } @@ -412,11 +412,14 @@ mod handlers { let name = cx.identifier_constant(id.into()); let value = match cx.global.as_any().downcast_ref::() { - Some(value) => match value.get_raw_property(name.as_ref().into()) { + Some(value) => match value.get_raw_property(name.into()) { Some(value) => value.kind().get_or_apply(&mut cx, Value::undefined())?, - None => throw!(&mut cx, ReferenceError, "{} is not defined", name), + None => { + let name = name.res(cx.scope).to_owned(); + throw!(&mut cx, ReferenceError, "{} is not defined", name) + } }, - None => cx.global.clone().get_property(&mut cx, name.as_ref().into())?, + None => cx.global.clone().get_property(&mut cx, name.into())?, }; cx.push_stack(value); @@ -434,15 +437,13 @@ mod handlers { let value = cx .global .clone() - .get_property(&mut cx, PropertyKey::String(Cow::Borrowed(&name))) + .get_property(&mut cx, PropertyKey::String(name)) .root(cx.scope)?; let res = $op(&value, &right, &mut cx)?; - cx.global.clone().set_property( - &mut cx, - ToString::to_string(&name).into(), - PropertyValue::static_default(res.clone()), - )?; + cx.global + .clone() + .set_property(&mut cx, name.into(), PropertyValue::static_default(res.clone()))?; cx.stack.push(res); }}; } @@ -452,17 +453,15 @@ mod handlers { let value = cx .global .clone() - .get_property(&mut cx, PropertyKey::String(Cow::Borrowed(&name))) + .get_property(&mut cx, PropertyKey::String(name)) .root(cx.scope)?; let value = Value::number(value.to_number(&mut cx)?); let right = Value::number(1.0); let res = $op(&value, &right, &mut cx)?; - cx.global.clone().set_property( - &mut cx, - ToString::to_string(&name).into(), - PropertyValue::static_default(res.clone()), - )?; + cx.global + .clone() + .set_property(&mut cx, name.into(), PropertyValue::static_default(res.clone()))?; cx.stack.push(res); }}; } @@ -472,17 +471,15 @@ mod handlers { let value = cx .global .clone() - .get_property(&mut cx, PropertyKey::String(Cow::Borrowed(&name))) + .get_property(&mut cx, PropertyKey::String(name)) .root(cx.scope)?; let value = Value::number(value.to_number(&mut cx)?); let right = Value::number(1.0); let res = $op(&value, &right, &mut cx)?; - cx.global.clone().set_property( - &mut cx, - ToString::to_string(&name).into(), - PropertyValue::static_default(res), - )?; + cx.global + .clone() + .set_property(&mut cx, name.into(), PropertyValue::static_default(res))?; cx.stack.push(value); }}; } @@ -491,11 +488,9 @@ mod handlers { AssignKind::Assignment => { let value = cx.pop_stack_rooted(); - cx.global.clone().set_property( - &mut cx, - ToString::to_string(&name).into(), - PropertyValue::static_default(value.clone()), - )?; + cx.global + .clone() + .set_property(&mut cx, name.into(), PropertyValue::static_default(value.clone()))?; cx.stack.push(value); } AssignKind::AddAssignment => op!(Value::add), @@ -556,10 +551,11 @@ mod handlers { let mut splice_args = SmallVec::<[_; 2]>::new(); for i in 0..length { + let i = cx.scope.intern_usize(i); let value = unsafe { // SAFETY: Rooting is not necessary here because the values being pushed to `splice_args` // are directly "injected" into the value stack - iterable.get_property(&mut cx, i.to_string().into())?.into_value() + iterable.get_property(&mut cx, i.into())?.into_value() }; splice_args.push(value); } @@ -595,9 +591,8 @@ mod handlers { is_constructor: bool, call_ip: u16, ) -> Result, Unrooted> { - let (args, refs) = { + let args = { let mut args = Vec::with_capacity(argc); - let mut refs = Vec::new(); let len = cx.fetch_and_inc_ip(); let spread_indices: SmallVec<[_; 4]> = (0..len).map(|_| cx.fetch_and_inc_ip()).collect(); @@ -607,10 +602,6 @@ mod handlers { if len == 0 { // Fast path for no spread arguments for value in iter { - if let Value::Object(handle) = &value { - refs.push(handle.clone()); - } - args.push(value); } } else { @@ -620,25 +611,23 @@ mod handlers { for (index, value) in raw_args.into_iter().enumerate() { if indices_iter.peek().is_some_and(|&v| usize::from(v) == index) { let len = value.length_of_array_like(cx.scope)?; + let index = cx.scope.intern_usize(index); for _ in 0..len { - let value = value.get_property(&mut cx, index.to_string().into())?.root(cx.scope); + let value = value.get_property(&mut cx, index.into())?.root(cx.scope); // NB: no need to push into `refs` since we already rooted it args.push(value); } indices_iter.next(); } else { - if let Value::Object(handle) = &value { - refs.push(handle.clone()); - } args.push(value); } } } - (args, refs) + args }; - cx.scope.add_many(refs); + cx.scope.add_many(&args); let ret = if is_constructor { callee.construct(&mut cx, this, args)? @@ -689,7 +678,7 @@ mod handlers { let offset = cx.fetchw_and_inc_ip() as i16; let value = cx.pop_stack_rooted(); - let jump = !value.is_truthy(); + let jump = !value.is_truthy(cx.scope); #[cfg(feature = "jit")] cx.record_conditional_jump(ip, jump); @@ -713,7 +702,7 @@ mod handlers { let offset = cx.fetchw_and_inc_ip() as i16; let value = cx.peek_stack(); - let jump = !value.is_truthy(); + let jump = !value.is_truthy(cx.scope); #[cfg(feature = "jit")] cx.record_conditional_jump(ip, jump); @@ -738,7 +727,7 @@ mod handlers { let offset = cx.fetchw_and_inc_ip() as i16; let value = cx.pop_stack_rooted(); - let jump = value.is_truthy(); + let jump = value.is_truthy(cx.scope); #[cfg(feature = "jit")] cx.record_conditional_jump(ip, jump); @@ -762,7 +751,7 @@ mod handlers { let offset = cx.fetchw_and_inc_ip() as i16; let value = cx.peek_stack(); - let jump = value.is_truthy(); + let jump = value.is_truthy(cx.scope); #[cfg(feature = "jit")] cx.record_conditional_jump(ip, jump); @@ -998,7 +987,8 @@ mod handlers { // TODO: make this work for array-like values, not just arrays, by calling @@iterator on it let len = value.length_of_array_like(cx.scope)?; for i in 0..len { - let value = value.get_property(cx.scope, i.to_string().into())?.root(cx.scope); + let i = cx.scope.intern_usize(i); + let value = value.get_property(cx.scope, i.into())?.root(cx.scope); new_elements.push(PropertyValue::static_default(value)); } } @@ -1028,20 +1018,17 @@ mod handlers { ObjectMemberKind::Static => { let id = cx.fetchw_and_inc_ip(); // TODO: optimization opportunity: do not reallocate string from Rc - let key = String::from(cx.identifier_constant(id.into()).as_ref()); + let key = cx.identifier_constant(id.into()); let value = cx.pop_stack_rooted(); - obj.insert( - PropertyKey::String(Cow::Owned(key)), - PropertyValue::static_default(value), - ); + obj.insert(key.into(), PropertyValue::static_default(value)); } ObjectMemberKind::Getter => { let id = cx.fetchw_and_inc_ip(); - let key = PropertyKey::String(Cow::Owned(String::from(cx.identifier_constant(id.into()).as_ref()))); + let key = cx.identifier_constant(id.into()); let Value::Object(value) = cx.pop_stack_rooted() else { panic!("Getter is not an object"); }; - obj.entry(key) + obj.entry(key.into()) .and_modify(|v| match v.kind_mut() { PropertyValueKind::Trap { get, .. } => *get = Some(value.clone()), _ => *v = PropertyValue::getter_default(value.clone()), @@ -1050,11 +1037,11 @@ mod handlers { } ObjectMemberKind::Setter => { let id = cx.fetchw_and_inc_ip(); - let key = PropertyKey::String(Cow::Owned(String::from(cx.identifier_constant(id.into()).as_ref()))); + let key = cx.identifier_constant(id.into()); let Value::Object(value) = cx.pop_stack_rooted() else { panic!("Setter is not an object"); }; - obj.entry(key) + obj.entry(key.into()) .and_modify(|v| match v.kind_mut() { PropertyValueKind::Trap { set, .. } => *set = Some(value.clone()), _ => *v = PropertyValue::setter_default(value.clone()), @@ -1064,7 +1051,7 @@ mod handlers { ObjectMemberKind::Spread => { let value = cx.pop_stack_rooted(); if let Value::Object(target) = &value { - for key in target.own_keys()? { + for key in target.own_keys(cx.scope)? { let key = PropertyKey::from_value(cx.scope, key)?; let value = target.get_property(&mut cx, key.clone())?.root(cx.scope); obj.insert(key, PropertyValue::static_default(value)); @@ -1094,7 +1081,7 @@ mod handlers { cx.pop_stack_rooted() }; - let value = target.get_property(&mut cx, ident.as_ref().into())?; + let value = target.get_property(&mut cx, ident.into())?; cx.push_stack(value); Ok(None) } @@ -1111,16 +1098,10 @@ mod handlers { let target = target.root(cx.scope); let value = value.root(cx.scope); - let p = target - .get_property(&mut cx, PropertyKey::String(Cow::Borrowed(&key)))? - .root(cx.scope); + let p = target.get_property(&mut cx, key.into())?.root(cx.scope); let res = $op(&p, &value, &mut cx)?; - target.set_property( - &mut cx, - ToString::to_string(&key).into(), - PropertyValue::static_default(res.clone()), - )?; + target.set_property(&mut cx, key.into(), PropertyValue::static_default(res.clone()))?; cx.stack.push(res); }}; } @@ -1128,17 +1109,11 @@ mod handlers { macro_rules! postfix { ($op:expr) => {{ let target = cx.pop_stack_rooted(); - let prop = target - .get_property(&mut cx, PropertyKey::String(Cow::Borrowed(&key)))? - .root(cx.scope); + let prop = target.get_property(&mut cx, key.into())?.root(cx.scope); let prop = Value::number(prop.to_number(&mut cx)?); let one = Value::number(1.0); let res = $op(&prop, &one, &mut cx)?; - target.set_property( - &mut cx, - ToString::to_string(&key).into(), - PropertyValue::static_default(res), - )?; + target.set_property(&mut cx, key.into(), PropertyValue::static_default(res))?; cx.stack.push(prop); }}; } @@ -1146,17 +1121,11 @@ mod handlers { macro_rules! prefix { ($op:expr) => {{ let target = cx.pop_stack_rooted(); - let prop = target - .get_property(&mut cx, PropertyKey::String(Cow::Borrowed(&key)))? - .root(cx.scope); + let prop = target.get_property(&mut cx, key.into())?.root(cx.scope); let prop = Value::number(prop.to_number(&mut cx)?); let one = Value::number(1.0); let res = $op(&prop, &one, &mut cx)?; - target.set_property( - &mut cx, - ToString::to_string(&key).into(), - PropertyValue::static_default(res.clone()), - )?; + target.set_property(&mut cx, key.into(), PropertyValue::static_default(res.clone()))?; cx.stack.push(res); }}; } @@ -1166,11 +1135,7 @@ mod handlers { let (target, value) = cx.pop_stack2_new(); let target = target.root(cx.scope); let value = value.root(cx.scope); - target.set_property( - &mut cx, - ToString::to_string(&key).into(), - PropertyValue::static_default(value.clone()), - )?; + target.set_property(&mut cx, key.into(), PropertyValue::static_default(value.clone()))?; cx.stack.push(value); } AssignKind::AddAssignment => op!(Value::add), @@ -1429,7 +1394,7 @@ mod handlers { let path = cx.string_constant(path_id.into()); let value = match cx.params.static_import_callback() { - Some(cb) => cb(&mut cx, ty, &path)?, + Some(cb) => cb(&mut cx, ty, path)?, None => throw!(cx, Error, "Static imports are disabled for this context."), }; @@ -1474,7 +1439,7 @@ mod handlers { let ident = cx.identifier_constant(ident_id.into()); - let value = cx.global.clone().get_property(&mut cx, ident.as_ref().into())?; + let value = cx.global.clone().get_property(&mut cx, ident.into())?; (value, ident) } @@ -1552,8 +1517,8 @@ mod handlers { let value = cx.pop_stack_rooted(); let keys = match value { - Value::Object(obj) => obj.own_keys()?, - Value::External(obj) => obj.own_keys()?, + Value::Object(obj) => obj.own_keys(cx.scope)?, + Value::External(obj) => obj.own_keys(cx.scope)?, _ => Vec::new(), } .into_iter() @@ -1587,8 +1552,7 @@ mod handlers { let target = cx.pop_stack_rooted(); let cid = cx.fetchw_and_inc_ip(); let con = cx.identifier_constant(cid.into()); - let key = PropertyKey::from(con.as_ref()); - let value = target.delete_property(&mut cx, key)?; + let value = target.delete_property(&mut cx, con.into())?; // TODO: not correct, as `undefined` might have been the actual value let did_delete = !matches!(value.root(cx.scope), Value::Undefined(..)); @@ -1609,7 +1573,7 @@ mod handlers { let case_offset = cx.fetchw_and_inc_ip() as usize; let ip = cx.active_frame().ip; - let is_eq = switch_expr.strict_eq(&case_value, cx.scope)?.to_boolean()?; + let is_eq = switch_expr.strict_eq(&case_value, cx.scope)?.to_boolean(cx.scope)?; let has_matching_case = target_ip.is_some(); if is_eq && !has_matching_case { @@ -1644,7 +1608,7 @@ mod handlers { let id = cx.number_constant(loc_id.into()) as usize; let ident = cx.identifier_constant(ident_id.into()); - let prop = obj.get_property(&mut cx, PropertyKey::from(ident.as_ref()))?; + let prop = obj.get_property(&mut cx, ident.into())?; cx.set_local(id, prop.into()); } @@ -1660,7 +1624,9 @@ mod handlers { let id = cx.number_constant(loc_id.into()) as usize; - let prop = array.get_property(&mut cx, PropertyKey::from(i.to_string().as_ref()))?; + let i = cx.scope.intern_usize(i.into()); + + let prop = array.get_property(&mut cx, i.into())?; cx.set_local(id, prop.into()); } @@ -1763,7 +1729,7 @@ mod handlers { } macro_rules! fn_call { - ($fun:ident, $k:ident, $v:ident) => {{ + ($fun:ident, $k:expr, $v:expr) => {{ let argc = cx.fetch_and_inc_ip(); let args = cx.pop_stack_many(argc.into()).collect::>(); let fun = cx.statics.$fun.clone(); @@ -1776,14 +1742,8 @@ mod handlers { // TODO: don't warn here but when purity was violated warn!("missed spec call due to impurity"); // Builtins impure, fallback to slow dynamic property lookup - let k = cx - .global - .clone() - .get_property(&mut cx, PropertyKey::from(stringify!($k)))? - .root(cx.scope); - let fun = k - .get_property(&mut cx, PropertyKey::from(stringify!($v)))? - .root(cx.scope); + let k = cx.global.clone().get_property(&mut cx, $k.into())?.root(cx.scope); + let fun = k.get_property(&mut cx, $v.into())?.root(cx.scope); let result = fun.apply(&mut cx, Value::undefined(), args)?; cx.push_stack(result); } else { @@ -1826,34 +1786,34 @@ mod handlers { IntrinsicOperation::GeNumLConstR32 => bin_op_numl_constr_n!(>=, u32), IntrinsicOperation::LtNumLConstR32 => bin_op_numl_constr_n!(<, u32), IntrinsicOperation::LeNumLConstR32 => bin_op_numl_constr_n!(<=, u32), - IntrinsicOperation::Exp => fn_call!(math_exp, Math, exp), - IntrinsicOperation::Log2 => fn_call!(math_log2, Math, log2), - IntrinsicOperation::Expm1 => fn_call!(math_expm1, Math, expm1), - IntrinsicOperation::Cbrt => fn_call!(math_cbrt, Math, cbrt), - IntrinsicOperation::Clz32 => fn_call!(math_clz32, Math, clz32), - IntrinsicOperation::Atanh => fn_call!(math_atanh, Math, atanh), - IntrinsicOperation::Atan2 => fn_call!(math_atan2, Math, atan2), - IntrinsicOperation::Round => fn_call!(math_round, Math, round), - IntrinsicOperation::Acosh => fn_call!(math_acosh, Math, acosh), - IntrinsicOperation::Abs => fn_call!(math_abs, Math, abs), - IntrinsicOperation::Sinh => fn_call!(math_sinh, Math, sinh), - IntrinsicOperation::Sin => fn_call!(math_sin, Math, sin), - IntrinsicOperation::Ceil => fn_call!(math_ceil, Math, ceil), - IntrinsicOperation::Tan => fn_call!(math_tan, Math, tan), - IntrinsicOperation::Trunc => fn_call!(math_trunc, Math, trunc), - IntrinsicOperation::Asinh => fn_call!(math_asinh, Math, asinh), - IntrinsicOperation::Log10 => fn_call!(math_log10, Math, log10), - IntrinsicOperation::Asin => fn_call!(math_asin, Math, asin), - IntrinsicOperation::Random => fn_call!(math_random, Math, random), - IntrinsicOperation::Log1p => fn_call!(math_log1p, Math, log1p), - IntrinsicOperation::Sqrt => fn_call!(math_sqrt, Math, sqrt), - IntrinsicOperation::Atan => fn_call!(math_atan, Math, atan), - IntrinsicOperation::Cos => fn_call!(math_cos, Math, cos), - IntrinsicOperation::Tanh => fn_call!(math_tanh, Math, tanh), - IntrinsicOperation::Log => fn_call!(math_log, Math, log), - IntrinsicOperation::Floor => fn_call!(math_floor, Math, floor), - IntrinsicOperation::Cosh => fn_call!(math_cosh, Math, cosh), - IntrinsicOperation::Acos => fn_call!(math_acos, Math, acos), + IntrinsicOperation::Exp => fn_call!(math_exp, sym::Math, sym::exp), + IntrinsicOperation::Log2 => fn_call!(math_log2, sym::Math, sym::log2), + IntrinsicOperation::Expm1 => fn_call!(math_expm1, sym::Math, sym::expm1), + IntrinsicOperation::Cbrt => fn_call!(math_cbrt, sym::Math, sym::cbrt), + IntrinsicOperation::Clz32 => fn_call!(math_clz32, sym::Math, sym::clz32), + IntrinsicOperation::Atanh => fn_call!(math_atanh, sym::Math, sym::atanh), + IntrinsicOperation::Atan2 => fn_call!(math_atan2, sym::Math, sym::atan2), + IntrinsicOperation::Round => fn_call!(math_round, sym::Math, sym::round), + IntrinsicOperation::Acosh => fn_call!(math_acosh, sym::Math, sym::acosh), + IntrinsicOperation::Abs => fn_call!(math_abs, sym::Math, sym::abs), + IntrinsicOperation::Sinh => fn_call!(math_sinh, sym::Math, sym::sinh), + IntrinsicOperation::Sin => fn_call!(math_sin, sym::Math, sym::sin), + IntrinsicOperation::Ceil => fn_call!(math_ceil, sym::Math, sym::ceil), + IntrinsicOperation::Tan => fn_call!(math_tan, sym::Math, sym::tan), + IntrinsicOperation::Trunc => fn_call!(math_trunc, sym::Math, sym::trunc), + IntrinsicOperation::Asinh => fn_call!(math_asinh, sym::Math, sym::asinh), + IntrinsicOperation::Log10 => fn_call!(math_log10, sym::Math, sym::log10), + IntrinsicOperation::Asin => fn_call!(math_asin, sym::Math, sym::asin), + IntrinsicOperation::Random => fn_call!(math_random, sym::Math, sym::random), + IntrinsicOperation::Log1p => fn_call!(math_log1p, sym::Math, sym::log1p), + IntrinsicOperation::Sqrt => fn_call!(math_sqrt, sym::Math, sym::sqrt), + IntrinsicOperation::Atan => fn_call!(math_atan, sym::Math, sym::atan), + IntrinsicOperation::Cos => fn_call!(math_cos, sym::Math, sym::cos), + IntrinsicOperation::Tanh => fn_call!(math_tanh, sym::Math, sym::tanh), + IntrinsicOperation::Log => fn_call!(math_log, sym::Math, sym::log), + IntrinsicOperation::Floor => fn_call!(math_floor, sym::Math, sym::floor), + IntrinsicOperation::Cosh => fn_call!(math_cosh, sym::Math, sym::cosh), + IntrinsicOperation::Acos => fn_call!(math_acos, sym::Math, sym::acos), } Ok(None) diff --git a/crates/dash_vm/src/eval.rs b/crates/dash_vm/src/eval.rs index 88897968..08077918 100644 --- a/crates/dash_vm/src/eval.rs +++ b/crates/dash_vm/src/eval.rs @@ -1,12 +1,12 @@ use dash_compiler::FunctionCompiler; use dash_lexer::Lexer; use dash_middle::compiler::StaticImportKind; -use dash_middle::interner::StringInterner; use dash_optimizer::type_infer::TypeInferCtx; use dash_optimizer::OptLevel; use dash_parser::Parser; use crate::frame::Frame; +use crate::gc::interner::sym; use crate::localscope::LocalScope; use crate::value::object::{NamedObject, Object, PropertyValue}; use crate::value::{Root, Unrooted, Value}; @@ -19,19 +19,16 @@ pub enum EvalError { } impl Vm { - pub fn eval_with_interner( - &mut self, - interner: &mut StringInterner, - input: &str, - opt: OptLevel, - ) -> Result { - let tokens = Lexer::new(interner, input).scan_all().map_err(EvalError::Middle)?; - let (ast, counter) = Parser::new(interner, input, tokens) + pub fn eval(&mut self, input: &str, opt: OptLevel) -> Result { + let tokens = Lexer::new(&mut self.interner, input) + .scan_all() + .map_err(EvalError::Middle)?; + let (ast, counter) = Parser::new(&mut self.interner, input, tokens) .parse_all() .map_err(EvalError::Middle)?; let tcx = TypeInferCtx::new(counter); - let cr = FunctionCompiler::new(input, opt, tcx, interner) + let cr = FunctionCompiler::new(input, opt, tcx, &mut self.interner) .compile_ast(ast, true) .map_err(|err| EvalError::Middle(vec![err]))?; let mut frame = Frame::from_compile_result(cr); @@ -40,20 +37,13 @@ impl Vm { Ok(val.into_value()) } - pub fn eval(&mut self, input: &str, opt: OptLevel) -> Result { - let mut interner = StringInterner::new(); - self.eval_with_interner(&mut interner, input, opt) - .map_err(|err| (err, interner)) - } - - pub fn evaluate_module_with_interner( + pub fn evaluate_module( sc: &mut LocalScope, - interner: &mut StringInterner, input: &str, import_ty: StaticImportKind, opt: OptLevel, ) -> Result { - let re = match FunctionCompiler::compile_str(interner, input, opt) { + let re = match FunctionCompiler::compile_str(&mut sc.interner, input, opt) { Ok(re) => re, Err(err) => throw!(sc, SyntaxError, "Middle error: {:?}", err), }; @@ -75,7 +65,7 @@ impl Vm { if let Some(default) = exports.default { let default = default.root(sc); - export_obj.set_property(sc, "default".into(), PropertyValue::static_default(default))?; + export_obj.set_property(sc, sym::default.into(), PropertyValue::static_default(default))?; } Value::Object(sc.register(export_obj)) @@ -84,18 +74,9 @@ impl Vm { for (k, v) in exports.named { let v = v.root(sc); - export_obj.set_property(sc, String::from(k.as_ref()).into(), PropertyValue::static_default(v))?; + export_obj.set_property(sc, k.into(), PropertyValue::static_default(v))?; } Ok(export_obj.into()) } - - pub fn evaluate_module( - sc: &mut LocalScope, - input: &str, - import_ty: StaticImportKind, - opt: OptLevel, - ) -> Result { - Self::evaluate_module_with_interner(sc, &mut StringInterner::new(), input, import_ty, opt) - } } diff --git a/crates/dash_vm/src/external.rs b/crates/dash_vm/src/external.rs index 45eb2db7..50b458ee 100644 --- a/crates/dash_vm/src/external.rs +++ b/crates/dash_vm/src/external.rs @@ -1,7 +1,7 @@ use rustc_hash::FxHashMap; use crate::gc::handle::Handle; -use crate::gc::trace::Trace; +use crate::gc::trace::{Trace, TraceCtxt}; use super::localscope::LocalScope; use super::value::object::Object; @@ -10,9 +10,9 @@ use super::value::object::Object; pub struct Externals(FxHashMap<*const (), Vec>>); unsafe impl Trace for Externals { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { for ext in self.0.values() { - ext.trace(); + ext.trace(cx); } } } diff --git a/crates/dash_vm/src/frame.rs b/crates/dash_vm/src/frame.rs index de19c44d..043ed4a0 100755 --- a/crates/dash_vm/src/frame.rs +++ b/crates/dash_vm/src/frame.rs @@ -8,7 +8,8 @@ use dash_middle::parser::statement::FunctionKind; use dash_proc_macro::Trace; use crate::gc::handle::Handle; -use crate::gc::trace::Trace; +use crate::gc::trace::{Trace, TraceCtxt}; +use crate::value::string::JsString; use crate::value::{ExternalValue, Unrooted}; use super::value::function::user::UserFunction; @@ -24,14 +25,15 @@ pub struct TryBlock { #[derive(Debug, Clone, Default)] pub struct Exports { pub default: Option, - pub named: Vec<(Rc, Unrooted)>, + pub named: Vec<(JsString, Unrooted)>, } unsafe impl Trace for Exports { - fn trace(&self) { - self.default.trace(); - for (_, v) in &self.named { - v.trace(); + fn trace(&self, cx: &mut TraceCtxt<'_>) { + self.default.trace(cx); + for (k, v) in &self.named { + k.trace(cx); + v.trace(cx); } } } @@ -50,10 +52,13 @@ pub enum FrameState { } unsafe impl Trace for FrameState { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { match self { - Self::Module(exports) => exports.trace(), - Self::Function { .. } => {} + Self::Module(exports) => exports.trace(cx), + Self::Function { + is_constructor_call: _, + is_flat_call: _, + } => {} } } } @@ -81,7 +86,7 @@ impl LoopCounterMap { } unsafe impl Trace for LoopCounterMap { - fn trace(&self) {} + fn trace(&self, _: &mut TraceCtxt<'_>) {} } #[derive(Debug, Clone, Trace)] diff --git a/crates/dash_vm/src/gc/handle.rs b/crates/dash_vm/src/gc/handle.rs index 08d6ab82..d239fc0a 100644 --- a/crates/dash_vm/src/gc/handle.rs +++ b/crates/dash_vm/src/gc/handle.rs @@ -7,7 +7,7 @@ use bitflags::bitflags; use crate::value::object::Object; -use super::trace::Trace; +use super::trace::{Trace, TraceCtxt}; bitflags! { #[derive(Default)] @@ -127,7 +127,7 @@ impl Hash for Handle { } unsafe impl Trace for Handle { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { unsafe { let this = self.0.as_ref(); if this.flags.is_marked() { @@ -137,6 +137,6 @@ unsafe impl Trace for Handle { this.flags.mark(); }; - T::trace(self); + T::trace(self, cx); } } diff --git a/crates/dash_vm/src/gc/interner.rs b/crates/dash_vm/src/gc/interner.rs new file mode 100644 index 00000000..4851d852 --- /dev/null +++ b/crates/dash_vm/src/gc/interner.rs @@ -0,0 +1 @@ +pub use dash_middle::interner::{sym, StringInterner, Symbol}; diff --git a/crates/dash_vm/src/gc/mod.rs b/crates/dash_vm/src/gc/mod.rs index 05223a76..39fa3049 100644 --- a/crates/dash_vm/src/gc/mod.rs +++ b/crates/dash_vm/src/gc/mod.rs @@ -12,6 +12,7 @@ use crate::value::object::Object; use self::handle::{GcNode, Handle}; pub mod handle; +pub mod interner; pub mod persistent; pub mod trace; @@ -181,7 +182,7 @@ mod tests { unsafe { let mut gc = Gc::new(); let h1 = register_gc!(gc, 123.4); - let h2 = register_gc!(gc, Rc::from("hi")); + let h1 = register_gc!(gc, true); gc.sweep(); gc.sweep(); } @@ -204,7 +205,7 @@ mod tests { assert!(!(*h1.as_ptr()).flags.is_marked()); assert!(gc.node_count == 1); - let h2 = register_gc!(gc, Rc::from("hi")); + let h2 = register_gc!(gc, 123.4); assert!(gc.head == NonNull::new(h1.as_ptr())); assert!(gc.tail == NonNull::new(h2.as_ptr())); @@ -244,9 +245,6 @@ mod tests { let h1_c = h1.cast_handle::(); assert_eq!(h1_c.as_deref(), Some(&123.0)); - let h2_c = h2.cast_handle::>(); - assert_eq!(h2_c.as_ref().map(|x| &***x), Some("hi")); - let h3_c = h3.cast_handle::(); assert_eq!(h3_c.as_deref(), Some(&true)); @@ -291,7 +289,7 @@ mod tests { } // lastly, test if Gc::drop works correctly. run under miri to see possible leaks - register_gc!(gc, Rc::from("test")); + register_gc!(gc, false); } } } diff --git a/crates/dash_vm/src/gc/trace.rs b/crates/dash_vm/src/gc/trace.rs index 71bdcbb2..f0eeaf75 100644 --- a/crates/dash_vm/src/gc/trace.rs +++ b/crates/dash_vm/src/gc/trace.rs @@ -3,87 +3,104 @@ use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use std::rc::Rc; -use dash_middle::compiler::constant::Function; +use dash_middle::compiler::constant::{Constant, Function}; +use crate::value::function::native::CallContext; use crate::value::primitive::{Null, Number, Symbol, Undefined}; use crate::value::regex::RegExpInner; use crate::value::typedarray::TypedArrayKind; use crate::value::Unrooted; +use super::interner::StringInterner; + +pub struct TraceCtxt<'vm> { + pub interner: &'vm mut StringInterner, +} + +impl<'vm> TraceCtxt<'vm> { + pub fn new(interner: &'vm mut StringInterner) -> Self { + Self { interner } + } + + pub fn mark_symbol(&self, symbol: dash_middle::interner::Symbol) { + self.interner.mark(symbol); + } +} + /// # Safety /// Implementors of this trait must provide a valid trace implementation /// by calling any possible, reachable [`super::Handle`]s /// /// Consider deriving this trait using the derive macro provided by the `dash_proc_macro` crate pub unsafe trait Trace { - fn trace(&self); + fn trace(&self, cx: &mut TraceCtxt<'_>); } unsafe impl Trace for [T] { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { for item in self { - item.trace(); + item.trace(cx); } } } unsafe impl Trace for Option { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { if let Some(t) = self { - t.trace(); + t.trace(cx); } } } unsafe impl Trace for Unrooted { - fn trace(&self) { - unsafe { self.get().trace() } + fn trace(&self, cx: &mut TraceCtxt<'_>) { + unsafe { self.get().trace(cx) } } } unsafe impl Trace for Vec { - fn trace(&self) { - self.as_slice().trace(); + fn trace(&self, cx: &mut TraceCtxt<'_>) { + self.as_slice().trace(cx); } } unsafe impl Trace for HashSet { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { for t in self.iter() { - t.trace(); + t.trace(cx); } } } unsafe impl Trace for HashMap { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { for (k, v) in self.iter() { - k.trace(); - v.trace(); + k.trace(cx); + v.trace(cx); } } } unsafe impl Trace for Rc { - fn trace(&self) { - T::trace(self) + fn trace(&self, cx: &mut TraceCtxt<'_>) { + T::trace(self, cx) } } unsafe impl Trace for Cell { - fn trace(&self) { - Cell::get(self).trace(); + fn trace(&self, cx: &mut TraceCtxt<'_>) { + Cell::get(self).trace(cx); } } unsafe impl Trace for Box { - fn trace(&self) { - T::trace(self) + fn trace(&self, cx: &mut TraceCtxt<'_>) { + T::trace(self, cx) } } unsafe impl Trace for RefCell { - fn trace(&self) { - T::trace(&RefCell::borrow(self)); + fn trace(&self, cx: &mut TraceCtxt<'_>) { + T::trace(&RefCell::borrow(self), cx); } } @@ -91,15 +108,56 @@ macro_rules! unsafe_empty_trace { ( $($t:ty),* ) => { $( unsafe impl Trace for $t { - fn trace(&self) { + fn trace(&self, _: &mut TraceCtxt<'_>) { } } )* }; } +unsafe impl Trace for dash_middle::interner::Symbol { + fn trace(&self, cx: &mut TraceCtxt<'_>) { + cx.mark_symbol(*self); + } +} + +unsafe impl Trace for dash_middle::compiler::constant::Function { + fn trace(&self, cx: &mut TraceCtxt<'_>) { + let Self { + name, + buffer: _, + ty: _, + locals: _, + params: _, + constants, + externals: _, + r#async: _, + rest_local: _, + poison_ips: _, + source: _, + debug_symbols: _, + } = self; + name.trace(cx); + constants.trace(cx); + } +} + +unsafe impl Trace for Constant { + fn trace(&self, cx: &mut TraceCtxt<'_>) { + match self { + Constant::Number(_) => {} + Constant::String(sym) => sym.trace(cx), + Constant::Identifier(sym) => sym.trace(cx), + Constant::Boolean(_) => {} + Constant::Function(func) => func.trace(cx), + Constant::Regex(_, _, sym) => sym.trace(cx), + Constant::Null => {} + Constant::Undefined => {} + } + } +} + unsafe_empty_trace!( - Function, usize, u8, f64, @@ -107,9 +165,8 @@ unsafe_empty_trace!( str, Undefined, Null, - Symbol, + // Symbol, Number, - RegExpInner, TypedArrayKind, PathBuf, Path, diff --git a/crates/dash_vm/src/js_std/array.rs b/crates/dash_vm/src/js_std/array.rs index 6f4f21df..9d34e538 100644 --- a/crates/dash_vm/src/js_std/array.rs +++ b/crates/dash_vm/src/js_std/array.rs @@ -1,5 +1,6 @@ use std::ops::Range; +use crate::gc::interner::sym; use crate::localscope::LocalScope; use crate::throw; use crate::value::array::{Array, ArrayIterator}; @@ -8,6 +9,7 @@ use crate::value::object::PropertyValue; use crate::value::ops::conversions::ValueConversion; use crate::value::ops::equality::ValueEquality; use crate::value::root_ext::RootErrExt; +use crate::value::string::JsString; use crate::value::{array, Root, Value, ValueContext}; pub fn constructor(cx: CallContext) -> Result { @@ -17,32 +19,32 @@ pub fn constructor(cx: CallContext) -> Result { Ok(cx.scope.gc_mut().register(array).into()) } -fn join_inner(sc: &mut LocalScope, array: Value, separator: &str) -> Result { +fn join_inner(sc: &mut LocalScope, array: Value, separator: JsString) -> Result { let length = array.length_of_array_like(sc)?; let mut result = String::new(); for i in 0..length { if i > 0 { - result.push_str(separator); + result.push_str(separator.res(sc)); } - let i = i.to_string(); - let element = array.get_property(sc, i.as_str().into()).root(sc)?; - let s = element.to_string(sc)?; - result.push_str(&s); + let i = sc.intern_usize(i); + let element = array.get_property(sc, i.into()).root(sc)?; + let s = element.to_js_string(sc)?; + result.push_str(s.res(sc)); } - Ok(Value::String(result.into())) + Ok(Value::String(sc.intern(result).into())) } pub fn to_string(cx: CallContext) -> Result { - join_inner(cx.scope, cx.this, ",") + join_inner(cx.scope, cx.this, sym::comma.into()) } pub fn join(cx: CallContext) -> Result { - let sep = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - join_inner(cx.scope, cx.this, &sep) + let sep = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; + join_inner(cx.scope, cx.this, sep) } pub fn values(cx: CallContext) -> Result { @@ -63,8 +65,8 @@ pub fn at(cx: CallContext) -> Result { return Ok(Value::undefined()); } - let index = index.to_string(); - this.get_property(cx.scope, index.as_str().into()).root(cx.scope) + let index = cx.scope.intern_usize(index as usize); + this.get_property(cx.scope, index.into()).root(cx.scope) } pub fn concat(cx: CallContext) -> Result { @@ -75,8 +77,8 @@ pub fn concat(cx: CallContext) -> Result { for arg in &cx.args { let len = arg.length_of_array_like(cx.scope)?; for i in 0..len { - let i = i.to_string(); - let element = arg.get_property(cx.scope, i.as_str().into()).root(cx.scope)?; + let i = cx.scope.intern_usize(i); + let element = arg.get_property(cx.scope, i.into()).root(cx.scope)?; array.push(PropertyValue::static_default(element)); } } @@ -100,13 +102,13 @@ pub fn every(cx: CallContext) -> Result { let callback = cx.args.first().unwrap_or_undefined(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; let args = vec![pkv, Value::number(k as f64)]; let test = callback .apply(cx.scope, Value::undefined(), args) .root(cx.scope)? - .to_boolean()?; + .to_boolean(cx.scope)?; if !test { return Ok(false.into()); @@ -122,13 +124,13 @@ pub fn some(cx: CallContext) -> Result { let callback = cx.args.first().unwrap_or_undefined(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; let args = vec![pkv, Value::number(k as f64)]; let test = callback .apply(cx.scope, Value::undefined(), args) .root(cx.scope)? - .to_boolean()?; + .to_boolean(cx.scope)?; if test { return Ok(true.into()); @@ -144,7 +146,7 @@ pub fn fill(cx: CallContext) -> Result { let value = cx.args.first().unwrap_or_undefined(); for i in 0..len { - let pk = i.to_string(); + let pk = cx.scope.intern_usize(i); this.set_property(cx.scope, pk.into(), PropertyValue::static_default(value.clone()))?; } @@ -158,16 +160,15 @@ pub fn filter(cx: CallContext) -> Result { let mut values = Vec::new(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; let args = vec![pkv.clone(), Value::number(k as f64)]; let test = callback .apply(cx.scope, Value::undefined(), args) .root(cx.scope)? - .to_boolean()?; + .to_boolean(cx.scope)?; if test { - cx.scope.add_value(pkv.clone()); values.push(PropertyValue::static_default(pkv)); } } @@ -188,21 +189,20 @@ pub fn reduce(cx: CallContext) -> Result { (0, Some(_)) => return Ok(initial_value.unwrap().clone()), (_, Some(initial)) => (0, initial.clone()), (1, None) => { - let pk = 0.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pkv = this.get_property(cx.scope, sym::zero.into()).root(cx.scope)?; return Ok(pkv); } (_, None) => { - let pkv = this.get_property(cx.scope, "0".into()).root(cx.scope)?; - let pkv2 = this.get_property(cx.scope, "1".into()).root(cx.scope)?; + let pkv = this.get_property(cx.scope, sym::zero.into()).root(cx.scope)?; + let pkv2 = this.get_property(cx.scope, sym::one.into()).root(cx.scope)?; let args = vec![pkv, pkv2, Value::number(1_f64)]; (2, callback.apply(cx.scope, Value::undefined(), args).root(cx.scope)?) } }; for k in start..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; let args = vec![accumulator, pkv, Value::number(k as f64)]; accumulator = callback.apply(cx.scope, Value::undefined(), args).root(cx.scope)?; } @@ -216,13 +216,13 @@ pub fn find(cx: CallContext) -> Result { let callback = cx.args.first().unwrap_or_undefined(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; let args = vec![pkv.clone(), Value::number(k as f64)]; let test = callback .apply(cx.scope, Value::undefined(), args) .root(cx.scope)? - .to_boolean()?; + .to_boolean(cx.scope)?; if test { return Ok(pkv); @@ -238,13 +238,13 @@ pub fn find_index(cx: CallContext) -> Result { let callback = cx.args.first().unwrap_or_undefined(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; let args = vec![pkv, Value::number(k as f64)]; let test = callback .apply(cx.scope, Value::undefined(), args) .root(cx.scope)? - .to_boolean()?; + .to_boolean(cx.scope)?; if test { return Ok(Value::number(k as f64)); @@ -264,8 +264,8 @@ pub fn for_each(cx: CallContext) -> Result { let callback = cx.args.first().unwrap_or_undefined(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; let args = vec![pkv, Value::number(k as f64)]; callback.apply(cx.scope, Value::undefined(), args).root_err(cx.scope)?; } @@ -279,9 +279,9 @@ pub fn includes(cx: CallContext) -> Result { let search_element = cx.args.first().unwrap_or_undefined(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; - if pkv.strict_eq(&search_element, cx.scope)?.is_truthy() { + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; + if pkv.strict_eq(&search_element, cx.scope)?.is_truthy(cx.scope) { return Ok(true.into()); } } @@ -295,9 +295,9 @@ pub fn index_of(cx: CallContext) -> Result { let search_element = cx.args.first().unwrap_or_undefined(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; - if pkv.strict_eq(&search_element, cx.scope)?.is_truthy() { + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; + if pkv.strict_eq(&search_element, cx.scope)?.is_truthy(cx.scope) { return Ok(Value::number(k as f64)); } } @@ -317,9 +317,9 @@ pub fn last_index_of(cx: CallContext) -> Result { .unwrap_or(len); for k in (0..from_index).rev() { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; - if pkv.strict_eq(&search_element, cx.scope)?.is_truthy() { + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; + if pkv.strict_eq(&search_element, cx.scope)?.is_truthy(cx.scope) { return Ok(Value::number(k as f64)); } } @@ -334,8 +334,8 @@ pub fn map(cx: CallContext) -> Result { let mut values = Vec::new(); for k in 0..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; let args = vec![pkv.clone(), Value::number(k as f64)]; let value = callback.apply(cx.scope, Value::undefined(), args).root(cx.scope)?; @@ -356,12 +356,11 @@ pub fn pop(cx: CallContext) -> Result { } let new_len = len - 1; - let value = this - .delete_property(cx.scope, new_len.to_string().into())? - .root(cx.scope); + let new_len_sym = cx.scope.intern_usize(len - 1); + let value = this.delete_property(cx.scope, new_len_sym.into())?.root(cx.scope); this.set_property( cx.scope, - "length".into(), + sym::length.into(), PropertyValue::static_default(Value::number(new_len as f64)), )?; @@ -375,11 +374,8 @@ pub fn push(cx: CallContext) -> Result { let mut last = Value::undefined(); if cx.args.is_empty() { - this.set_property( - cx.scope, - len.to_string().into(), - PropertyValue::static_default(Value::undefined()), - )?; + let len = cx.scope.intern_usize(len); + this.set_property(cx.scope, len.into(), PropertyValue::static_default(Value::undefined()))?; } for (idx, arg) in cx.args.into_iter().enumerate() { @@ -396,10 +392,10 @@ pub fn reverse(cx: CallContext) -> Result { // Strategy: Given [1,2,3,4,5], swap `i` with `len - i - 1` for every index `i` in `0..len / 2` for k in 0..len / 2 { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; - let pk2 = (len - k - 1).to_string(); - let pk2v = this.get_property(cx.scope, pk2.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; + let pk2 = cx.scope.intern_usize(len - k - 1); + let pk2v = this.get_property(cx.scope, pk2.into()).root(cx.scope)?; this.set_property(cx.scope, pk.into(), PropertyValue::static_default(pk2v))?; this.set_property(cx.scope, pk2.into(), PropertyValue::static_default(pkv))?; } @@ -415,17 +411,18 @@ pub fn shift(cx: CallContext) -> Result { return Ok(Value::undefined()); } - let prop = this.delete_property(cx.scope, "0".into())?.root(cx.scope); + let prop = this.delete_property(cx.scope, sym::zero.into())?.root(cx.scope); for k in 1..len { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; - this.set_property(cx.scope, (k - 1).to_string().into(), PropertyValue::static_default(pkv))?; + let pk = cx.scope.intern_usize(k); + let prev_pk = cx.scope.intern_usize(k - 1); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; + this.set_property(cx.scope, prev_pk.into(), PropertyValue::static_default(pkv))?; } this.set_property( cx.scope, - "length".into(), + sym::length.into(), PropertyValue::static_default(Value::number((len - 1) as f64)), )?; @@ -450,20 +447,17 @@ fn shift_array( if range.end + shift_by > len as isize { arr.set_property( scope, - "length".into(), + sym::length.into(), PropertyValue::static_default(Value::number(new_len as f64)), )?; } // Start shifting the elements by the shift_by (can be either negative or positive) amount for k in range { - let pk = k.to_string(); - let pkv = arr.get_property(scope, pk.as_str().into()).root(scope)?; - arr.set_property( - scope, - (k + shift_by).to_string().into(), - PropertyValue::static_default(pkv), - )?; + let pk = scope.intern_isize(k); + let shift_pk = scope.intern_isize(k + shift_by); + let pkv = arr.get_property(scope, pk.into()).root(scope)?; + arr.set_property(scope, shift_pk.into(), PropertyValue::static_default(pkv))?; } // If the shift_by is negative, we need to delete the remaining elements at the end that were shifted @@ -471,7 +465,7 @@ fn shift_array( if shift_by < 0 { arr.set_property( scope, - "length".into(), + sym::length.into(), PropertyValue::static_default(Value::number(new_len as f64)), )?; } @@ -488,7 +482,8 @@ pub fn unshift(cx: CallContext) -> Result { shift_array(cx.scope, &this, len, arg_len as isize, 0..len)?; for (idx, arg) in cx.args.into_iter().enumerate() { - this.set_property(cx.scope, idx.to_string().into(), PropertyValue::static_default(arg))?; + let idx = cx.scope.intern_usize(idx); + this.set_property(cx.scope, idx.into(), PropertyValue::static_default(arg))?; } Ok(Value::number(new_len as f64)) @@ -523,8 +518,8 @@ pub fn slice(cx: CallContext) -> Result { let mut values = Vec::new(); for k in start..end { - let pk = k.to_string(); - let pkv = this.get_property(cx.scope, pk.as_str().into()).root(cx.scope)?; + let pk = cx.scope.intern_usize(k); + let pkv = this.get_property(cx.scope, pk.into()).root(cx.scope)?; values.push(PropertyValue::static_default(pkv)); } @@ -543,14 +538,14 @@ pub fn from(cx: CallContext) -> Result { fn with_iterator(scope: &mut LocalScope, items: Value, mapper: Option) -> Result { let mut values = Vec::new(); - let next = items.get_property(scope, "next".into()).root(scope)?; + let next = items.get_property(scope, sym::next.into()).root(scope)?; loop { let item = next.apply(scope, items.clone(), Vec::new()).root(scope)?; - let done = item.get_property(scope, "done".into()).root(scope)?.is_truthy(); + let done = item.get_property(scope, sym::done.into()).root(scope)?.is_truthy(scope); if done { break; } - let value = item.get_property(scope, "value".into()).root(scope)?; + let value = item.get_property(scope, sym::value.into()).root(scope)?; let value = match &mapper { Some(mapper) => mapper.apply(scope, Value::undefined(), vec![value]).root(scope)?, None => value, @@ -568,7 +563,8 @@ pub fn from(cx: CallContext) -> Result { let mut values = Vec::new(); for i in 0..len { - let value = items.get_property(scope, i.to_string().into()).root(scope)?; + let i = scope.intern_usize(i); + let value = items.get_property(scope, i.into()).root(scope)?; let value = match &mapper { Some(mapper) => mapper.apply(scope, Value::undefined(), vec![value]).root(scope)?, None => value, @@ -621,20 +617,19 @@ pub fn sort(cx: CallContext) -> Result { for i in 1..len { for j in (1..=i).rev() { - let previous = this.get_property(cx.scope, (j - 1).to_string().into()).root(cx.scope)?; - let current = this.get_property(cx.scope, j.to_string().into()).root(cx.scope)?; + let idx = cx.scope.intern_usize(j); + let prev_idx = cx.scope.intern_usize(j - 1); + + let previous = this.get_property(cx.scope, prev_idx.into()).root(cx.scope)?; + let current = this.get_property(cx.scope, idx.into()).root(cx.scope)?; let ordering = compare_fn .apply(cx.scope, Value::undefined(), vec![previous.clone(), current.clone()]) .root(cx.scope)? .to_int32(cx.scope)?; if ordering > 0 { - this.set_property( - cx.scope, - (j - 1).to_string().into(), - PropertyValue::static_default(current), - )?; - this.set_property(cx.scope, j.to_string().into(), PropertyValue::static_default(previous))?; + this.set_property(cx.scope, prev_idx.into(), PropertyValue::static_default(current))?; + this.set_property(cx.scope, idx.into(), PropertyValue::static_default(previous))?; } else { break; } diff --git a/crates/dash_vm/src/js_std/array_iterator.rs b/crates/dash_vm/src/js_std/array_iterator.rs index cc151858..f7f982e6 100644 --- a/crates/dash_vm/src/js_std/array_iterator.rs +++ b/crates/dash_vm/src/js_std/array_iterator.rs @@ -1,3 +1,4 @@ +use crate::gc::interner::sym; use crate::throw; use crate::value::array::ArrayIterator; use crate::value::function::native::CallContext; @@ -16,12 +17,12 @@ pub fn next(cx: CallContext) -> Result { let obj = NamedObject::new(cx.scope); obj.set_property( cx.scope, - "value".into(), + sym::value.into(), PropertyValue::static_default(next.unwrap_or_undefined()), )?; obj.set_property( cx.scope, - "done".into(), + sym::done.into(), PropertyValue::static_default(Value::Boolean(done)), )?; diff --git a/crates/dash_vm/src/js_std/boolean.rs b/crates/dash_vm/src/js_std/boolean.rs index 21bea3c9..28770b0e 100644 --- a/crates/dash_vm/src/js_std/boolean.rs +++ b/crates/dash_vm/src/js_std/boolean.rs @@ -1,18 +1,17 @@ +use crate::gc::interner::sym; use crate::throw; use crate::value::function::native::CallContext; use crate::value::ops::conversions::ValueConversion; use crate::value::{Value, ValueContext}; pub fn constructor(cx: CallContext) -> Result { - let value = cx.args.get(0).unwrap_or_undefined().to_boolean()?; + let value = cx.args.get(0).unwrap_or_undefined().to_boolean(cx.scope)?; Ok(Value::Boolean(value)) } pub fn to_string(cx: CallContext) -> Result { if let Value::Boolean(b) = cx.this { - let s = b - .then(|| cx.scope.statics().get_true()) - .unwrap_or_else(|| cx.scope.statics().get_false()); + let s = b.then(|| sym::true_.into()).unwrap_or_else(|| sym::false_.into()); Ok(Value::String(s)) } else { diff --git a/crates/dash_vm/src/js_std/error.rs b/crates/dash_vm/src/js_std/error.rs index 2c507ef3..27d90e73 100644 --- a/crates/dash_vm/src/js_std/error.rs +++ b/crates/dash_vm/src/js_std/error.rs @@ -1,3 +1,4 @@ +use crate::gc::interner::sym; use crate::value::error::{ AggregateError, Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, }; @@ -8,13 +9,13 @@ use crate::value::{Root, Value, ValueContext}; macro_rules! define_other_error_constructors { ( $( $fun:ident $t:ident ),* ) => { $( - pub fn $fun(mut cx: CallContext) -> Result { - let message = cx.args.first().unwrap_or_undefined().to_string(&mut cx.scope)?; - let error = $t::new(&cx.scope, message); + pub fn $fun(mut cx: CallContext) -> Result { + let message = cx.args.first().unwrap_or_undefined().to_js_string(&mut cx.scope)?; + let error = $t::new_with_js_string(cx.scope, message); - Ok(cx.scope.register(error).into()) - } - )* + Ok(cx.scope.register(error).into()) + } + )* }; } define_other_error_constructors!( @@ -28,16 +29,16 @@ define_other_error_constructors!( ); pub fn error_constructor(cx: CallContext) -> Result { - let message = cx.args.first().cloned().map(|v| v.to_string(cx.scope)).transpose()?; + let message = cx.args.first().cloned().map(|v| v.to_js_string(cx.scope)).transpose()?; - let err = Error::new(cx.scope, message.as_deref().unwrap_or_default()); + let err = Error::new_with_js_string(cx.scope, message.unwrap_or(sym::empty.into())); Ok(cx.scope.register(err).into()) } pub fn to_string(cx: CallContext) -> Result { cx.this - .get_property(cx.scope, "stack".into()) + .get_property(cx.scope, sym::stack.into()) .root(cx.scope) - .and_then(|v| v.to_string(cx.scope).map(Value::String)) + .and_then(|v| v.to_js_string(cx.scope).map(Value::String)) } diff --git a/crates/dash_vm/src/js_std/function.rs b/crates/dash_vm/src/js_std/function.rs index 211b67b4..bfb0cca3 100644 --- a/crates/dash_vm/src/js_std/function.rs +++ b/crates/dash_vm/src/js_std/function.rs @@ -41,11 +41,9 @@ pub fn to_string(cx: CallContext) -> Result { let Some(this) = cx.this.downcast_ref::() else { throw!(cx.scope, TypeError, "Incompatible receiver"); }; - Ok(Value::String( - format!( - "function {}() {{ [native code] }}", - this.name().as_deref().unwrap_or(&cx.scope.statics.empty_str) - ) - .into(), - )) + let name = format!( + "function {}() {{ [native code] }}", + this.name().map(|s| s.res(cx.scope)).unwrap_or_default() + ); + Ok(Value::String(cx.scope.intern(name.as_ref()).into())) } diff --git a/crates/dash_vm/src/js_std/generator.rs b/crates/dash_vm/src/js_std/generator.rs index 1f83ff5f..8eba158b 100644 --- a/crates/dash_vm/src/js_std/generator.rs +++ b/crates/dash_vm/src/js_std/generator.rs @@ -2,12 +2,14 @@ use std::mem; use crate::dispatch::HandleResult; use crate::frame::Frame; +use crate::gc::interner::sym; use crate::localscope::LocalScope; use crate::throw; use crate::value::function::generator::{as_generator, GeneratorState}; use crate::value::function::native::CallContext; use crate::value::function::{Function, FunctionKind}; use crate::value::object::{NamedObject, Object, PropertyValue}; +use crate::value::root_ext::RootErrExt; use crate::value::{Root, Value, ValueContext}; pub fn next(cx: CallContext) -> Result { @@ -32,7 +34,7 @@ pub fn next(cx: CallContext) -> Result { }; let current_sp = cx.scope.stack_size(); - cx.scope.try_extend_stack(old_stack)?; + cx.scope.try_extend_stack(old_stack).root_err(cx.scope)?; let mut frame = Frame::from_function(None, function, false, false); frame.set_ip(ip); @@ -83,10 +85,10 @@ pub fn next(cx: CallContext) -> Result { fn create_generator_value(scope: &mut LocalScope, done: bool, value: Option) -> Result { let obj = NamedObject::new(scope); - obj.set_property(scope, "done".into(), PropertyValue::static_default(done.into()))?; + obj.set_property(scope, sym::done.into(), PropertyValue::static_default(done.into()))?; obj.set_property( scope, - "value".into(), + sym::value.into(), PropertyValue::static_default(value.unwrap_or_undefined()), )?; Ok(scope.register(obj).into()) diff --git a/crates/dash_vm/src/js_std/global.rs b/crates/dash_vm/src/js_std/global.rs index a9d562a0..554a6d89 100755 --- a/crates/dash_vm/src/js_std/global.rs +++ b/crates/dash_vm/src/js_std/global.rs @@ -13,8 +13,8 @@ pub fn is_nan(cx: CallContext) -> Result { pub fn log(cx: CallContext) -> Result { for arg in cx.args { - let tstr = arg.to_string(cx.scope)?; - println!("{tstr} "); + let tstr = arg.to_js_string(cx.scope)?; + println!("{} ", tstr.res(cx.scope)); } Ok(Value::undefined()) @@ -30,9 +30,9 @@ pub fn is_finite(cx: CallContext) -> Result { pub fn parse_float(cx: CallContext) -> Result { // 1. Let inputString be ? ToString(string). - let input_string = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let input_string = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; // 2. Let trimmedString be ! TrimString(inputString, start). - let trimmed_string = input_string.trim(); + let trimmed_string = input_string.res(cx.scope).trim(); // TODO: follow spec let num = Value::number(trimmed_string.parse().unwrap_or(f64::NAN)); @@ -41,8 +41,7 @@ pub fn parse_float(cx: CallContext) -> Result { } pub fn parse_int(cx: CallContext) -> Result { - let input_string = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - let trimmed_string = input_string.trim(); + let input_string = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; let radix = cx .args .get(1) @@ -52,6 +51,8 @@ pub fn parse_int(cx: CallContext) -> Result { .map(|r| r as u32) .unwrap_or(10); + let trimmed_string = input_string.res(cx.scope).trim(); + // TODO: follow spec let num = Value::number( i32::from_str_radix(trimmed_string, radix) diff --git a/crates/dash_vm/src/js_std/json.rs b/crates/dash_vm/src/js_std/json.rs index 7c72a172..d2189605 100644 --- a/crates/dash_vm/src/js_std/json.rs +++ b/crates/dash_vm/src/js_std/json.rs @@ -8,8 +8,9 @@ pub fn constructor(cx: CallContext) -> Result { } pub fn parse(cx: CallContext) -> Result { - let value = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - let parse = match json::parser::Parser::new(value.as_bytes(), cx.scope).parse() { + let value = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; + let bytes = value.res(cx.scope).as_bytes().to_owned(); + let parse = match json::parser::Parser::new(&bytes, cx.scope).parse() { Ok(v) => v, Err(e) => { throw!(cx.scope, SyntaxError, "{}", e.to_string()) diff --git a/crates/dash_vm/src/js_std/map.rs b/crates/dash_vm/src/js_std/map.rs index e351dcf2..7a42e503 100644 --- a/crates/dash_vm/src/js_std/map.rs +++ b/crates/dash_vm/src/js_std/map.rs @@ -1,3 +1,4 @@ +use crate::gc::interner::sym; use crate::throw; use crate::value::function::native::CallContext; use crate::value::map::Map; @@ -11,15 +12,15 @@ pub fn constructor(cx: CallContext) -> Result { let len = iter.length_of_array_like(cx.scope)?; for i in 0..len { - let i = i.to_string(); + let i = cx.scope.intern_usize(i); let item = iter .get_property(cx.scope, PropertyKey::String(i.into())) .root(cx.scope)?; let k = item - .get_property(cx.scope, PropertyKey::String("0".into())) + .get_property(cx.scope, PropertyKey::String(sym::zero.into())) .root(cx.scope)?; let v = item - .get_property(cx.scope, PropertyKey::String("1".into())) + .get_property(cx.scope, PropertyKey::String(sym::one.into())) .root(cx.scope)?; map.set(k, v); } diff --git a/crates/dash_vm/src/js_std/number.rs b/crates/dash_vm/src/js_std/number.rs index 539ff97b..e9d02c54 100644 --- a/crates/dash_vm/src/js_std/number.rs +++ b/crates/dash_vm/src/js_std/number.rs @@ -31,7 +31,7 @@ pub fn to_string(cx: CallContext) -> Result { _ => throw!(cx.scope, RangeError, "Invalid radix: {}", radix), }; - Ok(Value::String(re.into())) + Ok(Value::String(cx.scope.intern(re.as_ref()).into())) } pub fn is_finite(cx: CallContext) -> Result { @@ -74,5 +74,5 @@ pub fn to_fixed(cx: CallContext) -> Result { let re = format!("{num:.decimals$}"); - Ok(Value::String(re.into())) + Ok(Value::String(cx.scope.intern(re.as_ref()).into())) } diff --git a/crates/dash_vm/src/js_std/object.rs b/crates/dash_vm/src/js_std/object.rs index 7b917a71..f6d69d95 100644 --- a/crates/dash_vm/src/js_std/object.rs +++ b/crates/dash_vm/src/js_std/object.rs @@ -1,4 +1,5 @@ use crate::gc::handle::Handle; +use crate::gc::interner::sym; use crate::localscope::LocalScope; use crate::throw; use crate::value::array::Array; @@ -28,7 +29,7 @@ pub fn create(cx: CallContext) -> Result { pub fn keys(cx: CallContext) -> Result { let obj = cx.args.first().unwrap_or_undefined().to_object(cx.scope)?; - let keys = obj.own_keys()?; + let keys = obj.own_keys(cx.scope)?; let array = Array::from_vec(cx.scope, keys.into_iter().map(PropertyValue::static_default).collect()); Ok(cx.scope.gc_mut().register(array).into()) } @@ -36,18 +37,20 @@ pub fn keys(cx: CallContext) -> Result { pub fn to_string(cx: CallContext) -> Result { fn to_string_inner(scope: &mut LocalScope<'_>, o: &Handle) -> Result { let constructor = o - .get_property(scope, "constructor".into()) + .get_property(scope, sym::constructor.into()) .root(scope)? - .get_property(scope, "name".into()) + .get_property(scope, sym::name.into()) .root(scope)? - .to_string(scope)?; + .to_js_string(scope)?; - Ok(Value::String(format!("[object {constructor}]").into())) + let constructor = format!("[object {}]", constructor.res(scope)); + + Ok(Value::String(scope.intern(constructor).into())) } let value = match &cx.this { - Value::Undefined(_) => Value::String("[object Undefined]".into()), - Value::Null(_) => Value::String("[object Null]".into()), + Value::Undefined(_) => Value::String(cx.scope.intern("[object Undefined]").into()), + Value::Null(_) => Value::String(cx.scope.intern("[object Null]").into()), Value::Object(o) => to_string_inner(cx.scope, o)?, Value::External(o) => to_string_inner(cx.scope, &o.inner)?, _ => unreachable!(), // `this` is always object/null/undefined. TODO: wrong, `Object.prototype.toString..call('a')` crashes @@ -90,7 +93,7 @@ pub fn get_own_property_descriptors(cx: CallContext) -> Result { }; let mut descriptors = Vec::new(); - let keys = o.own_keys()?; + let keys = o.own_keys(cx.scope)?; for key in keys { let key = PropertyKey::from_value(cx.scope, key)?; @@ -138,7 +141,7 @@ pub fn define_property(cx: CallContext) -> Result { let property = match cx.args.get(1) { Some(Value::Symbol(sym)) => PropertyKey::from(sym.clone()), - Some(other) => PropertyKey::from(ToString::to_string(&other.to_string(cx.scope)?)), + Some(other) => PropertyKey::from(other.to_js_string(cx.scope)?), _ => throw!(cx.scope, TypeError, "Property must be a string or symbol"), }; let descriptor = match cx.args.get(2) { @@ -159,7 +162,7 @@ pub fn assign(cx: CallContext) -> Result { let to = args.next().unwrap_or_undefined().to_object(cx.scope)?; for source in args { let source = source.to_object(cx.scope)?; - for key in source.own_keys()? { + for key in source.own_keys(cx.scope)? { let key = PropertyKey::from_value(cx.scope, key)?; let desc = source.get_own_property(cx.scope, key.clone()).root(cx.scope)?; to.set_property(cx.scope, key, PropertyValue::static_default(desc))?; @@ -171,7 +174,7 @@ pub fn assign(cx: CallContext) -> Result { pub fn entries(cx: CallContext) -> Result { let mut entries = Vec::new(); let obj = cx.args.first().unwrap_or_undefined().to_object(cx.scope)?; - for key in obj.own_keys()? { + for key in obj.own_keys(cx.scope)? { let key = PropertyKey::from_value(cx.scope, key)?; let value = obj.get_own_property(cx.scope, key.clone()).root(cx.scope)?; let entry = Array::from_vec( diff --git a/crates/dash_vm/src/js_std/promise.rs b/crates/dash_vm/src/js_std/promise.rs index 4a2f7d5f..b6fe45b0 100644 --- a/crates/dash_vm/src/js_std/promise.rs +++ b/crates/dash_vm/src/js_std/promise.rs @@ -1,6 +1,7 @@ use dash_proc_macro::Trace; use crate::gc::handle::Handle; +use crate::gc::interner::sym; use crate::value::function::bound::BoundFunction; use crate::value::function::native::CallContext; use crate::value::object::{NamedObject, Object, PropertyKey}; @@ -143,7 +144,9 @@ impl Object for ThenTask { .apply(scope, Value::undefined(), vec![resolved]) .root(scope)?; - let ret_then = ret.get_property(scope, PropertyKey::String("then".into()))?.root(scope); + let ret_then = ret + .get_property(scope, PropertyKey::String(sym::then.into()))? + .root(scope); match ret_then { Value::Undefined(..) => { diff --git a/crates/dash_vm/src/js_std/regex.rs b/crates/dash_vm/src/js_std/regex.rs index 97a51338..156ce739 100644 --- a/crates/dash_vm/src/js_std/regex.rs +++ b/crates/dash_vm/src/js_std/regex.rs @@ -1,3 +1,4 @@ +use crate::gc::interner::sym; use crate::throw; use crate::value::array::Array; use crate::value::function::native::CallContext; @@ -10,20 +11,20 @@ use dash_regex::parser::Parser as RegexParser; use dash_regex::Flags; pub fn constructor(cx: CallContext) -> Result { - let pattern = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let pattern = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; let flags = match cx .args .get(1) - .map(|v| v.to_string(cx.scope)) + .map(|v| v.to_js_string(cx.scope)) .transpose()? - .map(|s| s.parse::()) + .map(|s| s.res(cx.scope).parse::()) { Some(Ok(flags)) => flags, Some(Err(err)) => throw!(cx.scope, SyntaxError, "Invalid RegExp flags: {:?}", err), None => Flags::empty(), }; - let nodes = match RegexParser::new(pattern.as_bytes()).parse_all() { + let nodes = match RegexParser::new(pattern.res(cx.scope).as_bytes()).parse_all() { Ok(nodes) => nodes, Err(err) => throw!(cx.scope, SyntaxError, "Regex parser error: {}", err), }; @@ -34,7 +35,7 @@ pub fn constructor(cx: CallContext) -> Result { } pub fn test(cx: CallContext) -> Result { - let text = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let text = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; let regex = match cx.this.downcast_ref::() { Some(regex) => regex, @@ -51,6 +52,7 @@ pub fn test(cx: CallContext) -> Result { None => throw!(cx.scope, TypeError, "Receiver must be an initialized RegExp object"), }; + let text = text.res(cx.scope); let is_global = flags.contains(Flags::GLOBAL); if is_global && last_index.get() >= text.len() { @@ -73,7 +75,7 @@ pub fn test(cx: CallContext) -> Result { } pub fn exec(cx: CallContext<'_, '_>) -> Result { - let text = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let text = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; let regex = match cx.this.downcast_ref::() { Some(regex) => regex, @@ -90,6 +92,7 @@ pub fn exec(cx: CallContext<'_, '_>) -> Result { None => throw!(cx.scope, TypeError, "Receiver must be an initialized RegExp object"), }; + let text = text.res(cx.scope).to_owned(); let is_global = flags.contains(Flags::GLOBAL); if is_global && last_index.get() >= text.len() { @@ -103,20 +106,19 @@ pub fn exec(cx: CallContext<'_, '_>) -> Result { last_index.set(last_index.get() + matcher.groups.get(0).unwrap().end); } - let groups = Array::from_vec( - cx.scope, - matcher - .groups - .iter() - .map(|g| { - let sub = match g { - Some(r) => text[r].into(), - None => cx.scope.statics.null_str(), - }; - PropertyValue::static_default(Value::String(sub)) - }) - .collect(), - ); + let groups = matcher + .groups + .iter() + .map(|g| { + let sub = match g { + Some(r) => cx.scope.intern(&text[r]).into(), + None => sym::null.into(), + }; + PropertyValue::static_default(Value::String(sub)) + }) + .collect(); + + let groups = Array::from_vec(cx.scope, groups); Ok(Value::Object(cx.scope.register(groups))) } else { if is_global { diff --git a/crates/dash_vm/src/js_std/set.rs b/crates/dash_vm/src/js_std/set.rs index a094f40a..4ab5dfd2 100644 --- a/crates/dash_vm/src/js_std/set.rs +++ b/crates/dash_vm/src/js_std/set.rs @@ -11,7 +11,7 @@ pub fn constructor(cx: CallContext) -> Result { let len = iter.length_of_array_like(cx.scope)?; for i in 0..len { - let i = i.to_string(); + let i = cx.scope.intern_usize(i); let item = iter .get_property(cx.scope, PropertyKey::String(i.into())) .root(cx.scope)?; diff --git a/crates/dash_vm/src/js_std/string.rs b/crates/dash_vm/src/js_std/string.rs index 3eb694bd..c6ba0a08 100644 --- a/crates/dash_vm/src/js_std/string.rs +++ b/crates/dash_vm/src/js_std/string.rs @@ -6,12 +6,10 @@ use crate::value::function::native::CallContext; use crate::value::object::PropertyValue; use crate::value::ops::conversions::ValueConversion; use crate::value::{Value, ValueContext}; -use std::borrow::Cow; use std::fmt::Write; -use std::rc::Rc; pub fn constructor(cx: CallContext) -> Result { - let value = cx.args.get(0).unwrap_or_undefined().to_string(cx.scope)?; + let value = cx.args.get(0).unwrap_or_undefined().to_js_string(cx.scope)?; if cx.is_constructor_call { let boxed = BoxedString::new(cx.scope, value); Ok(Value::Object(cx.scope.register(boxed))) @@ -31,7 +29,7 @@ fn create_html( attribute: Option<(&str, Value)>, ) -> Result { // 2. Let S be ? ToString(str). - let s = string.to_string(sc)?; + let s = string.to_js_string(sc)?; // 3. Let p1 be the string-concatenation of "<" and tag. let mut p1 = format!("<{tag}"); @@ -39,10 +37,10 @@ fn create_html( // 4. If attribute is not the empty String, then if let Some((key, value)) = attribute { // Let V be ? ToString(value). - let v = value.to_string(sc)?; + let v = value.to_js_string(sc)?; // b. Let escapedV be the String value that is ... - let escaped_v = v.replace('"', """); + let escaped_v = v.res(sc).replace('"', """); // c. Set p1 to the string-concatenation of: ... let _ = write!(p1, " {key}=\"{escaped_v}\""); @@ -52,9 +50,9 @@ fn create_html( // 6. Let p3 be the string-concatenation of p2 and S. // Let p4 be the string-concatenation of p3, "". // 8. Return p4. - let _ = write!(p1, ">{s}"); + let _ = write!(p1, ">{}", s.res(sc)); - Ok(Value::String(p1.into())) + Ok(Value::String(sc.intern(p1).into())) } macro_rules! define_html_methods_no_attribute { @@ -97,17 +95,17 @@ define_html_methods_with_attribute! { pub fn char_at(cx: CallContext) -> Result { let index = cx.args.first().unwrap_or_undefined().to_number(cx.scope)? as usize; - let this = cx.this.to_string(cx.scope)?; + let this = cx.this.to_js_string(cx.scope)?.res(cx.scope); // TODO: this isn't right, but it is what it is match this.as_bytes().get(index) { - Some(&c) => Ok(Value::String((c as char).to_string().into())), + Some(&c) => Ok(Value::String(cx.scope.intern_char(c as char).into())), None => Ok(Value::undefined()), } } pub fn char_code_at(cx: CallContext) -> Result { let index = cx.args.first().unwrap_or_undefined().to_number(cx.scope)? as usize; - let this = cx.this.to_string(cx.scope)?; + let this = cx.this.to_js_string(cx.scope)?.res(cx.scope); // TODO: this isn't right, but it is what it is match this.as_bytes().get(index) { Some(&c) => Ok(Value::number(c as f64)), @@ -116,41 +114,49 @@ pub fn char_code_at(cx: CallContext) -> Result { } pub fn concat(cx: CallContext) -> Result { - let this = cx.this.to_string(cx.scope)?; - let other = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - let concat = String::from(this.as_ref()) + other.as_ref(); - Ok(Value::String(concat.into())) + let this = cx.this.to_js_string(cx.scope)?; + let other = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; + let concat = String::from(this.res(cx.scope)) + other.res(cx.scope); + Ok(Value::String(cx.scope.intern(concat.as_ref()).into())) } pub fn ends_with(cx: CallContext) -> Result { - let this = cx.this.to_string(cx.scope)?; - let other = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - Ok(Value::Boolean(this.ends_with(other.as_ref()))) + let this = cx.this.to_js_string(cx.scope)?; + let other = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; + Ok(Value::Boolean(this.res(cx.scope).ends_with(other.res(cx.scope)))) } pub fn starts_with(cx: CallContext) -> Result { - let this = cx.this.to_string(cx.scope)?; - let other = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - Ok(Value::Boolean(this.starts_with(other.as_ref()))) + let this = cx.this.to_js_string(cx.scope)?; + let other = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; + Ok(Value::Boolean(this.res(cx.scope).starts_with(other.res(cx.scope)))) } pub fn includes(cx: CallContext) -> Result { - let this = cx.this.to_string(cx.scope)?; - let other = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - Ok(Value::Boolean(this.contains(other.as_ref()))) + let this = cx.this.to_js_string(cx.scope)?; + let other = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; + Ok(Value::Boolean(this.res(cx.scope).contains(other.res(cx.scope)))) } pub fn index_of(cx: CallContext) -> Result { - let this = cx.this.to_string(cx.scope)?; - let other = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - let pos = this.find(other.as_ref()).map(|i| i as f64).unwrap_or(-1.0); + let this = cx.this.to_js_string(cx.scope)?; + let other = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; + let pos = this + .res(cx.scope) + .find(other.res(cx.scope)) + .map(|i| i as f64) + .unwrap_or(-1.0); Ok(Value::number(pos)) } pub fn last_index_of(cx: CallContext) -> Result { - let this = cx.this.to_string(cx.scope)?; - let other = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; - let pos = this.rfind(other.as_ref()).map(|i| i as f64).unwrap_or(-1.0); + let this = cx.this.to_js_string(cx.scope)?; + let other = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; + let pos = this + .res(cx.scope) + .rfind(other.res(cx.scope)) + .map(|i| i as f64) + .unwrap_or(-1.0); Ok(Value::number(pos)) } @@ -161,13 +167,13 @@ enum PadPlacement { fn string_pad(cx: CallContext, placement: PadPlacement) -> Result { // 1. Let S be ? ToString(O). - let s = cx.this.to_string(cx.scope)?; + let s = cx.this.to_js_string(cx.scope)?; // 2. Let intMaxLength be ℝ(? ToLength(maxLength)). let int_max_length = cx.args.get(1).unwrap_or_undefined().to_length_u(cx.scope)?; // 3. Let stringLength be the length of S. - let string_length = s.len(); + let string_length = s.res(cx.scope).len(); // If intMaxLength ≤ stringLength, return S. if int_max_length <= string_length { @@ -177,16 +183,16 @@ fn string_pad(cx: CallContext, placement: PadPlacement) -> Result // 5. If fillString is undefined, let filler be the String value consisting solely of the code unit 0x0020 (SPACE). let filler = if let Some(filler) = cx.args.get(2) { // Else, let filler be ? ToString(fillString). - let filler = filler.to_string(cx.scope)?; + let filler = filler.to_js_string(cx.scope)?.res(cx.scope); // 7. If filler is the empty String, return S. if filler.is_empty() { return Ok(Value::String(s)); } - Cow::Owned(String::from(filler.as_ref())) + filler } else { - Cow::Borrowed(" ") + " " }; // 8. Let fillLen be intMaxLength - stringLength. @@ -197,12 +203,11 @@ fn string_pad(cx: CallContext, placement: PadPlacement) -> Result // 10. If placement is start, return the string-concatenation of truncatedStringFiller and S. // Else, return the string-concatenation of S and truncatedStringFiller. - match placement { - PadPlacement::Start => Ok(Value::String((truncated_string_filler + s.as_ref()).into())), - PadPlacement::End => Ok(Value::String( - (String::from(s.as_ref()) + &truncated_string_filler).into(), - )), - } + let string = match placement { + PadPlacement::Start => truncated_string_filler + s.res(cx.scope), + PadPlacement::End => String::from(s.res(cx.scope)) + &truncated_string_filler, + }; + Ok(Value::String(cx.scope.intern(string.as_ref()).into())) } pub fn pad_end(cx: CallContext) -> Result { @@ -215,7 +220,7 @@ pub fn pad_start(cx: CallContext) -> Result { pub fn repeat(cx: CallContext) -> Result { // 1. Let O be ? ToString(string). - let o = cx.this.to_string(cx.scope)?; + let o = cx.this.to_js_string(cx.scope)?; // 2. Let n be ? ToInteger(times). let n = cx.args.first().unwrap_or_undefined().to_integer_or_infinity(cx.scope)?; @@ -226,44 +231,54 @@ pub fn repeat(cx: CallContext) -> Result { } // 4. Let result be the String value that is the concatenation of n copies of O. - let result = o.repeat(n as usize); + let result = o.res(cx.scope).repeat(n as usize); // 5. Return result. - Ok(Value::String(result.into())) + Ok(Value::String(cx.scope.intern(result).into())) } pub fn replace(cx: CallContext) -> Result { // TODO: once we have regexp, we can properly implement this - let string = cx.this.to_string(cx.scope)?; + let string = cx.this.to_js_string(cx.scope)?; - let search_string = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let search_string = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; - let replace_value = cx.args.get(1).unwrap_or_undefined().to_string(cx.scope)?; + let replace_value = cx.args.get(1).unwrap_or_undefined().to_js_string(cx.scope)?; - let string = string.replacen(search_string.as_ref(), replace_value.as_ref(), 1); + let string = string + .res(cx.scope) + .replacen(search_string.res(cx.scope), replace_value.res(cx.scope), 1); - Ok(Value::String(string.into())) + Ok(Value::String(cx.scope.intern(string).into())) } pub fn replace_all(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; + let string = cx.this.to_js_string(cx.scope)?; - let search_string = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let search_string = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; - let replace_value = cx.args.get(1).unwrap_or_undefined().to_string(cx.scope)?; + let replace_value = cx.args.get(1).unwrap_or_undefined().to_js_string(cx.scope)?; - let string = string.replace(search_string.as_ref(), replace_value.as_ref()); + let string = string + .res(cx.scope) + .replace(search_string.res(cx.scope), replace_value.res(cx.scope)); - Ok(Value::String(string.into())) + Ok(Value::String(cx.scope.intern(string).into())) } pub fn split(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; - let separator = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let string = cx.this.to_js_string(cx.scope)?.res(cx.scope).to_owned(); + let separator = cx + .args + .first() + .unwrap_or_undefined() + .to_js_string(cx.scope)? + .res(cx.scope) + .to_owned(); let result = string - .split(separator.as_ref()) - .map(|s| PropertyValue::static_default(Value::String(s.into()))) + .split(&separator) + .map(|s| PropertyValue::static_default(Value::String(cx.scope.intern(s).into()))) .collect(); let array = Array::from_vec(cx.scope, result); @@ -271,49 +286,49 @@ pub fn split(cx: CallContext) -> Result { } pub fn to_uppercase(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; - let result = string.to_uppercase(); - Ok(Value::String(result.into())) + let string = cx.this.to_js_string(cx.scope)?; + let result = string.res(cx.scope).to_uppercase(); + Ok(Value::String(cx.scope.intern(result).into())) } pub fn to_lowercase(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; - let result = string.to_lowercase(); - Ok(Value::String(result.into())) + let string = cx.this.to_js_string(cx.scope)?; + let result = string.res(cx.scope).to_lowercase(); + Ok(Value::String(cx.scope.intern(result).into())) } pub fn trim(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; - let result = string.trim(); - Ok(Value::String(result.into())) + let string = cx.this.to_js_string(cx.scope)?; + let result = string.res(cx.scope).trim().to_owned(); + Ok(Value::String(cx.scope.intern(result.as_ref()).into())) } pub fn trim_start(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; - let result = string.trim_start(); - Ok(Value::String(result.into())) + let string = cx.this.to_js_string(cx.scope)?; + let result = string.res(cx.scope).trim_start().to_owned(); + Ok(Value::String(cx.scope.intern(result.as_ref()).into())) } pub fn trim_end(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; - let result = string.trim_start(); - Ok(Value::String(result.into())) + let string = cx.this.to_js_string(cx.scope)?; + let result = string.res(cx.scope).trim_start().to_owned(); + Ok(Value::String(cx.scope.intern(result.as_ref()).into())) } pub fn from_char_code(cx: CallContext) -> Result { let code = cx.args.first().unwrap_or_undefined().to_int32(cx.scope)?; let s = char::from_u32(code as u32).unwrap_or(char::REPLACEMENT_CHARACTER); - Ok(Value::String(s.to_string().into())) + Ok(Value::String(cx.scope.intern_char(s).into())) } pub fn substr(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; + let string = cx.this.to_js_string(cx.scope)?; let (start, end) = { let start = match cx.args.first() { Some(arg) => { let num = arg.to_int32(cx.scope)?; if num < 0 { - (num + string.len() as i32) as usize + (num + string.len(cx.scope) as i32) as usize } else { num as usize } @@ -322,28 +337,30 @@ pub fn substr(cx: CallContext) -> Result { }; let end = match cx.args.get(1) { Some(arg) => arg.to_int32(cx.scope)? as usize, - None => string.len(), + None => string.len(cx.scope), }; (start, start + end) }; - let bytes = string.as_bytes().get(start..end.min(string.len())).unwrap_or(&[]); + let end = end.min(string.len(cx.scope)); + + let bytes = string.res(cx.scope).as_bytes().get(start..end).unwrap_or(&[]); let result = String::from_utf8_lossy(bytes).into_owned(); - Ok(Value::String(result.into())) + Ok(Value::String(cx.scope.intern(result.as_ref()).into())) } pub fn substring(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; + let string = cx.this.to_js_string(cx.scope)?; let (mut start, mut end) = { let start = match cx.args.first() { Some(arg) => arg.to_int32(cx.scope)?.max(0) as usize, None => 0, }; let end = match cx.args.get(1) { - Some(arg) => (arg.to_int32(cx.scope)? as usize).min(string.len()), - None => string.len(), + Some(arg) => (arg.to_int32(cx.scope)? as usize).min(string.len(cx.scope)), + None => string.len(cx.scope), }; (start, end) @@ -353,17 +370,17 @@ pub fn substring(cx: CallContext) -> Result { std::mem::swap(&mut start, &mut end); } - let bytes = string.as_bytes().get(start..end).unwrap_or(&[]); + let bytes = string.res(cx.scope).as_bytes().get(start..end).unwrap_or(&[]); let result = String::from_utf8_lossy(bytes).into_owned(); - Ok(Value::String(result.into())) + Ok(Value::String(cx.scope.intern(result.as_ref()).into())) } pub fn iterator(cx: CallContext) -> Result { - let string = cx.this.to_string(cx.scope)?; + let string = cx.this.to_js_string(cx.scope)?.res(cx.scope).to_owned(); let chars = string .chars() - .map(|c| Rc::from(c.to_string())) + .map(|c| cx.scope.intern_char(c).into()) .map(Value::String) .map(PropertyValue::static_default) .collect::>(); diff --git a/crates/dash_vm/src/js_std/symbol.rs b/crates/dash_vm/src/js_std/symbol.rs index 2cab2aa3..09a32997 100644 --- a/crates/dash_vm/src/js_std/symbol.rs +++ b/crates/dash_vm/src/js_std/symbol.rs @@ -4,7 +4,7 @@ use crate::value::primitive::Symbol; use crate::value::{Value, ValueContext}; pub fn constructor(cx: CallContext) -> Result { - let description = cx.args.first().unwrap_or_undefined().to_string(cx.scope)?; + let description = cx.args.first().unwrap_or_undefined().to_js_string(cx.scope)?; let symbol = Symbol::new(description); Ok(symbol.into()) } diff --git a/crates/dash_vm/src/json/parser.rs b/crates/dash_vm/src/json/parser.rs index 7753fe2c..8287ccab 100644 --- a/crates/dash_vm/src/json/parser.rs +++ b/crates/dash_vm/src/json/parser.rs @@ -226,17 +226,14 @@ impl<'a, 'sc, 'vm> Parser<'a, 'sc, 'vm> { self.skip_whitespaces(); let key = self.read_string_literal()?; // "key" + let key = self + .sc + .intern(std::str::from_utf8(key).map_err(|err| JsonParseError::Utf8Error(err, self.idx))?); + self.skip_whitespaces(); // spaces self.idx += 1; // : let value = self.parse()?; - obj.insert( - PropertyKey::String(Cow::Owned( - std::str::from_utf8(key) - .map_err(|err| JsonParseError::Utf8Error(err, self.idx))? - .to_owned(), - )), - PropertyValue::static_default(value), - ); + obj.insert(PropertyKey::String(key.into()), PropertyValue::static_default(value)); } let obj = NamedObject::with_values(self.sc, obj); @@ -247,7 +244,7 @@ impl<'a, 'sc, 'vm> Parser<'a, 'sc, 'vm> { let string = self.read_string_literal()?; std::str::from_utf8(string) .map_err(|err| JsonParseError::Utf8Error(err, self.idx)) - .map(|s| Value::String(s.into())) + .map(|s| Value::String(self.sc.intern(s).into())) } _ if util::is_digit(cur) => { let num = std::str::from_utf8(self.read_number_literal()?) diff --git a/crates/dash_vm/src/lib.rs b/crates/dash_vm/src/lib.rs index 817783e9..028450cb 100644 --- a/crates/dash_vm/src/lib.rs +++ b/crates/dash_vm/src/lib.rs @@ -4,7 +4,8 @@ use std::ops::RangeBounds; use std::vec::Drain; use std::{fmt, mem}; -use crate::gc::trace::Trace; +use crate::gc::interner::{self, sym}; +use crate::gc::trace::{Trace, TraceCtxt}; use crate::util::cold_path; use crate::value::function::Function; use crate::value::primitive::Symbol; @@ -21,6 +22,7 @@ use self::value::Value; use dash_log::{debug, error, span, Level}; use dash_middle::compiler::instruction::Instruction; use gc::handle::Handle; +use gc::interner::StringInterner; use gc::Gc; use localscope::{scope, LocalScopeList}; use rustc_hash::FxHashSet; @@ -57,6 +59,7 @@ pub struct Vm { // popping from the stack must return `Unrooted` stack: Vec, gc: Gc, + pub interner: StringInterner, global: Handle, // "External refs" currently refers to existing `Persistent`s. // Persistent values already manage the reference count when cloning or dropping them @@ -97,6 +100,7 @@ impl Vm { async_tasks: Vec::new(), stack: Vec::with_capacity(512), gc, + interner: StringInterner::new(), global, external_refs: FxHashSet::default(), scopes: LocalScopeList::new(), @@ -125,7 +129,7 @@ impl Vm { #[rustfmt::skip] fn prepare(&mut self) { debug!("initialize vm intrinsics"); - fn set_fn_prototype(v: &dyn Object, proto: &Handle, name: &str) { + fn set_fn_prototype(v: &dyn Object, proto: &Handle, name: interner::Symbol) { let fun = v.as_any().downcast_ref::().unwrap(); fun.set_name(name.into()); fun.set_fn_prototype(proto.clone()); @@ -138,17 +142,17 @@ impl Vm { base: Handle, prototype: impl Into, constructor: Handle, - methods: impl IntoIterator)>, + methods: impl IntoIterator)>, symbols: impl IntoIterator)>, - fields: impl IntoIterator, + fields: impl IntoIterator, // Contrary to `prototype`, this optionally sets the function prototype. Should only be `Some` // when base is a function - fn_prototype: Option<(&'static str, Handle)>, + fn_prototype: Option<(interner::Symbol, Handle)>, // LocalScope needs to be the last parameter because we don't have two phase borrows in user code scope: &mut LocalScope<'_>, ) -> Handle { - base.set_property(scope, "constructor".into(), PropertyValue::static_default(constructor.into())).unwrap(); + base.set_property(scope, sym::constructor.into(), PropertyValue::static_default(constructor.into())).unwrap(); base.set_prototype(scope, prototype.into()).unwrap(); for (key, value) in methods { @@ -192,7 +196,7 @@ impl Vm { let mut scope = self.scope(); let global = scope.global.clone(); - + let function_ctor = register( scope.statics.function_ctor.clone(), scope.statics.function_proto.clone(), @@ -200,144 +204,144 @@ impl Vm { [], [], [], - Some(("Function", scope.statics.function_proto.clone())), + Some((sym::Function, scope.statics.function_proto.clone())), &mut scope, ); - + let function_proto = register( scope.statics.function_proto.clone(), scope.statics.object_prototype.clone(), function_ctor.clone(), [ - ("bind", scope.statics.function_bind.clone()), - ("call", scope.statics.function_call.clone()), - ("toString", scope.statics.function_to_string.clone()), + (sym::bind, scope.statics.function_bind.clone()), + (sym::call, scope.statics.function_call.clone()), + (sym::toString, scope.statics.function_to_string.clone()), ], [], [], None, &mut scope, ); - + let object_ctor = register( scope.statics.object_ctor.clone(), function_proto.clone(), function_ctor.clone(), [ - ("create", scope.statics.object_create.clone()), - ("keys", scope.statics.object_keys.clone()), - ("getOwnPropertyDescriptor", scope.statics.object_get_own_property_descriptor.clone()), - ("getOwnPropertyDescriptors", scope.statics.object_get_own_property_descriptors.clone()), - ("defineProperty", scope.statics.object_define_property.clone()), - ("entries", scope.statics.object_entries.clone()), - ("assign", scope.statics.object_assign.clone()), + (sym::create, scope.statics.object_create.clone()), + (sym::keys, scope.statics.object_keys.clone()), + (sym::getOwnPropertyDescriptor, scope.statics.object_get_own_property_descriptor.clone()), + (sym::getOwnPropertyDescriptors, scope.statics.object_get_own_property_descriptors.clone()), + (sym::defineProperty, scope.statics.object_define_property.clone()), + (sym::entries, scope.statics.object_entries.clone()), + (sym::assign, scope.statics.object_assign.clone()), ], [], [], - Some(("Object", scope.statics.object_prototype.clone())), + Some((sym::Object, scope.statics.object_prototype.clone())), &mut scope, ); - + let object_proto = register( scope.statics.object_prototype.clone(), Value::null(), object_ctor.clone(), [ - ("toString", scope.statics.object_to_string.clone()), - ("hasOwnProperty", scope.statics.object_has_own_property.clone()), + (sym::toString, scope.statics.object_to_string.clone()), + (sym::hasOwnProperty, scope.statics.object_has_own_property.clone()), ], [], [], None, &mut scope, ); - + let console = register( scope.statics.console.clone(), object_proto.clone(), object_ctor.clone(), [ - ("log", scope.statics.console_log.clone()), + (sym::log, scope.statics.console_log.clone()), ], [], [], None, &mut scope, ); - + let math = register( scope.statics.math.clone(), object_proto.clone(), object_ctor.clone(), [ - ("floor", scope.statics.math_floor.clone()), - ("abs", scope.statics.math_abs.clone()), - ("acos", scope.statics.math_acos.clone()), - ("acosh", scope.statics.math_acosh.clone()), - ("asin", scope.statics.math_asin.clone()), - ("asinh", scope.statics.math_asinh.clone()), - ("atan", scope.statics.math_atan.clone()), - ("atanh", scope.statics.math_atanh.clone()), - ("atan2", scope.statics.math_atan2.clone()), - ("cbrt", scope.statics.math_cbrt.clone()), - ("ceil", scope.statics.math_ceil.clone()), - ("clz32", scope.statics.math_clz32.clone()), - ("cos", scope.statics.math_cos.clone()), - ("cosh", scope.statics.math_cosh.clone()), - ("exp", scope.statics.math_exp.clone()), - ("expm1", scope.statics.math_expm1.clone()), - ("log", scope.statics.math_log.clone()), - ("log1p", scope.statics.math_log1p.clone()), - ("log10", scope.statics.math_log10.clone()), - ("log2", scope.statics.math_log2.clone()), - ("round", scope.statics.math_round.clone()), - ("sin", scope.statics.math_sin.clone()), - ("sinh", scope.statics.math_sinh.clone()), - ("sqrt", scope.statics.math_sqrt.clone()), - ("tan", scope.statics.math_tan.clone()), - ("tanh", scope.statics.math_tanh.clone()), - ("trunc", scope.statics.math_trunc.clone()), - ("random", scope.statics.math_random.clone()), - ("max", scope.statics.math_max.clone()), - ("min", scope.statics.math_min.clone()), + (sym::floor, scope.statics.math_floor.clone()), + (sym::abs, scope.statics.math_abs.clone()), + (sym::acos, scope.statics.math_acos.clone()), + (sym::acosh, scope.statics.math_acosh.clone()), + (sym::asin, scope.statics.math_asin.clone()), + (sym::asinh, scope.statics.math_asinh.clone()), + (sym::atan, scope.statics.math_atan.clone()), + (sym::atanh, scope.statics.math_atanh.clone()), + (sym::atan2, scope.statics.math_atan2.clone()), + (sym::cbrt, scope.statics.math_cbrt.clone()), + (sym::ceil, scope.statics.math_ceil.clone()), + (sym::clz32, scope.statics.math_clz32.clone()), + (sym::cos, scope.statics.math_cos.clone()), + (sym::cosh, scope.statics.math_cosh.clone()), + (sym::exp, scope.statics.math_exp.clone()), + (sym::expm1, scope.statics.math_expm1.clone()), + (sym::log, scope.statics.math_log.clone()), + (sym::log1p, scope.statics.math_log1p.clone()), + (sym::log10, scope.statics.math_log10.clone()), + (sym::log2, scope.statics.math_log2.clone()), + (sym::round, scope.statics.math_round.clone()), + (sym::sin, scope.statics.math_sin.clone()), + (sym::sinh, scope.statics.math_sinh.clone()), + (sym::sqrt, scope.statics.math_sqrt.clone()), + (sym::tan, scope.statics.math_tan.clone()), + (sym::tanh, scope.statics.math_tanh.clone()), + (sym::trunc, scope.statics.math_trunc.clone()), + (sym::random, scope.statics.math_random.clone()), + (sym::max, scope.statics.math_max.clone()), + (sym::min, scope.statics.math_min.clone()), ], [], [ - ("PI", Value::number(std::f64::consts::PI)), + (sym::PI, Value::number(std::f64::consts::PI)), ], None, &mut scope, ); - + let number_ctor = register( scope.statics.number_ctor.clone(), function_proto.clone(), function_ctor.clone(), [ - ("isFinite", scope.statics.number_is_finite.clone()), - ("isNaN", scope.statics.number_is_nan.clone()), - ("isSafeInteger", scope.statics.number_is_safe_integer.clone()), + (sym::isFinite, scope.statics.number_is_finite.clone()), + (sym::isNaN, scope.statics.number_is_nan.clone()), + (sym::isSafeInteger, scope.statics.number_is_safe_integer.clone()), ], [], [], - Some(("Number", scope.statics.number_prototype.clone())), + Some((sym::Number, scope.statics.number_prototype.clone())), &mut scope, ); - + register( scope.statics.number_prototype.clone(), object_proto.clone(), number_ctor.clone(), [ - ("toString", scope.statics.number_tostring.clone()), - ("toFixed", scope.statics.number_to_fixed.clone()), + (sym::toString, scope.statics.number_tostring.clone()), + (sym::toFixed, scope.statics.number_to_fixed.clone()), ], [], [], None, &mut scope, ); - + let boolean_ctor = register( scope.statics.boolean_ctor.clone(), function_proto.clone(), @@ -345,141 +349,141 @@ impl Vm { [], [], [], - Some(("Boolean", scope.statics.boolean_prototype.clone())), + Some((sym::Boolean, scope.statics.boolean_prototype.clone())), &mut scope, ); - + register( scope.statics.boolean_prototype.clone(), object_proto.clone(), boolean_ctor.clone(), [ - ("toString", scope.statics.boolean_tostring.clone()), - ("valueOf", scope.statics.boolean_valueof.clone()), + (sym::toString, scope.statics.boolean_tostring.clone()), + (sym::valueOf, scope.statics.boolean_valueof.clone()), ], [], [], None, &mut scope, ); - + let string_ctor = register( scope.statics.string_ctor.clone(), function_proto.clone(), function_ctor.clone(), [ - ("fromCharCode", scope.statics.string_from_char_code.clone()), + (sym::fromCharCode, scope.statics.string_from_char_code.clone()), ], [], [], - Some(("String", scope.statics.string_prototype.clone())), + Some((sym::String, scope.statics.string_prototype.clone())), &mut scope, ); - + register( scope.statics.string_prototype.clone(), scope.statics.object_prototype.clone(), scope.statics.string_ctor.clone(), [ - ("toString", scope.statics.string_tostring.clone()), - ("charAt", scope.statics.string_char_at.clone()), - ("charCodeAt", scope.statics.string_char_code_at.clone()), - ("concat", scope.statics.string_concat.clone()), - ("endsWith", scope.statics.string_ends_with.clone()), - ("startsWith", scope.statics.string_starts_with.clone()), - ("includes", scope.statics.string_includes.clone()), - ("indexOf", scope.statics.string_index_of.clone()), - ("lastIndexOf", scope.statics.string_last_index_of.clone()), - ("padEnd", scope.statics.string_pad_end.clone()), - ("padStart", scope.statics.string_pad_start.clone()), - ("repeat", scope.statics.string_repeat.clone()), - ("replace", scope.statics.string_replace.clone()), - ("replaceAll", scope.statics.string_replace_all.clone()), - ("split", scope.statics.string_split.clone()), - ("toLowerCase", scope.statics.string_to_lowercase.clone()), - ("toUpperCase", scope.statics.string_to_uppercase.clone()), - ("big", scope.statics.string_big.clone()), - ("blink", scope.statics.string_blink.clone()), - ("bold", scope.statics.string_bold.clone()), - ("fixed", scope.statics.string_fixed.clone()), - ("italics", scope.statics.string_italics.clone()), - ("strike", scope.statics.string_strike.clone()), - ("sub", scope.statics.string_sub.clone()), - ("sup", scope.statics.string_sup.clone()), - ("fontcolor", scope.statics.string_fontcolor.clone()), - ("fontsize", scope.statics.string_fontsize.clone()), - ("link", scope.statics.string_link.clone()), - ("trim", scope.statics.string_trim.clone()), - ("trimStart", scope.statics.string_trim_start.clone()), - ("trimEnd", scope.statics.string_trim_end.clone()), - ("substr", scope.statics.string_substr.clone()), - ("substring", scope.statics.string_substring.clone()), + (sym::toString, scope.statics.string_tostring.clone()), + (sym::charAt, scope.statics.string_char_at.clone()), + (sym::charCodeAt, scope.statics.string_char_code_at.clone()), + (sym::concat, scope.statics.string_concat.clone()), + (sym::endsWith, scope.statics.string_ends_with.clone()), + (sym::startsWith, scope.statics.string_starts_with.clone()), + (sym::includes, scope.statics.string_includes.clone()), + (sym::indexOf, scope.statics.string_index_of.clone()), + (sym::lastIndexOf, scope.statics.string_last_index_of.clone()), + (sym::padEnd, scope.statics.string_pad_end.clone()), + (sym::padStart, scope.statics.string_pad_start.clone()), + (sym::repeat, scope.statics.string_repeat.clone()), + (sym::replace, scope.statics.string_replace.clone()), + (sym::replaceAll, scope.statics.string_replace_all.clone()), + (sym::split, scope.statics.string_split.clone()), + (sym::toLowerCase, scope.statics.string_to_lowercase.clone()), + (sym::toUpperCase, scope.statics.string_to_uppercase.clone()), + (sym::big, scope.statics.string_big.clone()), + (sym::blink, scope.statics.string_blink.clone()), + (sym::bold, scope.statics.string_bold.clone()), + (sym::fixed, scope.statics.string_fixed.clone()), + (sym::italics, scope.statics.string_italics.clone()), + (sym::strike, scope.statics.string_strike.clone()), + (sym::sub, scope.statics.string_sub.clone()), + (sym::sup, scope.statics.string_sup.clone()), + (sym::fontcolor, scope.statics.string_fontcolor.clone()), + (sym::fontsize, scope.statics.string_fontsize.clone()), + (sym::link, scope.statics.string_link.clone()), + (sym::trim, scope.statics.string_trim.clone()), + (sym::trimStart, scope.statics.string_trim_start.clone()), + (sym::trimEnd, scope.statics.string_trim_end.clone()), + (sym::substr, scope.statics.string_substr.clone()), + (sym::substring, scope.statics.string_substring.clone()), ], [(scope.statics.symbol_iterator.clone(), scope.statics.string_iterator.clone())], [], None, &mut scope, ); - + let array_ctor = register( scope.statics.array_ctor.clone(), function_proto.clone(), function_ctor.clone(), [ - ("from", scope.statics.array_from.clone()), - ("isArray", scope.statics.array_is_array.clone()), + (sym::from, scope.statics.array_from.clone()), + (sym::isArray, scope.statics.array_is_array.clone()), ], [], [], - Some(("Array", scope.statics.array_prototype.clone())), + Some((sym::Array, scope.statics.array_prototype.clone())), &mut scope, ); - + register( scope.statics.array_prototype.clone(), object_proto.clone(), array_ctor.clone(), [ - ("toString", scope.statics.array_tostring.clone()), - ("join", scope.statics.array_join.clone()), - ("values", scope.statics.array_values.clone()), - ("at", scope.statics.array_at.clone()), - ("concat", scope.statics.array_concat.clone()), - ("entries", scope.statics.array_entries.clone()), - ("keys", scope.statics.array_keys.clone()), - ("every", scope.statics.array_every.clone()), - ("some", scope.statics.array_some.clone()), - ("fill", scope.statics.array_fill.clone()), - ("filter", scope.statics.array_filter.clone()), - ("reduce", scope.statics.array_reduce.clone()), - ("find", scope.statics.array_find.clone()), - ("findIndex", scope.statics.array_find_index.clone()), - ("flat", scope.statics.array_flat.clone()), - ("forEach", scope.statics.array_for_each.clone()), - ("includes", scope.statics.array_includes.clone()), - ("indexOf", scope.statics.array_index_of.clone()), - ("map", scope.statics.array_map.clone()), - ("pop", scope.statics.array_pop.clone()), - ("push", scope.statics.array_push.clone()), - ("reverse", scope.statics.array_reverse.clone()), - ("shift", scope.statics.array_shift.clone()), - ("sort", scope.statics.array_sort.clone()), - ("unshift", scope.statics.array_unshift.clone()), - ("slice", scope.statics.array_slice.clone()), - ("lastIndexOf", scope.statics.array_last_index_of.clone()), + (sym::toString, scope.statics.array_tostring.clone()), + (sym::join, scope.statics.array_join.clone()), + (sym::values, scope.statics.array_values.clone()), + (sym::at, scope.statics.array_at.clone()), + (sym::concat, scope.statics.array_concat.clone()), + (sym::entries, scope.statics.array_entries.clone()), + (sym::keys, scope.statics.array_keys.clone()), + (sym::every, scope.statics.array_every.clone()), + (sym::some, scope.statics.array_some.clone()), + (sym::fill, scope.statics.array_fill.clone()), + (sym::filter, scope.statics.array_filter.clone()), + (sym::reduce, scope.statics.array_reduce.clone()), + (sym::find, scope.statics.array_find.clone()), + (sym::findIndex, scope.statics.array_find_index.clone()), + (sym::flat, scope.statics.array_flat.clone()), + (sym::forEach, scope.statics.array_for_each.clone()), + (sym::includes, scope.statics.array_includes.clone()), + (sym::indexOf, scope.statics.array_index_of.clone()), + (sym::map, scope.statics.array_map.clone()), + (sym::pop, scope.statics.array_pop.clone()), + (sym::push, scope.statics.array_push.clone()), + (sym::reverse, scope.statics.array_reverse.clone()), + (sym::shift, scope.statics.array_shift.clone()), + (sym::sort, scope.statics.array_sort.clone()), + (sym::unshift, scope.statics.array_unshift.clone()), + (sym::slice, scope.statics.array_slice.clone()), + (sym::lastIndexOf, scope.statics.array_last_index_of.clone()), ], [(scope.statics.symbol_iterator.clone(), scope.statics.array_values.clone())], [], None, &mut scope, ); - + register( scope.statics.array_iterator_prototype.clone(), object_proto.clone(), // TODO: wrong function_ctor.clone(), // TODO: ^ [ - ("next", scope.statics.array_iterator_next.clone()), + (sym::next, scope.statics.array_iterator_next.clone()), ], [ (scope.statics.symbol_iterator.clone(), scope.statics.identity_this.clone()), @@ -488,13 +492,13 @@ impl Vm { None, &mut scope, ); - + register( scope.statics.generator_iterator_prototype.clone(), object_proto.clone(), // TODO: wrong function_ctor.clone(), // TODO: ^ [ - ("next", scope.statics.generator_iterator_next.clone()), + (sym::next, scope.statics.generator_iterator_next.clone()), ], [ (scope.statics.symbol_iterator.clone(), scope.statics.identity_this.clone()), @@ -503,7 +507,7 @@ impl Vm { None, &mut scope, ); - + let symbol_ctor = register( scope.statics.symbol_ctor.clone(), function_proto.clone(), @@ -511,23 +515,23 @@ impl Vm { [], [], [ - ("asyncIterator",Value::Symbol( scope.statics.symbol_async_iterator.clone())), - ("hasInstance", Value::Symbol(scope.statics.symbol_has_instance.clone())), - ("iterator", Value::Symbol(scope.statics.symbol_iterator.clone())), - ("match", Value::Symbol(scope.statics.symbol_match.clone())), - ("matchAll", Value::Symbol(scope.statics.symbol_match_all.clone())), - ("replace", Value::Symbol(scope.statics.symbol_replace.clone())), - ("search", Value::Symbol(scope.statics.symbol_search.clone())), - ("species", Value::Symbol(scope.statics.symbol_species.clone())), - ("split", Value::Symbol(scope.statics.symbol_split.clone())), - ("toPrimitive", Value::Symbol(scope.statics.symbol_to_primitive.clone())), - ("toStringTag", Value::Symbol(scope.statics.symbol_to_string_tag.clone())), - ("unscopables", Value::Symbol(scope.statics.symbol_unscopables.clone())), + (sym::asyncIterator,Value::Symbol( scope.statics.symbol_async_iterator.clone())), + (sym::hasInstance, Value::Symbol(scope.statics.symbol_has_instance.clone())), + (sym::iterator, Value::Symbol(scope.statics.symbol_iterator.clone())), + (sym::match_, Value::Symbol(scope.statics.symbol_match.clone())), + (sym::matchAll, Value::Symbol(scope.statics.symbol_match_all.clone())), + (sym::replace, Value::Symbol(scope.statics.symbol_replace.clone())), + (sym::search, Value::Symbol(scope.statics.symbol_search.clone())), + (sym::species, Value::Symbol(scope.statics.symbol_species.clone())), + (sym::split, Value::Symbol(scope.statics.symbol_split.clone())), + (sym::toPrimitive, Value::Symbol(scope.statics.symbol_to_primitive.clone())), + (sym::toStringTag, Value::Symbol(scope.statics.symbol_to_string_tag.clone())), + (sym::unscopables, Value::Symbol(scope.statics.symbol_unscopables.clone())), ], - Some(("Symbol", scope.statics.symbol_prototype.clone())), + Some((sym::JsSymbol, scope.statics.symbol_prototype.clone())), &mut scope, ); - + let error_ctor = register( scope.statics.error_ctor.clone(), function_proto.clone(), @@ -535,23 +539,23 @@ impl Vm { [], [], [], - Some(("Error", scope.statics.error_prototype.clone())), + Some((sym::Error, scope.statics.error_prototype.clone())), &mut scope, ); - + register( scope.statics.error_prototype.clone(), object_proto.clone(), error_ctor.clone(), [ - ("toString", scope.statics.error_to_string.clone()), + (sym::toString, scope.statics.error_to_string.clone()), ], [], [], None, &mut scope, ); - + let arraybuffer_ctor = register( scope.statics.arraybuffer_ctor.clone(), function_proto.clone(), @@ -559,23 +563,23 @@ impl Vm { [], [], [], - Some(("ArrayBuffer", scope.statics.arraybuffer_prototype.clone())), + Some((sym::ArrayBuffer, scope.statics.arraybuffer_prototype.clone())), &mut scope, ); - + register( scope.statics.arraybuffer_prototype.clone(), object_proto.clone(), arraybuffer_ctor.clone(), [ - ("byteLength", scope.statics.arraybuffer_byte_length.clone()) // TODO: should be a getter really + (sym::byteLength, scope.statics.arraybuffer_byte_length.clone()) // TODO: should be a getter really ], [], [], None, &mut scope, ); - + let u8array_ctor = register( scope.statics.uint8array_ctor.clone(), function_proto.clone(), @@ -583,23 +587,23 @@ impl Vm { [], [], [], - Some(("Uint8Array", scope.statics.uint8array_prototype.clone())), + Some((sym::Uint8Array, scope.statics.uint8array_prototype.clone())), &mut scope, ); - + register( scope.statics.uint8array_prototype.clone(), object_proto.clone(), u8array_ctor.clone(), [ - ("fill", scope.statics.typedarray_fill.clone()), + (sym::fill, scope.statics.typedarray_fill.clone()), ], [], [], None, &mut scope, ); - + let i8array_ctor = register( scope.statics.int8array_ctor.clone(), function_proto.clone(), @@ -607,23 +611,23 @@ impl Vm { [], [], [], - Some(("Int8Array", scope.statics.int8array_prototype.clone())), + Some((sym::Int8Array, scope.statics.int8array_prototype.clone())), &mut scope, ); - + register( scope.statics.int8array_prototype.clone(), object_proto.clone(), i8array_ctor.clone(), [ - ("fill", scope.statics.typedarray_fill.clone()), + (sym::fill, scope.statics.typedarray_fill.clone()), ], [], [], None, &mut scope, ); - + let u16array_ctor = register( scope.statics.uint16array_ctor.clone(), function_proto.clone(), @@ -631,23 +635,23 @@ impl Vm { [], [], [], - Some(("Uint16Array", scope.statics.uint16array_prototype.clone())), + Some((sym::Uint16Array, scope.statics.uint16array_prototype.clone())), &mut scope, ); - + register( scope.statics.uint16array_prototype.clone(), object_proto.clone(), u16array_ctor.clone(), [ - ("fill", scope.statics.typedarray_fill.clone()), + (sym::fill, scope.statics.typedarray_fill.clone()), ], [], [], None, &mut scope, ); - + let i16array_ctor = register( scope.statics.int16array_ctor.clone(), function_proto.clone(), @@ -655,23 +659,23 @@ impl Vm { [], [], [], - Some(("Int16Array", scope.statics.int16array_prototype.clone())), + Some((sym::Int16Array, scope.statics.int16array_prototype.clone())), &mut scope, ); - + register( scope.statics.int16array_prototype.clone(), object_proto.clone(), i16array_ctor.clone(), [ - ("fill", scope.statics.typedarray_fill.clone()), + (sym::fill, scope.statics.typedarray_fill.clone()), ], [], [], None, &mut scope, ); - + let u32array_ctor = register( scope.statics.uint32array_ctor.clone(), function_proto.clone(), @@ -679,23 +683,23 @@ impl Vm { [], [], [], - Some(("Uint32Array", scope.statics.uint32array_prototype.clone())), + Some((sym::Uint32Array, scope.statics.uint32array_prototype.clone())), &mut scope, ); - + register( scope.statics.uint32array_prototype.clone(), object_proto.clone(), u32array_ctor.clone(), [ - ("fill", scope.statics.typedarray_fill.clone()), + (sym::fill, scope.statics.typedarray_fill.clone()), ], [], [], None, &mut scope, ); - + let i32array_ctor = register( scope.statics.int32array_ctor.clone(), function_proto.clone(), @@ -703,23 +707,23 @@ impl Vm { [], [], [], - Some(("Int32Array", scope.statics.int32array_prototype.clone())), + Some((sym::Int32Array, scope.statics.int32array_prototype.clone())), &mut scope, ); - + register( scope.statics.int32array_prototype.clone(), object_proto.clone(), i32array_ctor.clone(), [ - ("fill", scope.statics.typedarray_fill.clone()), + (sym::fill, scope.statics.typedarray_fill.clone()), ], [], [], None, &mut scope, ); - + let f32array_ctor = register( scope.statics.float32array_ctor.clone(), function_proto.clone(), @@ -727,23 +731,23 @@ impl Vm { [], [], [], - Some(("Float32Array", scope.statics.float32array_prototype.clone())), + Some((sym::Float32Array, scope.statics.float32array_prototype.clone())), &mut scope, ); - + register( scope.statics.float32array_prototype.clone(), object_proto.clone(), f32array_ctor.clone(), [ - ("fill", scope.statics.typedarray_fill.clone()), + (sym::fill, scope.statics.typedarray_fill.clone()), ], [], [], None, &mut scope, ); - + let f64array_ctor = register( scope.statics.float64array_ctor.clone(), function_proto.clone(), @@ -751,50 +755,50 @@ impl Vm { [], [], [], - Some(("Float64Array", scope.statics.float64array_prototype.clone())), + Some((sym::Float64Array, scope.statics.float64array_prototype.clone())), &mut scope, ); - + register( scope.statics.float64array_prototype.clone(), object_proto.clone(), f64array_ctor.clone(), [ - ("fill", scope.statics.typedarray_fill.clone()), + (sym::fill, scope.statics.typedarray_fill.clone()), ], [], [], None, &mut scope, ); - + let promise_ctor = register( scope.statics.promise_ctor.clone(), function_proto.clone(), function_ctor.clone(), [ - ("resolve", scope.statics.promise_resolve.clone()), - ("reject", scope.statics.promise_reject.clone()), + (sym::resolve, scope.statics.promise_resolve.clone()), + (sym::reject, scope.statics.promise_reject.clone()), ], [], [], - Some(("Promise", scope.statics.promise_proto.clone())), + Some((sym::Promise, scope.statics.promise_proto.clone())), &mut scope, ); - + register( scope.statics.promise_proto.clone(), object_proto.clone(), promise_ctor.clone(), [ - ("then", scope.statics.promise_then.clone()), + (sym::then, scope.statics.promise_then.clone()), ], [], [], None, &mut scope, ); - + let set_ctor = register( scope.statics.set_constructor.clone(), function_proto.clone(), @@ -802,27 +806,27 @@ impl Vm { [], [], [], - Some(("Set", scope.statics.set_prototype.clone())), + Some((sym::Set, scope.statics.set_prototype.clone())), &mut scope, ); - + register( scope.statics.set_prototype.clone(), object_proto.clone(), set_ctor.clone(), [ - ("add", scope.statics.set_add.clone()), - ("has", scope.statics.set_has.clone()), - ("delete", scope.statics.set_delete.clone()), - ("clear", scope.statics.set_clear.clone()), - ("size", scope.statics.set_size.clone()), + (sym::add, scope.statics.set_add.clone()), + (sym::has, scope.statics.set_has.clone()), + (sym::delete, scope.statics.set_delete.clone()), + (sym::clear, scope.statics.set_clear.clone()), + (sym::size, scope.statics.set_size.clone()), ], [], [], None, &mut scope, ); - + let map_ctor = register( scope.statics.map_constructor.clone(), function_proto.clone(), @@ -830,28 +834,28 @@ impl Vm { [], [], [], - Some(("Map", scope.statics.map_prototype.clone())), + Some((sym::Map, scope.statics.map_prototype.clone())), &mut scope, ); - + register( scope.statics.map_prototype.clone(), object_proto.clone(), map_ctor.clone(), [ - ("set", scope.statics.map_set.clone()), - ("get", scope.statics.map_get.clone()), - ("has", scope.statics.map_has.clone()), - ("delete", scope.statics.map_delete.clone()), - ("clear", scope.statics.map_clear.clone()), - ("size", scope.statics.map_size.clone()), // TODO: this should be a getter + (sym::set, scope.statics.map_set.clone()), + (sym::get, scope.statics.map_get.clone()), + (sym::has, scope.statics.map_has.clone()), + (sym::delete, scope.statics.map_delete.clone()), + (sym::clear, scope.statics.map_clear.clone()), + (sym::size, scope.statics.map_size.clone()), // TODO: this should be a getter ], [], [], None, &mut scope, ); - + let regexp_ctor = register( scope.statics.regexp_ctor.clone(), function_proto.clone(), @@ -859,24 +863,24 @@ impl Vm { [], [], [], - Some(("RegExp", scope.statics.regexp_prototype.clone())), + Some((sym::RegExp, scope.statics.regexp_prototype.clone())), &mut scope, ); - + register( scope.statics.regexp_prototype.clone(), object_proto.clone(), regexp_ctor.clone(), [ - ("test", scope.statics.regexp_test.clone()), - ("exec", scope.statics.regexp_exec.clone()) + (sym::test, scope.statics.regexp_test.clone()), + (sym::exec, scope.statics.regexp_exec.clone()) ], [], [], None, &mut scope, ); - + let eval_error_ctor = register( scope.statics.eval_error_ctor.clone(), function_proto.clone(), @@ -884,23 +888,23 @@ impl Vm { [], [], [], - Some(("EvalError", scope.statics.eval_error_prototype.clone())), + Some((sym::EvalError, scope.statics.eval_error_prototype.clone())), &mut scope, ); - + register( scope.statics.eval_error_prototype.clone(), scope.statics.error_prototype.clone(), eval_error_ctor.clone(), [ - ("toString", scope.statics.error_to_string.clone()), + (sym::toString, scope.statics.error_to_string.clone()), ], [], [], None, &mut scope, ); - + let range_error_ctor = register( scope.statics.range_error_ctor.clone(), function_proto.clone(), @@ -908,23 +912,23 @@ impl Vm { [], [], [], - Some(("RangeError", scope.statics.range_error_prototype.clone())), + Some((sym::RangeError, scope.statics.range_error_prototype.clone())), &mut scope, ); - + register( scope.statics.range_error_prototype.clone(), scope.statics.error_prototype.clone(), range_error_ctor.clone(), [ - ("toString", scope.statics.error_to_string.clone()), + (sym::toString, scope.statics.error_to_string.clone()), ], [], [], None, &mut scope, ); - + let reference_error_ctor = register( scope.statics.reference_error_ctor.clone(), function_proto.clone(), @@ -932,23 +936,23 @@ impl Vm { [], [], [], - Some(("ReferenceError", scope.statics.reference_error_prototype.clone())), + Some((sym::ReferenceError, scope.statics.reference_error_prototype.clone())), &mut scope, ); - + register( scope.statics.reference_error_prototype.clone(), scope.statics.error_prototype.clone(), reference_error_ctor.clone(), [ - ("toString", scope.statics.error_to_string.clone()), + (sym::toString, scope.statics.error_to_string.clone()), ], [], [], None, &mut scope, ); - + let syntax_error_ctor = register( scope.statics.syntax_error_ctor.clone(), function_proto.clone(), @@ -956,23 +960,23 @@ impl Vm { [], [], [], - Some(("SyntaxError", scope.statics.syntax_error_prototype.clone())), + Some((sym::SyntaxError, scope.statics.syntax_error_prototype.clone())), &mut scope, ); - + register( scope.statics.syntax_error_prototype.clone(), scope.statics.error_prototype.clone(), syntax_error_ctor.clone(), [ - ("toString", scope.statics.error_to_string.clone()), + (sym::toString, scope.statics.error_to_string.clone()), ], [], [], None, &mut scope, ); - + let type_error_ctor = register( scope.statics.type_error_ctor.clone(), function_proto.clone(), @@ -980,23 +984,23 @@ impl Vm { [], [], [], - Some(("TypeError", scope.statics.type_error_prototype.clone())), + Some((sym::TypeError, scope.statics.type_error_prototype.clone())), &mut scope, ); - + register( scope.statics.type_error_prototype.clone(), scope.statics.error_prototype.clone(), type_error_ctor.clone(), [ - ("toString", scope.statics.error_to_string.clone()), + (sym::toString, scope.statics.error_to_string.clone()), ], [], [], None, &mut scope, ); - + let uri_error_ctor = register( scope.statics.uri_error_ctor.clone(), function_proto.clone(), @@ -1004,23 +1008,23 @@ impl Vm { [], [], [], - Some(("URIError", scope.statics.uri_error_prototype.clone())), + Some((sym::URIError, scope.statics.uri_error_prototype.clone())), &mut scope, ); - + register( scope.statics.uri_error_prototype.clone(), scope.statics.error_prototype.clone(), uri_error_ctor.clone(), [ - ("toString", scope.statics.error_to_string.clone()), + (sym::toString, scope.statics.error_to_string.clone()), ], [], [], None, &mut scope, ); - + let aggregate_error_ctor = register( scope.statics.aggregate_error_ctor.clone(), function_proto.clone(), @@ -1028,36 +1032,36 @@ impl Vm { [], [], [], - Some(("AggregateError", scope.statics.aggregate_error_prototype.clone())), + Some((sym::AggregateError, scope.statics.aggregate_error_prototype.clone())), &mut scope, ); - + register( scope.statics.aggregate_error_prototype.clone(), scope.statics.error_prototype.clone(), aggregate_error_ctor.clone(), [ - ("toString", scope.statics.error_to_string.clone()), + (sym::toString, scope.statics.error_to_string.clone()), ], [], [], None, &mut scope, ); - + let date_ctor = register( scope.statics.date_ctor.clone(), function_proto.clone(), function_ctor.clone(), [ - ("now", scope.statics.date_now.clone()), + (sym::now, scope.statics.date_now.clone()), ], [], [], - Some(("Date", scope.statics.date_prototype.clone())), + Some((sym::Date, scope.statics.date_prototype.clone())), &mut scope, ); - + register( scope.statics.date_prototype.clone(), object_proto.clone(), @@ -1068,68 +1072,66 @@ impl Vm { None, &mut scope, ); - + let json_ctor = register( scope.statics.json_ctor.clone(), function_proto.clone(), function_ctor.clone(), [ - ("parse", scope.statics.json_parse.clone()), + (sym::parse, scope.statics.json_parse.clone()), ], [], [], None, &mut scope, ); - + register( global, object_proto, object_ctor.clone(), [ - ("isNaN", scope.statics.is_nan.clone()), - ("isFinite", scope.statics.is_finite.clone()), - ("parseFloat", scope.statics.parse_float.clone()), - ("parseInt", scope.statics.parse_int.clone()), - ("RegExp", regexp_ctor.clone()), - ("Symbol", symbol_ctor.clone()), - ("Date", date_ctor.clone()), - ("ArrayBuffer", arraybuffer_ctor.clone()), - ("Uint8Array", u8array_ctor.clone()), - ("Int8Array", i8array_ctor.clone()), - ("Uint16Array", u16array_ctor.clone()), - ("Int16Array", i16array_ctor.clone()), - ("Uint32Array", u32array_ctor.clone()), - ("Int32Array", i32array_ctor.clone()), - ("Float32Array", f32array_ctor.clone()), - ("Float64Array", f64array_ctor.clone()), - ("Array", array_ctor.clone()), - ("Error", error_ctor.clone()), - ("EvalError", eval_error_ctor.clone()), - ("RangeError", range_error_ctor.clone()), - ("ReferenceError", reference_error_ctor.clone()), - ("SyntaxError", syntax_error_ctor.clone()), - ("TypeError", type_error_ctor.clone()), - ("URIError", uri_error_ctor.clone()), - ("AggregateError", aggregate_error_ctor.clone()), - ("String", string_ctor.clone()), - ("Object", object_ctor.clone()), - ("Set", set_ctor.clone()), - ("Map", map_ctor.clone()), - ("console", console.clone()), - ("Math", math.clone()), - ("Number", number_ctor.clone()), - ("Boolean", boolean_ctor.clone()), - ("Promise", promise_ctor.clone()), - ("JSON", json_ctor.clone()), + (sym::isNaN, scope.statics.is_nan.clone()), + (sym::isFinite, scope.statics.is_finite.clone()), + (sym::parseFloat, scope.statics.parse_float.clone()), + (sym::parseInt, scope.statics.parse_int.clone()), + (sym::RegExp, regexp_ctor.clone()), + (sym::JsSymbol, symbol_ctor.clone()), + (sym::Date, date_ctor.clone()), + (sym::ArrayBuffer, arraybuffer_ctor.clone()), + (sym::Uint8Array, u8array_ctor.clone()), + (sym::Int8Array, i8array_ctor.clone()), + (sym::Uint16Array, u16array_ctor.clone()), + (sym::Int16Array, i16array_ctor.clone()), + (sym::Uint32Array, u32array_ctor.clone()), + (sym::Int32Array, i32array_ctor.clone()), + (sym::Float32Array, f32array_ctor.clone()), + (sym::Float64Array, f64array_ctor.clone()), + (sym::Array, array_ctor.clone()), + (sym::Error, error_ctor.clone()), + (sym::EvalError, eval_error_ctor.clone()), + (sym::RangeError, range_error_ctor.clone()), + (sym::ReferenceError, reference_error_ctor.clone()), + (sym::SyntaxError, syntax_error_ctor.clone()), + (sym::TypeError, type_error_ctor.clone()), + (sym::URIError, uri_error_ctor.clone()), + (sym::AggregateError, aggregate_error_ctor.clone()), + (sym::String, string_ctor.clone()), + (sym::Object, object_ctor.clone()), + (sym::Set, set_ctor.clone()), + (sym::Map, map_ctor.clone()), + (sym::console, console.clone()), + (sym::Math, math.clone()), + (sym::Number, number_ctor.clone()), + (sym::Boolean, boolean_ctor.clone()), + (sym::Promise, promise_ctor.clone()), + (sym::JSON, json_ctor.clone()), ], [], [], None, &mut scope ); - - scope.builtins_pure = true; } /// Fetches the current instruction/value in the currently executing frame @@ -1194,12 +1196,14 @@ impl Vm { self.frames.push(frame); } else { cold_path(); - throw!(self, RangeError, "Maximum call stack size exceeded"); + // This is a bit sus (we're creating a temporary scope for the error creation and returning it past its scope), + // but the error type is `Unrooted`, so it needs to be re-rooted at callsite anyway. + throw!(&mut self.scope(), RangeError, "Maximum call stack size exceeded"); } Ok(()) } - pub(crate) fn try_extend_stack(&mut self, other: I) -> Result<(), Value> + pub(crate) fn try_extend_stack(&mut self, other: I) -> Result<(), Unrooted> where I: IntoIterator, ::IntoIter: ExactSizeIterator, @@ -1208,7 +1212,9 @@ impl Vm { let len = it.len(); if self.stack.len() + len > MAX_STACK_SIZE { debug!("vm exceeded stack size"); - throw!(self, RangeError, "Maximum stack size exceeded"); + // This is a bit sus (we're creating a temporary scope for the error creation and returning it past its scope), + // but the error type is `Unrooted`, so it needs to be re-rooted at callsite anyway. + throw!(&mut self.scope(), RangeError, "Maximum stack size exceeded"); } self.stack.extend(it); Ok(()) @@ -1400,6 +1406,9 @@ impl Vm { sweep.in_scope(|| unsafe { self.gc.sweep() }); debug!("object count after sweep: {}", self.gc.node_count()); + debug!("sweep interner"); + self.interner.sweep(); + // Adjust GC threshold let new_object_count = self.gc.node_count(); self.gc_object_threshold = new_object_count * 2; @@ -1407,16 +1416,23 @@ impl Vm { } fn trace_roots(&mut self) { + let mut cx = TraceCtxt::new(&mut self.interner); + + debug!("trace preinterned symbols"); + for (_, sym) in sym::PREINTERNED { + sym.trace(&mut cx); + } + debug!("trace frames"); - self.frames.trace(); + self.frames.trace(&mut cx); debug!("trace async tasks"); - self.async_tasks.trace(); + self.async_tasks.trace(&mut cx); debug!("trace stack"); - self.stack.trace(); + self.stack.trace(&mut cx); debug!("trace globals"); - self.global.trace(); + self.global.trace(&mut cx); debug!("trace scopes"); - self.scopes.trace(); + self.scopes.trace(&mut cx); debug!("trace externals"); // we do two things here: @@ -1428,13 +1444,13 @@ impl Vm { false } else { // Non-zero refcount, retain object and trace - e.trace(); + e.trace(&mut cx); true } }); debug!("trace statics"); - self.statics.trace(); + self.statics.trace(&mut cx); } pub fn statics(&self) -> &Statics { diff --git a/crates/dash_vm/src/localscope.rs b/crates/dash_vm/src/localscope.rs index d0292b39..85230b7e 100755 --- a/crates/dash_vm/src/localscope.rs +++ b/crates/dash_vm/src/localscope.rs @@ -3,6 +3,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use crate::gc::handle::Handle; +use crate::gc::interner::Symbol; use crate::value::function::bound::BoundFunction; use crate::value::promise::{Promise, PromiseState}; use crate::value::ValueContext; @@ -14,7 +15,7 @@ use super::Vm; use std::ptr::NonNull; -use crate::gc::trace::Trace; +use crate::gc::trace::{Trace, TraceCtxt}; #[derive(Debug)] pub struct LocalScopeList { @@ -79,7 +80,7 @@ impl Drop for LocalScopeList { } unsafe impl Trace for LocalScopeList { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { let Self { list, head: _ } = self; // We need to use the list instead of head, @@ -89,7 +90,8 @@ unsafe impl Trace for LocalScopeList { // we would miss those scopes!). for ptr in list { let data = unsafe { ptr.as_ref() }; - data.refs.trace(); + data.refs.trace(cx); + data.strings.trace(cx); } } } @@ -116,6 +118,7 @@ impl Default for LocalScopeList { #[derive(Clone, Debug)] pub struct ScopeData { refs: Vec>, + strings: Vec, next: Option>, } @@ -123,6 +126,7 @@ impl ScopeData { pub fn new(next: Option>) -> NonNull { NonNull::new(Box::into_raw(Box::new(Self { refs: Vec::with_capacity(4), + strings: Vec::with_capacity(4), next, }))) .unwrap() @@ -154,12 +158,20 @@ impl<'vm> LocalScope<'vm> { self.add_ref(o.inner.clone()); self.add_ref(o.into_dyn()); } + Value::String(s) => { + self.scope_data_mut().strings.push(s.sym()); + } + Value::Symbol(s) => { + self.scope_data_mut().strings.push(s.sym()); + } _ => {} } } - pub fn add_many(&mut self, mut v: Vec>) { - self.scope_data_mut().refs.append(&mut v); + pub fn add_many(&mut self, v: &[Value]) { + for val in v { + self.add_value(val.clone()); + } } /// Registers an object and roots it. @@ -191,6 +203,30 @@ impl<'vm> LocalScope<'vm> { PromiseAction::Reject => PromiseState::Rejected(arg), }; } + + pub fn intern(&mut self, s: impl std::borrow::Borrow) -> Symbol { + let sym = self.interner.intern(s); + self.scope_data_mut().strings.push(sym); + sym + } + + pub fn intern_usize(&mut self, n: usize) -> Symbol { + let sym = self.interner.intern_usize(n); + self.scope_data_mut().strings.push(sym); + sym + } + + pub fn intern_isize(&mut self, n: isize) -> Symbol { + let sym = self.interner.intern_isize(n); + self.scope_data_mut().strings.push(sym); + sym + } + + pub fn intern_char(&mut self, v: char) -> Symbol { + let sym = self.interner.intern_char(v); + self.scope_data_mut().strings.push(sym); + sym + } } // TODO: remove this Deref impl @@ -221,8 +257,7 @@ impl<'vm> Drop for LocalScope<'vm> { #[cfg(test)] mod tests { - use std::rc::Rc; - + use crate::value::string::JsString; use crate::Vm; #[test] @@ -230,7 +265,8 @@ mod tests { let mut vm = Vm::new(Default::default()); let mut scope = vm.scope(); for _ in 0..20 { - scope.register(Rc::from("test") as Rc); + let val = scope.intern("test"); + scope.register(JsString::from(val)); } } @@ -243,12 +279,15 @@ mod tests { let mut scope3 = scope2.scope(); let mut scope4 = scope3.scope(); let mut scope5 = scope4.scope(); - scope5.register(Rc::from("bar") as Rc); + let k = scope5.intern("bar"); + scope5.register(JsString::from(k)); let mut scope6 = scope5.scope(); let mut scope7 = scope6.scope(); let mut scope8 = scope7.scope(); - scope8.register(Rc::from("foo") as Rc); + let k = scope8.intern("foo"); + scope8.register(JsString::from(k)); let mut scope9 = scope8.scope(); - scope9.register(Rc::from("test") as Rc); + let k = scope9.intern("test"); + scope9.register(JsString::from(k)); } } diff --git a/crates/dash_vm/src/macros/value.rs b/crates/dash_vm/src/macros/value.rs index 615b117a..c50290d8 100644 --- a/crates/dash_vm/src/macros/value.rs +++ b/crates/dash_vm/src/macros/value.rs @@ -5,7 +5,7 @@ macro_rules! throw { // for some reason it warns about unused mut when it really is required, remove when fixed. (rust#105149) #[allow(unused_mut)] let mut vm = $vm; - let err = $crate::value::error::$err::new(&vm, $msg); + let err = $crate::value::error::$err::new(&mut vm as &mut $crate::localscope::LocalScope<'_>, $msg); Value::Object(vm.gc_mut().register(err)).into() }) }; @@ -13,8 +13,8 @@ macro_rules! throw { return Err({ // for some reason it warns about unused mut when it really is required, remove when fixed. (rust#105149) #[allow(unused_mut)] - let mut vm = $vm; - let err = $crate::value::error::$err::new(&vm, format!($msg, $($arg),*)); + let mut vm: &mut $crate::localscope::LocalScope<'_> = $vm; + let err = $crate::value::error::$err::new(vm, format!($msg, $($arg),*)); Value::Object(vm.gc_mut().register(err)).into() }) }; diff --git a/crates/dash_vm/src/params.rs b/crates/dash_vm/src/params.rs index 740da97b..88106f12 100644 --- a/crates/dash_vm/src/params.rs +++ b/crates/dash_vm/src/params.rs @@ -3,13 +3,14 @@ use std::any::Any; use dash_middle::compiler::StaticImportKind; use crate::localscope::LocalScope; +use crate::value::string::JsString; use super::value::Value; use super::Vm; pub type MathRandomCallback = fn(vm: &mut Vm) -> Result; pub type TimeMillisCallback = fn(vm: &mut Vm) -> Result; -pub type StaticImportCallback = fn(vm: &mut Vm, ty: StaticImportKind, path: &str) -> Result; +pub type StaticImportCallback = fn(vm: &mut Vm, ty: StaticImportKind, path: JsString) -> Result; pub type DynamicImportCallback = fn(vm: &mut Vm, val: Value) -> Result; pub type DebuggerCallback = fn(vm: &mut Vm) -> Result<(), Value>; pub type UnhandledTaskException = fn(vm: &mut LocalScope, exception: Value); diff --git a/crates/dash_vm/src/statics.rs b/crates/dash_vm/src/statics.rs index 7857d08e..038f24ee 100644 --- a/crates/dash_vm/src/statics.rs +++ b/crates/dash_vm/src/statics.rs @@ -1,6 +1,7 @@ use dash_proc_macro::Trace; use crate::gc::handle::Handle; +use crate::gc::interner::{self, sym}; use crate::gc::Gc; use crate::js_std; use crate::value::error::{AggregateError, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError}; @@ -21,20 +22,8 @@ use super::value::function::native::NativeFunction; use super::value::object::{NamedObject, Object}; use super::value::primitive::Symbol; -use std::rc::Rc; - #[derive(Trace)] pub struct Statics { - pub empty_str: Rc, - pub undefined_str: Rc, - pub null_str: Rc, - // Boolean strings - pub true_lit: Rc, - pub false_lit: Rc, - // PreferredType strings - pub number_str: Rc, - pub string_str: Rc, - pub default_str: Rc, pub function_proto: Handle, pub function_ctor: Handle, pub function_bind: Handle, @@ -263,273 +252,246 @@ fn empty_object(gc: &mut Gc) -> Handle { builtin_object(gc, NamedObject::null()) } -fn function(gc: &mut Gc, name: &str, cb: NativeFunction) -> Handle { +fn function(gc: &mut Gc, name: interner::Symbol, cb: NativeFunction) -> Handle { let f = Function::with_obj(Some(name.into()), FunctionKind::Native(cb), NamedObject::null()); gc.register(PureBuiltin::new(f)) } impl Statics { pub fn new(gc: &mut Gc) -> Self { - let empty_str: Rc = "".into(); - Self { - true_lit: "true".into(), - false_lit: "false".into(), - empty_str: empty_str.clone(), - null_str: "null".into(), - undefined_str: "undefined".into(), - default_str: "default".into(), - number_str: "number".into(), - string_str: "string".into(), function_proto: empty_object(gc), - function_ctor: function(gc, "Function", js_std::function::constructor), - function_bind: function(gc, "bind", js_std::function::bind), - function_call: function(gc, "call", js_std::function::call), - function_to_string: function(gc, "toString", js_std::function::to_string), + function_ctor: function(gc, sym::Function, js_std::function::constructor), + function_bind: function(gc, sym::bind, js_std::function::bind), + function_call: function(gc, sym::call, js_std::function::call), + function_to_string: function(gc, sym::toString, js_std::function::to_string), console: empty_object(gc), - console_log: function(gc, "log", js_std::global::log), + console_log: function(gc, sym::log, js_std::global::log), math: empty_object(gc), - math_floor: function(gc, "floor", js_std::math::floor), - object_ctor: function(gc, "Object", js_std::object::constructor), - object_create: function(gc, "create", js_std::object::create), - object_keys: function(gc, "keys", js_std::object::keys), + math_floor: function(gc, sym::floor, js_std::math::floor), + object_ctor: function(gc, sym::object, js_std::object::constructor), + object_create: function(gc, sym::create, js_std::object::create), + object_keys: function(gc, sym::keys, js_std::object::keys), object_prototype: empty_object(gc), - object_to_string: function(gc, "toString", js_std::object::to_string), + object_to_string: function(gc, sym::toString, js_std::object::to_string), object_get_own_property_descriptor: function( gc, - "getOwnPropertyDescriptor", + sym::getOwnPropertyDescriptor, js_std::object::get_own_property_descriptor, ), object_get_own_property_descriptors: function( gc, - "getOwnPropertyDescriptors", + sym::getOwnPropertyDescriptors, js_std::object::get_own_property_descriptors, ), - object_has_own_property: function(gc, "hasOwnProperty", js_std::object::has_own_property), - object_define_property: function(gc, "defineProperty", js_std::object::define_property), - object_assign: function(gc, "assign", js_std::object::assign), - object_entries: function(gc, "entries", js_std::object::entries), - number_ctor: function(gc, "Number", js_std::number::constructor), + object_has_own_property: function(gc, sym::hasOwnProperty, js_std::object::has_own_property), + object_define_property: function(gc, sym::defineProperty, js_std::object::define_property), + object_assign: function(gc, sym::assign, js_std::object::assign), + object_entries: function(gc, sym::entries, js_std::object::entries), + number_ctor: function(gc, sym::Number, js_std::number::constructor), number_prototype: builtin_object(gc, BoxedNumber::with_obj(0.0, NamedObject::null())), - number_tostring: function(gc, "toString", js_std::number::to_string), - boolean_ctor: function(gc, "Boolean", js_std::boolean::constructor), - boolean_tostring: function(gc, "toString", js_std::boolean::to_string), + number_tostring: function(gc, sym::toString, js_std::number::to_string), + boolean_ctor: function(gc, sym::Boolean, js_std::boolean::constructor), + boolean_tostring: function(gc, sym::toString, js_std::boolean::to_string), boolean_prototype: builtin_object(gc, BoxedBoolean::with_obj(false, NamedObject::null())), - string_ctor: function(gc, "Boolean", js_std::string::constructor), - string_prototype: builtin_object(gc, BoxedString::with_obj(empty_str.clone(), NamedObject::null())), - is_nan: function(gc, "isNaN", js_std::global::is_nan), - is_finite: function(gc, "isFinite", js_std::global::is_finite), - parse_float: function(gc, "parseFloat", js_std::global::parse_float), - parse_int: function(gc, "parseInt", js_std::global::parse_int), - math_abs: function(gc, "abs", js_std::math::abs), - math_acos: function(gc, "acos", js_std::math::acos), - math_acosh: function(gc, "acosh", js_std::math::acosh), - math_asin: function(gc, "asin", js_std::math::asin), - math_asinh: function(gc, "asinh", js_std::math::asinh), - math_atan: function(gc, "atan", js_std::math::atan), - math_atanh: function(gc, "atanh", js_std::math::atanh), - math_atan2: function(gc, "atan2", js_std::math::atan2), - math_cbrt: function(gc, "cbrt", js_std::math::cbrt), - math_ceil: function(gc, "ceil", js_std::math::ceil), - math_clz32: function(gc, "clz32", js_std::math::clz32), - math_cos: function(gc, "cos", js_std::math::cos), - math_cosh: function(gc, "cosh", js_std::math::cosh), - math_exp: function(gc, "exp", js_std::math::exp), - math_expm1: function(gc, "expm1", js_std::math::expm1), - math_log: function(gc, "log", js_std::math::log), - math_log1p: function(gc, "log1p", js_std::math::log1p), - math_log10: function(gc, "log10", js_std::math::log10), - math_log2: function(gc, "log2", js_std::math::log2), - math_round: function(gc, "round", js_std::math::round), - math_sin: function(gc, "sin", js_std::math::sin), - math_sinh: function(gc, "sinh", js_std::math::sinh), - math_sqrt: function(gc, "sqrt", js_std::math::sqrt), - math_tan: function(gc, "tan", js_std::math::tan), - math_tanh: function(gc, "tanh", js_std::math::tanh), - math_trunc: function(gc, "trunc", js_std::math::trunc), - math_random: function(gc, "random", js_std::math::random), - math_max: function(gc, "max", js_std::math::max), - math_min: function(gc, "min", js_std::math::min), - number_is_finite: function(gc, "isFinite", js_std::number::is_finite), - number_is_nan: function(gc, "isNaN", js_std::number::is_nan), - number_is_safe_integer: function(gc, "isSafeInteger", js_std::number::is_safe_integer), - number_to_fixed: function(gc, "toFixed", js_std::number::to_fixed), - boolean_valueof: function(gc, "valueOf", js_std::boolean::value_of), - string_tostring: function(gc, "toString", js_std::string::to_string), - string_char_at: function(gc, "charAt", js_std::string::char_at), - string_char_code_at: function(gc, "charCodeAt", js_std::string::char_code_at), - string_concat: function(gc, "concat", js_std::string::concat), - string_ends_with: function(gc, "endsWith", js_std::string::ends_with), - string_starts_with: function(gc, "startsWith", js_std::string::starts_with), - string_includes: function(gc, "includes", js_std::string::includes), - string_index_of: function(gc, "indexOf", js_std::string::index_of), - string_last_index_of: function(gc, "lastIndexOf", js_std::string::last_index_of), - string_pad_end: function(gc, "padEnd", js_std::string::pad_end), - string_pad_start: function(gc, "padStart", js_std::string::pad_start), - string_repeat: function(gc, "repeat", js_std::string::repeat), - string_replace: function(gc, "replace", js_std::string::replace), - string_replace_all: function(gc, "replaceAll", js_std::string::replace_all), - string_split: function(gc, "split", js_std::string::split), - string_to_uppercase: function(gc, "toUpperCase", js_std::string::to_uppercase), - string_to_lowercase: function(gc, "toLowerCase", js_std::string::to_lowercase), - string_big: function(gc, "big", js_std::string::big), - string_blink: function(gc, "blink", js_std::string::blink), - string_bold: function(gc, "bold", js_std::string::bold), - string_fixed: function(gc, "fixed", js_std::string::fixed), - string_italics: function(gc, "italics", js_std::string::italics), - string_strike: function(gc, "strike", js_std::string::strike), - string_sub: function(gc, "sub", js_std::string::sub), - string_sup: function(gc, "sup", js_std::string::sup), - string_fontcolor: function(gc, "fontcolor", js_std::string::fontcolor), - string_fontsize: function(gc, "fontsize", js_std::string::fontsize), - string_link: function(gc, "link", js_std::string::link), - string_trim: function(gc, "trim", js_std::string::trim), - string_trim_start: function(gc, "trimStart", js_std::string::trim_start), - string_trim_end: function(gc, "trimEnd", js_std::string::trim_end), - string_from_char_code: function(gc, "fromCharCode", js_std::string::from_char_code), - string_substr: function(gc, "substr", js_std::string::substr), - string_substring: function(gc, "substring", js_std::string::substring), - string_iterator: function(gc, "iterator", js_std::string::iterator), - array_ctor: function(gc, "Array", js_std::array::constructor), - array_tostring: function(gc, "toString", js_std::array::to_string), + string_ctor: function(gc, sym::String, js_std::string::constructor), + string_prototype: builtin_object(gc, BoxedString::with_obj(sym::empty.into(), NamedObject::null())), + is_nan: function(gc, sym::isNaN, js_std::global::is_nan), + is_finite: function(gc, sym::isFinite, js_std::global::is_finite), + parse_float: function(gc, sym::parseFloat, js_std::global::parse_float), + parse_int: function(gc, sym::parseInt, js_std::global::parse_int), + math_abs: function(gc, sym::abs, js_std::math::abs), + math_acos: function(gc, sym::acos, js_std::math::acos), + math_acosh: function(gc, sym::acosh, js_std::math::acosh), + math_asin: function(gc, sym::asin, js_std::math::asin), + math_asinh: function(gc, sym::asinh, js_std::math::asinh), + math_atan: function(gc, sym::atan, js_std::math::atan), + math_atanh: function(gc, sym::atanh, js_std::math::atanh), + math_atan2: function(gc, sym::atan2, js_std::math::atan2), + math_cbrt: function(gc, sym::cbrt, js_std::math::cbrt), + math_ceil: function(gc, sym::ceil, js_std::math::ceil), + math_clz32: function(gc, sym::clz32, js_std::math::clz32), + math_cos: function(gc, sym::cos, js_std::math::cos), + math_cosh: function(gc, sym::cosh, js_std::math::cosh), + math_exp: function(gc, sym::exp, js_std::math::exp), + math_expm1: function(gc, sym::expm1, js_std::math::expm1), + math_log: function(gc, sym::log, js_std::math::log), + math_log1p: function(gc, sym::log1p, js_std::math::log1p), + math_log10: function(gc, sym::log10, js_std::math::log10), + math_log2: function(gc, sym::log2, js_std::math::log2), + math_round: function(gc, sym::round, js_std::math::round), + math_sin: function(gc, sym::sin, js_std::math::sin), + math_sinh: function(gc, sym::sinh, js_std::math::sinh), + math_sqrt: function(gc, sym::sqrt, js_std::math::sqrt), + math_tan: function(gc, sym::tan, js_std::math::tan), + math_tanh: function(gc, sym::tanh, js_std::math::tanh), + math_trunc: function(gc, sym::trunc, js_std::math::trunc), + math_random: function(gc, sym::random, js_std::math::random), + math_max: function(gc, sym::max, js_std::math::max), + math_min: function(gc, sym::min, js_std::math::min), + number_is_finite: function(gc, sym::isFinite, js_std::number::is_finite), + number_is_nan: function(gc, sym::isNaN, js_std::number::is_nan), + number_is_safe_integer: function(gc, sym::isSafeInteger, js_std::number::is_safe_integer), + number_to_fixed: function(gc, sym::toFixed, js_std::number::to_fixed), + boolean_valueof: function(gc, sym::valueOf, js_std::boolean::value_of), + string_tostring: function(gc, sym::toString, js_std::string::to_string), + string_char_at: function(gc, sym::charAt, js_std::string::char_at), + string_char_code_at: function(gc, sym::charCodeAt, js_std::string::char_code_at), + string_concat: function(gc, sym::concat, js_std::string::concat), + string_ends_with: function(gc, sym::endsWith, js_std::string::ends_with), + string_starts_with: function(gc, sym::startsWith, js_std::string::starts_with), + string_includes: function(gc, sym::includes, js_std::string::includes), + string_index_of: function(gc, sym::indexOf, js_std::string::index_of), + string_last_index_of: function(gc, sym::lastIndexOf, js_std::string::last_index_of), + string_pad_end: function(gc, sym::padEnd, js_std::string::pad_end), + string_pad_start: function(gc, sym::padStart, js_std::string::pad_start), + string_repeat: function(gc, sym::repeat, js_std::string::repeat), + string_replace: function(gc, sym::replace, js_std::string::replace), + string_replace_all: function(gc, sym::replaceAll, js_std::string::replace_all), + string_split: function(gc, sym::split, js_std::string::split), + string_to_uppercase: function(gc, sym::toUpperCase, js_std::string::to_uppercase), + string_to_lowercase: function(gc, sym::toLowerCase, js_std::string::to_lowercase), + string_big: function(gc, sym::big, js_std::string::big), + string_blink: function(gc, sym::blink, js_std::string::blink), + string_bold: function(gc, sym::bold, js_std::string::bold), + string_fixed: function(gc, sym::fixed, js_std::string::fixed), + string_italics: function(gc, sym::italics, js_std::string::italics), + string_strike: function(gc, sym::strike, js_std::string::strike), + string_sub: function(gc, sym::sub, js_std::string::sub), + string_sup: function(gc, sym::sup, js_std::string::sup), + string_fontcolor: function(gc, sym::fontcolor, js_std::string::fontcolor), + string_fontsize: function(gc, sym::fontsize, js_std::string::fontsize), + string_link: function(gc, sym::link, js_std::string::link), + string_trim: function(gc, sym::trim, js_std::string::trim), + string_trim_start: function(gc, sym::trimStart, js_std::string::trim_start), + string_trim_end: function(gc, sym::trimEnd, js_std::string::trim_end), + string_from_char_code: function(gc, sym::fromCharCode, js_std::string::from_char_code), + string_substr: function(gc, sym::substr, js_std::string::substr), + string_substring: function(gc, sym::substring, js_std::string::substring), + string_iterator: function(gc, sym::iterator, js_std::string::iterator), + array_ctor: function(gc, sym::Array, js_std::array::constructor), + array_tostring: function(gc, sym::toString, js_std::array::to_string), array_prototype: builtin_object(gc, Array::with_obj(NamedObject::null())), - array_join: function(gc, "join", js_std::array::join), - array_values: function(gc, "values", js_std::array::values), - array_reverse: function(gc, "reverse", js_std::array::reverse), - symbol_ctor: function(gc, "Symbol", js_std::symbol::constructor), - symbol_prototype: builtin_object(gc, BoxedSymbol::with_obj(Symbol::new(empty_str), NamedObject::null())), - symbol_async_iterator: Symbol::new("Symbol.asyncIterator".into()), - symbol_has_instance: Symbol::new("Symbol.hasInstance".into()), - symbol_is_concat_spreadable: Symbol::new("Symbol.isConcatSpreadable".into()), - symbol_iterator: Symbol::new("Symbol.iterator".into()), - symbol_match: Symbol::new("Symbol.match".into()), - symbol_match_all: Symbol::new("Symbol.matchAll".into()), - symbol_replace: Symbol::new("Symbol.replace".into()), - symbol_search: Symbol::new("Symbol.search".into()), - symbol_species: Symbol::new("Symbol.species".into()), - symbol_split: Symbol::new("Symbol.split".into()), - symbol_to_primitive: Symbol::new("SymboltoPrimitive".into()), - symbol_to_string_tag: Symbol::new("Symbol.toStringTag".into()), - symbol_unscopables: Symbol::new("Symbol.unscopables".into()), + array_join: function(gc, sym::join, js_std::array::join), + array_values: function(gc, sym::values, js_std::array::values), + array_reverse: function(gc, sym::reverse, js_std::array::reverse), + symbol_ctor: function(gc, sym::JsSymbol, js_std::symbol::constructor), + symbol_prototype: builtin_object( + gc, + BoxedSymbol::with_obj(Symbol::new(sym::empty.into()), NamedObject::null()), + ), + symbol_async_iterator: Symbol::new(sym::asyncIterator.into()), + symbol_has_instance: Symbol::new(sym::hasInstance.into()), + symbol_is_concat_spreadable: Symbol::new(sym::isConcatSpreadable.into()), + symbol_iterator: Symbol::new(sym::iterator.into()), + symbol_match: Symbol::new(sym::match_.into()), + symbol_match_all: Symbol::new(sym::matchAll.into()), + symbol_replace: Symbol::new(sym::replace.into()), + symbol_search: Symbol::new(sym::search.into()), + symbol_species: Symbol::new(sym::species.into()), + symbol_split: Symbol::new(sym::split.into()), + symbol_to_primitive: Symbol::new(sym::toPrimitive.into()), + symbol_to_string_tag: Symbol::new(sym::toStringTag.into()), + symbol_unscopables: Symbol::new(sym::unscopables.into()), array_iterator_prototype: builtin_object(gc, ArrayIterator::empty()), - array_iterator_next: function(gc, "next", js_std::array_iterator::next), - identity_this: function(gc, "iterator", js_std::identity_this), - array_at: function(gc, "at", js_std::array::at), - array_concat: function(gc, "concat", js_std::array::concat), - array_entries: function(gc, "entries", js_std::array::entries), - array_keys: function(gc, "keys", js_std::array::keys), - array_every: function(gc, "every", js_std::array::every), - array_some: function(gc, "some", js_std::array::some), - array_fill: function(gc, "fill", js_std::array::fill), - array_filter: function(gc, "filter", js_std::array::filter), - array_reduce: function(gc, "reduce", js_std::array::reduce), - array_find: function(gc, "find", js_std::array::find), - array_find_index: function(gc, "findIndex", js_std::array::find_index), - array_flat: function(gc, "flat", js_std::array::flat), - array_for_each: function(gc, "forEach", js_std::array::for_each), - array_includes: function(gc, "includes", js_std::array::includes), - array_index_of: function(gc, "indexOf", js_std::array::index_of), - array_map: function(gc, "map", js_std::array::map), - array_pop: function(gc, "pop", js_std::array::pop), - array_push: function(gc, "push", js_std::array::push), - array_shift: function(gc, "shift", js_std::array::shift), - array_sort: function(gc, "sort", js_std::array::sort), - array_unshift: function(gc, "unshift", js_std::array::unshift), - array_slice: function(gc, "slice", js_std::array::slice), - array_last_index_of: function(gc, "lastIndexOf", js_std::array::last_index_of), - array_from: function(gc, "from", js_std::array::from), - array_is_array: function(gc, "isArray", js_std::array::is_array), + array_iterator_next: function(gc, sym::next, js_std::array_iterator::next), + identity_this: function(gc, sym::iterator, js_std::identity_this), + array_at: function(gc, sym::at, js_std::array::at), + array_concat: function(gc, sym::concat, js_std::array::concat), + array_entries: function(gc, sym::entries, js_std::array::entries), + array_keys: function(gc, sym::keys, js_std::array::keys), + array_every: function(gc, sym::every, js_std::array::every), + array_some: function(gc, sym::some, js_std::array::some), + array_fill: function(gc, sym::fill, js_std::array::fill), + array_filter: function(gc, sym::filter, js_std::array::filter), + array_reduce: function(gc, sym::reduce, js_std::array::reduce), + array_find: function(gc, sym::find, js_std::array::find), + array_find_index: function(gc, sym::findIndex, js_std::array::find_index), + array_flat: function(gc, sym::flat, js_std::array::flat), + array_for_each: function(gc, sym::forEach, js_std::array::for_each), + array_includes: function(gc, sym::includes, js_std::array::includes), + array_index_of: function(gc, sym::indexOf, js_std::array::index_of), + array_map: function(gc, sym::map, js_std::array::map), + array_pop: function(gc, sym::pop, js_std::array::pop), + array_push: function(gc, sym::push, js_std::array::push), + array_shift: function(gc, sym::shift, js_std::array::shift), + array_sort: function(gc, sym::sort, js_std::array::sort), + array_unshift: function(gc, sym::unshift, js_std::array::unshift), + array_slice: function(gc, sym::slice, js_std::array::slice), + array_last_index_of: function(gc, sym::lastIndexOf, js_std::array::last_index_of), + array_from: function(gc, sym::from, js_std::array::from), + array_is_array: function(gc, sym::isArray, js_std::array::is_array), generator_iterator_prototype: { let obj = gc.register(NamedObject::null()); builtin_object(gc, GeneratorIterator::empty(obj)) }, - generator_iterator_next: function(gc, "next", js_std::generator::next), - error_ctor: function(gc, "Error", js_std::error::error_constructor), + generator_iterator_next: function(gc, sym::next, js_std::generator::next), + error_ctor: function(gc, sym::Error, js_std::error::error_constructor), error_prototype: builtin_object(gc, Error::empty()), - error_to_string: function(gc, "toString", js_std::error::to_string), - eval_error_ctor: function(gc, "EvalError", js_std::error::eval_error_constructor), + error_to_string: function(gc, sym::toString, js_std::error::to_string), + eval_error_ctor: function(gc, sym::EvalError, js_std::error::eval_error_constructor), eval_error_prototype: builtin_object(gc, EvalError::empty()), - range_error_ctor: function(gc, "RangeError", js_std::error::range_error_constructor), + range_error_ctor: function(gc, sym::RangeError, js_std::error::range_error_constructor), range_error_prototype: builtin_object(gc, RangeError::empty()), - reference_error_ctor: function(gc, "ReferenceError", js_std::error::reference_error_constructor), + reference_error_ctor: function(gc, sym::ReferenceError, js_std::error::reference_error_constructor), reference_error_prototype: builtin_object(gc, ReferenceError::empty()), - syntax_error_ctor: function(gc, "SyntaxError", js_std::error::syntax_error_constructor), + syntax_error_ctor: function(gc, sym::SyntaxError, js_std::error::syntax_error_constructor), syntax_error_prototype: builtin_object(gc, SyntaxError::empty()), - type_error_ctor: function(gc, "TypeError", js_std::error::type_error_constructor), + type_error_ctor: function(gc, sym::TypeError, js_std::error::type_error_constructor), type_error_prototype: builtin_object(gc, TypeError::empty()), - uri_error_ctor: function(gc, "URIError", js_std::error::uri_error_constructor), + uri_error_ctor: function(gc, sym::URIError, js_std::error::uri_error_constructor), uri_error_prototype: builtin_object(gc, URIError::empty()), - aggregate_error_ctor: function(gc, "AggregateError", js_std::error::aggregate_error_constructor), + aggregate_error_ctor: function(gc, sym::AggregateError, js_std::error::aggregate_error_constructor), aggregate_error_prototype: builtin_object(gc, AggregateError::empty()), - arraybuffer_ctor: function(gc, "ArrayBuffer", js_std::arraybuffer::constructor), + arraybuffer_ctor: function(gc, sym::ArrayBuffer, js_std::arraybuffer::constructor), arraybuffer_prototype: builtin_object(gc, ArrayBuffer::empty()), - arraybuffer_byte_length: function(gc, "byteLength", js_std::arraybuffer::byte_length), - uint8array_ctor: function(gc, "Uint8Array", js_std::typedarray::u8array::constructor), + arraybuffer_byte_length: function(gc, sym::byteLength, js_std::arraybuffer::byte_length), + uint8array_ctor: function(gc, sym::Uint8Array, js_std::typedarray::u8array::constructor), uint8array_prototype: empty_object(gc), - int8array_ctor: function(gc, "Int8Array", js_std::typedarray::i8array::constructor), + int8array_ctor: function(gc, sym::Int8Array, js_std::typedarray::i8array::constructor), int8array_prototype: empty_object(gc), - uint16array_ctor: function(gc, "Uint16Array", js_std::typedarray::u16array::constructor), + uint16array_ctor: function(gc, sym::Uint16Array, js_std::typedarray::u16array::constructor), uint16array_prototype: empty_object(gc), - int16array_ctor: function(gc, "Int16Array", js_std::typedarray::i16array::constructor), + int16array_ctor: function(gc, sym::Int16Array, js_std::typedarray::i16array::constructor), int16array_prototype: empty_object(gc), - uint32array_ctor: function(gc, "Uint32Array", js_std::typedarray::u32array::constructor), + uint32array_ctor: function(gc, sym::Uint32Array, js_std::typedarray::u32array::constructor), uint32array_prototype: empty_object(gc), - int32array_ctor: function(gc, "Int32Array", js_std::typedarray::i32array::constructor), + int32array_ctor: function(gc, sym::Int32Array, js_std::typedarray::i32array::constructor), int32array_prototype: empty_object(gc), - float32array_ctor: function(gc, "Float32Array", js_std::typedarray::f32array::constructor), + float32array_ctor: function(gc, sym::Float32Array, js_std::typedarray::f32array::constructor), float32array_prototype: empty_object(gc), - float64array_ctor: function(gc, "Float64Array", js_std::typedarray::f64array::constructor), + float64array_ctor: function(gc, sym::Float64Array, js_std::typedarray::f64array::constructor), float64array_prototype: empty_object(gc), - typedarray_fill: function(gc, "fill", js_std::typedarray::fill), - promise_ctor: function(gc, "Promise", js_std::promise::constructor), + typedarray_fill: function(gc, sym::fill, js_std::typedarray::fill), + promise_ctor: function(gc, sym::Promise, js_std::promise::constructor), promise_proto: empty_object(gc), - promise_resolve: function(gc, "resolve", js_std::promise::resolve), - promise_reject: function(gc, "reject", js_std::promise::reject), - promise_then: function(gc, "then", js_std::promise::then), - set_constructor: function(gc, "Set", js_std::set::constructor), - set_add: function(gc, "add", js_std::set::add), - set_has: function(gc, "has", js_std::set::has), - set_delete: function(gc, "delete", js_std::set::delete), + promise_resolve: function(gc, sym::resolve, js_std::promise::resolve), + promise_reject: function(gc, sym::reject, js_std::promise::reject), + promise_then: function(gc, sym::then, js_std::promise::then), + set_constructor: function(gc, sym::Set, js_std::set::constructor), + set_add: function(gc, sym::add, js_std::set::add), + set_has: function(gc, sym::has, js_std::set::has), + set_delete: function(gc, sym::delete, js_std::set::delete), set_prototype: builtin_object(gc, Set::with_obj(NamedObject::null())), - set_clear: function(gc, "clear", js_std::set::clear), - set_size: function(gc, "size", js_std::set::size), - map_constructor: function(gc, "Map", js_std::map::constructor), - map_set: function(gc, "set", js_std::map::set), - map_get: function(gc, "get", js_std::map::get), - map_has: function(gc, "has", js_std::map::has), - map_delete: function(gc, "delete", js_std::map::delete), + set_clear: function(gc, sym::clear, js_std::set::clear), + set_size: function(gc, sym::size, js_std::set::size), + map_constructor: function(gc, sym::Map, js_std::map::constructor), + map_set: function(gc, sym::set, js_std::map::set), + map_get: function(gc, sym::get, js_std::map::get), + map_has: function(gc, sym::has, js_std::map::has), + map_delete: function(gc, sym::delete, js_std::map::delete), map_prototype: builtin_object(gc, Map::with_obj(NamedObject::null())), - map_clear: function(gc, "clear", js_std::map::clear), - map_size: function(gc, "size", js_std::map::size), - regexp_ctor: function(gc, "RegExp", js_std::regex::constructor), + map_clear: function(gc, sym::clear, js_std::map::clear), + map_size: function(gc, sym::size, js_std::map::size), + regexp_ctor: function(gc, sym::RegExp, js_std::regex::constructor), regexp_prototype: builtin_object(gc, RegExp::empty()), - regexp_test: function(gc, "test", js_std::regex::test), - regexp_exec: function(gc, "exec", js_std::regex::exec), - date_ctor: function(gc, "Date", js_std::date::constructor), + regexp_test: function(gc, sym::test, js_std::regex::test), + regexp_exec: function(gc, sym::exec, js_std::regex::exec), + date_ctor: function(gc, sym::Date, js_std::date::constructor), date_prototype: builtin_object(gc, NamedObject::null()), - date_now: function(gc, "now", js_std::date::now), - json_ctor: function(gc, "JSON", js_std::json::constructor), - json_parse: function(gc, "parse", js_std::json::parse), + date_now: function(gc, sym::now, js_std::date::now), + json_ctor: function(gc, sym::JSON, js_std::json::constructor), + json_parse: function(gc, sym::parse, js_std::json::parse), } } - - pub fn get_true(&self) -> Rc { - self.true_lit.clone() - } - - pub fn get_false(&self) -> Rc { - self.false_lit.clone() - } - - pub fn empty_str(&self) -> Rc { - self.empty_str.clone() - } - - pub fn null_str(&self) -> Rc { - self.null_str.clone() - } - - pub fn undefined_str(&self) -> Rc { - self.undefined_str.clone() - } } diff --git a/crates/dash_vm/src/test/mod.rs b/crates/dash_vm/src/test/mod.rs index 7097ea0f..f9c38ff3 100644 --- a/crates/dash_vm/src/test/mod.rs +++ b/crates/dash_vm/src/test/mod.rs @@ -1,6 +1,7 @@ use std::ptr; use std::rc::Rc; +use dash_middle::interner::sym; use dash_optimizer::OptLevel; use crate::gc::persistent::Persistent; @@ -52,7 +53,10 @@ fn simple() { .unwrap() .root(&mut scope); scope.perform_gc(); - let value = array.get_property(&mut scope, "0".into()).unwrap().root(&mut scope); + let value = array + .get_property(&mut scope, sym::zero.into()) + .unwrap() + .root(&mut scope); assert_eq!(scope.stack.len(), 0); assert_eq!(scope.frames.len(), 0); assert_eq!(value, Value::number(6.0)); @@ -67,12 +71,13 @@ fn persistent_trace() { let mut vm = Vm::new(Default::default()); let object = { let mut scope = vm.scope(); - let dummy_string = scope.register(Rc::::from("hi")); + let dummy_string = scope.register(NamedObject::null()); let object = NamedObject::new(&scope); + let key = scope.intern("foo"); object .set_property( &mut scope, - "foo".into(), + key.into(), PropertyValue::static_default(Value::Object(dummy_string)), ) .unwrap(); @@ -99,8 +104,9 @@ fn persistent_trace() { // Check that p1 and object are still alive after GC. let mut scope = vm.scope(); - let p = p1.get_property(&mut scope, "foo".into()).unwrap().root(&mut scope); - assert_eq!(p.downcast_ref::>().unwrap().as_ref(), "hi"); + let key = scope.intern("foo"); + let p = p1.get_property(&mut scope, key.into()).unwrap().root(&mut scope); + assert!(p.downcast_ref::().is_some()); } macro_rules! simple_test { diff --git a/crates/dash_vm/src/util.rs b/crates/dash_vm/src/util.rs index 8e2307f5..8b6e7f94 100644 --- a/crates/dash_vm/src/util.rs +++ b/crates/dash_vm/src/util.rs @@ -12,6 +12,13 @@ pub fn unlikely(b: bool) -> bool { b } +/// https://doc.rust-lang.org/beta/nightly-rustc/rustc_data_structures/captures/trait.Captures.html +/// and +/// https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999 +pub trait Captures<'a> {} + +impl<'a, T: ?Sized> Captures<'a> for T {} + pub fn format_f64(n: f64) -> String { // TODO: specialize zero, infinity, NaN by "interning" them in vm.statics match n.classify() { diff --git a/crates/dash_vm/src/value/array.rs b/crates/dash_vm/src/value/array.rs index bf8b3431..5e984bfb 100755 --- a/crates/dash_vm/src/value/array.rs +++ b/crates/dash_vm/src/value/array.rs @@ -4,6 +4,7 @@ use std::cell::{Cell, RefCell}; use dash_proc_macro::Trace; use crate::gc::handle::Handle; +use crate::gc::interner::sym; use crate::localscope::LocalScope; use crate::{delegate, throw, Vm}; @@ -60,11 +61,11 @@ impl Object for Array { let items = self.items.borrow(); if let PropertyKey::String(key) = &key { - if key == "length" { + if key.sym() == sym::length { return Ok(Some(PropertyValue::static_default(Value::number(items.len() as f64)))); } - if let Ok(index) = key.parse::() { + if let Ok(index) = key.res(sc).parse::() { if index < MAX_LENGTH { if let Some(element) = items.get(index).cloned() { return Ok(Some(element)); @@ -76,11 +77,11 @@ impl Object for Array { self.obj.get_property_descriptor(sc, key) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { if let PropertyKey::String(key) = &key { let mut items = self.items.borrow_mut(); - if key == "length" { + if key.sym() == sym::length { // TODO: this shouldnt be undefined let value = value.kind().get_or_apply(sc, Value::undefined()).root(sc)?; let new_len = value.to_number(sc)? as usize; @@ -93,7 +94,7 @@ impl Object for Array { return Ok(()); } - if let Ok(index) = key.parse::() { + if let Ok(index) = key.res(sc).parse::() { if index < MAX_LENGTH { if index >= items.len() { items.resize(index + 1, PropertyValue::static_default(Value::undefined())); @@ -110,11 +111,11 @@ impl Object for Array { fn delete_property(&self, sc: &mut LocalScope, key: PropertyKey) -> Result { if let PropertyKey::String(key) = &key { - if key == "length" { + if key.sym() == sym::length { return Ok(Unrooted::new(Value::undefined())); } - if let Ok(index) = key.parse::() { + if let Ok(index) = key.res(sc).parse::() { let mut items = self.items.borrow_mut(); if let Some(item) = items.get_mut(index) { @@ -152,9 +153,9 @@ impl Object for Array { self.obj.get_prototype(sc) } - fn own_keys(&self) -> Result, Value> { + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { let items = self.items.borrow(); - Ok(array_like_keys(items.len()).collect()) + Ok(array_like_keys(sc, items.len()).collect()) } } @@ -223,7 +224,8 @@ impl ArrayIterator { if index < self.length { self.index.set(index + 1); - self.value.get_property(sc, index.to_string().into()).map(Some) + let index = sc.intern_usize(index); + self.value.get_property(sc, index.into()).map(Some) } else { Ok(None) } @@ -241,7 +243,8 @@ pub fn spec_array_get_property(scope: &mut LocalScope, target: &Value, index: us }; } - match target.get_property(scope, index.to_string().into()) { + let index = scope.intern_usize(index); + match target.get_property(scope, index.into()) { Ok(v) => Ok(v.into()), Err(v) => Ok(v.into()), } @@ -267,5 +270,6 @@ pub fn spec_array_set_property( } } - target.set_property(scope, index.to_string().into(), value) + let index = scope.intern_usize(index); + target.set_property(scope, index.into(), value) } diff --git a/crates/dash_vm/src/value/boxed.rs b/crates/dash_vm/src/value/boxed.rs index 5849a297..1d066fed 100644 --- a/crates/dash_vm/src/value/boxed.rs +++ b/crates/dash_vm/src/value/boxed.rs @@ -2,11 +2,10 @@ use super::ops::conversions::{PreferredType, ValueConversion}; use super::ops::equality::ValueEquality; use crate::gc::handle::Handle; use crate::localscope::LocalScope; -use crate::value::{PropertyKey, Unrooted}; +use crate::value::{JsString, PropertyKey, Unrooted}; use crate::{delegate, PropertyValue, Vm}; use dash_proc_macro::Trace; use std::any::Any; -use std::rc::Rc; use super::object::{NamedObject, Object}; use super::primitive::{PrimitiveCapabilities, Symbol as PrimitiveSymbol}; @@ -114,12 +113,12 @@ macro_rules! boxed_primitive { ValueConversion::to_number(&self.inner, sc) } - fn to_boolean(&self) -> Result { - ValueConversion::to_boolean(&self.inner) + fn to_boolean(&self, sc: &mut LocalScope<'_>) -> Result { + ValueConversion::to_boolean(&self.inner, sc) } - fn to_string(&self, sc: &mut LocalScope) -> Result, Value> { - ValueConversion::to_string(&self.inner, sc) + fn to_js_string(&self, sc: &mut LocalScope) -> Result { + ValueConversion::to_js_string(&self.inner, sc) } fn length_of_array_like(&self, sc: &mut LocalScope) -> Result { @@ -137,7 +136,7 @@ macro_rules! boxed_primitive { boxed_primitive! { Number number_prototype number_ctor f64, // TODO: should this store a primitive::Number? Boolean boolean_prototype boolean_ctor bool, - String string_prototype string_ctor Rc, + String string_prototype string_ctor JsString, Symbol symbol_prototype symbol_ctor PrimitiveSymbol } @@ -162,12 +161,12 @@ impl PrimitiveCapabilities for Boolean { } impl PrimitiveCapabilities for String { - fn as_string(&self) -> Option> { + fn as_string(&self) -> Option { Some(self.inner.clone()) } fn unbox(&self) -> Value { - Value::String(Rc::clone(&self.inner)) + Value::String(self.inner.clone()) } } diff --git a/crates/dash_vm/src/value/error.rs b/crates/dash_vm/src/value/error.rs index c96bdfc4..d95265c7 100644 --- a/crates/dash_vm/src/value/error.rs +++ b/crates/dash_vm/src/value/error.rs @@ -1,44 +1,57 @@ use std::any::Any; use std::fmt::Write; -use std::rc::Rc; use dash_proc_macro::Trace; +use crate::delegate; use crate::gc::handle::Handle; +use crate::gc::interner::sym; use crate::localscope::LocalScope; -use crate::{delegate, Vm}; use super::object::{NamedObject, Object, PropertyKey, PropertyValue}; +use super::string::JsString; use super::{Unrooted, Value}; #[derive(Debug, Trace)] pub struct Error { - pub name: Rc, - pub message: Rc, - pub stack: Rc, + pub name: JsString, + pub message: JsString, + pub stack: JsString, pub obj: NamedObject, } -fn get_stack_trace(name: &str, message: &str, vm: &Vm) -> Rc { +fn get_stack_trace(name: JsString, message: JsString, sc: &mut LocalScope<'_>) -> JsString { + let name = name.res(sc); + let message = message.res(sc); let mut stack = format!("{name}: {message}"); - for frame in vm.frames.iter().rev().take(10) { - let name = frame.function.name.as_deref().unwrap_or(""); + for frame in sc.frames.iter().rev().take(10) { + let name = frame + .function + .name + .map(|s| sc.interner.resolve(s)) + .unwrap_or(""); let _ = write!(stack, "\n at {name}"); } - stack.into() + sc.intern(stack.as_ref()).into() } impl Error { - pub fn new>>(vm: &Vm, message: S) -> Self { - let ctor = vm.statics.error_ctor.clone(); - let proto = vm.statics.error_prototype.clone(); - Self::suberror(vm, "Error", message, ctor, proto) + pub fn new>(sc: &mut LocalScope<'_>, message: S) -> Self { + let ctor = sc.statics.error_ctor.clone(); + let proto = sc.statics.error_prototype.clone(); + Self::suberror(sc, sym::Error, message, ctor, proto) } - pub fn suberror>, S2: Into>>( - vm: &Vm, + pub fn new_with_js_string>(sc: &mut LocalScope<'_>, message: S) -> Self { + let ctor = sc.statics.error_ctor.clone(); + let proto = sc.statics.error_prototype.clone(); + Self::suberror_with_js_string(sc, sym::Error, message, ctor, proto) + } + + pub fn suberror_with_js_string, S2: Into>( + sc: &mut LocalScope<'_>, name: S1, message: S2, ctor: Handle, @@ -46,7 +59,26 @@ impl Error { ) -> Self { let name = name.into(); let message = message.into(); - let stack = get_stack_trace(&name, &message, vm); + let stack = get_stack_trace(name.clone(), message, sc); + + Self { + name, + message, + stack, + obj: NamedObject::with_prototype_and_constructor(proto, ctor), + } + } + + pub fn suberror, S2: Into>( + sc: &mut LocalScope<'_>, + name: S1, + message: S2, + ctor: Handle, + proto: Handle, + ) -> Self { + let name = name.into(); + let message = sc.intern(message.into().as_ref()).into(); + let stack = get_stack_trace(name, message, sc); Self { name, @@ -58,18 +90,18 @@ impl Error { pub fn empty() -> Self { Self { - name: "Error".into(), - message: "".into(), - stack: "".into(), + name: sym::Error.into(), + message: sym::empty.into(), + stack: sym::empty.into(), obj: NamedObject::null(), } } - pub fn empty_with_name>>(name: S) -> Self { + pub fn empty_with_name>(name: S) -> Self { Self { name: name.into(), - message: "".into(), - stack: "".into(), + message: sym::empty.into(), + stack: sym::empty.into(), obj: NamedObject::null(), } } @@ -82,20 +114,20 @@ impl Object for Error { key: PropertyKey, ) -> Result, Unrooted> { match key { - PropertyKey::String(s) if s == "name" => { + PropertyKey::String(s) if s.sym() == sym::name => { Ok(Some(PropertyValue::static_default(Value::String(self.name.clone())))) } - PropertyKey::String(s) if s == "message" => { + PropertyKey::String(s) if s.sym() == sym::message => { Ok(Some(PropertyValue::static_default(Value::String(self.message.clone())))) } - PropertyKey::String(s) if s == "stack" => { + PropertyKey::String(s) if s.sym() == sym::stack => { Ok(Some(PropertyValue::static_default(Value::String(self.stack.clone())))) } _ => self.obj.get_property_descriptor(sc, key), } } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { // TODO: this should special case name/stack self.obj.set_property(sc, key, value) } @@ -127,38 +159,47 @@ impl Object for Error { self.obj.get_prototype(sc) } - fn own_keys(&self) -> Result, Value> { - self.obj.own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + self.obj.own_keys(sc) } } // Other types of errors macro_rules! define_error_type { - ( $($t:ident $proto:ident $ctor:ident),* ) => { + ( $($s:ident, $t:expr, $proto:ident, $ctor:ident);* ) => { $( #[derive(Debug, Trace)] - pub struct $t { + pub struct $s { pub inner: Error, } - impl $t { - pub fn new>>(vm: &Vm, message: S) -> Self { + impl $s { + pub fn new>(vm: &mut LocalScope<'_>, message: S) -> Self { + let ctor = vm.statics.$ctor.clone(); + let proto = vm.statics.$proto.clone(); + + Self { + inner: Error::suberror(vm, $t, message, ctor, proto), + } + } + + pub fn new_with_js_string>(vm: &mut LocalScope<'_>, message: S) -> Self { let ctor = vm.statics.$ctor.clone(); let proto = vm.statics.$proto.clone(); Self { - inner: Error::suberror(vm, stringify!($t), message, ctor, proto), + inner: Error::suberror_with_js_string(vm, $t, message, ctor, proto), } } pub fn empty() -> Self { Self { - inner: Error::empty_with_name(stringify!($t)), + inner: Error::empty_with_name($t), } } } - impl Object for $t { + impl Object for $s { delegate!( inner, get_own_property_descriptor, @@ -178,11 +219,11 @@ macro_rules! define_error_type { } define_error_type!( - EvalError eval_error_prototype eval_error_ctor, - RangeError range_error_prototype range_error_ctor, - ReferenceError reference_error_prototype reference_error_ctor, - SyntaxError syntax_error_prototype syntax_error_ctor, - TypeError type_error_prototype type_error_ctor, - URIError uri_error_prototype uri_error_ctor, - AggregateError aggregate_error_prototype aggregate_error_ctor + EvalError, sym::EvalError, eval_error_prototype, eval_error_ctor; + RangeError, sym::RangeError, range_error_prototype, range_error_ctor; + ReferenceError, sym::ReferenceError, reference_error_prototype, reference_error_ctor; + SyntaxError, sym::SyntaxError, syntax_error_prototype, syntax_error_ctor; + TypeError, sym::TypeError, type_error_prototype, type_error_ctor; + URIError, sym::URIError, uri_error_prototype, uri_error_ctor; + AggregateError, sym::AggregateError, aggregate_error_prototype, aggregate_error_ctor ); diff --git a/crates/dash_vm/src/value/function/async.rs b/crates/dash_vm/src/value/function/async.rs index c3b16d4f..a240d6c6 100644 --- a/crates/dash_vm/src/value/function/async.rs +++ b/crates/dash_vm/src/value/function/async.rs @@ -1,6 +1,7 @@ use dash_proc_macro::Trace; use crate::gc::handle::Handle; +use crate::gc::interner::sym; use crate::localscope::LocalScope; use crate::value::object::{NamedObject, Object, PropertyKey}; use crate::value::promise::{wrap_promise, Promise}; @@ -44,7 +45,7 @@ impl AsyncFunction { .root(scope) .and_then(|result| { result - .get_property(scope, PropertyKey::String("value".into())) + .get_property(scope, PropertyKey::String(sym::value.into())) .root(scope) }); @@ -148,7 +149,7 @@ impl Object for ThenTask { .root(scope) .and_then(|result| { result - .get_property(scope, PropertyKey::String("value".into())) + .get_property(scope, PropertyKey::String(sym::value.into())) .root(scope) }); diff --git a/crates/dash_vm/src/value/function/generator.rs b/crates/dash_vm/src/value/function/generator.rs index ad112783..6662f315 100644 --- a/crates/dash_vm/src/value/function/generator.rs +++ b/crates/dash_vm/src/value/function/generator.rs @@ -4,7 +4,7 @@ use std::cell::RefCell; use dash_proc_macro::Trace; use crate::gc::handle::Handle; -use crate::gc::trace::Trace; +use crate::gc::trace::{Trace, TraceCtxt}; use crate::localscope::LocalScope; use crate::value::object::{NamedObject, Object}; use crate::value::{Typeof, Unrooted, Value}; @@ -76,9 +76,12 @@ impl Default for GeneratorState { } unsafe impl Trace for GeneratorState { - fn trace(&self) { - if let GeneratorState::Running { ref stack, .. } = self { - stack.trace(); + fn trace(&self, cx: &mut TraceCtxt<'_>) { + match self { + GeneratorState::Finished => {} + GeneratorState::Running { ip: _, stack } => { + stack.trace(cx); + } } } } diff --git a/crates/dash_vm/src/value/function/mod.rs b/crates/dash_vm/src/value/function/mod.rs index ef22d5b6..5bfac444 100755 --- a/crates/dash_vm/src/value/function/mod.rs +++ b/crates/dash_vm/src/value/function/mod.rs @@ -3,13 +3,13 @@ use std::cell::RefCell; use std::cmp::Ordering; use std::fmt::{self, Debug}; use std::iter::{self}; -use std::rc::Rc; use dash_proc_macro::Trace; use crate::dispatch::HandleResult; use crate::gc::handle::Handle; -use crate::gc::trace::Trace; +use crate::gc::interner::sym; +use crate::gc::trace::{Trace, TraceCtxt}; use crate::localscope::LocalScope; use crate::Vm; @@ -20,6 +20,7 @@ use self::user::UserFunction; use super::array::Array; use super::object::{NamedObject, Object, PropertyKey, PropertyValue}; +use super::string::JsString; use super::{Typeof, Unrooted, Value}; pub mod r#async; @@ -35,11 +36,11 @@ pub enum FunctionKind { } unsafe impl Trace for FunctionKind { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { match self { - Self::User(user) => user.trace(), - Self::Generator(generator) => generator.trace(), - Self::Async(async_) => async_.trace(), + Self::User(user) => user.trace(cx), + Self::Generator(generator) => generator.trace(cx), + Self::Async(async_) => async_.trace(cx), Self::Native(_) => {} } } @@ -88,14 +89,14 @@ impl fmt::Debug for FunctionKind { #[derive(Debug, Trace)] pub struct Function { - name: RefCell>>, + name: RefCell>, kind: FunctionKind, obj: NamedObject, prototype: RefCell>>, } impl Function { - pub fn new(vm: &Vm, name: Option>, kind: FunctionKind) -> Self { + pub fn new(vm: &Vm, name: Option, kind: FunctionKind) -> Self { let (proto, ctor) = (&vm.statics.function_proto, &vm.statics.function_ctor); Self::with_obj( @@ -105,7 +106,7 @@ impl Function { ) } - pub fn with_obj(name: Option>, kind: FunctionKind, obj: NamedObject) -> Self { + pub fn with_obj(name: Option, kind: FunctionKind, obj: NamedObject) -> Self { Self { name: RefCell::new(name), kind, @@ -118,11 +119,11 @@ impl Function { &self.kind } - pub fn set_name(&self, name: Rc) -> Option> { + pub fn set_name(&self, name: JsString) -> Option { self.name.borrow_mut().replace(name) } - pub fn name(&self) -> Option> { + pub fn name(&self) -> Option { self.name.borrow().clone() } @@ -198,12 +199,12 @@ impl Object for Function { key: PropertyKey, ) -> Result, Unrooted> { if let Some(key) = key.as_string() { - match key { - "name" => { - let name = self.name().unwrap_or_else(|| sc.statics.empty_str()); + match key.sym() { + sym::name => { + let name = self.name().unwrap_or_else(|| sym::empty.into()); return Ok(Some(PropertyValue::static_default(Value::String(name)))); } - "prototype" => { + sym::prototype => { let prototype = self.get_or_set_prototype(sc); return Ok(Some(PropertyValue::static_default(Value::Object(prototype.clone())))); } @@ -214,7 +215,7 @@ impl Object for Function { self.obj.get_own_property_descriptor(sc, key) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { self.obj.set_property(sc, key, value) } @@ -255,8 +256,8 @@ impl Object for Function { self.obj.get_prototype(sc) } - fn own_keys(&self) -> Result, Value> { - Ok(["length", "name"].iter().map(|&s| Value::String(s.into())).collect()) + fn own_keys(&self, _: &mut LocalScope<'_>) -> Result, Value> { + Ok(vec![Value::String(sym::length.into()), Value::String(sym::name.into())]) } fn type_of(&self) -> Typeof { diff --git a/crates/dash_vm/src/value/mod.rs b/crates/dash_vm/src/value/mod.rs index 9a1ed081..777ae364 100755 --- a/crates/dash_vm/src/value/mod.rs +++ b/crates/dash_vm/src/value/mod.rs @@ -13,7 +13,6 @@ pub mod promise; pub mod regex; pub mod set; pub mod typedarray; -use std::rc::Rc; use dash_middle::compiler::constant::Constant; use dash_middle::compiler::external::External; @@ -21,8 +20,10 @@ use dash_middle::parser::statement::FunctionKind as ParserFunctionKind; use dash_middle::util::ThreadSafeStorage; use dash_proc_macro::Trace; +pub mod string; use crate::gc::handle::Handle; -use crate::gc::trace::Trace; +use crate::gc::interner::sym; +use crate::gc::trace::{Trace, TraceCtxt}; use crate::util::cold_path; use crate::value::function::FunctionKind; use crate::value::primitive::{Null, Undefined}; @@ -35,6 +36,7 @@ use self::function::Function; use self::object::{Object, PropertyKey, PropertyValue}; use self::primitive::{Number, PrimitiveCapabilities, Symbol}; use self::regex::RegExp; +use self::string::JsString; use super::localscope::LocalScope; use super::Vm; @@ -50,7 +52,7 @@ pub enum Value { /// The boolean type Boolean(bool), /// The string type - String(Rc), + String(JsString), /// The undefined type Undefined(Undefined), /// The null type @@ -233,11 +235,16 @@ impl Object for ExternalValue { } unsafe impl Trace for Value { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { match self { - Value::Object(o) => o.trace(), - Value::External(e) => e.trace(), - _ => {} + Value::Object(o) => o.trace(cx), + Value::External(e) => e.trace(cx), + Value::String(s) => s.trace(cx), + Value::Number(_) => {} + Value::Boolean(_) => {} + Value::Undefined(_) => {} + Value::Null(_) => {} + Value::Symbol(s) => s.trace(cx), } } } @@ -294,17 +301,17 @@ impl Value { match constant { Constant::Number(n) => Value::number(n), Constant::Boolean(b) => Value::Boolean(b), - Constant::String(s) => Value::String(s), + Constant::String(s) => Value::String(s.into()), Constant::Undefined => Value::undefined(), Constant::Null => Value::null(), Constant::Regex(nodes, flags, source) => { - let regex = RegExp::new(nodes, flags, source, vm); + let regex = RegExp::new(nodes, flags, source.into(), vm); Value::Object(vm.register(regex)) } Constant::Function(f) => { let externals = register_function_externals(&f, vm); - let name: Option> = f.name.as_deref().map(Into::into); + let name = f.name.map(Into::into); let ty = f.ty; let is_async = f.r#async; @@ -328,12 +335,7 @@ impl Value { } } - pub fn set_property( - &self, - sc: &mut LocalScope, - key: PropertyKey<'static>, - value: PropertyValue, - ) -> Result<(), Value> { + pub fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { match self { Self::Object(h) => h.set_property(sc, key, value), Self::Number(n) => n.set_property(sc, key, value), @@ -378,7 +380,10 @@ impl Value { Self::External(o) => o.apply(sc, this, args), Self::Number(n) => throw!(sc, TypeError, "{} is not a function", n), Self::Boolean(b) => throw!(sc, TypeError, "{} is not a function", b), - Self::String(s) => throw!(sc, TypeError, "{} is not a function", s), + Self::String(s) => { + let s = s.res(sc).to_owned(); + throw!(sc, TypeError, "{} is not a function", s) + } Self::Undefined(_) => throw!(sc, TypeError, "undefined is not a function"), Self::Null(_) => throw!(sc, TypeError, "null is not a function"), Self::Symbol(s) => throw!(sc, TypeError, "{:?} is not a function", s), @@ -418,17 +423,20 @@ impl Value { Self::External(o) => o.construct(sc, this, args), Self::Number(n) => throw!(sc, TypeError, "{} is not a constructor", n), Self::Boolean(b) => throw!(sc, TypeError, "{} is not a constructor", b), - Self::String(s) => throw!(sc, TypeError, "{} is not a constructor", s), + Self::String(s) => { + let s = s.res(sc).to_owned(); + throw!(sc, TypeError, "{} is not a constructor", s) + } Self::Undefined(_) => throw!(sc, TypeError, "undefined is not a constructor"), Self::Null(_) => throw!(sc, TypeError, "null is not a constructor"), Self::Symbol(s) => throw!(sc, TypeError, "{:?} is not a constructor", s), } } - pub fn is_truthy(&self) -> bool { + pub fn is_truthy(&self, sc: &mut LocalScope<'_>) -> bool { match self { Value::Boolean(b) => *b, - Value::String(s) => !s.is_empty(), + Value::String(s) => !s.res(sc).is_empty(), Value::Number(Number(n)) => *n != 0.0 && !n.is_nan(), Value::Symbol(_) => true, Value::Object(_) => true, @@ -502,7 +510,7 @@ impl Value { _ => return Ok(false), }; - let target_proto = ctor.get_property(sc, "prototype".into()).root(sc)?; + let target_proto = ctor.get_property(sc, sym::prototype.into()).root(sc)?; let mut this_proto = obj.get_prototype(sc)?; // Look if self[prototype] == ctor.prototype, repeat for all objects in self's prototype chain loop { @@ -572,14 +580,14 @@ pub enum Typeof { impl Typeof { pub fn as_value(&self) -> Value { match self { - Self::Undefined => Value::String("undefined".into()), - Self::Object => Value::String("object".into()), - Self::Boolean => Value::String("boolean".into()), - Self::Number => Value::String("number".into()), - Self::Bigint => Value::String("bigint".into()), - Self::String => Value::String("string".into()), - Self::Symbol => Value::String("symbol".into()), - Self::Function => Value::String("function".into()), + Self::Undefined => Value::String(sym::undefined.into()), + Self::Object => Value::String(sym::object.into()), + Self::Boolean => Value::String(sym::boolean.into()), + Self::Number => Value::String(sym::number.into()), + Self::Bigint => Value::String(sym::bigint.into()), + Self::String => Value::String(sym::string.into()), + Self::Symbol => Value::String(sym::symbol.into()), + Self::Function => Value::String(sym::function.into()), } } } @@ -699,7 +707,7 @@ impl Object for PureBuiltin { type_of ); - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { sc.impure_builtins(); self.inner.set_property(sc, key, value) } @@ -718,8 +726,8 @@ impl Object for PureBuiltin { &self.inner } - fn own_keys(&self) -> Result, Value> { - self.inner.own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + self.inner.own_keys(sc) } fn as_primitive_capable(&self) -> Option<&dyn PrimitiveCapabilities> { diff --git a/crates/dash_vm/src/value/object.rs b/crates/dash_vm/src/value/object.rs index a96dcc8d..6a6fcb26 100755 --- a/crates/dash_vm/src/value/object.rs +++ b/crates/dash_vm/src/value/object.rs @@ -1,11 +1,10 @@ use std::any::Any; -use std::borrow::Cow; use std::cell::RefCell; use std::fmt::Debug; -use std::ptr::addr_of; +use crate::gc::interner::sym; use crate::gc::persistent::Persistent; -use crate::gc::trace::Trace; +use crate::gc::trace::{Trace, TraceCtxt}; use bitflags::bitflags; use dash_proc_macro::Trace; @@ -15,6 +14,7 @@ use crate::{throw, Vm}; use super::ops::conversions::ValueConversion; use super::primitive::{PrimitiveCapabilities, Symbol}; +use super::string::JsString; use super::{ExternalValue, Root, Typeof, Unrooted, Value, ValueContext}; pub type ObjectMap = ahash::HashMap; @@ -55,7 +55,7 @@ pub trait Object: Debug + Trace { } } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value>; + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value>; fn delete_property(&self, sc: &mut LocalScope, key: PropertyKey) -> Result; @@ -87,7 +87,7 @@ pub trait Object: Debug + Trace { None } - fn own_keys(&self) -> Result, Value>; + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value>; fn type_of(&self) -> Typeof { Typeof::Object @@ -128,7 +128,7 @@ macro_rules! delegate { fn set_property( &self, sc: &mut $crate::localscope::LocalScope, - key: $crate::value::object::PropertyKey<'static>, + key: $crate::value::object::PropertyKey, value: $crate::value::object::PropertyValue, ) -> Result<(), $crate::value::Value> { self.$field.set_property(sc, key, value) @@ -159,8 +159,8 @@ macro_rules! delegate { } }; (override $field:ident, own_keys) => { - fn own_keys(&self) -> Result, $crate::value::Value> { - self.$field.own_keys() + fn own_keys(&self, sc: &mut $crate::localscope::LocalScope<'_>) -> Result, $crate::value::Value> { + self.$field.own_keys(sc) } }; (override $field:ident, apply) => { @@ -207,16 +207,25 @@ macro_rules! delegate { pub struct NamedObject { prototype: RefCell>>, constructor: RefCell>>, - values: RefCell, PropertyValue>>, + values: RefCell>, } // TODO: optimization opportunity: some kind of Number variant for faster indexing without .to_string() #[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub enum PropertyKey<'a> { - String(Cow<'a, str>), +pub enum PropertyKey { + String(JsString), Symbol(Symbol), } +unsafe impl Trace for PropertyKey { + fn trace(&self, cx: &mut TraceCtxt<'_>) { + match self { + PropertyKey::String(s) => s.trace(cx), + PropertyKey::Symbol(s) => s.trace(cx), + } + } +} + bitflags! { pub struct PropertyDataDescriptor: u8 { const CONFIGURABLE = 1 << 0; @@ -226,7 +235,7 @@ bitflags! { } unsafe impl Trace for PropertyDataDescriptor { - fn trace(&self) {} + fn trace(&self, _: &mut TraceCtxt<'_>) {} } impl Default for PropertyDataDescriptor { @@ -296,19 +305,19 @@ impl PropertyValue { match &self.kind { PropertyValueKind::Static(value) => { - obj.set_property(sc, "value".into(), PropertyValue::static_default(value.clone()))?; + obj.set_property(sc, sym::value.into(), PropertyValue::static_default(value.clone()))?; } PropertyValueKind::Trap { get, set } => { let get = get.as_ref().map(|v| Value::Object(v.clone())).unwrap_or_undefined(); let set = set.as_ref().map(|v| Value::Object(v.clone())).unwrap_or_undefined(); - obj.set_property(sc, "get".into(), PropertyValue::static_default(get))?; - obj.set_property(sc, "set".into(), PropertyValue::static_default(set))?; + obj.set_property(sc, sym::get.into(), PropertyValue::static_default(get))?; + obj.set_property(sc, sym::set.into(), PropertyValue::static_default(set))?; } } obj.set_property( sc, - "writable".into(), + sym::writable.into(), PropertyValue::static_default(Value::Boolean( self.descriptor.contains(PropertyDataDescriptor::WRITABLE), )), @@ -316,7 +325,7 @@ impl PropertyValue { obj.set_property( sc, - "enumerable".into(), + sym::enumerable.into(), PropertyValue::static_default(Value::Boolean( self.descriptor.contains(PropertyDataDescriptor::ENUMERABLE), )), @@ -324,7 +333,7 @@ impl PropertyValue { obj.set_property( sc, - "configurable".into(), + sym::configurable.into(), PropertyValue::static_default(Value::Boolean( self.descriptor.contains(PropertyDataDescriptor::CONFIGURABLE), )), @@ -335,28 +344,28 @@ impl PropertyValue { pub fn from_descriptor_value(sc: &mut LocalScope<'_>, value: Value) -> Result { let mut flags = PropertyDataDescriptor::empty(); - let configurable = value.get_property(sc, "configurable".into()).root(sc)?.into_option(); - let enumerable = value.get_property(sc, "enumerable".into()).root(sc)?.into_option(); - let writable = value.get_property(sc, "writable".into()).root(sc)?.into_option(); + let configurable = value.get_property(sc, sym::configurable.into()).root(sc)?.into_option(); + let enumerable = value.get_property(sc, sym::enumerable.into()).root(sc)?.into_option(); + let writable = value.get_property(sc, sym::writable.into()).root(sc)?.into_option(); - if configurable.map_or(true, |v| v.is_truthy()) { + if configurable.map_or(true, |v| v.is_truthy(sc)) { flags |= PropertyDataDescriptor::CONFIGURABLE; } - if enumerable.map_or(true, |v| v.is_truthy()) { + if enumerable.map_or(true, |v| v.is_truthy(sc)) { flags |= PropertyDataDescriptor::ENUMERABLE; } - if writable.map_or(true, |v| v.is_truthy()) { + if writable.map_or(true, |v| v.is_truthy(sc)) { flags |= PropertyDataDescriptor::WRITABLE; } // TODO: make sure that if value is set, get/set are not - let static_value = value.get_property(sc, "value".into()).root(sc)?.into_option(); + let static_value = value.get_property(sc, sym::value.into()).root(sc)?.into_option(); let kind = match static_value { Some(static_value) => PropertyValueKind::Static(static_value), None => { let get = value - .get_property(sc, "get".into()) + .get_property(sc, sym::get.into()) .root(sc)? .into_option() .and_then(|v| match v { @@ -364,7 +373,7 @@ impl PropertyValue { _ => None, }); let set = value - .get_property(sc, "set".into()) + .get_property(sc, sym::set.into()) .root(sc)? .into_option() .and_then(|v| match v { @@ -427,52 +436,51 @@ impl PropertyValueKind { } unsafe impl Trace for PropertyValueKind { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { match self { - Self::Static(value) => value.trace(), + Self::Static(value) => value.trace(cx), Self::Trap { get, set } => { if let Some(get) = get { - get.trace(); + get.trace(cx); } if let Some(set) = set { - set.trace(); + set.trace(cx); } } } } } -impl<'a> PropertyKey<'a> { - pub fn as_string(&self) -> Option<&str> { +impl PropertyKey { + pub fn as_string(&self) -> Option { match self { - PropertyKey::String(s) => Some(s.as_ref()), + PropertyKey::String(s) => Some(s.clone()), _ => None, } } } -impl<'a> From<&'a str> for PropertyKey<'a> { - fn from(s: &'a str) -> Self { - PropertyKey::String(Cow::Borrowed(s)) +impl From for PropertyKey { + fn from(value: JsString) -> Self { + PropertyKey::String(value) } } - -impl From for PropertyKey<'static> { - fn from(s: String) -> Self { - PropertyKey::String(Cow::Owned(s)) +impl From for PropertyKey { + fn from(value: crate::gc::interner::Symbol) -> Self { + PropertyKey::String(value.into()) } } -impl From for PropertyKey<'static> { +impl From for PropertyKey { fn from(s: Symbol) -> Self { PropertyKey::Symbol(s) } } -impl<'a> PropertyKey<'a> { +impl PropertyKey { pub fn as_value(&self) -> Value { match self { - PropertyKey::String(s) => Value::String(s.as_ref().into()), + PropertyKey::String(s) => Value::String(s.clone()), PropertyKey::Symbol(s) => Value::Symbol(s.clone()), } } @@ -480,10 +488,7 @@ impl<'a> PropertyKey<'a> { pub fn from_value(sc: &mut LocalScope, value: Value) -> Result { match value { Value::Symbol(s) => Ok(Self::Symbol(s)), - other => { - let key = other.to_string(sc)?; - Ok(PropertyKey::String(ToString::to_string(&key).into())) - } + other => Ok(PropertyKey::String(other.to_js_string(sc)?)), } } } @@ -493,7 +498,7 @@ impl NamedObject { Self::with_values(vm, ObjectMap::default()) } - pub fn with_values(vm: &Vm, values: ObjectMap, PropertyValue>) -> Self { + pub fn with_values(vm: &Vm, values: ObjectMap) -> Self { let objp = vm.statics.object_prototype.clone(); let objc = vm.statics.object_ctor.clone(); // TODO: function_ctor instead @@ -527,21 +532,15 @@ impl NamedObject { } unsafe impl Trace for NamedObject { - fn trace(&self) { - let values = self.values.borrow(); - for value in values.values() { - value.trace(); - } - - let prototype = self.prototype.borrow(); - if let Some(prototype) = &*prototype { - prototype.trace(); - } - - let constructor = self.constructor.borrow(); - if let Some(constructor) = &*constructor { - constructor.trace(); - } + fn trace(&self, cx: &mut TraceCtxt<'_>) { + let Self { + prototype, + constructor, + values, + } = self; + values.trace(cx); + prototype.trace(cx); + constructor.trace(cx); } } @@ -552,9 +551,9 @@ impl Object for NamedObject { key: PropertyKey, ) -> Result, Unrooted> { if let PropertyKey::String(st) = &key { - match st.as_ref() { - "__proto__" => return Ok(Some(PropertyValue::static_default(self.get_prototype(sc)?))), - "constructor" => { + match st.sym() { + sym::__proto__ => return Ok(Some(PropertyValue::static_default(self.get_prototype(sc)?))), + sym::constructor => { return Ok(Some(PropertyValue::static_default( self.constructor .borrow() @@ -579,9 +578,9 @@ impl Object for NamedObject { Ok(None) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { - match key.as_string() { - Some("__proto__") => { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { + match key.as_string().map(JsString::sym) { + Some(sym::__proto__) => { return self.set_prototype( sc, match value.into_kind() { @@ -590,7 +589,7 @@ impl Object for NamedObject { }, ); } - Some("constructor") => { + Some(sym::constructor) => { let obj = match value.kind { PropertyValueKind::Static(Value::Object(obj)) => obj, PropertyValueKind::Static(Value::External(obj)) => obj.inner.clone(), @@ -610,10 +609,8 @@ impl Object for NamedObject { } fn delete_property(&self, sc: &mut LocalScope, key: PropertyKey) -> Result { - let key = unsafe { &*addr_of!(key).cast::>() }; - let mut values = self.values.borrow_mut(); - let value = values.remove(key); + let value = values.remove(&key); match value.map(PropertyValue::into_kind) { Some(PropertyValueKind::Static(value)) => { @@ -672,7 +669,7 @@ impl Object for NamedObject { } } - fn own_keys(&self) -> Result, Value> { + fn own_keys(&self, _: &mut LocalScope<'_>) -> Result, Value> { let values = self.values.borrow(); Ok(values.keys().map(PropertyKey::as_value).collect()) } @@ -703,7 +700,7 @@ impl Object for Box { (**self).get_property_descriptor(sc, key) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { (**self).set_property(sc, key, value) } @@ -743,8 +740,8 @@ impl Object for Box { self } - fn own_keys(&self) -> Result, Value> { - (**self).own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + (**self).own_keys(sc) } fn type_of(&self) -> Typeof { @@ -781,7 +778,7 @@ impl Object for Handle { (**self).get_property_descriptor(sc, key) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { (**self).set_property(sc, key, value) } @@ -821,8 +818,8 @@ impl Object for Handle { (**self).as_any() } - fn own_keys(&self) -> Result, Value> { - (**self).own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + (**self).own_keys(sc) } fn type_of(&self) -> Typeof { diff --git a/crates/dash_vm/src/value/ops/arithmetic.rs b/crates/dash_vm/src/value/ops/arithmetic.rs index fab2dc8a..a6bbf997 100755 --- a/crates/dash_vm/src/value/ops/arithmetic.rs +++ b/crates/dash_vm/src/value/ops/arithmetic.rs @@ -14,9 +14,10 @@ impl Value { let rightstr = matches!(right.type_of(), Typeof::String); if leftstr || rightstr { - let lstr = left.to_string(scope)?; - let rstr = right.to_string(scope)?; - Ok(Value::String(format!("{lstr}{rstr}").into())) + let lstr = left.to_js_string(scope)?; + let rstr = right.to_js_string(scope)?; + let out = format!("{}{}", lstr.res(scope), rstr.res(scope)); + Ok(Value::String(scope.intern(out).into())) } else { let lnum = left.to_number(scope)?; let rnum = right.to_number(scope)?; @@ -54,8 +55,8 @@ impl Value { Ok(Value::number(lnum.powf(rnum))) } - pub fn not(&self) -> Value { - Value::Boolean(!self.is_truthy()) + pub fn not(&self, sc: &mut LocalScope<'_>) -> Value { + Value::Boolean(!self.is_truthy(sc)) } pub fn bitor(&self, other: &Self, scope: &mut LocalScope) -> Result { diff --git a/crates/dash_vm/src/value/ops/conversions.rs b/crates/dash_vm/src/value/ops/conversions.rs index 49eb6861..bf07cb92 100755 --- a/crates/dash_vm/src/value/ops/conversions.rs +++ b/crates/dash_vm/src/value/ops/conversions.rs @@ -1,12 +1,12 @@ -use std::rc::Rc; - use crate::gc::handle::Handle; +use crate::gc::interner::sym; use crate::localscope::LocalScope; +use crate::throw; use crate::value::boxed::{Boolean, Number as BoxedNumber, String as BoxedString, Symbol as BoxedSymbol}; use crate::value::object::Object; use crate::value::primitive::{Number, MAX_SAFE_INTEGERF}; +use crate::value::string::JsString; use crate::value::{Root, Typeof, Value}; -use crate::{throw, Vm}; pub trait ValueConversion { fn to_primitive(&self, sc: &mut LocalScope, preferred_type: Option) -> Result; @@ -50,9 +50,9 @@ pub trait ValueConversion { if number < 0.0 { Ok(-integer) } else { Ok(integer) } } - fn to_boolean(&self) -> Result; + fn to_boolean(&self, sc: &mut LocalScope<'_>) -> Result; - fn to_string(&self, sc: &mut LocalScope) -> Result, Value>; + fn to_js_string(&self, sc: &mut LocalScope) -> Result; fn length_of_array_like(&self, sc: &mut LocalScope) -> Result; @@ -80,9 +80,9 @@ impl ValueConversion for Value { Value::Undefined(_) => Ok(f64::NAN), Value::Null(_) => Ok(0.0), Value::Boolean(b) => Ok(*b as i8 as f64), - Value::String(s) => match s.len() { + Value::String(s) => match s.len(sc) { 0 => Ok(0.0), - _ => Ok(s.parse::().unwrap_or(f64::NAN)), + _ => Ok(s.res(sc).parse::().unwrap_or(f64::NAN)), }, Value::Symbol(_) => throw!(sc, TypeError, "Cannot convert symbol to number"), Value::Object(o) => object_to_number(self, o, sc), @@ -90,35 +90,35 @@ impl ValueConversion for Value { } } - fn to_boolean(&self) -> Result { + fn to_boolean(&self, sc: &mut LocalScope<'_>) -> Result { match self { Value::Boolean(b) => Ok(*b), Value::Undefined(_) => Ok(false), Value::Null(_) => Ok(false), Value::Number(Number(n)) => Ok(*n != 0.0 && !n.is_nan()), - Value::String(s) => Ok(!s.is_empty()), + Value::String(s) => Ok(!s.res(sc).is_empty()), Value::Symbol(_) => Ok(true), Value::Object(_) => Ok(true), _ => todo!(), // TODO: implement other cases } } - fn to_string(&self, sc: &mut LocalScope) -> Result, Value> { - fn object_to_string(this: &Value, obj: &dyn Object, sc: &mut LocalScope) -> Result, Value> { + fn to_js_string(&self, sc: &mut LocalScope) -> Result { + fn object_to_string(this: &Value, obj: &dyn Object, sc: &mut LocalScope) -> Result { if let Some(prim) = obj.as_primitive_capable() { - ValueConversion::to_string(prim, sc) + ValueConversion::to_js_string(prim, sc) } else { let prim_value = this.to_primitive(sc, Some(PreferredType::String))?; - prim_value.to_string(sc) + prim_value.to_js_string(sc) } } match self { - Value::String(s) => ValueConversion::to_string(s, sc), - Value::Boolean(b) => ValueConversion::to_string(b, sc), - Value::Null(n) => ValueConversion::to_string(n, sc), - Value::Undefined(u) => ValueConversion::to_string(u, sc), - Value::Number(n) => ValueConversion::to_string(n, sc), + Value::String(s) => ValueConversion::to_js_string(s, sc), + Value::Boolean(b) => ValueConversion::to_js_string(b, sc), + Value::Null(n) => ValueConversion::to_js_string(n, sc), + Value::Undefined(u) => ValueConversion::to_js_string(u, sc), + Value::Number(n) => ValueConversion::to_js_string(n, sc), Value::Object(o) => object_to_string(self, o, sc), Value::External(o) => object_to_string(self, &o.inner, sc), Value::Symbol(_) => throw!(sc, TypeError, "Cannot convert symbol to a string"), @@ -146,7 +146,7 @@ impl ValueConversion for Value { // b. If exoticToPrim is not undefined, then if let Some(exotic_to_prim) = exotic_to_prim { - let preferred_type = preferred_type.to_value(sc); + let preferred_type = preferred_type.to_value(); // iv. Let result be ? Call(exoticToPrim, input, « hint »). let result = exotic_to_prim.apply(sc, self.clone(), vec![preferred_type]).root(sc)?; @@ -166,7 +166,7 @@ impl ValueConversion for Value { } fn length_of_array_like(&self, sc: &mut LocalScope) -> Result { - self.get_property(sc, "length".into()).root(sc)?.to_length_u(sc) + self.get_property(sc, sym::length.into()).root(sc)?.to_length_u(sc) } fn to_object(&self, sc: &mut LocalScope) -> Result, Value> { @@ -198,11 +198,11 @@ pub enum PreferredType { } impl PreferredType { - pub fn to_value(&self, vm: &Vm) -> Value { + pub fn to_value(&self) -> Value { let st = match self { - PreferredType::Default => vm.statics.default_str.clone(), - PreferredType::String => vm.statics.string_str.clone(), - PreferredType::Number => vm.statics.number_str.clone(), + PreferredType::Default => sym::default.into(), + PreferredType::String => sym::string.into(), + PreferredType::Number => sym::number.into(), }; Value::String(st) @@ -212,8 +212,8 @@ impl PreferredType { impl Value { pub fn ordinary_to_primitive(&self, sc: &mut LocalScope, preferred_type: PreferredType) -> Result { let method_names = match preferred_type { - PreferredType::String => ["toString", "valueOf"], - PreferredType::Number | PreferredType::Default => ["valueOf", "toString"], + PreferredType::String => [sym::toString, sym::valueOf], + PreferredType::Number | PreferredType::Default => [sym::valueOf, sym::toString], }; for name in method_names { diff --git a/crates/dash_vm/src/value/ops/equality.rs b/crates/dash_vm/src/value/ops/equality.rs index c8168058..945bc2de 100644 --- a/crates/dash_vm/src/value/ops/equality.rs +++ b/crates/dash_vm/src/value/ops/equality.rs @@ -9,9 +9,9 @@ pub trait ValueEquality { fn eq(&self, other: &Value, sc: &mut LocalScope) -> Result; fn strict_eq(&self, other: &Value, sc: &mut LocalScope) -> Result; fn ne(&self, other: &Value, sc: &mut LocalScope) -> Result { - self.eq(other, sc).map(|v| v.not()) + self.eq(other, sc).map(|v| v.not(sc)) } fn strict_ne(&self, other: &Value, sc: &mut LocalScope) -> Result { - self.strict_eq(other, sc).map(|v| v.not()) + self.strict_eq(other, sc).map(|v| v.not(sc)) } } diff --git a/crates/dash_vm/src/value/primitive.rs b/crates/dash_vm/src/value/primitive.rs index 7b7b8e00..b2515a22 100644 --- a/crates/dash_vm/src/value/primitive.rs +++ b/crates/dash_vm/src/value/primitive.rs @@ -1,17 +1,21 @@ use std::any::Any; use std::hash::{Hash, Hasher}; -use std::rc::Rc; use std::{fmt, iter}; +use dash_middle::interner; +use dash_proc_macro::Trace; + use crate::gc::handle::Handle; +use crate::gc::interner::sym; use crate::localscope::LocalScope; use crate::throw; -use crate::util::format_f64; +use crate::util::{format_f64, Captures}; -use super::boxed::{Boolean as BoxedBoolean, Number as BoxedNumber, String as BoxedString, Symbol as BoxedSymbol}; +use super::boxed::{Boolean as BoxedBoolean, Number as BoxedNumber, Symbol as BoxedSymbol}; use super::object::{Object, PropertyKey, PropertyValue}; use super::ops::conversions::{PreferredType, ValueConversion}; use super::ops::equality::ValueEquality; +use super::string::JsString; use super::{Typeof, Unrooted, Value}; pub const MAX_SAFE_INTEGER: u64 = 9007199254740991u64; @@ -26,12 +30,7 @@ impl Object for f64 { Ok(None) } - fn set_property( - &self, - _sc: &mut LocalScope, - _key: PropertyKey<'static>, - _value: PropertyValue, - ) -> Result<(), Value> { + fn set_property(&self, _sc: &mut LocalScope, _key: PropertyKey, _value: PropertyValue) -> Result<(), Value> { Ok(()) } @@ -62,7 +61,7 @@ impl Object for f64 { self } - fn own_keys(&self) -> Result, Value> { + fn own_keys(&self, _: &mut LocalScope<'_>) -> Result, Value> { Ok(Vec::new()) } @@ -84,12 +83,7 @@ impl Object for bool { Ok(None) } - fn set_property( - &self, - _sc: &mut LocalScope, - _key: PropertyKey<'static>, - _value: PropertyValue, - ) -> Result<(), Value> { + fn set_property(&self, _sc: &mut LocalScope, _key: PropertyKey, _value: PropertyValue) -> Result<(), Value> { Ok(()) } @@ -119,7 +113,7 @@ impl Object for bool { self } - fn own_keys(&self) -> Result, Value> { + fn own_keys(&self, _: &mut LocalScope<'_>) -> Result, Value> { Ok(Vec::new()) } @@ -132,69 +126,72 @@ impl Object for bool { } } -// TODO: impl, O: Object> Object for T possible? -impl Object for Rc { - fn get_own_property_descriptor( - &self, - sc: &mut LocalScope, - key: PropertyKey, - ) -> Result, Unrooted> { - str::get_own_property_descriptor(self, sc, key.clone()) - } - - fn set_property( - &self, - _sc: &mut LocalScope, - _key: PropertyKey<'static>, - _value: PropertyValue, - ) -> Result<(), Value> { - Ok(()) - } - - fn delete_property(&self, _sc: &mut LocalScope, _key: PropertyKey) -> Result { - Ok(Unrooted::new(Value::undefined())) - } - - fn set_prototype(&self, _sc: &mut LocalScope, _value: Value) -> Result<(), Value> { - Ok(()) - } - - fn get_prototype(&self, sc: &mut LocalScope) -> Result { - Ok(sc.statics.string_prototype.clone().into()) - } - - fn apply( - &self, - scope: &mut LocalScope, - _callee: Handle, - _this: Value, - _args: Vec, - ) -> Result { - throw!(scope, TypeError, "string is not a function") - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn own_keys(&self) -> Result, Value> { - str::own_keys(self) - } - - fn type_of(&self) -> Typeof { - str::type_of(self) - } - - fn as_primitive_capable(&self) -> Option<&dyn PrimitiveCapabilities> { - Some(self) - } -} - -pub fn array_like_keys(len: usize) -> impl Iterator { +// // TODO: impl, O: Object> Object for T possible? +// impl Object for Rc { +// fn get_own_property_descriptor( +// &self, +// sc: &mut LocalScope, +// key: PropertyKey, +// ) -> Result, Unrooted> { +// str::get_own_property_descriptor(self, sc, key.clone()) +// } + +// fn set_property( +// &self, +// _sc: &mut LocalScope, +// _key: PropertyKey<'static>, +// _value: PropertyValue, +// ) -> Result<(), Value> { +// Ok(()) +// } + +// fn delete_property(&self, _sc: &mut LocalScope, _key: PropertyKey) -> Result { +// Ok(Unrooted::new(Value::undefined())) +// } + +// fn set_prototype(&self, _sc: &mut LocalScope, _value: Value) -> Result<(), Value> { +// Ok(()) +// } + +// fn get_prototype(&self, sc: &mut LocalScope) -> Result { +// Ok(sc.statics.string_prototype.clone().into()) +// } + +// fn apply( +// &self, +// scope: &mut LocalScope, +// _callee: Handle, +// _this: Value, +// _args: Vec, +// ) -> Result { +// throw!(scope, TypeError, "string is not a function") +// } + +// fn as_any(&self) -> &dyn Any { +// self +// } + +// fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { +// str::own_keys(self, sc) +// } + +// fn type_of(&self) -> Typeof { +// str::type_of(self) +// } + +// fn as_primitive_capable(&self) -> Option<&dyn PrimitiveCapabilities> { +// Some(self) +// } +// } + +pub fn array_like_keys<'a, 'b>( + sc: &'a mut LocalScope<'b>, + len: usize, +) -> impl Iterator + Captures<'a> + Captures<'b> { (0..len) - .map(|i| i.to_string()) - .chain(iter::once_with(|| "length".to_string())) - .map(|x| Value::String(x.as_str().into())) + .map(|i| sc.intern_usize(i)) + .chain(iter::once_with(|| sym::length)) + .map(|x| Value::String(x.into())) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -212,7 +209,7 @@ impl Object for Undefined { throw!(sc, TypeError, "Cannot read property {:?} of undefined", key) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, _value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, _value: PropertyValue) -> Result<(), Value> { throw!(sc, TypeError, "Cannot set property {:?} of undefined", key) } @@ -242,7 +239,7 @@ impl Object for Undefined { self } - fn own_keys(&self) -> Result, Value> { + fn own_keys(&self, _: &mut LocalScope<'_>) -> Result, Value> { Ok(Vec::new()) } @@ -264,7 +261,7 @@ impl Object for Null { throw!(sc, TypeError, "Cannot read property {:?} of null", key) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, _value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, _value: PropertyValue) -> Result<(), Value> { throw!(sc, TypeError, "Cannot set property {:?} of null", key) } @@ -294,7 +291,7 @@ impl Object for Null { self } - fn own_keys(&self) -> Result, Value> { + fn own_keys(&self, _: &mut LocalScope<'_>) -> Result, Value> { Ok(Vec::new()) } @@ -303,80 +300,81 @@ impl Object for Null { } } -impl Object for str { - fn get_own_property_descriptor( - &self, - _sc: &mut LocalScope, - key: PropertyKey, - ) -> Result, Unrooted> { - if let PropertyKey::String(st) = key { - if st == "length" { - return Ok(Some(PropertyValue::static_default(Value::number(self.len() as f64)))); - } - - if let Ok(index) = st.parse::() { - let bytes = self.as_bytes(); - if let Some(&byte) = bytes.get(index) { - return Ok(Some(PropertyValue::static_default(Value::String( - (byte as char).to_string().into(), - )))); - } - } - } - - Ok(None) - } - - fn set_property( - &self, - _sc: &mut LocalScope, - _key: PropertyKey<'static>, - _value: PropertyValue, - ) -> Result<(), Value> { - Ok(()) - } - - fn delete_property(&self, _sc: &mut LocalScope, _key: PropertyKey) -> Result { - Ok(Unrooted::new(Value::undefined())) - } - - fn set_prototype(&self, _sc: &mut LocalScope, _value: Value) -> Result<(), Value> { - Ok(()) - } - - fn get_prototype(&self, sc: &mut LocalScope) -> Result { - Ok(sc.statics.string_prototype.clone().into()) - } - - fn apply( - &self, - scope: &mut LocalScope, - _callee: Handle, - _this: Value, - _args: Vec, - ) -> Result { - throw!(scope, TypeError, "string is not a function") - } - - fn as_any(&self) -> &dyn Any { - panic!("cannot convert string to any") - } - - fn own_keys(&self) -> Result, Value> { - Ok(array_like_keys(self.len()).collect()) - } - - fn type_of(&self) -> Typeof { - Typeof::String - } +// impl Object for str { +// fn get_own_property_descriptor( +// &self, +// sc: &mut LocalScope, +// key: PropertyKey, +// ) -> Result, Unrooted> { +// if let PropertyKey::String(st) = key { +// if st.sym() == sym::length { +// return Ok(Some(PropertyValue::static_default(Value::number(self.len() as f64)))); +// } + +// if let Ok(index) = st.res(sc).parse::() { +// let bytes = self.as_bytes(); +// if let Some(&byte) = bytes.get(index) { +// let s = sc.intern((byte as char).to_string().as_ref()); +// return Ok(Some(PropertyValue::static_default(Value::String(s.into())))); +// } +// } +// } + +// Ok(None) +// } + +// fn set_property(&self, _sc: &mut LocalScope, _key: PropertyKey, _value: PropertyValue) -> Result<(), Value> { +// Ok(()) +// } + +// fn delete_property(&self, _sc: &mut LocalScope, _key: PropertyKey) -> Result { +// Ok(Unrooted::new(Value::undefined())) +// } + +// fn set_prototype(&self, _sc: &mut LocalScope, _value: Value) -> Result<(), Value> { +// Ok(()) +// } + +// fn get_prototype(&self, sc: &mut LocalScope) -> Result { +// Ok(sc.statics.string_prototype.clone().into()) +// } + +// fn apply( +// &self, +// scope: &mut LocalScope, +// _callee: Handle, +// _this: Value, +// _args: Vec, +// ) -> Result { +// throw!(scope, TypeError, "string is not a function") +// } + +// fn as_any(&self) -> &dyn Any { +// panic!("cannot convert string to any") +// } + +// fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { +// Ok(array_like_keys(self.len()).collect()) +// } + +// fn type_of(&self) -> Typeof { +// Typeof::String +// } +// } + +// TODO: rename to JsSymbol +#[derive(Debug, Clone, Hash, PartialEq, Eq, Trace)] +pub struct Symbol { + description: JsString, } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct Symbol(Rc); - impl Symbol { - pub fn new(description: Rc) -> Self { - Symbol(description) + pub fn sym(&self) -> interner::Symbol { + self.description.sym() + } + + pub fn new(description: JsString) -> Self { + Symbol { description } } } @@ -389,12 +387,7 @@ impl Object for Symbol { Ok(None) } - fn set_property( - &self, - _sc: &mut LocalScope, - _key: PropertyKey<'static>, - _value: PropertyValue, - ) -> Result<(), Value> { + fn set_property(&self, _sc: &mut LocalScope, _key: PropertyKey, _value: PropertyValue) -> Result<(), Value> { Ok(()) } @@ -424,7 +417,7 @@ impl Object for Symbol { self } - fn own_keys(&self) -> Result, Value> { + fn own_keys(&self, _: &mut LocalScope<'_>) -> Result, Value> { Ok(Vec::new()) } @@ -438,7 +431,7 @@ impl Object for Symbol { } pub trait PrimitiveCapabilities: ValueConversion + ValueEquality + std::fmt::Debug { - fn as_string(&self) -> Option> { + fn as_string(&self) -> Option { None } fn as_number(&self) -> Option { @@ -501,12 +494,12 @@ impl ValueConversion for f64 { Ok(*self) } - fn to_boolean(&self) -> Result { + fn to_boolean(&self, _: &mut LocalScope<'_>) -> Result { Ok(*self != 0.0 && !self.is_nan()) } - fn to_string(&self, _sc: &mut LocalScope) -> Result, Value> { - Ok(format_f64(*self).into()) + fn to_js_string(&self, sc: &mut LocalScope) -> Result { + Ok(sc.intern(format_f64(*self).as_ref()).into()) } fn length_of_array_like(&self, _sc: &mut LocalScope) -> Result { @@ -530,32 +523,32 @@ impl PrimitiveCapabilities for bool { } impl ValueEquality for bool { - fn lt(&self, other: &Value, _sc: &mut LocalScope) -> Result { + fn lt(&self, other: &Value, sc: &mut LocalScope) -> Result { other - .to_boolean() + .to_boolean(sc) .map(|other| Value::Boolean((*self as u8) < other as u8)) } - fn le(&self, other: &Value, _sc: &mut LocalScope) -> Result { + fn le(&self, other: &Value, sc: &mut LocalScope) -> Result { other - .to_boolean() + .to_boolean(sc) .map(|other| Value::Boolean((*self as u8) <= other as u8)) } - fn gt(&self, other: &Value, _sc: &mut LocalScope) -> Result { + fn gt(&self, other: &Value, sc: &mut LocalScope) -> Result { other - .to_boolean() + .to_boolean(sc) .map(|other| Value::Boolean((*self as u8) > other as u8)) } - fn ge(&self, other: &Value, _sc: &mut LocalScope) -> Result { + fn ge(&self, other: &Value, sc: &mut LocalScope) -> Result { other - .to_boolean() + .to_boolean(sc) .map(|other| Value::Boolean((*self as u8) >= other as u8)) } - fn eq(&self, other: &Value, _sc: &mut LocalScope) -> Result { - other.to_boolean().map(|other| Value::Boolean(*self == other)) + fn eq(&self, other: &Value, sc: &mut LocalScope) -> Result { + other.to_boolean(sc).map(|other| Value::Boolean(*self == other)) } fn strict_eq(&self, other: &Value, sc: &mut LocalScope) -> Result { @@ -572,16 +565,12 @@ impl ValueConversion for bool { Ok(*self as u8 as f64) } - fn to_boolean(&self) -> Result { + fn to_boolean(&self, _sc: &mut LocalScope<'_>) -> Result { Ok(*self) } - fn to_string(&self, sc: &mut LocalScope) -> Result, Value> { - Ok(if *self { - sc.statics().get_true() - } else { - sc.statics().get_false() - }) + fn to_js_string(&self, _: &mut LocalScope) -> Result { + Ok(if *self { sym::true_.into() } else { sym::false_.into() }) } fn length_of_array_like(&self, _sc: &mut LocalScope) -> Result { @@ -594,68 +583,68 @@ impl ValueConversion for bool { } } -impl PrimitiveCapabilities for Rc { - fn as_string(&self) -> Option> { - Some(self.clone()) - } +// impl PrimitiveCapabilities for Rc { +// fn as_string(&self) -> Option { +// Some(self.clone()) +// } - fn unbox(&self) -> Value { - Value::String(Rc::clone(self)) - } -} +// fn unbox(&self) -> Value { +// Value::String(Rc::clone(self)) +// } +// } -impl ValueEquality for Rc { - fn lt(&self, other: &Value, sc: &mut LocalScope) -> Result { - other.to_string(sc).map(|other| Value::Boolean(self < &other)) - } +// impl ValueEquality for Rc { +// fn lt(&self, other: &Value, sc: &mut LocalScope) -> Result { +// other.to_js_string(sc).map(|other| Value::Boolean(self < &other)) +// } - fn le(&self, other: &Value, sc: &mut LocalScope) -> Result { - other.to_string(sc).map(|other| Value::Boolean(self <= &other)) - } +// fn le(&self, other: &Value, sc: &mut LocalScope) -> Result { +// other.to_js_string(sc).map(|other| Value::Boolean(self <= &other)) +// } - fn gt(&self, other: &Value, sc: &mut LocalScope) -> Result { - other.to_string(sc).map(|other| Value::Boolean(self > &other)) - } +// fn gt(&self, other: &Value, sc: &mut LocalScope) -> Result { +// other.to_js_string(sc).map(|other| Value::Boolean(self > &other)) +// } - fn ge(&self, other: &Value, sc: &mut LocalScope) -> Result { - other.to_string(sc).map(|other| Value::Boolean(self >= &other)) - } +// fn ge(&self, other: &Value, sc: &mut LocalScope) -> Result { +// other.to_js_string(sc).map(|other| Value::Boolean(self >= &other)) +// } - fn eq(&self, other: &Value, sc: &mut LocalScope) -> Result { - other.to_string(sc).map(|other| Value::Boolean(self == &other)) - } +// fn eq(&self, other: &Value, sc: &mut LocalScope) -> Result { +// other.to_js_string(sc).map(|other| Value::Boolean(self == &other)) +// } - fn strict_eq(&self, other: &Value, sc: &mut LocalScope) -> Result { - ValueEquality::eq(self, other, sc) - } -} +// fn strict_eq(&self, other: &Value, sc: &mut LocalScope) -> Result { +// ValueEquality::eq(self, other, sc) +// } +// } -impl ValueConversion for Rc { - fn to_primitive(&self, _sc: &mut LocalScope, _preferred_type: Option) -> Result { - Ok(Value::String(Rc::clone(self))) - } +// impl ValueConversion for Rc { +// fn to_primitive(&self, _sc: &mut LocalScope, _preferred_type: Option) -> Result { +// Ok(Value::String(Rc::clone(self))) +// } - fn to_number(&self, _sc: &mut LocalScope) -> Result { - Ok(self.parse().unwrap_or(f64::NAN)) - } +// fn to_number(&self, _sc: &mut LocalScope) -> Result { +// Ok(self.parse().unwrap_or(f64::NAN)) +// } - fn to_boolean(&self) -> Result { - Ok(!self.is_empty()) - } +// fn to_boolean(&self) -> Result { +// Ok(!self.is_empty()) +// } - fn to_string(&self, _sc: &mut LocalScope) -> Result, Value> { - Ok(Rc::clone(self)) - } +// fn to_js_string(&self, _sc: &mut LocalScope) -> Result, Value> { +// Ok(Rc::clone(self)) +// } - fn length_of_array_like(&self, _sc: &mut LocalScope) -> Result { - Ok(self.len()) - } +// fn length_of_array_like(&self, _sc: &mut LocalScope) -> Result { +// Ok(self.len()) +// } - fn to_object(&self, sc: &mut LocalScope) -> Result, Value> { - let bool = BoxedString::new(sc, self.clone()); - Ok(sc.register(bool)) - } -} +// fn to_object(&self, sc: &mut LocalScope) -> Result, Value> { +// let bool = BoxedString::new(sc, self.clone()); +// Ok(sc.register(bool)) +// } +// } impl PrimitiveCapabilities for Undefined { fn is_undefined(&self) -> bool { @@ -714,12 +703,12 @@ impl ValueConversion for Undefined { Ok(f64::NAN) } - fn to_boolean(&self) -> Result { + fn to_boolean(&self, _sc: &mut LocalScope<'_>) -> Result { Ok(false) } - fn to_string(&self, sc: &mut LocalScope) -> Result, Value> { - Ok(sc.statics().undefined_str()) + fn to_js_string(&self, _: &mut LocalScope) -> Result { + Ok(sym::undefined.into()) } fn length_of_array_like(&self, _sc: &mut LocalScope) -> Result { @@ -776,12 +765,12 @@ impl ValueConversion for Null { Ok(0.0) } - fn to_boolean(&self) -> Result { + fn to_boolean(&self, _sc: &mut LocalScope<'_>) -> Result { Ok(false) } - fn to_string(&self, sc: &mut LocalScope) -> Result, Value> { - Ok(sc.statics().null_str()) + fn to_js_string(&self, _: &mut LocalScope) -> Result { + Ok(sym::null.into()) } fn length_of_array_like(&self, _sc: &mut LocalScope) -> Result { @@ -834,11 +823,11 @@ impl ValueConversion for Symbol { throw!(sc, TypeError, "Cannot convert symbol to number"); } - fn to_boolean(&self) -> Result { + fn to_boolean(&self, _: &mut LocalScope<'_>) -> Result { Ok(true) } - fn to_string(&self, sc: &mut LocalScope) -> Result, Value> { + fn to_js_string(&self, sc: &mut LocalScope) -> Result { throw!(sc, TypeError, "Cannot convert symbol to string"); } @@ -879,7 +868,7 @@ impl Object for Number { self.0.get_own_property_descriptor(sc, key) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { self.0.set_property(sc, key, value) } @@ -909,8 +898,8 @@ impl Object for Number { self } - fn own_keys(&self) -> Result, Value> { - self.0.own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + self.0.own_keys(sc) } fn type_of(&self) -> Typeof { @@ -966,12 +955,12 @@ impl ValueConversion for Number { self.0.to_number(sc) } - fn to_boolean(&self) -> Result { - self.0.to_boolean() + fn to_boolean(&self, sc: &mut LocalScope<'_>) -> Result { + self.0.to_boolean(sc) } - fn to_string(&self, sc: &mut LocalScope) -> Result, Value> { - ValueConversion::to_string(&self.0, sc) + fn to_js_string(&self, sc: &mut LocalScope) -> Result { + ValueConversion::to_js_string(&self.0, sc) } fn length_of_array_like(&self, sc: &mut LocalScope) -> Result { diff --git a/crates/dash_vm/src/value/promise.rs b/crates/dash_vm/src/value/promise.rs index 2c8f61e7..d8bd1fc6 100644 --- a/crates/dash_vm/src/value/promise.rs +++ b/crates/dash_vm/src/value/promise.rs @@ -4,7 +4,7 @@ use std::cell::RefCell; use dash_proc_macro::Trace; use crate::gc::handle::Handle; -use crate::gc::trace::Trace; +use crate::gc::trace::{Trace, TraceCtxt}; use crate::localscope::LocalScope; use crate::{PromiseAction, Vm}; @@ -22,14 +22,14 @@ pub enum PromiseState { } unsafe impl Trace for PromiseState { - fn trace(&self) { + fn trace(&self, cx: &mut TraceCtxt<'_>) { match self { Self::Pending { resolve, reject } => { - resolve.trace(); - reject.trace(); + resolve.trace(cx); + reject.trace(cx); } - Self::Resolved(v) => v.trace(), - Self::Rejected(v) => v.trace(), + Self::Resolved(v) => v.trace(cx), + Self::Rejected(v) => v.trace(cx), } } } @@ -88,7 +88,7 @@ impl Object for Promise { fn set_property( &self, sc: &mut crate::localscope::LocalScope, - key: crate::value::object::PropertyKey<'static>, + key: crate::value::object::PropertyKey, value: crate::value::object::PropertyValue, ) -> Result<(), Value> { self.obj.set_property(sc, key, value) @@ -124,8 +124,8 @@ impl Object for Promise { self } - fn own_keys(&self) -> Result, Value> { - self.obj.own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + self.obj.own_keys(sc) } } @@ -156,7 +156,7 @@ impl Object for PromiseResolver { fn set_property( &self, sc: &mut crate::localscope::LocalScope, - key: crate::value::object::PropertyKey<'static>, + key: crate::value::object::PropertyKey, value: crate::value::object::PropertyValue, ) -> Result<(), Value> { self.obj.set_property(sc, key, value) @@ -198,8 +198,8 @@ impl Object for PromiseResolver { self } - fn own_keys(&self) -> Result, Value> { - self.obj.own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + self.obj.own_keys(sc) } fn type_of(&self) -> super::Typeof { @@ -234,7 +234,7 @@ impl Object for PromiseRejecter { fn set_property( &self, sc: &mut crate::localscope::LocalScope, - key: crate::value::object::PropertyKey<'static>, + key: crate::value::object::PropertyKey, value: crate::value::object::PropertyValue, ) -> Result<(), Value> { self.obj.set_property(sc, key, value) @@ -276,8 +276,8 @@ impl Object for PromiseRejecter { self } - fn own_keys(&self) -> Result, Value> { - self.obj.own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + self.obj.own_keys(sc) } fn type_of(&self) -> super::Typeof { diff --git a/crates/dash_vm/src/value/regex.rs b/crates/dash_vm/src/value/regex.rs index 539185e8..fb94329a 100644 --- a/crates/dash_vm/src/value/regex.rs +++ b/crates/dash_vm/src/value/regex.rs @@ -1,21 +1,34 @@ use std::cell::Cell; -use std::rc::Rc; use dash_proc_macro::Trace; use dash_regex::{Flags, ParsedRegex}; +use crate::gc::trace::{Trace, TraceCtxt}; use crate::{delegate, Vm}; use super::object::{NamedObject, Object}; +use super::string::JsString; #[derive(Debug)] pub struct RegExpInner { pub regex: ParsedRegex, pub flags: Flags, - pub source: Rc, + pub source: JsString, pub last_index: Cell, } +unsafe impl Trace for RegExpInner { + fn trace(&self, cx: &mut TraceCtxt<'_>) { + let Self { + regex: _, + flags: _, + source, + last_index: _, + } = self; + source.trace(cx); + } +} + #[derive(Debug, Trace)] pub struct RegExp { inner: Option, @@ -23,7 +36,7 @@ pub struct RegExp { } impl RegExp { - pub fn new(regex: ParsedRegex, flags: Flags, source: Rc, vm: &Vm) -> Self { + pub fn new(regex: ParsedRegex, flags: Flags, source: JsString, vm: &Vm) -> Self { let proto = vm.statics.regexp_prototype.clone(); let ctor = vm.statics.regexp_ctor.clone(); diff --git a/crates/dash_vm/src/value/string.rs b/crates/dash_vm/src/value/string.rs new file mode 100644 index 00000000..6e86785c --- /dev/null +++ b/crates/dash_vm/src/value/string.rs @@ -0,0 +1,181 @@ +use dash_middle::interner::sym; +use dash_proc_macro::Trace; + +use crate::gc::interner::Symbol; +use crate::localscope::LocalScope; +use crate::throw; +use crate::value::boxed::String as BoxedString; + +use super::object::{Object, PropertyKey, PropertyValue}; +use super::ops::conversions::{PreferredType, ValueConversion}; +use super::ops::equality::ValueEquality; +use super::primitive::{array_like_keys, PrimitiveCapabilities}; +use super::{Typeof, Unrooted, Value}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Trace)] +pub struct JsString { + sym: Symbol, +} + +impl From for JsString { + fn from(sym: Symbol) -> Self { + Self { sym } + } +} + +impl JsString { + pub fn sym(self) -> Symbol { + self.sym + } + + pub fn res<'a>(self, sc: &'a LocalScope<'_>) -> &'a str { + sc.interner.resolve(self.sym) + } + + pub fn len(self, sc: &mut LocalScope<'_>) -> usize { + self.res(&sc).len() + } +} + +impl ValueEquality for JsString { + fn lt(&self, other: &super::Value, sc: &mut LocalScope) -> Result { + let other = other.to_js_string(sc)?; + Ok(Value::Boolean(self.res(&sc) < other.res(&sc))) + } + + fn le(&self, other: &super::Value, sc: &mut LocalScope) -> Result { + let other = other.to_js_string(sc)?; + Ok(Value::Boolean(self.res(&sc) <= other.res(&sc))) + } + + fn gt(&self, other: &super::Value, sc: &mut LocalScope) -> Result { + let other = other.to_js_string(sc)?; + Ok(Value::Boolean(self.res(&sc) > other.res(&sc))) + } + + fn ge(&self, other: &super::Value, sc: &mut LocalScope) -> Result { + let other = other.to_js_string(sc)?; + Ok(Value::Boolean(self.res(&sc) >= other.res(&sc))) + } + + fn eq(&self, other: &super::Value, sc: &mut LocalScope) -> Result { + Ok(Value::Boolean(*self == other.to_js_string(sc)?)) + } + + fn strict_eq(&self, other: &Value, _: &mut LocalScope) -> Result { + if let Value::String(other) = other { + Ok(Value::Boolean(self == other)) + } else { + Ok(Value::Boolean(false)) + } + } +} + +impl ValueConversion for JsString { + fn to_primitive(&self, _: &mut LocalScope, _: Option) -> Result { + Ok(Value::String(self.clone())) + } + + fn to_number(&self, sc: &mut LocalScope) -> Result { + Ok(self.res(sc).parse().unwrap_or(f64::NAN)) + } + + fn to_boolean(&self, sc: &mut LocalScope<'_>) -> Result { + Ok(!self.res(sc).is_empty()) + } + + fn to_js_string(&self, _: &mut LocalScope) -> Result { + Ok(self.clone()) + } + + fn length_of_array_like(&self, sc: &mut LocalScope) -> Result { + Ok(self.res(sc).len()) + } + + fn to_object(&self, sc: &mut LocalScope) -> Result, Value> { + let bx = BoxedString::new(sc, self.clone()); + Ok(sc.register(bx)) + } +} + +impl Object for JsString { + fn get_own_property_descriptor( + &self, + sc: &mut LocalScope, + key: super::object::PropertyKey, + ) -> Result, super::Unrooted> { + if let PropertyKey::String(st) = key { + if st.sym() == sym::length { + return Ok(Some(PropertyValue::static_default(Value::number(self.len(sc) as f64)))); + } + + if let Ok(index) = st.res(sc).parse::() { + let bytes = self.res(sc).as_bytes(); + if let Some(&byte) = bytes.get(index) { + let s = sc.intern((byte as char).to_string().as_ref()); + return Ok(Some(PropertyValue::static_default(Value::String(s.into())))); + } + } + } + + Ok(None) + } + + fn set_property( + &self, + _: &mut LocalScope, + _: super::object::PropertyKey, + _: super::object::PropertyValue, + ) -> Result<(), Value> { + Ok(()) + } + + fn delete_property(&self, _: &mut LocalScope, _: super::object::PropertyKey) -> Result { + Ok(Unrooted::new(Value::undefined())) + } + + fn set_prototype(&self, _: &mut LocalScope, _: Value) -> Result<(), Value> { + Ok(()) + } + + fn get_prototype(&self, sc: &mut LocalScope) -> Result { + Ok(sc.statics.string_prototype.clone().into()) + } + + fn apply( + &self, + scope: &mut LocalScope, + _: crate::gc::handle::Handle, + _: Value, + _: Vec, + ) -> Result { + throw!(scope, TypeError, "string is not a function") + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + let len = self.len(sc); + Ok(array_like_keys(sc, len).collect()) + } + + fn type_of(&self) -> Typeof { + Typeof::String + } + + fn as_primitive_capable(&self) -> Option<&dyn PrimitiveCapabilities> { + Some(self) + } +} + +impl PrimitiveCapabilities for JsString { + fn as_string(&self) -> Option { + Some(self.clone()) + } + + fn unbox(&self) -> Value { + Value::String(self.clone()) + } +} diff --git a/crates/dash_vm/src/value/typedarray.rs b/crates/dash_vm/src/value/typedarray.rs index a15e1833..d4998748 100644 --- a/crates/dash_vm/src/value/typedarray.rs +++ b/crates/dash_vm/src/value/typedarray.rs @@ -83,7 +83,7 @@ impl Object for TypedArray { sc: &mut LocalScope, key: PropertyKey, ) -> Result, Unrooted> { - if let Some(Ok(index)) = key.as_string().map(|k| k.parse::()) { + if let Some(Ok(index)) = key.as_string().map(|k| k.res(sc).parse::()) { let arraybuffer = self.arraybuffer.as_any().downcast_ref::(); if let Some(arraybuffer) = arraybuffer { @@ -127,8 +127,8 @@ impl Object for TypedArray { self.obj.get_own_property_descriptor(sc, key) } - fn set_property(&self, sc: &mut LocalScope, key: PropertyKey<'static>, value: PropertyValue) -> Result<(), Value> { - if let Some(Ok(index)) = key.as_string().map(|k| k.parse::()) { + fn set_property(&self, sc: &mut LocalScope, key: PropertyKey, value: PropertyValue) -> Result<(), Value> { + if let Some(Ok(index)) = key.as_string().map(|k| k.res(sc).parse::()) { let arraybuffer = self.arraybuffer.as_any().downcast_ref::(); // TODO: not undefined as this @@ -199,7 +199,7 @@ impl Object for TypedArray { self } - fn own_keys(&self) -> Result, Value> { - self.obj.own_keys() + fn own_keys(&self, sc: &mut LocalScope<'_>) -> Result, Value> { + self.obj.own_keys(sc) } } diff --git a/crates/dash_wasm/src/externalfunction.rs b/crates/dash_wasm/src/externalfunction.rs index abe6e4f5..97d926b4 100644 --- a/crates/dash_wasm/src/externalfunction.rs +++ b/crates/dash_wasm/src/externalfunction.rs @@ -1,5 +1,5 @@ use dash_vm::gc::handle::Handle; -use dash_vm::gc::trace::Trace; +use dash_vm::gc::trace::{Trace, TraceCtxt}; use dash_vm::localscope::LocalScope; use dash_vm::value::object::{NamedObject, Object, PropertyKey, PropertyValue}; use dash_vm::value::{Typeof, Unrooted, Value}; @@ -16,8 +16,8 @@ impl ExternalFunction { } unsafe impl Trace for ExternalFunction { - fn trace(&self) { - self.1.trace(); + fn trace(&self, cx: &mut TraceCtxt<'_>) { + self.1.trace(cx); } } diff --git a/crates/dash_wasm/src/jsvalue.rs b/crates/dash_wasm/src/jsvalue.rs index bdee2632..2999d6b2 100644 --- a/crates/dash_wasm/src/jsvalue.rs +++ b/crates/dash_wasm/src/jsvalue.rs @@ -53,7 +53,7 @@ impl JsValue { } pub fn to_js_string(&self, vm: &mut ExternalVm) -> Result { - vm.with_scope(|scope| self.0.to_string(scope).map_err(JsValue).map(|s| s.as_ref().into())) + vm.with_scope(|scope| self.0.to_js_string(scope).map_err(JsValue).map(|s| s.as_ref().into())) } pub fn set_property(&self, vm: &mut ExternalVm, key: String, value: JsValue) -> Result<(), JsValue> { diff --git a/crates/dash_wasm/src/lib.rs b/crates/dash_wasm/src/lib.rs index cdc83207..77283301 100755 --- a/crates/dash_wasm/src/lib.rs +++ b/crates/dash_wasm/src/lib.rs @@ -71,7 +71,7 @@ pub fn evaluate(s: &str, opt: OptLevel, _context: Option) -> Res pub fn fmt_value(value: Value, vm: &mut Vm) -> String { let mut scope = vm.scope(); value - .to_string(&mut scope) + .to_js_string(&mut scope) .map(|s| ToString::to_string(&s)) .unwrap_or_else(|_| "".into()) } diff --git a/testrunner/.cargo/config.toml b/testrunner/.cargo/config.toml new file mode 100644 index 00000000..6cbd5f2d --- /dev/null +++ b/testrunner/.cargo/config.toml @@ -0,0 +1,4 @@ + +[profile.dev] +# override cranelift because the testrunner WILL require unwinding support +codegen-backend = "llvm" diff --git a/testrunner/src/cmd/run.rs b/testrunner/src/cmd/run.rs index cd992cc5..599bfd7c 100644 --- a/testrunner/src/cmd/run.rs +++ b/testrunner/src/cmd/run.rs @@ -142,20 +142,18 @@ fn run_test(setup: &str, path: &OsStr, verbose: bool) -> RunResult { (Ok(_), Some(..)) => RunResult::Fail, (Err(err), negative) => { let result = match (&err, negative) { - ((EvalError::Middle(..), _), Some(NegativePhase::Parse | NegativePhase::Resolution)) => { - RunResult::Pass - } - ((EvalError::Middle(..), _), None) => RunResult::Fail, - ((EvalError::Exception(..), _), Some(NegativePhase::Runtime)) => RunResult::Pass, - ((EvalError::Exception(..), _), None) => RunResult::Fail, + (EvalError::Middle(..), Some(NegativePhase::Parse | NegativePhase::Resolution)) => RunResult::Pass, + (EvalError::Middle(..), None) => RunResult::Fail, + (EvalError::Exception(..), Some(NegativePhase::Runtime)) => RunResult::Pass, + (EvalError::Exception(..), None) => RunResult::Fail, (_, Some(..)) => RunResult::Fail, }; if let RunResult::Fail = result { if verbose { let s = match &err { - (EvalError::Middle(errs), _) => format!("{errs:?}"), - (EvalError::Exception(_ex), _) => { + EvalError::Middle(errs) => format!("{errs:?}"), + EvalError::Exception(_ex) => { // let mut sc = LocalScope::new(&mut vm); // match ex.to_string(&mut sc) { // Ok(s) => ToString::to_string(&s), diff --git a/testrunner/src/util.rs b/testrunner/src/util.rs index a6d86c3a..6f636cc3 100644 --- a/testrunner/src/util.rs +++ b/testrunner/src/util.rs @@ -1,5 +1,4 @@ -use std::ffi::OsStr; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::fs::DirEntry; use std::io; @@ -59,6 +58,24 @@ pub const IGNORED_TESTS: &[&str] = &[ "try/S12.14_A9_T1.js", "while/S12.6.2_A9.js", "RegExp/S15.10.2_A1_T1.js", + // `[^]+` causes an infinite loop during regex AnchorStart+Repetition + "S15.10.2.13_A3_T3.js", + "S15.10.2.13_A3_T4.js", + "S15.10.2.13_A3_T5.js", + "S15.10.2.13_A3_T6.js", + "S15.10.2.13_A3_T7.js", + "S15.10.2.13_A3_T8.js", + "S15.10.2.13_A3_T9.js", + "S15.10.2.7_A4_T1.js", + "S15.10.2.7_A4_T2.js", + "S15.10.2.7_A4_T3.js", + "S15.10.2.7_A4_T4.js", + "S15.10.2.7_A4_T5.js", + "S15.10.2.7_A4_T6.js", + "S15.10.2.7_A4_T7.js", + "S15.10.2.7_A4_T8.js", + "S15.10.2.7_A4_T9.js", + "S15.10.2.13_A2_T4.js", ]; /// Returns a vector of path strings