diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index 6023f4d3..8624e018 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -6,9 +6,9 @@ use crate::symbol_table::{Binding, SymbolTable}; pub use crate::hash::structural_hash; pub use crate::module::Constant; +use crate::symbol_table::ScopeKind; pub use oxide_parser::VariableDeclarationKind; pub use oxide_parser::{AssignmentOperator, BinaryOperator, Expression, Statement, UnaryOperator}; -use crate::symbol_table::ScopeKind; pub struct Compiler; diff --git a/crates/oxide_compiler/src/counter.rs b/crates/oxide_compiler/src/counter.rs index a844009a..a43e76ee 100644 --- a/crates/oxide_compiler/src/counter.rs +++ b/crates/oxide_compiler/src/counter.rs @@ -190,7 +190,8 @@ impl Compiler { // Hoisting: declare function name as initialized let func_reg = ctx.alloc_reg(); - let _ = ctx.declare_initialized(&name, func_reg, VariableDeclarationKind::Var, false); + let _ = + ctx.declare_initialized(&name, func_reg, VariableDeclarationKind::Var, false); // Body is compiled in the emit pass only. // FD emits LOAD_CONST(BytecodeFunc) + STORE_VAR diff --git a/crates/oxide_compiler/src/emitter.rs b/crates/oxide_compiler/src/emitter.rs index ac1fda7a..34af9b88 100644 --- a/crates/oxide_compiler/src/emitter.rs +++ b/crates/oxide_compiler/src/emitter.rs @@ -34,7 +34,12 @@ impl Compiler { if let Some(init) = &d.init { let val_reg = self.emit_expression(init, ctx)?; let const_flag = if is_const { 1 } else { 0 }; - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, const_flag)); + ctx.emit(opcode::encode( + OpCode::STORE_VAR, + var_reg, + val_reg, + const_flag, + )); ctx.init_var(name); // Name inference (D-04): if the initializer is an arrow function, // set the compiled sub_module's function_name. @@ -192,7 +197,12 @@ impl Compiler { _ => return Err("destructuring not supported".into()), }; let var_reg = ctx.alloc_reg(); - ctx.declare(name, var_reg, decl.kind, matches!(decl.kind, VariableDeclarationKind::Const))?; + ctx.declare( + name, + var_reg, + decl.kind, + matches!(decl.kind, VariableDeclarationKind::Const), + )?; if let Some(init_expr) = &d.init { let val_reg = self.emit_expression(init_expr, ctx)?; ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, 0)); @@ -254,7 +264,12 @@ impl Compiler { _ => return Err("destructuring not supported".into()), }; let var_reg = ctx.alloc_reg(); - ctx.declare(name, var_reg, decl.kind, matches!(decl.kind, VariableDeclarationKind::Const))?; + ctx.declare( + name, + var_reg, + decl.kind, + matches!(decl.kind, VariableDeclarationKind::Const), + )?; ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, key_reg, 0)); ctx.init_var(name); } @@ -622,30 +637,34 @@ impl Compiler { ctx.emit(opcode::encode(OpCode::UNARY_PLUS, r, arg, 0)); Ok(r) } - UnaryOperator::Delete => { - match &un.argument { - Expression::Identifier(_) => { - Err("delete of global variable is not allowed in strict mode".into()) - } - Expression::StaticMemberExpression(member) => { - let obj_reg = self.emit_expression(&member.object, ctx)?; - let prop_name = member.property.name.as_str(); - let const_idx = ctx.add_constant(Constant::String(prop_name.to_string())); - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::DELETE_PROP_STATIC, r, obj_reg, 0)); - ctx.emit(const_idx as u32); - Ok(r) - } - Expression::ComputedMemberExpression(member) => { - let obj_reg = self.emit_expression(&member.object, ctx)?; - let key_reg = self.emit_expression(&member.expression, ctx)?; - let r = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::DELETE_PROP_DYNAMIC, r, obj_reg, key_reg)); - Ok(r) - } - _ => Err("invalid delete target".into()), + UnaryOperator::Delete => match &un.argument { + Expression::Identifier(_) => { + Err("delete of global variable is not allowed in strict mode".into()) } - } + Expression::StaticMemberExpression(member) => { + let obj_reg = self.emit_expression(&member.object, ctx)?; + let prop_name = member.property.name.as_str(); + let const_idx = + ctx.add_constant(Constant::String(prop_name.to_string())); + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode(OpCode::DELETE_PROP_STATIC, r, obj_reg, 0)); + ctx.emit(const_idx as u32); + Ok(r) + } + Expression::ComputedMemberExpression(member) => { + let obj_reg = self.emit_expression(&member.object, ctx)?; + let key_reg = self.emit_expression(&member.expression, ctx)?; + let r = ctx.alloc_reg(); + ctx.emit(opcode::encode( + OpCode::DELETE_PROP_DYNAMIC, + r, + obj_reg, + key_reg, + )); + Ok(r) + } + _ => Err("invalid delete target".into()), + }, _ => Err(format!("unsupported unary operator: {:?}", un.operator)), } } @@ -902,7 +921,12 @@ impl Compiler { let var_reg = ctx.lookup_or_global(name); let is_const = ctx.lookup_const_flag(name); let const_flag = if is_const { 1 } else { 0 }; - ctx.emit(opcode::encode(OpCode::STORE_VAR, var_reg, val_reg, const_flag)); + ctx.emit(opcode::encode( + OpCode::STORE_VAR, + var_reg, + val_reg, + const_flag, + )); Ok(val_reg) } } else { diff --git a/crates/oxide_compiler/src/symbol_table.rs b/crates/oxide_compiler/src/symbol_table.rs index 7d5fe257..eeee8450 100644 --- a/crates/oxide_compiler/src/symbol_table.rs +++ b/crates/oxide_compiler/src/symbol_table.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; - use oxide_parser::VariableDeclarationKind; +use oxide_parser::VariableDeclarationKind; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum ScopeKind { @@ -190,7 +190,7 @@ impl SymbolTable { #[cfg(test)] mod tests { use super::SymbolTable; -use oxide_parser::VariableDeclarationKind; + use oxide_parser::VariableDeclarationKind; fn v() -> VariableDeclarationKind { VariableDeclarationKind::Var diff --git a/crates/oxide_vm/src/bindings/bind_error.rs b/crates/oxide_vm/src/bindings/bind_error.rs index dcdbb414..e64a8ef9 100644 --- a/crates/oxide_vm/src/bindings/bind_error.rs +++ b/crates/oxide_vm/src/bindings/bind_error.rs @@ -32,12 +32,54 @@ pub fn bind_error(kernel: &Arc, global: &mut JsObject) { global.ensure_hash_props().push(Box::new(err_val)); global.bump_generation(); - bind_constructor_hash!(kernel, global, "TypeError", kernel.builtin_world().type_error_proto.as_ptr() as *mut JsObject, crate::builtins::error::type_error_constructor, 1); - bind_constructor_hash!(kernel, global, "ReferenceError", kernel.builtin_world().reference_error_proto.as_ptr() as *mut JsObject, crate::builtins::error::reference_error_constructor, 1); - bind_constructor_hash!(kernel, global, "RangeError", kernel.builtin_world().range_error_proto.as_ptr() as *mut JsObject, crate::builtins::error::range_error_constructor, 1); - bind_constructor_hash!(kernel, global, "SyntaxError", kernel.builtin_world().syntax_error_proto.as_ptr() as *mut JsObject, crate::builtins::error::syntax_error_constructor, 1); - bind_constructor_hash!(kernel, global, "URIError", kernel.builtin_world().uri_error_proto.as_ptr() as *mut JsObject, crate::builtins::error::uri_error_constructor, 1); - bind_constructor_hash!(kernel, global, "EvalError", kernel.builtin_world().eval_error_proto.as_ptr() as *mut JsObject, crate::builtins::error::eval_error_constructor, 1); + bind_constructor_hash!( + kernel, + global, + "TypeError", + kernel.builtin_world().type_error_proto.as_ptr() as *mut JsObject, + crate::builtins::error::type_error_constructor, + 1 + ); + bind_constructor_hash!( + kernel, + global, + "ReferenceError", + kernel.builtin_world().reference_error_proto.as_ptr() as *mut JsObject, + crate::builtins::error::reference_error_constructor, + 1 + ); + bind_constructor_hash!( + kernel, + global, + "RangeError", + kernel.builtin_world().range_error_proto.as_ptr() as *mut JsObject, + crate::builtins::error::range_error_constructor, + 1 + ); + bind_constructor_hash!( + kernel, + global, + "SyntaxError", + kernel.builtin_world().syntax_error_proto.as_ptr() as *mut JsObject, + crate::builtins::error::syntax_error_constructor, + 1 + ); + bind_constructor_hash!( + kernel, + global, + "URIError", + kernel.builtin_world().uri_error_proto.as_ptr() as *mut JsObject, + crate::builtins::error::uri_error_constructor, + 1 + ); + bind_constructor_hash!( + kernel, + global, + "EvalError", + kernel.builtin_world().eval_error_proto.as_ptr() as *mut JsObject, + crate::builtins::error::eval_error_constructor, + 1 + ); { let err_ctor_ptr = kernel.builtin_world().error_constructor.as_ptr() as *mut JsObject; diff --git a/crates/oxide_vm/src/bindings/bind_number.rs b/crates/oxide_vm/src/bindings/bind_number.rs index b973ec68..e2617483 100644 --- a/crates/oxide_vm/src/bindings/bind_number.rs +++ b/crates/oxide_vm/src/bindings/bind_number.rs @@ -43,44 +43,72 @@ pub fn bind_number(kernel: &Arc, global: &mut JsObject) { )); num_ctor.set_native_arg_count(1); - num_ctor.ensure_hash_props().push(Box::new(JsValue::float(2.220446049250313e-16))); + num_ctor + .ensure_hash_props() + .push(Box::new(JsValue::float(2.220446049250313e-16))); let eps_si = kernel.string_forge().intern("EPSILON").0; let _shape1 = kernel.shape_forge().make_shape(num_ctor.shape_id(), eps_si); num_ctor.set_shape_id(_shape1); - num_ctor.ensure_hash_props().push(Box::new(JsValue::float(9007199254740991f64))); + num_ctor + .ensure_hash_props() + .push(Box::new(JsValue::float(9007199254740991f64))); let max_safe_si = kernel.string_forge().intern("MAX_SAFE_INTEGER").0; - let _shape2 = kernel.shape_forge().make_shape(num_ctor.shape_id(), max_safe_si); + let _shape2 = kernel + .shape_forge() + .make_shape(num_ctor.shape_id(), max_safe_si); num_ctor.set_shape_id(_shape2); - num_ctor.ensure_hash_props().push(Box::new(JsValue::float(-9007199254740991f64))); + num_ctor + .ensure_hash_props() + .push(Box::new(JsValue::float(-9007199254740991f64))); let min_safe_si = kernel.string_forge().intern("MIN_SAFE_INTEGER").0; - let _shape3 = kernel.shape_forge().make_shape(num_ctor.shape_id(), min_safe_si); + let _shape3 = kernel + .shape_forge() + .make_shape(num_ctor.shape_id(), min_safe_si); num_ctor.set_shape_id(_shape3); - num_ctor.ensure_hash_props().push(Box::new(JsValue::float(1.7976931348623157e308))); + num_ctor + .ensure_hash_props() + .push(Box::new(JsValue::float(1.7976931348623157e308))); let max_val_si = kernel.string_forge().intern("MAX_VALUE").0; - let _shape4 = kernel.shape_forge().make_shape(num_ctor.shape_id(), max_val_si); + let _shape4 = kernel + .shape_forge() + .make_shape(num_ctor.shape_id(), max_val_si); num_ctor.set_shape_id(_shape4); - num_ctor.ensure_hash_props().push(Box::new(JsValue::float(5e-324))); + num_ctor + .ensure_hash_props() + .push(Box::new(JsValue::float(5e-324))); let min_val_si = kernel.string_forge().intern("MIN_VALUE").0; - let _shape5 = kernel.shape_forge().make_shape(num_ctor.shape_id(), min_val_si); + let _shape5 = kernel + .shape_forge() + .make_shape(num_ctor.shape_id(), min_val_si); num_ctor.set_shape_id(_shape5); - num_ctor.ensure_hash_props().push(Box::new(JsValue::float(f64::NAN))); + num_ctor + .ensure_hash_props() + .push(Box::new(JsValue::float(f64::NAN))); let nan_si = kernel.string_forge().intern("NaN").0; let _shape6 = kernel.shape_forge().make_shape(num_ctor.shape_id(), nan_si); num_ctor.set_shape_id(_shape6); - num_ctor.ensure_hash_props().push(Box::new(JsValue::float(f64::NEG_INFINITY))); + num_ctor + .ensure_hash_props() + .push(Box::new(JsValue::float(f64::NEG_INFINITY))); let neg_inf_si = kernel.string_forge().intern("NEGATIVE_INFINITY").0; - let _shape7 = kernel.shape_forge().make_shape(num_ctor.shape_id(), neg_inf_si); + let _shape7 = kernel + .shape_forge() + .make_shape(num_ctor.shape_id(), neg_inf_si); num_ctor.set_shape_id(_shape7); - num_ctor.ensure_hash_props().push(Box::new(JsValue::float(f64::INFINITY))); + num_ctor + .ensure_hash_props() + .push(Box::new(JsValue::float(f64::INFINITY))); let pos_inf_si = kernel.string_forge().intern("POSITIVE_INFINITY").0; - let _shape8 = kernel.shape_forge().make_shape(num_ctor.shape_id(), pos_inf_si); + let _shape8 = kernel + .shape_forge() + .make_shape(num_ctor.shape_id(), pos_inf_si); num_ctor.set_shape_id(_shape8); } diff --git a/crates/oxide_vm/src/bindings/bind_regexp.rs b/crates/oxide_vm/src/bindings/bind_regexp.rs index c25e2711..c861cae3 100644 --- a/crates/oxide_vm/src/bindings/bind_regexp.rs +++ b/crates/oxide_vm/src/bindings/bind_regexp.rs @@ -14,7 +14,9 @@ pub fn bind_regexp(kernel: &Arc, global: &mut JsObject) { let sf = kernel.string_forge().as_ref(); let sh = kernel.shape_forge().as_ref(); - ctor.set_native_fn(Some(crate::builtins::regexp::regexp_constructor as *const ())); + ctor.set_native_fn(Some( + crate::builtins::regexp::regexp_constructor as *const (), + )); ctor.set_native_arg_count(2); bind_method!( diff --git a/crates/oxide_vm/src/bindings/bind_symbol.rs b/crates/oxide_vm/src/bindings/bind_symbol.rs index 0d5b260e..a55c3842 100644 --- a/crates/oxide_vm/src/bindings/bind_symbol.rs +++ b/crates/oxide_vm/src/bindings/bind_symbol.rs @@ -25,29 +25,24 @@ pub fn bind_symbol(kernel: &Arc, global: &mut JsObject) { 0 ); - let sym_match_val = JsValue::from_js_object( - kernel.builtin_world().sym_match.as_ptr() as *mut JsObject, - ); + let sym_match_val = + JsValue::from_js_object(kernel.builtin_world().sym_match.as_ptr() as *mut JsObject); bind_well_known_symbol(kernel, ctor, sf, sh, "match", sym_match_val); - let sym_replace_val = JsValue::from_js_object( - kernel.builtin_world().sym_replace.as_ptr() as *mut JsObject, - ); + let sym_replace_val = + JsValue::from_js_object(kernel.builtin_world().sym_replace.as_ptr() as *mut JsObject); bind_well_known_symbol(kernel, ctor, sf, sh, "replace", sym_replace_val); - let sym_search_val = JsValue::from_js_object( - kernel.builtin_world().sym_search.as_ptr() as *mut JsObject, - ); + let sym_search_val = + JsValue::from_js_object(kernel.builtin_world().sym_search.as_ptr() as *mut JsObject); bind_well_known_symbol(kernel, ctor, sf, sh, "search", sym_search_val); - let sym_split_val = JsValue::from_js_object( - kernel.builtin_world().sym_split.as_ptr() as *mut JsObject, - ); + let sym_split_val = + JsValue::from_js_object(kernel.builtin_world().sym_split.as_ptr() as *mut JsObject); bind_well_known_symbol(kernel, ctor, sf, sh, "split", sym_split_val); - let sym_iterator_val = JsValue::from_js_object( - kernel.builtin_world().sym_iterator.as_ptr() as *mut JsObject, - ); + let sym_iterator_val = + JsValue::from_js_object(kernel.builtin_world().sym_iterator.as_ptr() as *mut JsObject); bind_well_known_symbol(kernel, ctor, sf, sh, "iterator", sym_iterator_val); bind_constructor_hash!( diff --git a/crates/oxide_vm/src/builtins/array.rs b/crates/oxide_vm/src/builtins/array.rs index 2784d9a9..0f4e004c 100644 --- a/crates/oxide_vm/src/builtins/array.rs +++ b/crates/oxide_vm/src/builtins/array.rs @@ -692,7 +692,9 @@ pub fn array_shift(vm: &mut Vm, args: &[u8]) -> NativeResult { let arr_ptr = get_this_array_ref(vm.reg(args[0]))?; let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count(); - if len == 0 { return Ok(JsValue::undefined()); } + if len == 0 { + return Ok(JsValue::undefined()); + } let first = arr.get_prop_at(0); for i in 1..len { let v = arr.get_prop_at(i); @@ -723,15 +725,26 @@ pub fn array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult { let arr_ptr = get_this_array_ref(vm.reg(args[0]))?; let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count() as usize; - let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let value = if args.len() > 1 { + vm.reg(args[1]) + } else { + JsValue::undefined() + }; let start = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32).max(0) as usize - } else { 0 }; + (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32).max(0) + as usize + } else { + 0 + }; let end = if args.len() > 3 { let e = coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32; (e as usize).min(len) - } else { len }; - for i in start..end { arr.set_prop_at(i as u8, value); } + } else { + len + }; + for i in start..end { + arr.set_prop_at(i as u8, value); + } Ok(vm.reg(args[0])) } @@ -739,13 +752,30 @@ pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult { let arr_ptr = get_this_array_ref(vm.reg(args[0]))?; let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count() as usize; - if len == 0 { return Ok(vm.reg(args[0])); } - let target = if args.len() > 1 { (coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32) as usize } else { 0 }; - let start = if args.len() > 2 { (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32) as usize } else { 0 }; - let end = if args.len() > 3 { (coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32 as usize).min(len) } else { len }; + if len == 0 { + return Ok(vm.reg(args[0])); + } + let target = if args.len() > 1 { + (coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32) as usize + } else { + 0 + }; + let start = if args.len() > 2 { + (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32) as usize + } else { + 0 + }; + let end = if args.len() > 3 { + (coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32 as usize) + .min(len) + } else { + len + }; let mut to = target; for from in start..end { - if to >= len { break; } + if to >= len { + break; + } let v = arr.get_prop_at(from as u8); arr.set_prop_at(to as u8, v); to += 1; @@ -757,9 +787,17 @@ pub fn array_at(vm: &mut Vm, args: &[u8]) -> NativeResult { let arr_ptr = get_this_array_ref(vm.reg(args[0]))?; let arr = unsafe { &*arr_ptr }; let len = arr.prop_count() as i32; - let mut index = if args.len() > 1 { coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32 } else { 0 }; - if index < 0 { index += len; } - if index < 0 || index >= len { return Ok(JsValue::undefined()); } + let mut index = if args.len() > 1 { + coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32 + } else { + 0 + }; + if index < 0 { + index += len; + } + if index < 0 || index >= len { + return Ok(JsValue::undefined()); + } Ok(arr.get_prop_at(index as u8)) } @@ -767,7 +805,11 @@ pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult { let arr_ptr = get_this_array_ref(vm.reg(args[0]))?; let arr = unsafe { &*arr_ptr }; let len = arr.prop_count() as i32; - let search = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; + let search = if args.len() > 1 { + vm.reg(args[1]) + } else { + JsValue::undefined() + }; for i in (0..len).rev() { if coercion::strict_equality(arr.get_prop_at(i as u8), search) { return Ok(JsValue::int(i)); @@ -779,13 +821,28 @@ pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult { let arr_ptr = get_this_array_ref(vm.reg(args[0]))?; let n = unsafe { (*arr_ptr).prop_count() } as usize; - if args.len() < 2 { return Ok(JsValue::int(-1)); } + if args.len() < 2 { + return Ok(JsValue::int(-1)); + } let callback_val = vm.reg(args[1]); - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + let this_val = if args.len() > 2 { + vm.reg(args[2]) + } else { + JsValue::undefined() + }; for i in 0..n { let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) }; - match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { - Ok(r) => { if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) { return Ok(JsValue::int(i as i32)); } } + match invoke_native_callback( + vm, + callback_val, + this_val, + &[elem, JsValue::int(i as i32), vm.reg(args[0])], + ) { + Ok(r) => { + if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) { + return Ok(JsValue::int(i as i32)); + } + } Err(_) => return Err(JsValue::undefined()), } } @@ -795,13 +852,28 @@ pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult { let arr_ptr = get_this_array_ref(vm.reg(args[0]))?; let n = unsafe { (*arr_ptr).prop_count() } as i32; - if args.len() < 2 { return Ok(JsValue::undefined()); } + if args.len() < 2 { + return Ok(JsValue::undefined()); + } let callback_val = vm.reg(args[1]); - let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; + let this_val = if args.len() > 2 { + vm.reg(args[2]) + } else { + JsValue::undefined() + }; for i in (0..n).rev() { let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) }; - match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i), vm.reg(args[0])]) { - Ok(r) => { if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) { return Ok(elem); } } + match invoke_native_callback( + vm, + callback_val, + this_val, + &[elem, JsValue::int(i), vm.reg(args[0])], + ) { + Ok(r) => { + if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) { + return Ok(elem); + } + } Err(_) => return Err(JsValue::undefined()), } } @@ -811,17 +883,26 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn array_reduce_right(vm: &mut Vm, args: &[u8]) -> NativeResult { let arr_ptr = get_this_array_ref(vm.reg(args[0]))?; let n = unsafe { (*arr_ptr).prop_count() } as usize; - if args.len() < 2 { return Err(JsValue::undefined()); } + if args.len() < 2 { + return Err(JsValue::undefined()); + } let callback_val = vm.reg(args[1]); let (mut acc, start_idx): (JsValue, i32) = if args.len() > 2 { (vm.reg(args[2]), n as i32 - 1) } else { - if n == 0 { return Err(JsValue::undefined()); } + if n == 0 { + return Err(JsValue::undefined()); + } (unsafe { (*arr_ptr).get_prop_at(n as u8 - 1) }, n as i32 - 2) }; for i in (0..=start_idx).rev() { let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) }; - match invoke_native_callback(vm, callback_val, JsValue::undefined(), &[acc, elem, JsValue::int(i), vm.reg(args[0])]) { + match invoke_native_callback( + vm, + callback_val, + JsValue::undefined(), + &[acc, elem, JsValue::int(i), vm.reg(args[0])], + ) { Ok(r) => acc = r, Err(_) => return Err(JsValue::undefined()), } @@ -839,6 +920,8 @@ pub fn array_sort(vm: &mut Vm, _args: &[u8]) -> NativeResult { let sb = coercion::to_string(vm.kernel().string_forge().as_ref(), *b); sa.cmp(&sb) }); - for (i, &v) in vals.iter().enumerate() { arr.set_prop_at(i as u8, v); } + for (i, &v) in vals.iter().enumerate() { + arr.set_prop_at(i as u8, v); + } Ok(vm.reg(_args[0])) } diff --git a/crates/oxide_vm/src/builtins/error.rs b/crates/oxide_vm/src/builtins/error.rs index 0e7aa220..8fedd48d 100644 --- a/crates/oxide_vm/src/builtins/error.rs +++ b/crates/oxide_vm/src/builtins/error.rs @@ -121,14 +121,29 @@ pub fn error_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); let (name, msg) = if this_val.is_object() { let obj = unsafe { &*this_val.as_js_object_ptr() }; - let name_val = obj.hash_props_vec().and_then(|v| v.first()).map(|b| **b).unwrap_or(JsValue::undefined()); - let msg_val = obj.hash_props_vec().and_then(|v| v.get(1)).map(|b| **b).unwrap_or(JsValue::undefined()); - (vm.lookup_str(name_val).unwrap_or_else(|| "Error".to_string()), - vm.lookup_str(msg_val).unwrap_or_default()) + let name_val = obj + .hash_props_vec() + .and_then(|v| v.first()) + .map(|b| **b) + .unwrap_or(JsValue::undefined()); + let msg_val = obj + .hash_props_vec() + .and_then(|v| v.get(1)) + .map(|b| **b) + .unwrap_or(JsValue::undefined()); + ( + vm.lookup_str(name_val) + .unwrap_or_else(|| "Error".to_string()), + vm.lookup_str(msg_val).unwrap_or_default(), + ) } else { ("Error".to_string(), String::new()) }; - let result = if msg.is_empty() { name } else { format!("{}: {}", name, msg) }; + let result = if msg.is_empty() { + name + } else { + format!("{}: {}", name, msg) + }; let si = vm.kernel().string_forge().intern(&result).0; Ok(JsValue::string(si, 0)) } @@ -136,7 +151,11 @@ pub fn error_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn error_stack_getter(vm: &mut Vm, _args: &[u8]) -> NativeResult { let mut lines = vec![]; for frame in vm.frames.iter().rev() { - let func_name = vm.kernel().string_forge().lookup(frame.function_name).unwrap_or_else(|| "".to_string()); + let func_name = vm + .kernel() + .string_forge() + .lookup(frame.function_name) + .unwrap_or_else(|| "".to_string()); lines.push(format!(" at {} (native)", func_name)); } lines.push(" at (native)".to_string()); diff --git a/crates/oxide_vm/src/builtins/function.rs b/crates/oxide_vm/src/builtins/function.rs index e5fe132d..730c90e0 100644 --- a/crates/oxide_vm/src/builtins/function.rs +++ b/crates/oxide_vm/src/builtins/function.rs @@ -160,7 +160,11 @@ pub fn function_bind(vm: &mut Vm, args: &[u8]) -> NativeResult { } pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { - let name = vm.kernel().string_forge().lookup(vm.reg(args[0]).as_string_index()).unwrap_or_default(); + let name = vm + .kernel() + .string_forge() + .lookup(vm.reg(args[0]).as_string_index()) + .unwrap_or_default(); let result = if name.is_empty() { "function () { [native code] }".to_string() } else { diff --git a/crates/oxide_vm/src/builtins/number.rs b/crates/oxide_vm/src/builtins/number.rs index 229a58a8..9600c25d 100644 --- a/crates/oxide_vm/src/builtins/number.rs +++ b/crates/oxide_vm/src/builtins/number.rs @@ -177,7 +177,10 @@ pub fn number_is_integer(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn number_is_safe_integer(vm: &mut Vm, args: &[u8]) -> NativeResult { let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] }); let n = coercion::to_number(val, vm.kernel().string_forge().as_ref()); - let safe = n.trunc() == n && n.is_finite() && n >= -9007199254740991i64 as f64 && n <= 9007199254740991i64 as f64; + let safe = n.trunc() == n + && n.is_finite() + && n >= -9007199254740991i64 as f64 + && n <= 9007199254740991i64 as f64; Ok(JsValue::bool(safe)) } @@ -185,10 +188,19 @@ pub fn number_to_exponential(vm: &mut Vm, args: &[u8]) -> NativeResult { let n = coercion::to_number(vm.reg(args[0]), vm.kernel().string_forge().as_ref()); let digits = if args.len() > 1 { coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize - } else { 0usize }.min(100); - if n.is_nan() { return Ok(vm.intern("NaN")); } + } else { + 0usize + } + .min(100); + if n.is_nan() { + return Ok(vm.intern("NaN")); + } if n.is_infinite() { - return Ok(vm.intern(if n.is_sign_positive() { "Infinity" } else { "-Infinity" })); + return Ok(vm.intern(if n.is_sign_positive() { + "Infinity" + } else { + "-Infinity" + })); } let formatted = format!("{:.digits$e}", n); Ok(vm.intern(&formatted)) @@ -198,10 +210,19 @@ pub fn number_to_precision(vm: &mut Vm, args: &[u8]) -> NativeResult { let n = coercion::to_number(vm.reg(args[0]), vm.kernel().string_forge().as_ref()); let precision = if args.len() > 1 { coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize - } else { 0usize }.min(21); - if n.is_nan() { return Ok(vm.intern("NaN")); } + } else { + 0usize + } + .min(21); + if n.is_nan() { + return Ok(vm.intern("NaN")); + } if n.is_infinite() { - return Ok(vm.intern(if n.is_sign_positive() { "Infinity" } else { "-Infinity" })); + return Ok(vm.intern(if n.is_sign_positive() { + "Infinity" + } else { + "-Infinity" + })); } let formatted = format!("{:.precision$}", n); Ok(vm.intern(&formatted)) diff --git a/crates/oxide_vm/src/builtins/object.rs b/crates/oxide_vm/src/builtins/object.rs index d13852d3..4810da55 100644 --- a/crates/oxide_vm/src/builtins/object.rs +++ b/crates/oxide_vm/src/builtins/object.rs @@ -352,18 +352,29 @@ pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult { } pub fn object_define_properties(vm: &mut Vm, args: &[u8]) -> NativeResult { - if args.len() < 3 { return Ok(vm.reg(args[1])); } + if args.len() < 3 { + return Ok(vm.reg(args[1])); + } let _obj_ptr = require_obj_arg(vm, args, "defineProperties")?; let desc_val = vm.reg(args[2]); - if !desc_val.is_object() { return Ok(vm.reg(args[1])); } + if !desc_val.is_object() { + return Ok(vm.reg(args[1])); + } Ok(vm.reg(args[1])) } pub fn object_from_entries(vm: &mut Vm, args: &[u8]) -> NativeResult { - if args.len() < 2 { return Ok(JsValue::null()); } + if args.len() < 2 { + return Ok(JsValue::null()); + } let entries = vm.reg(args[1]); - if !entries.is_object() { return Ok(JsValue::null()); } - let obj_ptr = vm.epoch().alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject))); + if !entries.is_object() { + return Ok(JsValue::null()); + } + let obj_ptr = vm.epoch().alloc(JsObject::new_empty( + EMPTY_SHAPE_ID, + JsValue::from_js_object(vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject), + )); Ok(JsValue::from_js_object(obj_ptr)) } @@ -373,10 +384,14 @@ pub fn object_get_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult { } pub fn object_has_own(vm: &mut Vm, args: &[u8]) -> NativeResult { - if args.len() < 3 { return Ok(JsValue::bool(false)); } + if args.len() < 3 { + return Ok(JsValue::bool(false)); + } let _obj_ptr = require_obj_arg(vm, args, "hasOwn")?; let key_val = vm.reg(args[2]); - if !key_val.is_string() { return Ok(JsValue::bool(false)); } + if !key_val.is_string() { + return Ok(JsValue::bool(false)); + } Ok(JsValue::bool(false)) } @@ -386,17 +401,29 @@ pub fn object_entries(vm: &mut Vm, args: &[u8]) -> NativeResult { let keys = walk_own_keys(vm, obj); let n = keys.len().min(255); let array_proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject; - let arr = vm.epoch().alloc(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(array_proto), n, vm.epoch().bump())); + let arr = vm.epoch().alloc(JsObject::new_array( + EMPTY_SHAPE_ID, + JsValue::from_js_object(array_proto), + n, + vm.epoch().bump(), + )); for (_i, (si, offset)) in keys.iter().enumerate().take(n) { let key_str = vm.kernel().string_forge().lookup(*si).unwrap_or_default(); let key_val = vm.intern(&key_str); let val = obj.get_prop_at(*offset); - let pair = vm.epoch().alloc(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(array_proto), 2, vm.epoch().bump())); + let pair = vm.epoch().alloc(JsObject::new_array( + EMPTY_SHAPE_ID, + JsValue::from_js_object(array_proto), + 2, + vm.epoch().bump(), + )); unsafe { (*pair).set_prop_at(0, key_val); (*pair).set_prop_at(1, val); (*pair).set_prop_count(2); - (*arr).ensure_hash_props().push(Box::new(JsValue::from_js_object(pair))); + (*arr) + .ensure_hash_props() + .push(Box::new(JsValue::from_js_object(pair))); } } Ok(JsValue::from_js_object(arr)) @@ -408,11 +435,20 @@ pub fn object_values(vm: &mut Vm, args: &[u8]) -> NativeResult { let keys = walk_own_keys(vm, obj); let n = keys.len().min(255); let array_proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject; - let arr = vm.epoch().alloc(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(array_proto), n, vm.epoch().bump())); + let arr = vm.epoch().alloc(JsObject::new_array( + EMPTY_SHAPE_ID, + JsValue::from_js_object(array_proto), + n, + vm.epoch().bump(), + )); for (i, (_si, offset)) in keys.iter().enumerate().take(n) { let val = obj.get_prop_at(*offset); - unsafe { (*arr).set_prop_at(i as u8, val); } + unsafe { + (*arr).set_prop_at(i as u8, val); + } + } + unsafe { + (*arr).set_prop_count(n as u8); } - unsafe { (*arr).set_prop_count(n as u8); } Ok(JsValue::from_js_object(arr)) } diff --git a/crates/oxide_vm/src/builtins/regexp.rs b/crates/oxide_vm/src/builtins/regexp.rs index f92afc33..b957182d 100644 --- a/crates/oxide_vm/src/builtins/regexp.rs +++ b/crates/oxide_vm/src/builtins/regexp.rs @@ -86,9 +86,7 @@ pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { let mut obj = JsObject::new_empty( EMPTY_SHAPE_ID, - JsValue::from_js_object( - vm.kernel().builtin_world().regexp_proto.as_ptr() as *mut JsObject, - ), + JsValue::from_js_object(vm.kernel().builtin_world().regexp_proto.as_ptr() as *mut JsObject), ); let compiled = regex::RegexBuilder::new(&pattern) @@ -185,8 +183,7 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult { let (last_index, is_global) = { let re = unsafe { &*re_ptr }; - let li = - coercion::to_number(get_prop(re, 0), vm.kernel().string_forge().as_ref()) as usize; + let li = coercion::to_number(get_prop(re, 0), vm.kernel().string_forge().as_ref()) as usize; let g = get_prop(re, 3).as_bool(); (li, g) }; @@ -204,7 +201,7 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult { let mut match_obj = JsObject::new_empty( EMPTY_SHAPE_ID, JsValue::from_js_object( - vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject, + vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject ), ); diff --git a/crates/oxide_vm/src/builtins/string.rs b/crates/oxide_vm/src/builtins/string.rs index 697452c8..a5de4d3f 100644 --- a/crates/oxide_vm/src/builtins/string.rs +++ b/crates/oxide_vm/src/builtins/string.rs @@ -387,7 +387,10 @@ pub fn string_match_fn(vm: &mut Vm, args: &[u8]) -> NativeResult { None => return Ok(JsValue::undefined()), }; let regex = unsafe { &*(fn_ptr as *const regex::Regex) }; - let matches: Vec = regex.find_iter(&s).map(|m| m.as_str().to_string()).collect(); + let matches: Vec = regex + .find_iter(&s) + .map(|m| m.as_str().to_string()) + .collect(); return Ok(make_string_array(vm, &matches)); } @@ -443,9 +446,13 @@ pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let pos = if args.len() > 1 { coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize - } else { 0 }; + } else { + 0 + }; let chars: Vec = s.chars().collect(); - if pos >= chars.len() { return Ok(JsValue::undefined()); } + if pos >= chars.len() { + return Ok(JsValue::undefined()); + } let c = chars[pos] as u32; if (0xD800..=0xDBFF).contains(&c) && pos + 1 < chars.len() { let next = chars[pos + 1] as u32; @@ -460,7 +467,11 @@ pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn string_normalize(vm: &mut Vm, args: &[u8]) -> NativeResult { use unicode_normalization::UnicodeNormalization; let s = this_string(vm, args); - let form = if args.len() > 1 { as_string(vm, vm.reg(args[1])) } else { "NFC".to_string() }; + let form = if args.len() > 1 { + as_string(vm, vm.reg(args[1])) + } else { + "NFC".to_string() + }; let result: String = match form.as_str() { "NFD" => s.nfd().collect(), "NFKC" => s.nfkc().collect(), @@ -472,14 +483,22 @@ pub fn string_normalize(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult { let s = this_string(vm, args); - if args.len() < 2 { return Err(JsValue::undefined()); } + if args.len() < 2 { + return Err(JsValue::undefined()); + } let pattern_val = vm.reg(args[1]); if is_regexp_obj(pattern_val, vm) { let re_ptr = pattern_val.as_js_object_ptr(); let re = unsafe { &*re_ptr }; - let fn_ptr = match re.native_fn() { Some(p) => p, None => return Ok(JsValue::undefined()) }; + let fn_ptr = match re.native_fn() { + Some(p) => p, + None => return Ok(JsValue::undefined()), + }; let regex = unsafe { &*(fn_ptr as *const regex::Regex) }; - let matches: Vec = regex.find_iter(&s).map(|m| m.as_str().to_string()).collect(); + let matches: Vec = regex + .find_iter(&s) + .map(|m| m.as_str().to_string()) + .collect(); return Ok(make_string_array(vm, &matches)); } Ok(JsValue::undefined()) @@ -487,13 +506,22 @@ pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult { pub fn string_replace_all(vm: &mut Vm, args: &[u8]) -> NativeResult { let s = this_string(vm, args); - if args.len() < 2 { return Ok(vm.intern(&s)); } + if args.len() < 2 { + return Ok(vm.intern(&s)); + } let pattern_val = vm.reg(args[1]); - let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() }; + let replacement = if args.len() > 2 { + as_string(vm, vm.reg(args[2])) + } else { + String::new() + }; if is_regexp_obj(pattern_val, vm) { let re_ptr = pattern_val.as_js_object_ptr(); let re = unsafe { &*re_ptr }; - let fn_ptr = match re.native_fn() { Some(p) => p, None => return Ok(vm.intern(&s)) }; + let fn_ptr = match re.native_fn() { + Some(p) => p, + None => return Ok(vm.intern(&s)), + }; let regex = unsafe { &*(fn_ptr as *const regex::Regex) }; let result = regex.replace_all(&s, replacement.as_str()).to_string(); return Ok(vm.intern(&result)); diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index c313130d..a3af3289 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -675,11 +675,7 @@ impl Vm { if b != 0 { // const guard: check if already initialized if !self.regs[rd].is_undefined() { - throw_err!( - self, - TypeError, - "Assignment to constant variable" - ); + throw_err!(self, TypeError, "Assignment to constant variable"); } } self.regs[rd] = self.regs[a]; @@ -1307,10 +1303,11 @@ impl Vm { continue; } let prop_name_si = prop_name_val.as_string_index(); - if let Some(pos) = self.kernel.shape_forge().lookup_position( - obj.shape_id(), - prop_name_si, - ) { + if let Some(pos) = self + .kernel + .shape_forge() + .lookup_position(obj.shape_id(), prop_name_si) + { obj.set_prop_at(pos, JsValue::undefined()); } self.regs[rd] = JsValue::bool(true); @@ -1334,10 +1331,11 @@ impl Vm { }; let obj_ptr = obj_val.as_js_object_ptr(); let obj = unsafe { &mut *obj_ptr }; - if let Some(pos) = self.kernel.shape_forge().lookup_position( - obj.shape_id(), - prop_name_si, - ) { + if let Some(pos) = self + .kernel + .shape_forge() + .lookup_position(obj.shape_id(), prop_name_si) + { obj.set_prop_at(pos, JsValue::undefined()); } self.regs[rd] = JsValue::bool(true); @@ -1349,7 +1347,11 @@ impl Vm { let rhs_val = self.regs[b]; if !rhs_val.is_object() { - throw_err!(self, TypeError, "INSTANCEOF right-hand side is not callable"); + throw_err!( + self, + TypeError, + "INSTANCEOF right-hand side is not callable" + ); } if !lhs_val.is_object() { self.regs[rd] = JsValue::bool(false); diff --git a/crates/oxide_vm/tests/builtins_error_tests.rs b/crates/oxide_vm/tests/builtins_error_tests.rs index 545e8d84..f3df53c1 100644 --- a/crates/oxide_vm/tests/builtins_error_tests.rs +++ b/crates/oxide_vm/tests/builtins_error_tests.rs @@ -10,7 +10,9 @@ fn make_vm() -> Vm { fn eval(source: &str) -> Result { let allocator = oxide_parser::Allocator::default(); let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; - let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?; + let module = Compiler::new() + .compile(&program) + .map_err(|e| format!("Compile: {}", e))?; let mut vm = make_vm(); vm.run(&module) } diff --git a/crates/oxide_vm/tests/builtins_number_tests.rs b/crates/oxide_vm/tests/builtins_number_tests.rs index 01091c92..01d068f8 100644 --- a/crates/oxide_vm/tests/builtins_number_tests.rs +++ b/crates/oxide_vm/tests/builtins_number_tests.rs @@ -124,6 +124,10 @@ fn number_epsilon_is_positive() { fn number_to_exponential() { let mut vm = Vm::new(); let result = eval(&mut vm, "var n = 123.456; n.toExponential(2)").unwrap(); - let s = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default(); + let s = vm + .kernel() + .string_forge() + .lookup(result.as_string_index()) + .unwrap_or_default(); assert_eq!(s, "1.23e2"); } diff --git a/crates/oxide_vm/tests/builtins_regexp_tests.rs b/crates/oxide_vm/tests/builtins_regexp_tests.rs index 4a8f3236..facf7d21 100644 --- a/crates/oxide_vm/tests/builtins_regexp_tests.rs +++ b/crates/oxide_vm/tests/builtins_regexp_tests.rs @@ -154,7 +154,11 @@ fn regexp_exec_global_last_index_advances() { #[test] fn regexp_exec_global_resets_on_exhaustion() { let mut vm = Vm::new(); - let result = eval(&mut vm, "var re = /a/g; re.exec('a'); re.exec('a') === null").unwrap(); + let result = eval( + &mut vm, + "var re = /a/g; re.exec('a'); re.exec('a') === null", + ) + .unwrap(); assert!(result.as_bool()); } diff --git a/crates/oxide_vm/tests/scoping_tests.rs b/crates/oxide_vm/tests/scoping_tests.rs index d009cd92..94089e3f 100644 --- a/crates/oxide_vm/tests/scoping_tests.rs +++ b/crates/oxide_vm/tests/scoping_tests.rs @@ -5,7 +5,9 @@ use oxide_vm::vm::Vm; fn eval(source: &str) -> Result { let allocator = oxide_parser::Allocator::default(); let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?; - let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?; + let module = Compiler::new() + .compile(&program) + .map_err(|e| format!("Compile: {}", e))?; let mut vm = Vm::new(); vm.run(&module) } @@ -21,7 +23,11 @@ fn const_reassignment_throws() { let result = eval("const x = 1; x = 2"); assert!(result.is_err()); let err = result.unwrap_err(); - assert!(err.contains("TypeError") || err.contains("constant"), "got: {}", err); + assert!( + err.contains("TypeError") || err.contains("constant"), + "got: {}", + err + ); } #[test]