diff --git a/core/engine/src/builtins/async_generator/mod.rs b/core/engine/src/builtins/async_generator/mod.rs index 6ec1f0872b5..6009b590b17 100644 --- a/core/engine/src/builtins/async_generator/mod.rs +++ b/core/engine/src/builtins/async_generator/mod.rs @@ -57,7 +57,7 @@ pub(crate) struct AsyncGeneratorRequest { } /// The internal representation of an `AsyncGenerator` object. -#[derive(Debug, Clone, Finalize, Trace, JsData)] +#[derive(Debug, Finalize, Trace, JsData)] pub struct AsyncGenerator { /// The `[[AsyncGeneratorState]]` internal slot. #[unsafe_ignore_trace] diff --git a/core/engine/src/builtins/eval/mod.rs b/core/engine/src/builtins/eval/mod.rs index c86b084fee4..a743f3a2779 100644 --- a/core/engine/src/builtins/eval/mod.rs +++ b/core/engine/src/builtins/eval/mod.rs @@ -19,7 +19,7 @@ use crate::{ object::JsObject, realm::Realm, string::StaticJsStrings, - vm::{CallFrame, CallFrameFlags, Constant, Opcode}, + vm::{CallFrame, CallFrameFlags, Constant, Opcode, Registers}, Context, JsArgs, JsResult, JsString, JsValue, }; use boa_ast::{ @@ -328,7 +328,8 @@ impl Eval { context.realm().resize_global_env(); - let record = context.run(); + let register_count = context.vm.frame().code_block().register_count; + let record = context.run(&mut Registers::new(register_count as usize)); context.vm.pop_frame(); record.consume() diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 94751379289..2d92f229892 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -988,6 +988,7 @@ pub(crate) fn function_call( let code = function.code.clone(); let environments = function.environments.clone(); let script_or_module = function.script_or_module.clone(); + let register_count = code.register_count as usize; drop(function); @@ -1036,7 +1037,7 @@ pub(crate) fn function_call( ); } - Ok(CallValue::Ready) + Ok(CallValue::Ready { register_count }) } /// Construct an instance of this object with the specified arguments. @@ -1065,6 +1066,7 @@ fn function_construct( let code = function.code.clone(); let environments = function.environments.clone(); let script_or_module = function.script_or_module.clone(); + let register_count = code.register_count as usize; drop(function); let env_fp = environments.len() as u32; @@ -1143,5 +1145,5 @@ fn function_construct( this.map(JsValue::new).unwrap_or_default(), ); - Ok(CallValue::Ready) + Ok(CallValue::Ready { register_count }) } diff --git a/core/engine/src/builtins/generator/mod.rs b/core/engine/src/builtins/generator/mod.rs index 5304ae56459..ede96897914 100644 --- a/core/engine/src/builtins/generator/mod.rs +++ b/core/engine/src/builtins/generator/mod.rs @@ -20,7 +20,7 @@ use crate::{ string::StaticJsStrings, symbol::JsSymbol, value::JsValue, - vm::{CallFrame, CallFrameFlags, CompletionRecord, GeneratorResumeKind}, + vm::{CallFrame, CallFrameFlags, CompletionRecord, GeneratorResumeKind, Registers}, Context, JsArgs, JsData, JsError, JsResult, JsString, }; use boa_gc::{custom_trace, Finalize, Trace}; @@ -29,7 +29,7 @@ use boa_profiler::Profiler; use super::{BuiltInBuilder, IntrinsicObject}; /// Indicates the state of a generator. -#[derive(Debug, Clone, Finalize)] +#[derive(Debug, Finalize)] pub(crate) enum GeneratorState { SuspendedStart { /// The `[[GeneratorContext]]` internal slot. @@ -57,15 +57,20 @@ unsafe impl Trace for GeneratorState { /// /// All of the fields must be changed with those that are currently present in the /// context/vm before the generator execution starts/resumes and after it has ended/yielded. -#[derive(Debug, Clone, Trace, Finalize)] +#[derive(Debug, Trace, Finalize)] pub(crate) struct GeneratorContext { - pub(crate) stack: Vec, - pub(crate) call_frame: Option, + stack: Vec, + call_frame: Option, + registers: Registers, } impl GeneratorContext { /// Creates a new `GeneratorContext` from the current `Context` state. - pub(crate) fn from_current(context: &mut Context) -> Self { + pub(crate) fn from_current( + context: &mut Context, + mut registers: Registers, + async_generator: Option, + ) -> Self { let mut frame = context.vm.frame().clone(); frame.environments = context.vm.environments.clone(); frame.realm = context.realm().clone(); @@ -78,9 +83,17 @@ impl GeneratorContext { // So we don't need to push the registers in subsequent calls. frame.flags |= CallFrameFlags::REGISTERS_ALREADY_PUSHED; + if let Some(async_generator) = async_generator { + registers.set( + CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX, + async_generator.into(), + ); + } + Self { call_frame: Some(frame), stack, + registers, } } @@ -105,7 +118,7 @@ impl GeneratorContext { } context.vm.push(resume_kind); - let result = context.run(); + let result = context.run(&mut self.registers); std::mem::swap(&mut context.vm.stack, &mut self.stack); self.call_frame = context.vm.pop_frame(); @@ -117,7 +130,7 @@ impl GeneratorContext { pub(crate) fn async_generator_object(&self) -> Option { self.call_frame .as_ref() - .and_then(|frame| frame.async_generator_object(&self.stack)) + .and_then(|frame| frame.async_generator_object(&self.registers)) } } diff --git a/core/engine/src/builtins/json/mod.rs b/core/engine/src/builtins/json/mod.rs index 78e9710f554..fd9843e4a6e 100644 --- a/core/engine/src/builtins/json/mod.rs +++ b/core/engine/src/builtins/json/mod.rs @@ -31,7 +31,7 @@ use crate::{ string::{CodePoint, StaticJsStrings}, symbol::JsSymbol, value::IntegerOrInfinity, - vm::{CallFrame, CallFrameFlags}, + vm::{CallFrame, CallFrameFlags, Registers}, Context, JsArgs, JsBigInt, JsResult, JsString, JsValue, }; use boa_gc::Gc; @@ -142,7 +142,8 @@ impl Json { ); context.realm().resize_global_env(); - let record = context.run(); + let register_count = context.vm.frame().code_block().register_count; + let record = context.run(&mut Registers::new(register_count as usize)); context.vm.pop_frame(); let unfiltered = record.consume()?; diff --git a/core/engine/src/bytecompiler/class.rs b/core/engine/src/bytecompiler/class.rs index 3cc2c7bedf3..9052e3be658 100644 --- a/core/engine/src/bytecompiler/class.rs +++ b/core/engine/src/bytecompiler/class.rs @@ -1,4 +1,4 @@ -use super::{ByteCompiler, InstructionOperand, Literal, Operand, Operand2, ToJsString}; +use super::{ByteCompiler, Literal, Operand, Register, ToJsString}; use crate::{ js_string, vm::{BindingOpcode, CodeBlock, CodeBlockFlags, Opcode}, @@ -22,7 +22,12 @@ enum StaticElement { StaticBlock(Gc), // A static class field with it's function code, an optional name index and the information if the function is an anonymous function. - StaticField((Gc, Option, bool)), + StaticField((Gc, StaticFieldName, bool)), +} + +enum StaticFieldName { + Index(u32), + Register(Register), } /// Describes the complete specification of a class. @@ -68,7 +73,7 @@ impl ByteCompiler<'_> { /// The compilation of a class declaration and expression is mostly equal. /// A class declaration binds the resulting class object to it's identifier. /// A class expression leaves the resulting class object on the stack for following operations. - pub(crate) fn compile_class(&mut self, class: ClassSpec<'_>, expression: bool) { + pub(crate) fn compile_class(&mut self, class: ClassSpec<'_>, dst: Option<&Register>) { // 11.2.2 Strict Mode Code - // - All parts of a ClassDeclaration or a ClassExpression are strict mode code. let strict = self.strict(); @@ -95,6 +100,7 @@ impl ByteCompiler<'_> { compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; + let value = compiler.register_allocator.alloc(); if let Some(expr) = &class.constructor { compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = compiler.push_scope(expr.scopes().function_scope()); @@ -114,20 +120,22 @@ impl ByteCompiler<'_> { compiler.compile_statement_list(expr.body().statement_list(), false, false); - compiler.emit_opcode(Opcode::PushUndefined); + compiler.push_undefined(&value); } else if class.super_ref.is_some() { // We push an empty, unused function scope since the compiler expects a function scope. compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = compiler.push_scope(&Scope::new(compiler.lexical_scope.clone(), true)); compiler.emit_opcode(Opcode::SuperCallDerived); - compiler.emit_opcode(Opcode::BindThisValue); + compiler.pop_into_register(&value); + compiler.emit(Opcode::BindThisValue, &[Operand::Register(&value)]); } else { // We push an empty, unused function scope since the compiler expects a function scope. compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = compiler.push_scope(&Scope::new(compiler.lexical_scope.clone(), true)); - compiler.emit_opcode(Opcode::PushUndefined); + compiler.push_undefined(&value); } - compiler.emit_opcode(Opcode::SetAccumulatorFromStack); + compiler.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + compiler.register_allocator.dealloc(value); // 17. If ClassHeritageopt is present, set F.[[ConstructorKind]] to derived. compiler.code_block_flags.set( @@ -144,36 +152,33 @@ impl ByteCompiler<'_> { let prototype_register = self.register_allocator.alloc(); if let Some(node) = class.super_ref { - self.compile_expr(node, true); - self.pop_into_register(&prototype_register); + self.compile_expr(node, &prototype_register); - self.emit2( + self.emit( Opcode::PushClassPrototype, &[ - Operand2::Register(&prototype_register), - Operand2::Operand(InstructionOperand::Register(&class_register)), - Operand2::Operand(InstructionOperand::Register(&prototype_register)), + Operand::Register(&prototype_register), + Operand::Register(&class_register), + Operand::Register(&prototype_register), ], ); } else { - self.emit_opcode(Opcode::PushUndefined); - self.pop_into_register(&prototype_register); + self.push_undefined(&prototype_register); } let proto_register = self.register_allocator.alloc(); - self.emit2( + self.emit( Opcode::SetClassPrototype, &[ - Operand2::Register(&proto_register), - Operand2::Operand(InstructionOperand::Register(&prototype_register)), - Operand2::Operand(InstructionOperand::Register(&class_register)), + Operand::Register(&proto_register), + Operand::Register(&prototype_register), + Operand::Register(&class_register), ], ); self.register_allocator.dealloc(prototype_register); - let count_label = - self.emit_push_private_environment(InstructionOperand::Register(&class_register)); + let count_label = self.emit_push_private_environment(&class_register); let mut count = 0; for element in class.elements { match element { @@ -200,114 +205,170 @@ impl ByteCompiler<'_> { self.patch_jump_with_target(count_label, count); let mut static_elements = Vec::new(); - let mut static_field_name_count = 0; if let Some(scope) = class.name_scope { let binding = scope.get_identifier_reference(class_name.clone()); let index = self.get_or_insert_binding(binding); - self.push_from_register(&class_register); - self.emit_binding_access(Opcode::PutLexicalValue, &index); + self.emit_binding_access(Opcode::PutLexicalValue, &index, &class_register); } - self.push_from_register(&proto_register); - self.push_from_register(&class_register); - - self.register_allocator.dealloc(proto_register); - self.register_allocator.dealloc(class_register); - - // TODO: set function name for getter and setters for element in class.elements { match element { - ClassElement::MethodDefinition(m) => { - if !m.is_static() && !m.is_private() { - self.emit_opcode(Opcode::Swap); - self.emit_opcode(Opcode::Dup); - } else { - self.emit_opcode(Opcode::Dup); + ClassElement::MethodDefinition(m) => match m.name() { + ClassElementName::PropertyName(PropertyName::Literal(name)) => { + let index = self.get_or_insert_name((*name).into()); + let method = self.method(m.into()); + let opcode = match (m.is_static(), m.kind()) { + (true, MethodDefinitionKind::Get) => { + Opcode::DefineClassStaticGetterByName + } + (true, MethodDefinitionKind::Set) => { + Opcode::DefineClassStaticSetterByName + } + (true, _) => Opcode::DefineClassStaticMethodByName, + (false, MethodDefinitionKind::Get) => Opcode::DefineClassGetterByName, + (false, MethodDefinitionKind::Set) => Opcode::DefineClassSetterByName, + (false, _) => Opcode::DefineClassMethodByName, + }; + + let object_register = if m.is_static() { + &class_register + } else { + &proto_register + }; + + self.emit( + opcode, + &[ + Operand::Register(&method), + Operand::Register(object_register), + Operand::Varying(index), + ], + ); + + self.register_allocator.dealloc(method); } - - match m.name() { - ClassElementName::PropertyName(PropertyName::Literal(name)) => { - self.method(m.into()); - let index = self.get_or_insert_name((*name).into()); - let opcode = match (m.is_static(), m.kind()) { - (true, MethodDefinitionKind::Get) => { - Opcode::DefineClassStaticGetterByName - } - (true, MethodDefinitionKind::Set) => { - Opcode::DefineClassStaticSetterByName - } - (true, _) => Opcode::DefineClassStaticMethodByName, - (false, MethodDefinitionKind::Get) => { - Opcode::DefineClassGetterByName - } - (false, MethodDefinitionKind::Set) => { - Opcode::DefineClassSetterByName - } - (false, _) => Opcode::DefineClassMethodByName, - }; - self.emit_with_varying_operand(opcode, index); - } - ClassElementName::PropertyName(PropertyName::Computed(name)) => { - self.compile_expr(name, true); - self.emit_opcode(Opcode::ToPropertyKey); - self.method(m.into()); - let opcode = match (m.is_static(), m.kind()) { - (true, MethodDefinitionKind::Get) => { - Opcode::DefineClassStaticGetterByValue - } - (true, MethodDefinitionKind::Set) => { - Opcode::DefineClassStaticSetterByValue - } - (true, _) => Opcode::DefineClassStaticMethodByValue, - (false, MethodDefinitionKind::Get) => { - Opcode::DefineClassGetterByValue - } - (false, MethodDefinitionKind::Set) => { - Opcode::DefineClassSetterByValue - } - (false, _) => Opcode::DefineClassMethodByValue, - }; - self.emit_opcode(opcode); - } - ClassElementName::PrivateName(name) => { - let index = self.get_or_insert_private_name(*name); - let opcode = match (m.is_static(), m.kind()) { - (true, MethodDefinitionKind::Get) => Opcode::SetPrivateGetter, - (true, MethodDefinitionKind::Set) => Opcode::SetPrivateSetter, - (true, _) => Opcode::SetPrivateMethod, - (false, MethodDefinitionKind::Get) => { - Opcode::PushClassPrivateGetter - } - (false, MethodDefinitionKind::Set) => { - Opcode::PushClassPrivateSetter - } - (false, _) => { - self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); - self.emit_opcode(Opcode::Dup); - self.emit(Opcode::RotateRight, &[Operand::U8(4)]); - Opcode::PushClassPrivateMethod - } - }; - self.method(m.into()); - self.emit_with_varying_operand(opcode, index); - } + ClassElementName::PropertyName(PropertyName::Computed(name)) => { + let key = self.register_allocator.alloc(); + self.compile_expr(name, &key); + self.emit( + Opcode::ToPropertyKey, + &[Operand::Register(&key), Operand::Register(&key)], + ); + let method = self.method(m.into()); + let opcode = match (m.is_static(), m.kind()) { + (true, MethodDefinitionKind::Get) => { + Opcode::DefineClassStaticGetterByValue + } + (true, MethodDefinitionKind::Set) => { + Opcode::DefineClassStaticSetterByValue + } + (true, _) => Opcode::DefineClassStaticMethodByValue, + (false, MethodDefinitionKind::Get) => Opcode::DefineClassGetterByValue, + (false, MethodDefinitionKind::Set) => Opcode::DefineClassSetterByValue, + (false, _) => Opcode::DefineClassMethodByValue, + }; + + let object_register = if m.is_static() { + &class_register + } else { + &proto_register + }; + + self.emit( + opcode, + &[ + Operand::Register(&method), + Operand::Register(&key), + Operand::Register(object_register), + ], + ); + + self.register_allocator.dealloc(method); + self.register_allocator.dealloc(key); } - - if !m.is_static() && !m.is_private() { - self.emit_opcode(Opcode::Swap); + ClassElementName::PrivateName(name) => { + let index = self.get_or_insert_private_name(*name); + let method = self.method(m.into()); + match (m.is_static(), m.kind()) { + (true, MethodDefinitionKind::Get) => { + self.emit( + Opcode::SetPrivateGetter, + &[ + Operand::Register(&class_register), + Operand::Register(&method), + Operand::Varying(index), + ], + ); + } + (true, MethodDefinitionKind::Set) => { + self.emit( + Opcode::SetPrivateSetter, + &[ + Operand::Register(&class_register), + Operand::Register(&method), + Operand::Varying(index), + ], + ); + } + (true, _) => { + self.emit( + Opcode::SetPrivateMethod, + &[ + Operand::Register(&class_register), + Operand::Register(&method), + Operand::Varying(index), + ], + ); + } + (false, MethodDefinitionKind::Get) => { + self.emit( + Opcode::PushClassPrivateGetter, + &[ + Operand::Register(&class_register), + Operand::Register(&method), + Operand::Varying(index), + ], + ); + } + (false, MethodDefinitionKind::Set) => { + self.emit( + Opcode::PushClassPrivateSetter, + &[ + Operand::Register(&class_register), + Operand::Register(&method), + Operand::Varying(index), + ], + ); + } + (false, _) => { + self.emit( + Opcode::PushClassPrivateMethod, + &[ + Operand::Register(&class_register), + Operand::Register(&proto_register), + Operand::Register(&method), + Operand::Varying(index), + ], + ); + } + } + self.register_allocator.dealloc(method); } - } + }, ClassElement::FieldDefinition(field) => { - self.emit_opcode(Opcode::Dup); + let name = self.register_allocator.alloc(); match field.name() { - PropertyName::Literal(name) => { - self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*name).into_common(false), - )); + PropertyName::Literal(ident) => { + self.emit_push_literal( + Literal::String( + self.interner().resolve_expect(*ident).into_common(false), + ), + &name, + ); } - PropertyName::Computed(name) => { - self.compile_expr(name, true); + PropertyName::Computed(expr) => { + self.compile_expr(expr, &name); } } let mut field_compiler = ByteCompiler::new( @@ -325,15 +386,17 @@ impl ByteCompiler<'_> { // Function environment field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = field_compiler.push_scope(field.scope()); + let value = field_compiler.register_allocator.alloc(); let is_anonymous_function = if let Some(node) = &field.field() { - field_compiler.compile_expr(node, true); + field_compiler.compile_expr(node, &value); node.is_anonymous_function_definition() } else { - field_compiler.emit_opcode(Opcode::PushUndefined); + field_compiler.push_undefined(&value); false }; - field_compiler.emit_opcode(Opcode::SetAccumulatorFromStack); + field_compiler.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + field_compiler.register_allocator.dealloc(value); field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; @@ -342,16 +405,21 @@ impl ByteCompiler<'_> { let dst = self.register_allocator.alloc(); self.emit_get_function(&dst, index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); self.emit( Opcode::PushClassField, - &[Operand::Bool(is_anonymous_function)], + &[ + Operand::Register(&class_register), + Operand::Register(&name), + Operand::Register(&dst), + Operand::Bool(is_anonymous_function), + ], ); + + self.register_allocator.dealloc(name); + self.register_allocator.dealloc(dst); } ClassElement::PrivateFieldDefinition(field) => { - self.emit_opcode(Opcode::Dup); let name_index = self.get_or_insert_private_name(*field.name()); let mut field_compiler = ByteCompiler::new( class_name.clone(), @@ -366,12 +434,14 @@ impl ByteCompiler<'_> { ); field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = field_compiler.push_scope(field.scope()); + let value = field_compiler.register_allocator.alloc(); if let Some(node) = field.field() { - field_compiler.compile_expr(node, true); + field_compiler.compile_expr(node, &value); } else { - field_compiler.emit_opcode(Opcode::PushUndefined); + field_compiler.push_undefined(&value); } - field_compiler.emit_opcode(Opcode::SetAccumulatorFromStack); + field_compiler.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + field_compiler.register_allocator.dealloc(value); field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; @@ -379,23 +449,27 @@ impl ByteCompiler<'_> { let index = self.push_function_to_constants(code); let dst = self.register_allocator.alloc(); self.emit_get_function(&dst, index); - self.push_from_register(&dst); + + self.emit( + Opcode::PushClassFieldPrivate, + &[ + Operand::Register(&class_register), + Operand::Register(&dst), + Operand::Varying(name_index), + ], + ); + self.register_allocator.dealloc(dst); - self.emit_with_varying_operand(Opcode::PushClassFieldPrivate, name_index); } ClassElement::StaticFieldDefinition(field) => { let name_index = match field.name() { PropertyName::Literal(name) => { - Some(self.get_or_insert_name((*name).into())) + StaticFieldName::Index(self.get_or_insert_name((*name).into())) } PropertyName::Computed(name) => { - self.compile_expr(name, true); - self.emit( - Opcode::RotateRight, - &[Operand::U8(3 + static_field_name_count)], - ); - static_field_name_count += 1; - None + let name_register = self.register_allocator.alloc(); + self.compile_expr(name, &name_register); + StaticFieldName::Register(name_register) } }; let mut field_compiler = ByteCompiler::new( @@ -411,15 +485,17 @@ impl ByteCompiler<'_> { ); field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE; let _ = field_compiler.push_scope(field.scope()); + let value = field_compiler.register_allocator.alloc(); let is_anonymous_function = if let Some(node) = &field.field() { - field_compiler.compile_expr(node, true); + field_compiler.compile_expr(node, &value); node.is_anonymous_function_definition() } else { - field_compiler.emit_opcode(Opcode::PushUndefined); + field_compiler.push_undefined(&value); false }; - field_compiler.emit_opcode(Opcode::SetAccumulatorFromStack); + field_compiler.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + field_compiler.register_allocator.dealloc(value); field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; @@ -433,14 +509,22 @@ impl ByteCompiler<'_> { ))); } ClassElement::PrivateStaticFieldDefinition(name, field) => { - self.emit_opcode(Opcode::Dup); - if let Some(node) = field { - self.compile_expr(&node, true); + let value = self.register_allocator.alloc(); + if let Some(expr) = &field { + self.compile_expr(expr, &value); } else { - self.emit_opcode(Opcode::PushUndefined); + self.push_undefined(&value); } let index = self.get_or_insert_private_name(*name); - self.emit_with_varying_operand(Opcode::DefinePrivateField, index); + self.emit( + Opcode::DefinePrivateField, + &[ + Operand::Register(&class_register), + Operand::Register(&value), + Operand::Varying(index), + ], + ); + self.register_allocator.dealloc(value); } ClassElement::StaticBlock(block) => { let mut compiler = ByteCompiler::new( @@ -481,56 +565,96 @@ impl ByteCompiler<'_> { for element in static_elements { match element { StaticElement::StaticBlock(code) => { - self.emit_opcode(Opcode::Dup); let index = self.push_function_to_constants(code); - let dst = self.register_allocator.alloc(); - self.emit_get_function(&dst, index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); - self.emit_opcode(Opcode::SetHomeObject); + let function = self.register_allocator.alloc(); + self.emit_get_function(&function, index); + self.emit( + Opcode::SetHomeObject, + &[ + Operand::Register(&function), + Operand::Register(&class_register), + ], + ); + self.push_from_register(&class_register); + self.push_from_register(&function); + self.register_allocator.dealloc(function); self.emit_with_varying_operand(Opcode::Call, 0); self.emit_opcode(Opcode::Pop); } - StaticElement::StaticField((code, name_index, is_anonymous_function)) => { - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + StaticElement::StaticField((code, name, is_anonymous_function)) => { let index = self.push_function_to_constants(code); - let dst = self.register_allocator.alloc(); - self.emit_get_function(&dst, index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); - self.emit_opcode(Opcode::SetHomeObject); + let function = self.register_allocator.alloc(); + self.emit_get_function(&function, index); + self.emit( + Opcode::SetHomeObject, + &[ + Operand::Register(&function), + Operand::Register(&class_register), + ], + ); + self.push_from_register(&class_register); + self.push_from_register(&function); + self.register_allocator.dealloc(function); self.emit_with_varying_operand(Opcode::Call, 0); - if let Some(name_index) = name_index { - self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, name_index); - } else { - self.emit(Opcode::RotateLeft, &[Operand::U8(5)]); - if is_anonymous_function { - self.emit_opcode(Opcode::Dup); - self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); - self.emit_opcode(Opcode::Swap); - self.emit_opcode(Opcode::ToPropertyKey); - self.emit_opcode(Opcode::Swap); - self.emit(Opcode::SetFunctionName, &[Operand::U8(0)]); - } else { - self.emit_opcode(Opcode::Swap); + let value = self.register_allocator.alloc(); + self.pop_into_register(&value); + match name { + StaticFieldName::Index(name) => { + self.emit( + Opcode::DefineOwnPropertyByName, + &[ + Operand::Register(&class_register), + Operand::Register(&value), + Operand::Varying(name), + ], + ); + } + StaticFieldName::Register(key) => { + if is_anonymous_function { + self.emit( + Opcode::ToPropertyKey, + &[Operand::Register(&key), Operand::Register(&key)], + ); + self.emit( + Opcode::SetFunctionName, + &[ + Operand::Register(&value), + Operand::Register(&key), + Operand::U8(0), + ], + ); + } + self.emit( + Opcode::DefineOwnPropertyByValue, + &[ + Operand::Register(&value), + Operand::Register(&key), + Operand::Register(&class_register), + ], + ); + + self.register_allocator.dealloc(key); } - self.emit_opcode(Opcode::DefineOwnPropertyByValue); } + + self.register_allocator.dealloc(value); } } } - self.emit_opcode(Opcode::Swap); - self.emit_opcode(Opcode::Pop); + self.register_allocator.dealloc(proto_register); self.pop_declarative_scope(outer_scope); self.emit_opcode(Opcode::PopPrivateEnvironment); - if !expression { - self.emit_binding(BindingOpcode::InitVar, class_name); + if let Some(dst) = dst { + self.emit_move(dst, &class_register); + } else { + self.emit_binding(BindingOpcode::InitVar, class_name, &class_register); } + self.register_allocator.dealloc(class_register); + // NOTE: Reset strict mode to before class declaration/expression evalutation. self.code_block_flags.set(CodeBlockFlags::STRICT, strict); } diff --git a/core/engine/src/bytecompiler/declaration/declaration_pattern.rs b/core/engine/src/bytecompiler/declaration/declaration_pattern.rs index 2c15ff208fc..1094c9a52f0 100644 --- a/core/engine/src/bytecompiler/declaration/declaration_pattern.rs +++ b/core/engine/src/bytecompiler/declaration/declaration_pattern.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, Literal, Operand, ToJsString}, + bytecompiler::{Access, ByteCompiler, Literal, Operand, Register, ToJsString}, vm::{BindingOpcode, Opcode}, }; use boa_ast::{ @@ -12,15 +12,16 @@ impl ByteCompiler<'_> { &mut self, pattern: &Pattern, def: BindingOpcode, + object: &Register, ) { match pattern { Pattern::Object(pattern) => { - self.emit_opcode(Opcode::ValueNotNullOrUndefined); + self.emit( + Opcode::ValueNotNullOrUndefined, + &[Operand::Register(object)], + ); - self.emit_opcode(Opcode::RequireObjectCoercible); - - let mut excluded_keys = Vec::new(); - let mut additional_excluded_keys_count = 0; + let mut excluded_keys_registers = Vec::new(); let rest_exits = pattern.has_rest(); for binding in pattern.bindings() { @@ -36,181 +37,222 @@ impl ByteCompiler<'_> { name, default_init, } => { - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + let dst = self.register_allocator.alloc(); + match name { - PropertyName::Literal(name) => { - self.emit_get_property_by_name(*name); - excluded_keys.push(*name); + PropertyName::Literal(ident) => { + self.emit_get_property_by_name(&dst, object, object, *ident); + let key = self.register_allocator.alloc(); + self.emit_push_literal( + Literal::String( + self.interner() + .resolve_expect(*ident) + .into_common(false), + ), + &key, + ); + excluded_keys_registers.push(key); } PropertyName::Computed(node) => { - self.compile_expr(node, true); + let key = self.register_allocator.alloc(); + self.compile_expr(node, &key); if rest_exits { - self.emit_opcode(Opcode::GetPropertyByValuePush); + self.emit( + Opcode::GetPropertyByValuePush, + &[ + Operand::Register(&dst), + Operand::Register(&key), + Operand::Register(object), + Operand::Register(object), + ], + ); + excluded_keys_registers.push(key); } else { - self.emit_opcode(Opcode::GetPropertyByValue); + self.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(&dst), + Operand::Register(&key), + Operand::Register(object), + Operand::Register(object), + ], + ); + self.register_allocator.dealloc(key); } } } if let Some(init) = default_init { - let skip = - self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - self.compile_expr(init, true); + let skip = self.emit_jump_if_not_undefined(&dst); + self.compile_expr(init, &dst); self.patch_jump(skip); } - self.emit_binding(def, ident.to_js_string(self.interner())); - if rest_exits && name.computed().is_some() { - self.emit_opcode(Opcode::Swap); - additional_excluded_keys_count += 1; - } + self.emit_binding(def, ident.to_js_string(self.interner()), &dst); + self.register_allocator.dealloc(dst); } // BindingRestProperty : ... BindingIdentifier RestProperty { ident } => { - self.emit_opcode(Opcode::PushEmptyObject); - - for key in &excluded_keys { - self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*key).into_common(false), - )); + let value = self.register_allocator.alloc(); + self.emit(Opcode::PushEmptyObject, &[Operand::Register(&value)]); + let mut args = Vec::from([ + Operand::Register(&value), + Operand::Register(object), + Operand::Varying(excluded_keys_registers.len() as u32), + ]); + for r in &excluded_keys_registers { + args.push(Operand::Register(r)); } - - self.emit( - Opcode::CopyDataProperties, - &[ - Operand::Varying(excluded_keys.len() as u32), - Operand::Varying(additional_excluded_keys_count), - ], - ); - self.emit_binding(def, ident.to_js_string(self.interner())); + self.emit(Opcode::CopyDataProperties, &args); + while let Some(r) = excluded_keys_registers.pop() { + self.register_allocator.dealloc(r); + } + self.emit_binding(def, ident.to_js_string(self.interner()), &value); + self.register_allocator.dealloc(value); } AssignmentRestPropertyAccess { access } => { - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::PushEmptyObject); - for key in &excluded_keys { - self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*key).into_common(false), - )); + let value = self.register_allocator.alloc(); + self.emit(Opcode::PushEmptyObject, &[Operand::Register(&value)]); + let mut args = Vec::from([ + Operand::Register(&value), + Operand::Register(object), + Operand::Varying(excluded_keys_registers.len() as u32), + ]); + for r in &excluded_keys_registers { + args.push(Operand::Register(r)); } - self.emit( - Opcode::CopyDataProperties, - &[ - Operand::Varying(excluded_keys.len() as u32), - Operand::Varying(0), - ], - ); - self.access_set( - Access::Property { access }, - false, - ByteCompiler::access_set_top_of_stack_expr_fn, - ); + self.emit(Opcode::CopyDataProperties, &args); + while let Some(r) = excluded_keys_registers.pop() { + self.register_allocator.dealloc(r); + } + self.access_set(Access::Property { access }, |_| &value); + self.register_allocator.dealloc(value); } AssignmentPropertyAccess { name, access, default_init, } => { - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + let key = self.register_allocator.alloc(); match &name { - PropertyName::Literal(name) => excluded_keys.push(*name), + PropertyName::Literal(ident) => { + let key = self.register_allocator.alloc(); + self.emit_push_literal( + Literal::String( + self.interner() + .resolve_expect(*ident) + .into_common(false), + ), + &key, + ); + excluded_keys_registers.push(key); + } PropertyName::Computed(node) => { - self.compile_expr(node, true); - self.emit_opcode(Opcode::ToPropertyKey); - self.emit_opcode(Opcode::Swap); + self.compile_expr(node, &key); + self.emit( + Opcode::ToPropertyKey, + &[Operand::Register(&key), Operand::Register(&key)], + ); } } + let dst = self.register_allocator.alloc(); self.access_set( Access::Property { access }, - false, - |compiler: &mut ByteCompiler<'_>, level: u8| { - match level { - 0 => {} - 1 => compiler.emit_opcode(Opcode::Swap), - _ => { - compiler.emit( - Opcode::RotateLeft, - &[Operand::U8(level + 1)], - ); - } - } - compiler.emit_opcode(Opcode::Dup); - + |compiler: &mut ByteCompiler<'_>| { match name { - PropertyName::Literal(name) => { - compiler.emit_get_property_by_name(*name); + PropertyName::Literal(ident) => { + compiler.emit_get_property_by_name( + &dst, object, object, *ident, + ); + compiler.register_allocator.dealloc(key); } PropertyName::Computed(_) => { - compiler.emit( - Opcode::RotateLeft, - &[Operand::U8(level + 3)], - ); if rest_exits { - compiler - .emit_opcode(Opcode::GetPropertyByValuePush); - compiler.emit_opcode(Opcode::Swap); compiler.emit( - Opcode::RotateRight, - &[Operand::U8(level + 2)], + Opcode::GetPropertyByValuePush, + &[ + Operand::Register(&dst), + Operand::Register(&key), + Operand::Register(object), + Operand::Register(object), + ], ); + excluded_keys_registers.push(key); } else { - compiler.emit_opcode(Opcode::GetPropertyByValue); + compiler.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(&dst), + Operand::Register(&key), + Operand::Register(object), + Operand::Register(object), + ], + ); + compiler.register_allocator.dealloc(key); } } } if let Some(init) = default_init { - let skip = compiler - .emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - compiler.compile_expr(init, true); + let skip = compiler.emit_jump_if_not_undefined(&dst); + compiler.compile_expr(init, &dst); compiler.patch_jump(skip); } + + &dst }, ); - - if rest_exits && name.computed().is_some() { - self.emit_opcode(Opcode::Swap); - additional_excluded_keys_count += 1; - } + self.register_allocator.dealloc(dst); } Pattern { name, pattern, default_init, } => { - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + let dst = self.register_allocator.alloc(); + match name { - PropertyName::Literal(name) => { - self.emit_get_property_by_name(*name); + PropertyName::Literal(ident) => { + self.emit_get_property_by_name(&dst, object, object, *ident); } PropertyName::Computed(node) => { - self.compile_expr(node, true); - self.emit_opcode(Opcode::GetPropertyByValue); + let key = self.register_allocator.alloc(); + self.compile_expr(node, &key); + self.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(&dst), + Operand::Register(&key), + Operand::Register(object), + Operand::Register(object), + ], + ); + self.register_allocator.dealloc(key); } } if let Some(init) = default_init { - let skip = - self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - self.compile_expr(init, true); + let skip = self.emit_jump_if_not_undefined(&dst); + self.compile_expr(init, &dst); self.patch_jump(skip); } - - self.compile_declaration_pattern(pattern, def); + self.compile_declaration_pattern(pattern, def, &dst); + self.register_allocator.dealloc(dst); } } } - if !rest_exits { - self.emit_opcode(Opcode::Pop); + while let Some(r) = excluded_keys_registers.pop() { + self.register_allocator.dealloc(r); } } Pattern::Array(pattern) => { - self.emit_opcode(Opcode::ValueNotNullOrUndefined); - self.emit_opcode(Opcode::GetIterator); + self.emit( + Opcode::ValueNotNullOrUndefined, + &[Operand::Register(object)], + ); + self.emit(Opcode::GetIterator, &[Operand::Register(object)]); let handler_index = self.push_handler(); for element in pattern.bindings() { @@ -219,18 +261,27 @@ impl ByteCompiler<'_> { let no_exception_thrown = self.jump(); self.patch_handler(handler_index); - self.emit_opcode(Opcode::MaybeException); - // stack: hasPending, exception? + let has_exception = self.register_allocator.alloc(); + let exception = self.register_allocator.alloc(); + self.emit( + Opcode::MaybeException, + &[ + Operand::Register(&has_exception), + Operand::Register(&exception), + ], + ); - self.current_stack_value_count += 2; let iterator_close_handler = self.push_handler(); self.iterator_close(false); self.patch_handler(iterator_close_handler); - self.current_stack_value_count -= 2; - let jump = self.jump_if_false(); - self.emit_opcode(Opcode::Throw); + let jump = self.jump_if_false(&has_exception); + self.register_allocator.dealloc(has_exception); + + self.emit(Opcode::Throw, &[Operand::Register(&exception)]); + self.register_allocator.dealloc(exception); + self.patch_jump(jump); self.emit_opcode(Opcode::ReThrow); @@ -250,67 +301,101 @@ impl ByteCompiler<'_> { match element { // ArrayBindingPattern : [ Elision ] Elision => { - self.emit_opcode(Opcode::IteratorNextWithoutPop); + self.emit_opcode(Opcode::IteratorNext); } // SingleNameBinding : BindingIdentifier Initializer[opt] SingleName { ident, default_init, } => { - self.emit_opcode(Opcode::IteratorNextWithoutPop); - self.emit_opcode(Opcode::IteratorValueWithoutPop); + self.emit_opcode(Opcode::IteratorNext); + let value = self.register_allocator.alloc(); + self.emit(Opcode::IteratorDone, &[Operand::Register(&value)]); + let done = self.jump_if_true(&value); + self.emit(Opcode::IteratorValue, &[Operand::Register(&value)]); + let skip_push = self.jump(); + self.patch_jump(done); + self.push_undefined(&value); + self.patch_jump(skip_push); + if let Some(init) = default_init { - let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - self.compile_expr(init, true); + let skip = self.emit_jump_if_not_undefined(&value); + self.compile_expr(init, &value); self.patch_jump(skip); } - self.emit_binding(def, ident.to_js_string(self.interner())); + + self.emit_binding(def, ident.to_js_string(self.interner()), &value); + self.register_allocator.dealloc(value); } PropertyAccess { access, default_init, } => { - self.access_set(Access::Property { access }, false, |compiler, _level| { - compiler.emit_opcode(Opcode::IteratorNextWithoutPop); - compiler.emit_opcode(Opcode::IteratorValueWithoutPop); + let value = self.register_allocator.alloc(); + self.access_set(Access::Property { access }, |compiler| { + compiler.emit_opcode(Opcode::IteratorNext); + compiler.emit(Opcode::IteratorDone, &[Operand::Register(&value)]); + let done = compiler.jump_if_true(&value); + compiler.emit(Opcode::IteratorValue, &[Operand::Register(&value)]); + let skip_push = compiler.jump(); + compiler.patch_jump(done); + compiler.push_undefined(&value); + compiler.patch_jump(skip_push); if let Some(init) = default_init { - let skip = compiler.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - compiler.compile_expr(init, true); + let skip = compiler.emit_jump_if_not_undefined(&value); + compiler.compile_expr(init, &value); compiler.patch_jump(skip); } + + &value }); + self.register_allocator.dealloc(value); } // BindingElement : BindingPattern Initializer[opt] Pattern { pattern, default_init, } => { - self.emit_opcode(Opcode::IteratorNextWithoutPop); - self.emit_opcode(Opcode::IteratorValueWithoutPop); + self.emit_opcode(Opcode::IteratorNext); + let value = self.register_allocator.alloc(); + self.emit(Opcode::IteratorDone, &[Operand::Register(&value)]); + let done = self.jump_if_true(&value); + self.emit(Opcode::IteratorValue, &[Operand::Register(&value)]); + let skip_push = self.jump(); + self.patch_jump(done); + self.push_undefined(&value); + self.patch_jump(skip_push); if let Some(init) = default_init { - let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - self.compile_expr(init, true); + let skip = self.emit_jump_if_not_undefined(&value); + self.compile_expr(init, &value); self.patch_jump(skip); } - - self.compile_declaration_pattern(pattern, def); + self.compile_declaration_pattern(pattern, def, &value); + self.register_allocator.dealloc(value); } // BindingRestElement : ... BindingIdentifier SingleNameRest { ident } => { - self.emit_opcode(Opcode::IteratorToArray); - self.emit_binding(def, ident.to_js_string(self.interner())); + let value = self.register_allocator.alloc(); + self.emit(Opcode::IteratorToArray, &[Operand::Register(&value)]); + self.emit_binding(def, ident.to_js_string(self.interner()), &value); + self.register_allocator.dealloc(value); } PropertyAccessRest { access } => { - self.access_set(Access::Property { access }, false, |compiler, _level| { - compiler.emit_opcode(Opcode::IteratorToArray); + let value = self.register_allocator.alloc(); + self.access_set(Access::Property { access }, |compiler| { + compiler.emit(Opcode::IteratorToArray, &[Operand::Register(&value)]); + &value }); + self.register_allocator.dealloc(value); } // BindingRestElement : ... BindingPattern PatternRest { pattern } => { - self.emit_opcode(Opcode::IteratorToArray); - self.compile_declaration_pattern(pattern, def); + let value = self.register_allocator.alloc(); + self.emit(Opcode::IteratorToArray, &[Operand::Register(&value)]); + self.compile_declaration_pattern(pattern, def, &value); + self.register_allocator.dealloc(value); } } } diff --git a/core/engine/src/bytecompiler/declarations.rs b/core/engine/src/bytecompiler/declarations.rs index dd9f89f4a08..10ce075face 100644 --- a/core/engine/src/bytecompiler/declarations.rs +++ b/core/engine/src/bytecompiler/declarations.rs @@ -390,11 +390,17 @@ impl ByteCompiler<'_> { let name = name.to_js_string(self.interner()); // c. Let hasRestrictedGlobal be ? env.HasRestrictedGlobalProperty(name). + let value = self.register_allocator.alloc(); let index = self.get_or_insert_string(name); - self.emit_with_varying_operand(Opcode::HasRestrictedGlobalProperty, index); + self.emit( + Opcode::HasRestrictedGlobalProperty, + &[Operand::Register(&value), Operand::Varying(index)], + ); // d. If hasRestrictedGlobal is true, throw a SyntaxError exception. - let exit = self.jump_if_false(); + let exit = self.jump_if_false(&value); + self.register_allocator.dealloc(value); + self.emit_syntax_error("cannot redefine non-configurable global property"); self.patch_jump(exit); } @@ -426,11 +432,16 @@ impl ByteCompiler<'_> { // a.iv. If declaredFunctionNames does not contain fn, then if !declared_function_names.contains(&name) { // 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn). + let value = self.register_allocator.alloc(); let index = self.get_or_insert_name(name); - self.emit_with_varying_operand(Opcode::CanDeclareGlobalFunction, index); + self.emit( + Opcode::CanDeclareGlobalFunction, + &[Operand::Register(&value), Operand::Varying(index)], + ); // 2. If fnDefinable is false, throw a TypeError exception. - let exit = self.jump_if_true(); + let exit = self.jump_if_true(&value); + self.register_allocator.dealloc(value); self.emit_type_error("cannot declare global function"); self.patch_jump(exit); @@ -459,11 +470,16 @@ impl ByteCompiler<'_> { // 1. If declaredFunctionNames does not contain vn, then if !declared_function_names.contains(&name) { // a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn). + let value = self.register_allocator.alloc(); let index = self.get_or_insert_name(name); - self.emit_with_varying_operand(Opcode::CanDeclareGlobalVar, index); + self.emit( + Opcode::CanDeclareGlobalVar, + &[Operand::Register(&value), Operand::Varying(index)], + ); // b. If vnDefinable is false, throw a TypeError exception. - let exit = self.jump_if_true(); + let exit = self.jump_if_true(&value); + self.register_allocator.dealloc(value); self.emit_type_error("cannot declare global variable"); self.patch_jump(exit); @@ -542,15 +558,19 @@ impl ByteCompiler<'_> { // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv. let dst = self.register_allocator.alloc(); self.emit_get_function(&dst, function_index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). let name_index = self.get_or_insert_name(name); self.emit( Opcode::CreateGlobalFunctionBinding, - &[Operand::Bool(false), Operand::Varying(name_index)], + &[ + Operand::Register(&dst), + Operand::Bool(false), + Operand::Varying(name_index), + ], ); + + self.register_allocator.dealloc(dst); } // 17. For each String vn of declaredVarNames, do @@ -590,16 +610,24 @@ impl ByteCompiler<'_> { for d in declarations { match d { LexicallyScopedDeclaration::FunctionDeclaration(function) => { - self.function_with_binding(function.into(), NodeKind::Declaration, false); + let dst = self.register_allocator.alloc(); + self.function_with_binding(function.into(), NodeKind::Declaration, &dst); + self.register_allocator.dealloc(dst); } LexicallyScopedDeclaration::GeneratorDeclaration(function) => { - self.function_with_binding(function.into(), NodeKind::Declaration, false); + let dst = self.register_allocator.alloc(); + self.function_with_binding(function.into(), NodeKind::Declaration, &dst); + self.register_allocator.dealloc(dst); } LexicallyScopedDeclaration::AsyncFunctionDeclaration(function) => { - self.function_with_binding(function.into(), NodeKind::Declaration, false); + let dst = self.register_allocator.alloc(); + self.function_with_binding(function.into(), NodeKind::Declaration, &dst); + self.register_allocator.dealloc(dst); } LexicallyScopedDeclaration::AsyncGeneratorDeclaration(function) => { - self.function_with_binding(function.into(), NodeKind::Declaration, false); + let dst = self.register_allocator.alloc(); + self.function_with_binding(function.into(), NodeKind::Declaration, &dst); + self.register_allocator.dealloc(dst); } _ => {} } @@ -661,13 +689,17 @@ impl ByteCompiler<'_> { if !declared_function_names.contains(&name) { // 1. If varEnv is a Global Environment Record, then if var_env.is_global() { - let index = self.get_or_insert_name(name); - // a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn). - self.emit_with_varying_operand(Opcode::CanDeclareGlobalFunction, index); + let value = self.register_allocator.alloc(); + let index = self.get_or_insert_name(name); + self.emit( + Opcode::CanDeclareGlobalFunction, + &[Operand::Register(&value), Operand::Varying(index)], + ); // b. If fnDefinable is false, throw a TypeError exception. - let exit = self.jump_if_true(); + let exit = self.jump_if_true(&value); + self.register_allocator.dealloc(value); self.emit_type_error("cannot declare global function"); self.patch_jump(exit); } @@ -695,8 +727,10 @@ impl ByteCompiler<'_> { // i. Perform ! varEnv.CreateMutableBinding(F, true). // ii. Perform ! varEnv.InitializeBinding(F, undefined). let index = self.get_or_insert_binding(binding); - self.emit_opcode(Opcode::PushUndefined); - self.emit_binding_access(Opcode::DefInitVar, &index); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.emit_binding_access(Opcode::DefInitVar, &index, &value); + self.register_allocator.dealloc(value); } } } @@ -717,13 +751,17 @@ impl ByteCompiler<'_> { if !declared_function_names.contains(&name) { // a. If varEnv is a Global Environment Record, then if var_env.is_global() { - let index = self.get_or_insert_name(name); - // i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn). - self.emit_with_varying_operand(Opcode::CanDeclareGlobalVar, index); + let value = self.register_allocator.alloc(); + let index = self.get_or_insert_name(name); + self.emit( + Opcode::CanDeclareGlobalVar, + &[Operand::Register(&value), Operand::Varying(index)], + ); // ii. If vnDefinable is false, throw a TypeError exception. - let exit = self.jump_if_true(); + let exit = self.jump_if_true(&value); + self.register_allocator.dealloc(value); self.emit_type_error("cannot declare global function"); self.patch_jump(exit); } @@ -814,15 +852,19 @@ impl ByteCompiler<'_> { // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. let dst = self.register_allocator.alloc(); self.emit_get_function(&dst, index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); // i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true). let name_index = self.get_or_insert_name(name); self.emit( Opcode::CreateGlobalFunctionBinding, - &[Operand::Bool(true), Operand::Varying(name_index)], + &[ + Operand::Register(&dst), + Operand::Bool(true), + Operand::Varying(name_index), + ], ); + + self.register_allocator.dealloc(dst); } // d. Else, else { @@ -830,8 +872,6 @@ impl ByteCompiler<'_> { let index = self.push_function_to_constants(code); let dst = self.register_allocator.alloc(); self.emit_get_function(&dst, index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); // i. Let bindingExists be ! varEnv.HasBinding(fn). let (binding, binding_exists) = bindings @@ -844,14 +884,15 @@ impl ByteCompiler<'_> { if *binding_exists { // 1. Perform ! varEnv.SetMutableBinding(fn, fo, false). let index = self.get_or_insert_binding(binding.clone()); - self.emit_binding_access(Opcode::SetName, &index); + self.emit_binding_access(Opcode::SetName, &index, &dst); } else { // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14. // 2. Perform ! varEnv.CreateMutableBinding(fn, true). // 3. Perform ! varEnv.InitializeBinding(fn, fo). let index = self.get_or_insert_binding(binding.clone()); - self.emit_binding_access(Opcode::DefInitVar, &index); + self.emit_binding_access(Opcode::DefInitVar, &index, &dst); } + self.register_allocator.dealloc(dst); } } @@ -876,8 +917,10 @@ impl ByteCompiler<'_> { // 2. Perform ! varEnv.CreateMutableBinding(vn, true). // 3. Perform ! varEnv.InitializeBinding(vn, undefined). let index = self.get_or_insert_binding(binding); - self.emit_opcode(Opcode::PushUndefined); - self.emit_binding_access(Opcode::DefInitVar, &index); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.emit_binding_access(Opcode::DefInitVar, &index, &value); + self.register_allocator.dealloc(value); } // 19. Return unused. @@ -996,9 +1039,13 @@ impl ByteCompiler<'_> { let arguments = arguments.to_js_string(self.interner()); // a. If strict is true or simpleParameterList is false, then + let value = self.register_allocator.alloc(); if strict || !formals.is_simple() { // i. Let ao be CreateUnmappedArgumentsObject(argumentsList). - self.emit_opcode(Opcode::CreateUnmappedArgumentsObject); + self.emit( + Opcode::CreateUnmappedArgumentsObject, + &[Operand::Register(&value)], + ); } // b. Else, else { @@ -1006,12 +1053,16 @@ impl ByteCompiler<'_> { // that don't have a rest parameter, any parameter // default value initializers, or any destructured parameters. // ii. Let ao be CreateMappedArgumentsObject(func, formals, argumentsList, env). - self.emit_opcode(Opcode::CreateMappedArgumentsObject); + self.emit( + Opcode::CreateMappedArgumentsObject, + &[Operand::Register(&value)], + ); self.emitted_mapped_arguments_object_opcode = true; } // e. Perform ! env.InitializeBinding("arguments", ao). - self.emit_binding(BindingOpcode::InitLexical, arguments); + self.emit_binding(BindingOpcode::InitLexical, arguments, &value); + self.register_allocator.dealloc(value); } // 22. If argumentsObjectNeeded is true, then @@ -1034,30 +1085,36 @@ impl ByteCompiler<'_> { // 26. Else, // a. Perform ? IteratorBindingInitialization of formals with arguments iteratorRecord and env. for (i, parameter) in formals.as_ref().iter().enumerate() { + let value = self.register_allocator.alloc(); if parameter.is_rest_param() { - self.emit_opcode(Opcode::RestParameterInit); + self.emit(Opcode::RestParameterInit, &[Operand::Register(&value)]); } else { - self.emit_with_varying_operand(Opcode::GetArgument, i as u32); + self.emit( + Opcode::GetArgument, + &[Operand::Varying(i as u32), Operand::Register(&value)], + ); } + match parameter.variable().binding() { Binding::Identifier(ident) => { let ident = ident.to_js_string(self.interner()); if let Some(init) = parameter.variable().init() { - let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - self.compile_expr(init, true); + let skip = self.emit_jump_if_not_undefined(&value); + self.compile_expr(init, &value); self.patch_jump(skip); } - self.emit_binding(BindingOpcode::InitLexical, ident); + self.emit_binding(BindingOpcode::InitLexical, ident, &value); } Binding::Pattern(pattern) => { if let Some(init) = parameter.variable().init() { - let skip = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - self.compile_expr(init, true); + let skip = self.emit_jump_if_not_undefined(&value); + self.compile_expr(init, &value); self.patch_jump(skip); } - self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical, &value); } } + self.register_allocator.dealloc(value); } if generator { @@ -1096,10 +1153,11 @@ impl ByteCompiler<'_> { .get_binding_reference(&n_string) .expect("must have binding"); + let value = self.register_allocator.alloc(); // 3. If parameterBindings does not contain n, or if functionNames contains n, then if !parameter_bindings.contains(&n) || function_names.contains(&n) { // a. Let initialValue be undefined. - self.emit_opcode(Opcode::PushUndefined); + self.push_undefined(&value); } // 4. Else, else { @@ -1108,13 +1166,16 @@ impl ByteCompiler<'_> { .get_binding_reference(&n_string) .expect("must have binding"); let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::GetName, &index); + self.emit_binding_access(Opcode::GetName, &index, &value); } // 5. Perform ! varEnv.InitializeBinding(n, initialValue). let index = self.get_or_insert_binding(binding); - self.emit_opcode(Opcode::PushUndefined); - self.emit_binding_access(Opcode::DefInitVar, &index); + + // TODO: What? + self.push_undefined(&value); + self.emit_binding_access(Opcode::DefInitVar, &index, &value); + self.register_allocator.dealloc(value); // 6. NOTE: A var with the same name as a formal parameter initially has // the same value as the corresponding initialized parameter. @@ -1140,8 +1201,10 @@ impl ByteCompiler<'_> { // 3. Perform ! env.InitializeBinding(n, undefined). let binding = scope.get_binding_reference(&n).expect("binding must exist"); let index = self.get_or_insert_binding(binding); - self.emit_opcode(Opcode::PushUndefined); - self.emit_binding_access(Opcode::DefInitVar, &index); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.emit_binding_access(Opcode::DefInitVar, &index, &value); + self.register_allocator.dealloc(value); } } @@ -1174,8 +1237,10 @@ impl ByteCompiler<'_> { .get_binding_reference(&f_string) .expect("binding must exist"); let index = self.get_or_insert_binding(binding); - self.emit_opcode(Opcode::PushUndefined); - self.emit_binding_access(Opcode::DefInitVar, &index); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.emit_binding_access(Opcode::DefInitVar, &index, &value); + self.register_allocator.dealloc(value); // c. Append F to instantiatedVarNames. instantiated_var_names.push(f); @@ -1202,7 +1267,9 @@ impl ByteCompiler<'_> { // a. Let fn be the sole element of the BoundNames of f. // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. // c. Perform ! varEnv.SetMutableBinding(fn, fo, false). - self.function_with_binding(function, NodeKind::Declaration, false); + let dst = self.register_allocator.alloc(); + self.function_with_binding(function, NodeKind::Declaration, &dst); + self.register_allocator.dealloc(dst); } // 37. Return unused. diff --git a/core/engine/src/bytecompiler/expression/assign.rs b/core/engine/src/bytecompiler/expression/assign.rs index 2a940b75b89..dd56cf812fc 100644 --- a/core/engine/src/bytecompiler/expression/assign.rs +++ b/core/engine/src/bytecompiler/expression/assign.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, InstructionOperand, Operand, Operand2, ToJsString}, + bytecompiler::{Access, ByteCompiler, Operand, Register, ToJsString}, vm::{BindingOpcode, Opcode}, }; use boa_ast::{ @@ -11,18 +11,18 @@ use boa_ast::{ }; impl ByteCompiler<'_> { - pub(crate) fn compile_assign(&mut self, assign: &Assign, use_expr: bool) { + pub(crate) fn compile_assign(&mut self, assign: &Assign, dst: &Register) { if assign.op() == AssignOp::Assign { match Access::from_assign_target(assign.lhs()) { - Ok(access) => self.access_set(access, use_expr, |compiler, _| { - compiler.compile_expr(assign.rhs(), true); - }), + Ok(access) => { + self.access_set(access, |compiler| { + compiler.compile_expr(assign.rhs(), dst); + dst + }); + } Err(pattern) => { - self.compile_expr(assign.rhs(), true); - if use_expr { - self.emit_opcode(Opcode::Dup); - } - self.compile_declaration_pattern(pattern, BindingOpcode::SetName); + self.compile_expr(assign.rhs(), dst); + self.compile_declaration_pattern(pattern, BindingOpcode::SetName, dst); } } } else { @@ -52,28 +52,8 @@ impl ByteCompiler<'_> { assign.op(), AssignOp::BoolAnd | AssignOp::BoolOr | AssignOp::Coalesce ); - let mut pop_count = 0; let mut early_exit = None; - let lhs = self.register_allocator.alloc(); - - let emit_stack_opcode = |this: &mut ByteCompiler<'_>| { - let rhs = this.register_allocator.alloc(); - this.pop_into_register(&rhs); - this.pop_into_register(&lhs); - this.emit2( - opcode, - &[ - Operand2::Register(&lhs), - Operand2::Operand(InstructionOperand::Register(&lhs)), - Operand2::Operand(InstructionOperand::Register(&rhs)), - ], - ); - this.register_allocator.dealloc(rhs); - - this.push_from_register(&lhs); - }; - match access { Access::Variable { name } => { let name = name.to_js_string(self.interner()); @@ -83,196 +63,251 @@ impl ByteCompiler<'_> { let index = self.get_or_insert_binding(binding); if is_lexical { - self.emit_binding_access(Opcode::GetName, &index); + self.emit_binding_access(Opcode::GetName, &index, dst); } else { - self.emit_binding_access(Opcode::GetNameAndLocator, &index); + self.emit_binding_access(Opcode::GetNameAndLocator, &index, dst); } if short_circuit { - self.pop_into_register(&lhs); - early_exit = - Some(self.emit_opcode_with_operand2( - opcode, - InstructionOperand::Register(&lhs), - )); - self.compile_expr(assign.rhs(), true); + early_exit = Some(self.emit_with_label(opcode, &[Operand::Register(dst)])); - self.pop_into_register(&lhs); - self.push_from_register(&lhs); + self.compile_expr(assign.rhs(), dst); } else { - self.compile_expr(assign.rhs(), true); - emit_stack_opcode(self); + let rhs = self.register_allocator.alloc(); + self.compile_expr(assign.rhs(), &rhs); + self.emit( + opcode, + &[ + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), + ], + ); + self.register_allocator.dealloc(rhs); } - if use_expr { - self.emit_opcode(Opcode::Dup); - } if is_lexical { match self.lexical_scope.set_mutable_binding(name.clone()) { Ok(binding) => { let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::SetName, &index); + self.emit_binding_access(Opcode::SetName, &index, dst); } Err(BindingLocatorError::MutateImmutable) => { let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } + Err(BindingLocatorError::Silent) => {} } } else { - self.emit_binding_access(Opcode::SetNameByLocator, &index); + self.emit_binding_access(Opcode::SetNameByLocator, &index, dst); } } Access::Property { access } => match access { PropertyAccess::Simple(access) => match access.field() { PropertyAccessField::Const(name) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); - self.emit_get_property_by_name(*name); + self.emit_get_property_by_name(dst, &object, &object, *name); if short_circuit { - pop_count = 2; - self.pop_into_register(&lhs); - early_exit = Some(self.emit_opcode_with_operand2( - opcode, - InstructionOperand::Register(&lhs), - )); - self.compile_expr(assign.rhs(), true); - self.pop_into_register(&lhs); - self.push_from_register(&lhs); + early_exit = + Some(self.emit_with_label(opcode, &[Operand::Register(dst)])); + self.compile_expr(assign.rhs(), dst); } else { - self.compile_expr(assign.rhs(), true); - emit_stack_opcode(self); + let rhs = self.register_allocator.alloc(); + self.compile_expr(assign.rhs(), &rhs); + self.emit( + opcode, + &[ + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), + ], + ); + self.register_allocator.dealloc(rhs); } - self.emit_set_property_by_name(*name); + self.emit_set_property_by_name(dst, &object, &object, *name); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.register_allocator.dealloc(object); } PropertyAccessField::Expr(expr) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); - self.compile_expr(expr, true); + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); + + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + + self.emit( + Opcode::GetPropertyByValuePush, + &[ + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&object), + Operand::Register(&object), + ], + ); - self.emit_opcode(Opcode::GetPropertyByValuePush); if short_circuit { - pop_count = 3; - - self.pop_into_register(&lhs); - early_exit = Some(self.emit_opcode_with_operand2( - opcode, - InstructionOperand::Register(&lhs), - )); - self.compile_expr(assign.rhs(), true); - self.pop_into_register(&lhs); - self.push_from_register(&lhs); + early_exit = + Some(self.emit_with_label(opcode, &[Operand::Register(dst)])); + self.compile_expr(assign.rhs(), dst); } else { - self.compile_expr(assign.rhs(), true); - emit_stack_opcode(self); + let rhs = self.register_allocator.alloc(); + self.compile_expr(assign.rhs(), &rhs); + self.emit( + opcode, + &[ + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), + ], + ); + self.register_allocator.dealloc(rhs); } - self.emit_opcode(Opcode::SetPropertyByValue); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.emit( + Opcode::SetPropertyByValue, + &[ + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&object), + Operand::Register(&object), + ], + ); + + self.register_allocator.dealloc(key); + self.register_allocator.dealloc(object); } }, PropertyAccess::Private(access) => { let index = self.get_or_insert_private_name(access.field()); - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.emit_with_varying_operand(Opcode::GetPrivateField, index); + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); + + self.emit( + Opcode::GetPrivateField, + &[ + Operand::Register(dst), + Operand::Register(&object), + Operand::Varying(index), + ], + ); + if short_circuit { - pop_count = 1; + early_exit = + Some(self.emit_with_label(opcode, &[Operand::Register(dst)])); + self.compile_expr(assign.rhs(), dst); + } else { + let rhs = self.register_allocator.alloc(); + self.compile_expr(assign.rhs(), &rhs); - self.pop_into_register(&lhs); - early_exit = Some(self.emit_opcode_with_operand2( + self.emit( opcode, - InstructionOperand::Register(&lhs), - )); - self.compile_expr(assign.rhs(), true); - self.pop_into_register(&lhs); - self.push_from_register(&lhs); - } else { - self.compile_expr(assign.rhs(), true); - emit_stack_opcode(self); + &[ + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), + ], + ); + self.register_allocator.dealloc(rhs); } - self.emit_with_varying_operand(Opcode::SetPrivateField, index); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.emit( + Opcode::SetPrivateField, + &[ + Operand::Register(dst), + Operand::Register(&object), + Operand::Varying(index), + ], + ); + + self.register_allocator.dealloc(object); } PropertyAccess::Super(access) => match access.field() { PropertyAccessField::Const(name) => { - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::This); - self.emit_opcode(Opcode::Swap); - self.emit_opcode(Opcode::This); + let object = self.register_allocator.alloc(); + let receiver = self.register_allocator.alloc(); + self.emit(Opcode::Super, &[Operand::Register(&object)]); + self.emit(Opcode::This, &[Operand::Register(&receiver)]); - self.emit_get_property_by_name(*name); - if short_circuit { - pop_count = 2; + self.emit_get_property_by_name(dst, &receiver, &object, *name); - self.pop_into_register(&lhs); - early_exit = Some(self.emit_opcode_with_operand2( - opcode, - InstructionOperand::Register(&lhs), - )); - self.compile_expr(assign.rhs(), true); - self.pop_into_register(&lhs); - self.push_from_register(&lhs); + if short_circuit { + early_exit = + Some(self.emit_with_label(opcode, &[Operand::Register(dst)])); + self.compile_expr(assign.rhs(), dst); } else { - self.compile_expr(assign.rhs(), true); - emit_stack_opcode(self); + let rhs = self.register_allocator.alloc(); + self.compile_expr(assign.rhs(), &rhs); + self.emit( + opcode, + &[ + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), + ], + ); + self.register_allocator.dealloc(rhs); } - self.emit_set_property_by_name(*name); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.emit_set_property_by_name(dst, &receiver, &object, *name); + + self.register_allocator.dealloc(receiver); + self.register_allocator.dealloc(object); } PropertyAccessField::Expr(expr) => { - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::This); - self.compile_expr(expr, true); + let object = self.register_allocator.alloc(); + let receiver = self.register_allocator.alloc(); + self.emit(Opcode::Super, &[Operand::Register(&object)]); + self.emit(Opcode::This, &[Operand::Register(&receiver)]); + + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + + self.emit( + Opcode::GetPropertyByValuePush, + &[ + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&receiver), + Operand::Register(&object), + ], + ); - self.emit_opcode(Opcode::GetPropertyByValuePush); if short_circuit { - pop_count = 2; - - self.pop_into_register(&lhs); - early_exit = Some(self.emit_opcode_with_operand2( - opcode, - InstructionOperand::Register(&lhs), - )); - self.compile_expr(assign.rhs(), true); - self.pop_into_register(&lhs); - self.push_from_register(&lhs); + early_exit = + Some(self.emit_with_label(opcode, &[Operand::Register(dst)])); + self.compile_expr(assign.rhs(), dst); } else { - self.compile_expr(assign.rhs(), true); - emit_stack_opcode(self); + let rhs = self.register_allocator.alloc(); + self.compile_expr(assign.rhs(), &rhs); + self.emit( + opcode, + &[ + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), + ], + ); + self.register_allocator.dealloc(rhs); } - self.emit_opcode(Opcode::This); - self.emit(Opcode::RotateRight, &[Operand::U8(2)]); - - self.emit_opcode(Opcode::SetPropertyByValue); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.emit( + Opcode::SetPropertyByValue, + &[ + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&receiver), + Operand::Register(&object), + ], + ); + + self.register_allocator.dealloc(key); + self.register_allocator.dealloc(receiver); + self.register_allocator.dealloc(object); } }, }, @@ -280,17 +315,10 @@ impl ByteCompiler<'_> { } if let Some(early_exit) = early_exit { - let exit = self.emit_opcode_with_operand(Opcode::Jump); - + let exit = self.jump(); self.patch_jump(early_exit); - for _ in 0..pop_count { - self.emit_opcode(Opcode::Pop); - } - self.push_from_register(&lhs); self.patch_jump(exit); } - - self.register_allocator.dealloc(lhs); } } } diff --git a/core/engine/src/bytecompiler/expression/binary.rs b/core/engine/src/bytecompiler/expression/binary.rs index d2991945198..11c924aee2c 100644 --- a/core/engine/src/bytecompiler/expression/binary.rs +++ b/core/engine/src/bytecompiler/expression/binary.rs @@ -4,23 +4,19 @@ use boa_ast::expression::operator::{ }; use crate::{ - bytecompiler::{ByteCompiler, InstructionOperand, Operand2, Register}, + bytecompiler::{ByteCompiler, Operand, Register}, vm::Opcode, }; impl ByteCompiler<'_> { pub(crate) fn compile_binary(&mut self, binary: &Binary, dst: &Register) { - self.compile_expr(binary.lhs(), true); + self.compile_expr(binary.lhs(), dst); match binary.op() { BinaryOp::Arithmetic(op) => { - self.compile_expr(binary.rhs(), true); - let rhs = self.register_allocator.alloc(); - let lhs = self.register_allocator.alloc(); + self.compile_expr(binary.rhs(), &rhs); - self.pop_into_register(&rhs); - self.pop_into_register(&lhs); let opcode = match op { ArithmeticOp::Add => Opcode::Add, ArithmeticOp::Sub => Opcode::Sub, @@ -30,25 +26,21 @@ impl ByteCompiler<'_> { ArithmeticOp::Mod => Opcode::Mod, }; - self.emit2( + self.emit( opcode, &[ - Operand2::Register(dst), - Operand2::Operand(InstructionOperand::Register(&lhs)), - Operand2::Operand(InstructionOperand::Register(&rhs)), + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), ], ); - self.register_allocator.dealloc(lhs); + self.register_allocator.dealloc(rhs); } BinaryOp::Bitwise(op) => { - self.compile_expr(binary.rhs(), true); - let rhs = self.register_allocator.alloc(); - let lhs = self.register_allocator.alloc(); + self.compile_expr(binary.rhs(), &rhs); - self.pop_into_register(&rhs); - self.pop_into_register(&lhs); let opcode = match op { BitwiseOp::And => Opcode::BitAnd, BitwiseOp::Or => Opcode::BitOr, @@ -58,25 +50,21 @@ impl ByteCompiler<'_> { BitwiseOp::UShr => Opcode::UnsignedShiftRight, }; - self.emit2( + self.emit( opcode, &[ - Operand2::Register(dst), - Operand2::Operand(InstructionOperand::Register(&lhs)), - Operand2::Operand(InstructionOperand::Register(&rhs)), + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), ], ); - self.register_allocator.dealloc(lhs); + self.register_allocator.dealloc(rhs); } BinaryOp::Relational(op) => { - self.compile_expr(binary.rhs(), true); - let rhs = self.register_allocator.alloc(); - let lhs = self.register_allocator.alloc(); + self.compile_expr(binary.rhs(), &rhs); - self.pop_into_register(&rhs); - self.pop_into_register(&lhs); let opcode = match op { RelationalOp::Equal => Opcode::Eq, RelationalOp::NotEqual => Opcode::NotEq, @@ -90,50 +78,43 @@ impl ByteCompiler<'_> { RelationalOp::InstanceOf => Opcode::InstanceOf, }; - self.emit2( + self.emit( opcode, &[ - Operand2::Register(dst), - Operand2::Operand(InstructionOperand::Register(&lhs)), - Operand2::Operand(InstructionOperand::Register(&rhs)), + Operand::Register(dst), + Operand::Register(dst), + Operand::Register(&rhs), ], ); - self.register_allocator.dealloc(lhs); + self.register_allocator.dealloc(rhs); } BinaryOp::Logical(op) => { - self.pop_into_register(dst); - let opcode = match op { LogicalOp::And => Opcode::LogicalAnd, LogicalOp::Or => Opcode::LogicalOr, LogicalOp::Coalesce => Opcode::Coalesce, }; - let exit = - self.emit_opcode_with_operand2(opcode, InstructionOperand::Register(dst)); - self.compile_expr(binary.rhs(), true); - self.pop_into_register(dst); + let exit = self.emit_with_label(opcode, &[Operand::Register(dst)]); + self.compile_expr(binary.rhs(), dst); self.patch_jump(exit); } BinaryOp::Comma => { - self.emit_opcode(Opcode::Pop); - self.compile_expr(binary.rhs(), true); - self.pop_into_register(dst); + self.compile_expr(binary.rhs(), dst); } } } pub(crate) fn compile_binary_in_private(&mut self, binary: &BinaryInPrivate, dst: &Register) { let index = self.get_or_insert_private_name(*binary.lhs()); - self.compile_expr(binary.rhs(), true); - self.pop_into_register(dst); - self.emit2( + self.compile_expr(binary.rhs(), dst); + self.emit( Opcode::InPrivate, &[ - Operand2::Register(dst), - Operand2::Varying(index), - Operand2::Operand(InstructionOperand::Register(dst)), + Operand::Register(dst), + Operand::Varying(index), + Operand::Register(dst), ], ); } diff --git a/core/engine/src/bytecompiler/expression/mod.rs b/core/engine/src/bytecompiler/expression/mod.rs index 22c4d1cb501..23bb63c635b 100644 --- a/core/engine/src/bytecompiler/expression/mod.rs +++ b/core/engine/src/bytecompiler/expression/mod.rs @@ -6,7 +6,7 @@ mod update; use std::ops::Deref; -use super::{Access, Callable, NodeKind, Operand, ToJsString}; +use super::{Access, Callable, NodeKind, Operand, Register, ToJsString}; use crate::{ bytecompiler::{ByteCompiler, Literal}, vm::{GeneratorResumeKind, Opcode}, @@ -22,211 +22,203 @@ use boa_ast::{ }; impl ByteCompiler<'_> { - fn compile_literal(&mut self, lit: &AstLiteral, use_expr: bool) { + fn compile_literal(&mut self, lit: &AstLiteral, dst: &Register) { match lit { AstLiteral::String(v) => { - self.emit_push_literal(Literal::String(v.to_js_string(self.interner()))); + self.emit_push_literal(Literal::String(v.to_js_string(self.interner())), dst); } - AstLiteral::Int(v) => self.emit_push_integer(*v), - AstLiteral::Num(v) => self.emit_push_rational(*v), + AstLiteral::Int(v) => self.emit_push_integer(*v, dst), + AstLiteral::Num(v) => self.emit_push_rational(*v, dst), AstLiteral::BigInt(v) => { - self.emit_push_literal(Literal::BigInt(v.clone().into())); + self.emit_push_literal(Literal::BigInt(v.clone().into()), dst); } - AstLiteral::Bool(true) => self.emit(Opcode::PushTrue, &[]), - AstLiteral::Bool(false) => self.emit(Opcode::PushFalse, &[]), - AstLiteral::Null => self.emit(Opcode::PushNull, &[]), - AstLiteral::Undefined => self.emit(Opcode::PushUndefined, &[]), - } - - if !use_expr { - self.emit_opcode(Opcode::Pop); + AstLiteral::Bool(true) => self.push_true(dst), + AstLiteral::Bool(false) => self.push_false(dst), + AstLiteral::Null => self.push_null(dst), + AstLiteral::Undefined => self.push_undefined(dst), } } - fn compile_conditional(&mut self, op: &Conditional, use_expr: bool) { - self.compile_expr(op.condition(), true); - let jelse = self.jump_if_false(); - self.compile_expr(op.if_true(), true); + fn compile_conditional(&mut self, op: &Conditional, dst: &Register) { + self.compile_expr(op.condition(), dst); + let jelse = self.jump_if_false(dst); + self.compile_expr(op.if_true(), dst); let exit = self.jump(); self.patch_jump(jelse); - self.compile_expr(op.if_false(), true); + self.compile_expr(op.if_false(), dst); self.patch_jump(exit); - - if !use_expr { - self.emit_opcode(Opcode::Pop); - }; } - fn compile_template_literal(&mut self, template_literal: &TemplateLiteral, use_expr: bool) { + fn compile_template_literal(&mut self, template_literal: &TemplateLiteral, dst: &Register) { + let mut registers = Vec::with_capacity(template_literal.elements().len()); for element in template_literal.elements() { + let value = self.register_allocator.alloc(); match element { TemplateElement::String(s) => { - self.emit_push_literal(Literal::String(s.to_js_string(self.interner()))); + self.emit_push_literal( + Literal::String(s.to_js_string(self.interner())), + &value, + ); } TemplateElement::Expr(expr) => { - self.compile_expr(expr, true); + self.compile_expr(expr, &value); } } + registers.push(value); } - self.emit_with_varying_operand( - Opcode::ConcatToString, - template_literal.elements().len() as u32, - ); - - if !use_expr { - self.emit_opcode(Opcode::Pop); + let mut args = Vec::with_capacity(registers.len() + 2); + args.push(Operand::Register(dst)); + args.push(Operand::Varying(registers.len() as u32)); + for reg in ®isters { + args.push(Operand::Register(reg)); + } + self.emit(Opcode::ConcatToString, &args); + for reg in registers { + self.register_allocator.dealloc(reg); } } - pub(crate) fn compile_expr_impl(&mut self, expr: &Expression, use_expr: bool) { + pub(crate) fn compile_expr_impl(&mut self, expr: &Expression, dst: &Register) { match expr { - Expression::Literal(lit) => self.compile_literal(lit, use_expr), + Expression::Literal(lit) => self.compile_literal(lit, dst), Expression::RegExpLiteral(regexp) => { let pattern_index = self.get_or_insert_name(Identifier::new(regexp.pattern())); let flags_index = self.get_or_insert_name(Identifier::new(regexp.flags())); self.emit( Opcode::PushRegExp, &[ + Operand::Register(dst), Operand::Varying(pattern_index), Operand::Varying(flags_index), ], ); } - Expression::Unary(unary) => self.compile_unary(unary, use_expr), - Expression::Update(update) => self.compile_update(update, use_expr), - Expression::Binary(binary) => { - let reg = self.register_allocator.alloc(); - self.compile_binary(binary, ®); - if use_expr { - self.push_from_register(®); - } - self.register_allocator.dealloc(reg); - } - Expression::BinaryInPrivate(binary) => { - let reg = self.register_allocator.alloc(); - self.compile_binary_in_private(binary, ®); - if use_expr { - self.push_from_register(®); - } - self.register_allocator.dealloc(reg); - } - Expression::Assign(assign) => self.compile_assign(assign, use_expr), - Expression::ObjectLiteral(object) => { - self.compile_object_literal(object, use_expr); - } - Expression::Identifier(name) => { - self.access_get(Access::Variable { name: *name }, use_expr); - } - Expression::PropertyAccess(access) => { - self.access_get(Access::Property { access }, use_expr); - } - Expression::Conditional(op) => self.compile_conditional(op, use_expr), - Expression::ArrayLiteral(array) => { - self.emit_opcode(Opcode::PushNewArray); - - for element in array.as_ref() { + Expression::Unary(unary) => self.compile_unary(unary, dst), + Expression::Update(update) => self.compile_update(update, dst), + Expression::Binary(binary) => self.compile_binary(binary, dst), + Expression::BinaryInPrivate(binary) => self.compile_binary_in_private(binary, dst), + Expression::Assign(assign) => self.compile_assign(assign, dst), + Expression::ObjectLiteral(object) => self.compile_object_literal(object, dst), + Expression::Identifier(name) => self.access_get(Access::Variable { name: *name }, dst), + Expression::PropertyAccess(access) => self.access_get(Access::Property { access }, dst), + Expression::Conditional(op) => self.compile_conditional(op, dst), + Expression::ArrayLiteral(literal) => { + let value = self.register_allocator.alloc(); + + self.emit(Opcode::PushNewArray, &[Operand::Register(dst)]); + + for element in literal.as_ref() { if let Some(element) = element { - self.compile_expr(element, true); + self.compile_expr(element, &value); if let Expression::Spread(_) = element { - self.emit_opcode(Opcode::GetIterator); - self.emit_opcode(Opcode::PushIteratorToArray); + self.emit(Opcode::GetIterator, &[Operand::Register(&value)]); + self.emit(Opcode::PushIteratorToArray, &[Operand::Register(dst)]); } else { - self.emit_opcode(Opcode::PushValueToArray); + self.emit( + Opcode::PushValueToArray, + &[Operand::Register(&value), Operand::Register(dst)], + ); } } else { - self.emit_opcode(Opcode::PushElisionToArray); + self.emit(Opcode::PushElisionToArray, &[Operand::Register(dst)]); } } - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } - } - Expression::This => { - self.access_get(Access::This, use_expr); + self.register_allocator.dealloc(value); } - Expression::Spread(spread) => self.compile_expr(spread.target(), true), + Expression::This => self.access_get(Access::This, dst), + Expression::Spread(spread) => self.compile_expr(spread.target(), dst), Expression::FunctionExpression(function) => { - self.function_with_binding(function.into(), NodeKind::Expression, use_expr); + self.function_with_binding(function.into(), NodeKind::Expression, dst); } Expression::ArrowFunction(function) => { - self.function_with_binding(function.into(), NodeKind::Expression, use_expr); + self.function_with_binding(function.into(), NodeKind::Expression, dst); } Expression::AsyncArrowFunction(function) => { - self.function_with_binding(function.into(), NodeKind::Expression, use_expr); + self.function_with_binding(function.into(), NodeKind::Expression, dst); } Expression::GeneratorExpression(function) => { - self.function_with_binding(function.into(), NodeKind::Expression, use_expr); + self.function_with_binding(function.into(), NodeKind::Expression, dst); } Expression::AsyncFunctionExpression(function) => { - self.function_with_binding(function.into(), NodeKind::Expression, use_expr); + self.function_with_binding(function.into(), NodeKind::Expression, dst); } Expression::AsyncGeneratorExpression(function) => { - self.function_with_binding(function.into(), NodeKind::Expression, use_expr); + self.function_with_binding(function.into(), NodeKind::Expression, dst); } - Expression::Call(call) => self.call(Callable::Call(call), use_expr), - Expression::New(new) => self.call(Callable::New(new), use_expr), + Expression::Call(call) => self.call(Callable::Call(call), dst), + Expression::New(new) => self.call(Callable::New(new), dst), Expression::TemplateLiteral(template_literal) => { - self.compile_template_literal(template_literal, use_expr); + self.compile_template_literal(template_literal, dst); } Expression::Await(expr) => { - self.compile_expr(expr.target(), true); - self.emit_opcode(Opcode::Await); - self.emit_opcode(Opcode::GeneratorNext); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.compile_expr(expr.target(), dst); + self.emit(Opcode::Await, &[Operand::Register(dst)]); + let resume_kind = self.register_allocator.alloc(); + self.pop_into_register(&resume_kind); + self.pop_into_register(dst); + self.emit( + Opcode::GeneratorNext, + &[Operand::Register(&resume_kind), Operand::Register(dst)], + ); + self.register_allocator.dealloc(resume_kind); } Expression::Yield(r#yield) => { - // stack: if let Some(expr) = r#yield.target() { - self.compile_expr(expr, true); + self.compile_expr(expr, dst); } else { - self.emit_opcode(Opcode::PushUndefined); + self.push_undefined(dst); } - // stack: value - if r#yield.delegate() { if self.is_async() { - self.emit_opcode(Opcode::GetAsyncIterator); + self.emit(Opcode::GetAsyncIterator, &[Operand::Register(dst)]); } else { - self.emit_opcode(Opcode::GetIterator); + self.emit(Opcode::GetIterator, &[Operand::Register(dst)]); } - // stack: - self.emit_opcode(Opcode::PushUndefined); - - // stack: undefined - self.emit_resume_kind(GeneratorResumeKind::Normal); + let resume_kind = self.register_allocator.alloc(); + let is_return = self.register_allocator.alloc(); + self.push_undefined(dst); + self.emit_resume_kind(GeneratorResumeKind::Normal, &resume_kind); - // stack: resume_kind, undefined let start_address = self.next_opcode_location(); + let (throw_method_undefined, return_method_undefined) = - self.emit_opcode_with_two_operands(Opcode::GeneratorDelegateNext); + self.generator_delegate_next(dst, &resume_kind, &is_return); if self.is_async() { - self.emit_opcode(Opcode::Pop); - self.emit_opcode(Opcode::Await); + self.emit(Opcode::Await, &[Operand::Register(dst)]); + self.pop_into_register(&resume_kind); + self.pop_into_register(dst); + } else { + self.emit_resume_kind(GeneratorResumeKind::Normal, &resume_kind); } let (return_gen, exit) = - self.emit_opcode_with_two_operands(Opcode::GeneratorDelegateResume); + self.generator_delegate_resume(dst, &resume_kind, &is_return); + if self.is_async() { - self.emit_opcode(Opcode::IteratorValue); - self.async_generator_yield(); + self.emit(Opcode::IteratorValue, &[Operand::Register(dst)]); + self.async_generator_yield(dst, &resume_kind); } else { - self.emit_opcode(Opcode::IteratorResult); - self.emit_opcode(Opcode::GeneratorYield); + self.emit(Opcode::IteratorResult, &[Operand::Register(dst)]); + self.emit(Opcode::GeneratorYield, &[Operand::Register(dst)]); + self.pop_into_register(&resume_kind); + self.pop_into_register(dst); } self.emit(Opcode::Jump, &[Operand::U32(start_address)]); + self.register_allocator.dealloc(resume_kind); + self.register_allocator.dealloc(is_return); + self.patch_jump(return_gen); self.patch_jump(return_method_undefined); if self.is_async() { - self.emit_opcode(Opcode::Await); + self.emit(Opcode::Await, &[Operand::Register(dst)]); self.emit_opcode(Opcode::Pop); + } else { + self.push_from_register(dst); } self.close_active_iterators(); @@ -234,78 +226,122 @@ impl ByteCompiler<'_> { self.patch_jump(throw_method_undefined); self.iterator_close(self.is_async()); - self.emit_opcode(Opcode::Throw); + self.emit_type_error("iterator does not have a throw method"); self.patch_jump(exit); } else { - self.r#yield(); - } - - if !use_expr { - self.emit_opcode(Opcode::Pop); + self.r#yield(dst); } } Expression::TaggedTemplate(template) => { + let this = self.register_allocator.alloc(); + let function = self.register_allocator.alloc(); + match template.tag() { Expression::PropertyAccess(PropertyAccess::Simple(access)) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + self.compile_expr(access.target(), &this); match access.field() { - PropertyAccessField::Const(field) => { - self.emit_get_property_by_name(*field); + PropertyAccessField::Const(ident) => { + self.emit_get_property_by_name(&function, &this, &this, *ident); } PropertyAccessField::Expr(field) => { - self.compile_expr(field, true); - self.emit_opcode(Opcode::GetPropertyByValue); + let key = self.register_allocator.alloc(); + self.compile_expr(field, &key); + self.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(&function), + Operand::Register(&key), + Operand::Register(&this), + Operand::Register(&this), + ], + ); + self.register_allocator.dealloc(key); } } } Expression::PropertyAccess(PropertyAccess::Private(access)) => { - self.compile_expr(access.target(), true); - self.emit(Opcode::Dup, &[]); let index = self.get_or_insert_private_name(access.field()); - self.emit_with_varying_operand(Opcode::GetPrivateField, index); + self.compile_expr(access.target(), &this); + self.emit( + Opcode::GetPrivateField, + &[ + Operand::Register(&function), + Operand::Register(&this), + Operand::Varying(index), + ], + ); } expr => { - self.emit_opcode(Opcode::PushUndefined); - self.compile_expr(expr, true); + self.push_undefined(&this); + self.compile_expr(expr, &function); } } + self.push_from_register(&this); + self.push_from_register(&function); + + self.register_allocator.dealloc(this); + self.register_allocator.dealloc(function); + let site = template.identifier(); let count = template.cookeds().len() as u32; + let jump_label = self.template_lookup(dst, site); - let jump_label = self.emit_opcode_with_operand(Opcode::TemplateLookup); - self.emit_u64(site); + let mut part_registers = Vec::with_capacity(count as usize * 2); for (cooked, raw) in template.cookeds().iter().zip(template.raws()) { + let value = self.register_allocator.alloc(); if let Some(cooked) = cooked { - self.emit_push_literal(Literal::String( - cooked.to_js_string(self.interner()), - )); + self.emit_push_literal( + Literal::String(cooked.to_js_string(self.interner())), + &value, + ); } else { - self.emit_opcode(Opcode::PushUndefined); + self.push_undefined(&value); } - self.emit_push_literal(Literal::String(raw.to_js_string(self.interner()))); + part_registers.push(value); + let value = self.register_allocator.alloc(); + self.emit_push_literal( + Literal::String(raw.to_js_string(self.interner())), + &value, + ); + part_registers.push(value); } - self.emit( - Opcode::TemplateCreate, - &[Operand::Varying(count), Operand::U64(site)], - ); + let mut args = Vec::with_capacity(count as usize * 2 + 2); + args.push(Operand::U64(site)); + args.push(Operand::Register(dst)); + args.push(Operand::Varying(count)); + for r in &part_registers { + args.push(Operand::Register(r)); + } + self.emit(Opcode::TemplateCreate, &args); + for r in part_registers { + self.register_allocator.dealloc(r); + } self.patch_jump(jump_label); + self.push_from_register(dst); for expr in template.exprs() { - self.compile_expr(expr, true); + let value = self.register_allocator.alloc(); + self.compile_expr(expr, &value); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } self.emit_with_varying_operand(Opcode::Call, template.exprs().len() as u32 + 1); + self.pop_into_register(dst); + } + Expression::ClassExpression(class) => { + self.compile_class(class.deref().into(), Some(dst)); } - Expression::ClassExpression(class) => self.class(class.deref().into(), true), Expression::SuperCall(super_call) => { - self.emit_opcode(Opcode::SuperCallPrepare); + let value = self.register_allocator.alloc(); + self.emit(Opcode::SuperCallPrepare, &[Operand::Register(&value)]); + self.push_from_register(&value); + self.register_allocator.dealloc(value); let contains_spread = super_call .arguments() @@ -313,19 +349,34 @@ impl ByteCompiler<'_> { .any(|arg| matches!(arg, Expression::Spread(_))); if contains_spread { - self.emit_opcode(Opcode::PushNewArray); + let array = self.register_allocator.alloc(); + let value = self.register_allocator.alloc(); + + self.emit(Opcode::PushNewArray, &[Operand::Register(&array)]); + for arg in super_call.arguments() { - self.compile_expr(arg, true); + self.compile_expr(arg, &value); if let Expression::Spread(_) = arg { - self.emit_opcode(Opcode::GetIterator); - self.emit_opcode(Opcode::PushIteratorToArray); + self.emit(Opcode::GetIterator, &[Operand::Register(&value)]); + self.emit(Opcode::PushIteratorToArray, &[Operand::Register(&array)]); } else { - self.emit_opcode(Opcode::PushValueToArray); + self.emit( + Opcode::PushValueToArray, + &[Operand::Register(&value), Operand::Register(&array)], + ); } } + + self.push_from_register(&array); + + self.register_allocator.dealloc(value); + self.register_allocator.dealloc(array); } else { for arg in super_call.arguments() { - self.compile_expr(arg, true); + let value = self.register_allocator.alloc(); + self.compile_expr(arg, &value); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } } @@ -337,41 +388,26 @@ impl ByteCompiler<'_> { super_call.arguments().len() as u32, ); } - self.emit_opcode(Opcode::BindThisValue); - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.pop_into_register(dst); + self.emit(Opcode::BindThisValue, &[Operand::Register(dst)]); } Expression::ImportCall(import) => { - self.compile_expr(import.argument(), true); - self.emit_opcode(Opcode::ImportCall); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.compile_expr(import.argument(), dst); + self.emit(Opcode::ImportCall, &[Operand::Register(dst)]); } Expression::NewTarget => { - if use_expr { - self.emit_opcode(Opcode::NewTarget); - } + self.emit(Opcode::NewTarget, &[Operand::Register(dst)]); } Expression::ImportMeta => { - if use_expr { - self.emit_opcode(Opcode::ImportMeta); - } + self.emit(Opcode::ImportMeta, &[Operand::Register(dst)]); } Expression::Optional(opt) => { - self.compile_optional_preserve_this(opt); - - self.emit_opcode(Opcode::Swap); - self.emit_opcode(Opcode::Pop); - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let this = self.register_allocator.alloc(); + self.compile_optional_preserve_this(opt, &this, dst); + self.register_allocator.dealloc(this); } Expression::Parenthesized(parenthesized) => { - self.compile_expr(parenthesized.expression(), use_expr); + self.compile_expr(parenthesized.expression(), dst); } // TODO: try to remove this variant somehow Expression::FormalParameterList(_) => unreachable!(), diff --git a/core/engine/src/bytecompiler/expression/object_literal.rs b/core/engine/src/bytecompiler/expression/object_literal.rs index 84676647f5a..9f983a42581 100644 --- a/core/engine/src/bytecompiler/expression/object_literal.rs +++ b/core/engine/src/bytecompiler/expression/object_literal.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, FunctionSpec, MethodKind, Operand}, + bytecompiler::{Access, ByteCompiler, FunctionSpec, MethodKind, Operand, Register}, vm::Opcode, }; use boa_ast::{ @@ -10,37 +10,76 @@ use boa_ast::{ use boa_interner::Sym; impl ByteCompiler<'_> { - pub(crate) fn compile_object_literal(&mut self, object: &ObjectLiteral, use_expr: bool) { - self.emit_opcode(Opcode::PushEmptyObject); - for property in object.properties() { - self.emit_opcode(Opcode::Dup); + pub(crate) fn compile_object_literal(&mut self, literal: &ObjectLiteral, dst: &Register) { + self.emit(Opcode::PushEmptyObject, &[Operand::Register(dst)]); + + for property in literal.properties() { match property { PropertyDefinition::IdentifierReference(ident) => { + let value = self.register_allocator.alloc(); + self.access_get(Access::Variable { name: *ident }, &value); let index = self.get_or_insert_name(*ident); - self.access_get(Access::Variable { name: *ident }, true); - self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); + self.emit( + Opcode::DefineOwnPropertyByName, + &[ + Operand::Register(dst), + Operand::Register(&value), + Operand::Varying(index), + ], + ); + self.register_allocator.dealloc(value); } PropertyDefinition::Property(name, expr) => match name { PropertyName::Literal(name) => { - self.compile_expr(expr, true); - let index = self.get_or_insert_name((*name).into()); + let value = self.register_allocator.alloc(); + self.compile_expr(expr, &value); if *name == Sym::__PROTO__ && !self.json_parse { - self.emit_opcode(Opcode::SetPrototype); + self.emit( + Opcode::SetPrototype, + &[Operand::Register(dst), Operand::Register(&value)], + ); } else { - self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, index); + let index = self.get_or_insert_name((*name).into()); + self.emit( + Opcode::DefineOwnPropertyByName, + &[ + Operand::Register(dst), + Operand::Register(&value), + Operand::Varying(index), + ], + ); } + self.register_allocator.dealloc(value); } PropertyName::Computed(name_node) => { - self.compile_expr(name_node, true); - self.emit_opcode(Opcode::ToPropertyKey); + let key = self.register_allocator.alloc(); + self.compile_expr(name_node, &key); + self.emit( + Opcode::ToPropertyKey, + &[Operand::Register(&key), Operand::Register(&key)], + ); + let function = self.register_allocator.alloc(); + self.compile_expr(expr, &function); if expr.is_anonymous_function_definition() { - self.emit_opcode(Opcode::Dup); - self.compile_expr(expr, true); - self.emit(Opcode::SetFunctionName, &[Operand::U8(0)]); - } else { - self.compile_expr(expr, true); + self.emit( + Opcode::SetFunctionName, + &[ + Operand::Register(&function), + Operand::Register(&key), + Operand::U8(0), + ], + ); } - self.emit_opcode(Opcode::DefineOwnPropertyByValue); + self.emit( + Opcode::DefineOwnPropertyByValue, + &[ + Operand::Register(&function), + Operand::Register(&key), + Operand::Register(dst), + ], + ); + self.register_allocator.dealloc(key); + self.register_allocator.dealloc(function); } }, PropertyDefinition::MethodDefinition(m) => { @@ -51,89 +90,106 @@ impl ByteCompiler<'_> { }; match m.name() { PropertyName::Literal(name) => { + let method = self.object_method(m.into(), kind); + self.emit( + Opcode::SetHomeObject, + &[Operand::Register(&method), Operand::Register(dst)], + ); + let index = self.get_or_insert_name((*name).into()); let opcode = match kind { MethodKind::Get => Opcode::SetPropertyGetterByName, MethodKind::Set => Opcode::SetPropertySetterByName, MethodKind::Ordinary => Opcode::DefineOwnPropertyByName, }; - self.object_method(m.into(), kind); - self.emit_opcode(Opcode::SetHomeObject); - let index = self.get_or_insert_name((*name).into()); - self.emit_with_varying_operand(opcode, index); + self.emit( + opcode, + &[ + Operand::Register(dst), + Operand::Register(&method), + Operand::Varying(index), + ], + ); + + self.register_allocator.dealloc(method); } PropertyName::Computed(name_node) => { - self.compile_object_literal_computed_method(name_node, m.into(), kind); + self.compile_object_literal_computed_method( + name_node, + m.into(), + kind, + dst, + ); } } } PropertyDefinition::SpreadObject(expr) => { - self.compile_expr(expr, true); - self.emit_opcode(Opcode::Swap); + let source = self.register_allocator.alloc(); + self.compile_expr(expr, &source); self.emit( Opcode::CopyDataProperties, - &[Operand::Varying(0), Operand::Varying(0)], + &[ + Operand::Register(dst), + Operand::Register(&source), + Operand::Varying(0), + ], ); - self.emit_opcode(Opcode::Pop); + self.register_allocator.dealloc(source); } PropertyDefinition::CoverInitializedName(_, _) => { unreachable!("invalid assignment pattern in object literal") } } } - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } } fn compile_object_literal_computed_method( &mut self, - name: &Expression, + expr: &Expression, function: FunctionSpec<'_>, kind: MethodKind, + object: &Register, ) { - // stack: object - self.compile_expr(name, true); - - // stack: object, name - self.emit_opcode(Opcode::ToPropertyKey); - - // stack: object, ToPropertyKey(name) - self.emit_opcode(Opcode::Dup); + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); - // stack: object, object, ToPropertyKey(name), ToPropertyKey(name) - self.object_method(function, kind); + self.emit( + Opcode::ToPropertyKey, + &[Operand::Register(&key), Operand::Register(&key)], + ); - // stack: object, ToPropertyKey(name), ToPropertyKey(name), method + let method = self.object_method(function, kind); let value = match kind { MethodKind::Get => 1, MethodKind::Set => 2, MethodKind::Ordinary => 0, }; - self.emit(Opcode::SetFunctionName, &[Operand::U8(value)]); - - // stack: object, ToPropertyKey(name), method - self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); - - // stack: ToPropertyKey(name), method, object - self.emit_opcode(Opcode::Swap); - - // stack: ToPropertyKey(name), object, method - self.emit_opcode(Opcode::SetHomeObject); - // stack: ToPropertyKey(name), object, method - self.emit_opcode(Opcode::Swap); + self.emit( + Opcode::SetFunctionName, + &[ + Operand::Register(&method), + Operand::Register(&key), + Operand::U8(value), + ], + ); - // stack: ToPropertyKey(name), method, object - self.emit(Opcode::RotateRight, &[Operand::U8(3)]); + self.emit( + Opcode::SetHomeObject, + &[Operand::Register(&method), Operand::Register(object)], + ); - // stack: object, ToPropertyKey(name), method + let operands = &[ + Operand::Register(&method), + Operand::Register(&key), + Operand::Register(object), + ]; match kind { - MethodKind::Get => self.emit_opcode(Opcode::SetPropertyGetterByValue), - MethodKind::Set => self.emit_opcode(Opcode::SetPropertySetterByValue), - MethodKind::Ordinary => self.emit_opcode(Opcode::DefineOwnPropertyByValue), + MethodKind::Get => self.emit(Opcode::SetPropertyGetterByValue, operands), + MethodKind::Set => self.emit(Opcode::SetPropertySetterByValue, operands), + MethodKind::Ordinary => self.emit(Opcode::DefineOwnPropertyByValue, operands), } - // stack: + self.register_allocator.dealloc(key); + self.register_allocator.dealloc(method); } } diff --git a/core/engine/src/bytecompiler/expression/unary.rs b/core/engine/src/bytecompiler/expression/unary.rs index 298e80ae4c5..2da5ee665fe 100644 --- a/core/engine/src/bytecompiler/expression/unary.rs +++ b/core/engine/src/bytecompiler/expression/unary.rs @@ -4,19 +4,19 @@ use boa_ast::{ }; use crate::{ - bytecompiler::{Access, ByteCompiler, ToJsString}, + bytecompiler::{Access, ByteCompiler, Operand, Register, ToJsString}, vm::Opcode, }; impl ByteCompiler<'_> { - pub(crate) fn compile_unary(&mut self, unary: &Unary, use_expr: bool) { + pub(crate) fn compile_unary(&mut self, unary: &Unary, dst: &Register) { let opcode = match unary.op() { UnaryOp::Delete => { if let Some(access) = Access::from_expression(unary.target()) { - self.access_delete(access); + self.access_delete(access, dst); } else { - self.compile_expr(unary.target(), false); - self.emit(Opcode::PushTrue, &[]); + self.compile_expr(unary.target(), dst); + self.push_true(dst); } None } @@ -30,23 +30,23 @@ impl ByteCompiler<'_> { let identifier = identifier.to_js_string(self.interner()); let binding = self.lexical_scope.get_identifier_reference(identifier); let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::GetNameOrUndefined, &index); + self.emit_binding_access(Opcode::GetNameOrUndefined, &index, dst); } - expr => self.compile_expr(expr, true), + expr => self.compile_expr(expr, dst), } - self.emit_opcode(Opcode::TypeOf); + self.emit(Opcode::TypeOf, &[Operand::Register(dst)]); + None + } + UnaryOp::Void => { + self.compile_expr(unary.target(), dst); + self.push_undefined(dst); None } - UnaryOp::Void => Some(Opcode::Void), }; if let Some(opcode) = opcode { - self.compile_expr(unary.target(), true); - self.emit(opcode, &[]); - } - - if !use_expr { - self.emit_opcode(Opcode::Pop); + self.compile_expr(unary.target(), dst); + self.emit(opcode, &[Operand::Register(dst)]); } } } diff --git a/core/engine/src/bytecompiler/expression/update.rs b/core/engine/src/bytecompiler/expression/update.rs index e1e1836145a..78b3a56fc9a 100644 --- a/core/engine/src/bytecompiler/expression/update.rs +++ b/core/engine/src/bytecompiler/expression/update.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, InstructionOperand, Operand2, ToJsString}, + bytecompiler::{Access, ByteCompiler, Operand, Register, ToJsString}, vm::Opcode, }; use boa_ast::{ @@ -11,7 +11,7 @@ use boa_ast::{ }; impl ByteCompiler<'_> { - pub(crate) fn compile_update(&mut self, update: &Update, use_expr: bool) { + pub(crate) fn compile_update(&mut self, update: &Update, dst: &Register) { let opcode = match update.op() { UpdateOp::IncrementPost | UpdateOp::IncrementPre => Opcode::Inc, UpdateOp::DecrementPre | UpdateOp::DecrementPost => Opcode::Dec, @@ -29,264 +29,186 @@ impl ByteCompiler<'_> { let index = self.get_or_insert_binding(binding); if is_lexical { - self.emit_binding_access(Opcode::GetName, &index); + self.emit_binding_access(Opcode::GetName, &index, dst); } else { - self.emit_binding_access(Opcode::GetNameAndLocator, &index); + self.emit_binding_access(Opcode::GetNameAndLocator, &index, dst); } - let src = self.register_allocator.alloc(); - let dst = self.register_allocator.alloc(); - - self.pop_into_register(&src); - - self.emit2( - Opcode::ToNumeric, - &[ - Operand2::Register(&dst), - Operand2::Operand(InstructionOperand::Register(&src)), - ], - ); - self.emit2( - opcode, - &[ - Operand2::Register(&src), - Operand2::Operand(InstructionOperand::Register(&dst)), - ], - ); - self.push_from_register(&src); + let value = self.register_allocator.alloc(); + self.emit(opcode, &[Operand::Register(&value), Operand::Register(dst)]); if is_lexical { match self.lexical_scope.set_mutable_binding(name.clone()) { Ok(binding) => { let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::SetName, &index); + self.emit_binding_access(Opcode::SetName, &index, &value); } Err(BindingLocatorError::MutateImmutable) => { let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } + Err(BindingLocatorError::Silent) => {} } } else { - self.emit_binding_access(Opcode::SetNameByLocator, &index); + self.emit_binding_access(Opcode::SetNameByLocator, &index, &value); } - if post { - self.push_from_register(&dst); - } else { - self.push_from_register(&src); + if !post { + self.emit_move(dst, &value); } - self.register_allocator.dealloc(src); - self.register_allocator.dealloc(dst); + self.register_allocator.dealloc(value); } Access::Property { access } => match access { PropertyAccess::Simple(access) => { - self.compile_expr(access.target(), true); - - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); - // Stack: value, value, value, value match access.field() { - PropertyAccessField::Const(name) => { - self.emit_get_property_by_name(*name); + PropertyAccessField::Const(ident) => { + self.emit_get_property_by_name(dst, &object, &object, *ident); + let value = self.register_allocator.alloc(); + self.emit(opcode, &[Operand::Register(&value), Operand::Register(dst)]); - let src = self.register_allocator.alloc(); - let dst = self.register_allocator.alloc(); - self.pop_into_register(&src); + self.emit_set_property_by_name(&value, &object, &object, *ident); - self.emit2( - Opcode::ToNumeric, - &[ - Operand2::Register(&dst), - Operand2::Operand(InstructionOperand::Register(&src)), - ], - ); - self.emit2( - opcode, - &[ - Operand2::Register(&src), - Operand2::Operand(InstructionOperand::Register(&dst)), - ], - ); - - self.push_from_register(&src); - - self.emit_set_property_by_name(*name); - - if post { - self.emit_opcode(Opcode::Pop); - self.push_from_register(&dst); + if !post { + self.emit_move(dst, &value); } - self.register_allocator.dealloc(src); - self.register_allocator.dealloc(dst); + self.register_allocator.dealloc(object); + self.register_allocator.dealloc(value); } PropertyAccessField::Expr(expr) => { - self.compile_expr(expr, true); - - self.emit_opcode(Opcode::GetPropertyByValuePush); - - let src = self.register_allocator.alloc(); - let dst = self.register_allocator.alloc(); - self.pop_into_register(&src); + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); - self.emit2( - Opcode::ToNumeric, + self.emit( + Opcode::GetPropertyByValuePush, &[ - Operand2::Register(&dst), - Operand2::Operand(InstructionOperand::Register(&src)), + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&object), + Operand::Register(&object), ], ); - self.emit2( - opcode, + + let value = self.register_allocator.alloc(); + + self.emit(opcode, &[Operand::Register(&value), Operand::Register(dst)]); + + self.emit( + Opcode::SetPropertyByValue, &[ - Operand2::Register(&src), - Operand2::Operand(InstructionOperand::Register(&dst)), + Operand::Register(&value), + Operand::Register(&key), + Operand::Register(&object), + Operand::Register(&object), ], ); - self.push_from_register(&src); - - self.emit_opcode(Opcode::SetPropertyByValue); - - if post { - self.emit_opcode(Opcode::Pop); - self.push_from_register(&dst); + if !post { + self.emit_move(dst, &value); } - self.register_allocator.dealloc(src); - self.register_allocator.dealloc(dst); + self.register_allocator.dealloc(key); + self.register_allocator.dealloc(object); + self.register_allocator.dealloc(value); } } } PropertyAccess::Private(access) => { let index = self.get_or_insert_private_name(access.field()); - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); - self.emit_with_varying_operand(Opcode::GetPrivateField, index); - - let src = self.register_allocator.alloc(); - let dst = self.register_allocator.alloc(); - self.pop_into_register(&src); - - self.emit2( - Opcode::ToNumeric, + self.emit( + Opcode::GetPrivateField, &[ - Operand2::Register(&dst), - Operand2::Operand(InstructionOperand::Register(&src)), + Operand::Register(dst), + Operand::Register(&object), + Operand::Varying(index), ], ); - self.emit2( - opcode, + + let value = self.register_allocator.alloc(); + self.emit(opcode, &[Operand::Register(&value), Operand::Register(dst)]); + + self.emit( + Opcode::SetPrivateField, &[ - Operand2::Register(&src), - Operand2::Operand(InstructionOperand::Register(&dst)), + Operand::Register(&value), + Operand::Register(&object), + Operand::Varying(index), ], ); - self.push_from_register(&src); - - self.emit_with_varying_operand(Opcode::SetPrivateField, index); - if post { - self.emit_opcode(Opcode::Pop); - self.push_from_register(&dst); + if !post { + self.emit_move(dst, &value); } - self.register_allocator.dealloc(src); - self.register_allocator.dealloc(dst); + self.register_allocator.dealloc(value); + self.register_allocator.dealloc(object); } PropertyAccess::Super(access) => match access.field() { - PropertyAccessField::Const(name) => { - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::This); - self.emit_opcode(Opcode::Swap); - self.emit_opcode(Opcode::This); + PropertyAccessField::Const(ident) => { + let object = self.register_allocator.alloc(); + let receiver = self.register_allocator.alloc(); + self.emit(Opcode::Super, &[Operand::Register(&object)]); + self.emit(Opcode::This, &[Operand::Register(&receiver)]); - self.emit_get_property_by_name(*name); + self.emit_get_property_by_name(dst, &receiver, &object, *ident); - let src = self.register_allocator.alloc(); - let dst = self.register_allocator.alloc(); - self.pop_into_register(&src); + let value = self.register_allocator.alloc(); + self.emit(opcode, &[Operand::Register(&value), Operand::Register(dst)]); - self.emit2( - Opcode::ToNumeric, - &[ - Operand2::Register(&dst), - Operand2::Operand(InstructionOperand::Register(&src)), - ], - ); - self.emit2( - opcode, - &[ - Operand2::Register(&src), - Operand2::Operand(InstructionOperand::Register(&dst)), - ], - ); - - self.push_from_register(&src); - - self.emit_set_property_by_name(*name); - if post { - self.emit_opcode(Opcode::Pop); - self.push_from_register(&dst); + self.emit_set_property_by_name(&value, &receiver, &object, *ident); + if !post { + self.emit_move(dst, &value); } - self.register_allocator.dealloc(src); - self.register_allocator.dealloc(dst); + self.register_allocator.dealloc(receiver); + self.register_allocator.dealloc(object); + self.register_allocator.dealloc(value); } PropertyAccessField::Expr(expr) => { - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::This); - self.compile_expr(expr, true); - - self.emit_opcode(Opcode::GetPropertyByValuePush); + let object = self.register_allocator.alloc(); + let receiver = self.register_allocator.alloc(); + self.emit(Opcode::Super, &[Operand::Register(&object)]); + self.emit(Opcode::This, &[Operand::Register(&receiver)]); - let src = self.register_allocator.alloc(); - let dst = self.register_allocator.alloc(); - self.pop_into_register(&src); + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); - self.emit2( - Opcode::ToNumeric, + self.emit( + Opcode::GetPropertyByValue, &[ - Operand2::Register(&dst), - Operand2::Operand(InstructionOperand::Register(&src)), + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&receiver), + Operand::Register(&object), ], ); - self.emit2( - opcode, + + self.emit(opcode, &[Operand::Register(dst), Operand::Register(dst)]); + + self.emit( + Opcode::SetPropertyByValue, &[ - Operand2::Register(&src), - Operand2::Operand(InstructionOperand::Register(&dst)), + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&receiver), + Operand::Register(&object), ], ); - self.emit_opcode(Opcode::This); - self.push_from_register(&src); - - self.emit_opcode(Opcode::SetPropertyByValue); - if post { - self.emit_opcode(Opcode::Pop); - self.push_from_register(&dst); - } - - self.register_allocator.dealloc(src); - self.register_allocator.dealloc(dst); + self.register_allocator.dealloc(receiver); + self.register_allocator.dealloc(object); + self.register_allocator.dealloc(key); } }, }, Access::This => unreachable!(), } - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } } } diff --git a/core/engine/src/bytecompiler/jump_control.rs b/core/engine/src/bytecompiler/jump_control.rs index 3736534b6e9..1da0510880c 100644 --- a/core/engine/src/bytecompiler/jump_control.rs +++ b/core/engine/src/bytecompiler/jump_control.rs @@ -16,6 +16,8 @@ use crate::{ use bitflags::bitflags; use boa_interner::Sym; +use super::{Operand, Register}; + /// An actions to be performed for the local control flow. #[derive(Debug, Clone, Copy)] pub(crate) enum JumpRecordAction { @@ -58,6 +60,8 @@ pub(crate) enum JumpRecordAction { HandleFinally { /// Jump table index. index: u32, + /// Register for the flag that indicated if the finally block needs to re throw. + finally_throw: u32, }, } @@ -102,11 +106,17 @@ impl JumpRecord { compiler.emit_opcode(Opcode::PopEnvironment); } } - JumpRecordAction::HandleFinally { index: value } => { + JumpRecordAction::HandleFinally { + index: value, + finally_throw, + } => { // Note: +1 because 0 is reserved for default entry in jump table (for fallthrough). let index = value as i32 + 1; - compiler.emit_push_integer(index); - compiler.emit_opcode(Opcode::PushFalse); + let value = compiler.register_allocator.alloc(); + compiler.emit_push_integer(index, &value); + compiler.push_from_register(&value); + compiler.emit(Opcode::PushFalse, &[Operand::Varying(finally_throw)]); + compiler.register_allocator.dealloc(value); } JumpRecordAction::CloseIterator { r#async } => { compiler.iterator_close(r#async); @@ -122,7 +132,10 @@ impl JumpRecord { return_value_on_stack, } => { if return_value_on_stack { - compiler.emit_opcode(Opcode::SetAccumulatorFromStack); + let value = compiler.register_allocator.alloc(); + compiler.pop_into_register(&value); + compiler.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + compiler.register_allocator.dealloc(value); } match (compiler.is_async(), compiler.is_generator()) { @@ -159,6 +172,7 @@ pub(crate) struct JumpControlInfo { pub(crate) flags: JumpControlInfoFlags, pub(crate) jumps: Vec, current_open_environments_count: u32, + pub(crate) finally_throw: Option, } bitflags! { @@ -202,6 +216,7 @@ impl JumpControlInfo { flags: JumpControlInfoFlags::default(), jumps: Vec::new(), current_open_environments_count, + finally_throw: None, } } @@ -225,9 +240,8 @@ impl JumpControlInfo { self } - pub(crate) fn with_try_with_finally_flag(mut self, value: bool) -> Self { - self.flags - .set(JumpControlInfoFlags::TRY_WITH_FINALLY, value); + pub(crate) fn with_try_with_finally_flag(mut self, dst: &Register) -> Self { + self.finally_throw = Some(dst.index()); self } @@ -267,7 +281,7 @@ impl JumpControlInfo { } pub(crate) const fn is_try_with_finally_block(&self) -> bool { - self.flags.contains(JumpControlInfoFlags::TRY_WITH_FINALLY) + self.finally_throw.is_some() } pub(crate) const fn is_labelled(&self) -> bool { @@ -344,7 +358,6 @@ impl ByteCompiler<'_> { self.handlers.push(Handler { start: start_address, end: Self::DUMMY_ADDRESS, - stack_count: self.current_stack_value_count, environment_count, }); @@ -516,9 +529,13 @@ impl ByteCompiler<'_> { // ---- `TryStatement`'s `JumpControlInfo` methods ---- // /// Pushes a `TryStatement`'s `JumpControlInfo` onto the `jump_info` stack. - pub(crate) fn push_try_with_finally_control_info(&mut self, use_expr: bool) { + pub(crate) fn push_try_with_finally_control_info( + &mut self, + finally_throw: &Register, + use_expr: bool, + ) { let new_info = JumpControlInfo::new(self.current_open_environments_count) - .with_try_with_finally_flag(true); + .with_try_with_finally_flag(finally_throw); self.push_contol_info(new_info, use_expr); } diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index 50f29f02070..0883e91320a 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -46,7 +46,6 @@ use boa_ast::{ use boa_gc::Gc; use boa_interner::{Interner, Sym}; use boa_macros::js_str; -use class::ClassSpec; use rustc_hash::FxHashMap; use thin_vec::ThinVec; @@ -369,51 +368,21 @@ impl Access<'_> { /// An opcode operand. #[derive(Debug, Clone, Copy)] -#[allow(dead_code)] -pub(crate) enum Operand { - Bool(bool), - I8(i8), - U8(u8), - I16(i16), - U16(u16), - I32(i32), - U32(u32), - I64(i64), - U64(u64), - Varying(u32), -} - -/// An opcode operand. -#[derive(Debug, Clone, Copy)] -#[allow(dead_code)] -pub(crate) enum Operand2<'a> { +pub(crate) enum Operand<'a> { Bool(bool), I8(i8), U8(u8), I16(i16), + #[allow(unused)] U16(u16), I32(i32), U32(u32), + #[allow(unused)] I64(i64), U64(u64), Varying(u32), Register(&'a Register), - Operand(InstructionOperand<'a>), -} - -/// An opcode operand. -/// -// | 00 Register -// | 01 Argements -// | 10 Immediate -// | 11 ??? -#[derive(Debug, Clone, Copy)] -#[allow(dead_code)] -pub(crate) enum InstructionOperand<'a> { - Register(&'a Register), - Argument(u32), - Constant(i32), } /// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode. @@ -454,7 +423,6 @@ pub struct ByteCompiler<'ctx> { pub(crate) lexical_scope: Scope, pub(crate) current_open_environments_count: u32, - current_stack_value_count: u32, code_block_flags: CodeBlockFlags, handlers: ThinVec, pub(crate) ic: Vec, @@ -553,7 +521,6 @@ impl<'ctx> ByteCompiler<'ctx> { current_open_environments_count: 0, register_allocator, - current_stack_value_count: 0, code_block_flags, handlers: ThinVec::default(), ic: Vec::default(), @@ -661,45 +628,41 @@ impl<'ctx> ByteCompiler<'ctx> { index } - fn emit_binding(&mut self, opcode: BindingOpcode, name: JsString) { + fn emit_binding(&mut self, opcode: BindingOpcode, name: JsString, value: &Register) { match opcode { BindingOpcode::Var => { let binding = self.variable_scope.get_identifier_reference(name); if !binding.locator().is_global() { let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::DefVar, &index); + self.emit_binding_access(Opcode::DefVar, &index, value); } } BindingOpcode::InitVar => match self.lexical_scope.set_mutable_binding(name.clone()) { Ok(binding) => { let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::DefInitVar, &index); + self.emit_binding_access(Opcode::DefInitVar, &index, value); } Err(BindingLocatorError::MutateImmutable) => { let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } + Err(BindingLocatorError::Silent) => {} }, BindingOpcode::InitLexical => { let binding = self.lexical_scope.get_identifier_reference(name); let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::PutLexicalValue, &index); + self.emit_binding_access(Opcode::PutLexicalValue, &index, value); } BindingOpcode::SetName => match self.lexical_scope.set_mutable_binding(name.clone()) { Ok(binding) => { let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::SetName, &index); + self.emit_binding_access(Opcode::SetName, &index, value); } Err(BindingLocatorError::MutateImmutable) => { let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } + Err(BindingLocatorError::Silent) => {} }, } } @@ -709,97 +672,28 @@ impl<'ctx> ByteCompiler<'ctx> { self.bytecode.len() as u32 } - pub(crate) fn emit(&mut self, opcode: Opcode, operands: &[Operand]) { - let mut varying_kind = VaryingOperandKind::U8; - for operand in operands { - if let Operand::Varying(operand) = *operand { - if u8::try_from(operand).is_ok() { - } else if u16::try_from(operand).is_ok() { - varying_kind = VaryingOperandKind::U16; - } else { - varying_kind = VaryingOperandKind::U32; - break; - } - } - } - - match varying_kind { - VaryingOperandKind::U8 => {} - VaryingOperandKind::U16 => self.emit_opcode(Opcode::U16Operands), - VaryingOperandKind::U32 => self.emit_opcode(Opcode::U32Operands), - } - self.emit_opcode(opcode); - for operand in operands { - self.emit_operand(*operand, varying_kind); - } - } - - pub(crate) fn emit2(&mut self, opcode: Opcode, operands: &[Operand2<'_>]) { + pub(crate) fn emit(&mut self, opcode: Opcode, operands: &[Operand<'_>]) { let mut varying_kind = VaryingOperandKind::U8; - let mut operand_types = 0; - let mut has_operand_types = false; for operand in operands { - let operand = match *operand { - Operand2::Register(operand) => { + match *operand { + Operand::Register(operand) => { if u8::try_from(operand.index()).is_ok() { } else if u16::try_from(operand.index()).is_ok() { varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U16); } else { varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U32); } - None } - Operand2::Varying(operand) => { + Operand::Varying(operand) => { if u8::try_from(operand).is_ok() { } else if u16::try_from(operand).is_ok() { varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U16); } else { varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U32); } - None - } - Operand2::Operand(operand) => Some(operand), - _ => None, - }; - - let Some(operand) = operand else { - continue; - }; - - has_operand_types = true; - - let type_ = match operand { - InstructionOperand::Register(reg) => { - if u8::try_from(reg.index()).is_ok() { - } else if u16::try_from(reg.index()).is_ok() { - varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U16); - } else { - varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U32); - } - 0b0000_0000 - } - InstructionOperand::Argument(index) => { - if u8::try_from(index).is_ok() { - } else if u16::try_from(index).is_ok() { - varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U16); - } else { - varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U32); - } - 0b0000_0001 - } - InstructionOperand::Constant(value) => { - if i8::try_from(value).is_ok() { - } else if i16::try_from(value).is_ok() { - varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U16); - } else { - varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U32); - } - 0b0000_0010 } + _ => {} }; - - operand_types <<= 2; - operand_types |= type_; } match varying_kind { @@ -808,42 +702,39 @@ impl<'ctx> ByteCompiler<'ctx> { VaryingOperandKind::U32 => self.emit_opcode(Opcode::U32Operands), } self.emit_opcode(opcode); - if has_operand_types { - self.emit_u8(operand_types); - } for operand in operands { self.emit_operand2(*operand, varying_kind); } } - pub(crate) fn emit_operand2( - &mut self, - operand: Operand2<'_>, - varying_kind: VaryingOperandKind, - ) { + /// Emit an opcode with a dummy operand. + /// Return the `Label` of the operand. + pub(crate) fn emit_with_label(&mut self, opcode: Opcode, operands: &[Operand<'_>]) -> Label { + let index = self.next_opcode_location(); + let mut ops = Vec::with_capacity(operands.len() + 1); + ops.push(Operand::U32(Self::DUMMY_ADDRESS)); + ops.extend_from_slice(operands); + self.emit(opcode, &ops); + Label { index } + } + + pub(crate) fn emit_operand2(&mut self, operand: Operand<'_>, varying_kind: VaryingOperandKind) { match operand { - Operand2::Bool(v) => self.emit_u8(v.into()), - Operand2::I8(v) => self.emit_i8(v), - Operand2::U8(v) => self.emit_u8(v), - Operand2::I16(v) => self.emit_i16(v), - Operand2::U16(v) => self.emit_u16(v), - Operand2::I32(v) => self.emit_i32(v), - Operand2::U32(v) => self.emit_u32(v), - Operand2::I64(v) => self.emit_i64(v), - Operand2::U64(v) => self.emit_u64(v), - Operand2::Varying(v) | Operand2::Operand(InstructionOperand::Argument(v)) => { - match varying_kind { - VaryingOperandKind::U8 => self.emit_u8(v as u8), - VaryingOperandKind::U16 => self.emit_u16(v as u16), - VaryingOperandKind::U32 => self.emit_u32(v), - } - } - Operand2::Operand(InstructionOperand::Constant(v)) => match varying_kind { - VaryingOperandKind::U8 => self.emit_i8(v as i8), - VaryingOperandKind::U16 => self.emit_i16(v as i16), - VaryingOperandKind::U32 => self.emit_i32(v), + Operand::Bool(v) => self.emit_u8(v.into()), + Operand::I8(v) => self.emit_i8(v), + Operand::U8(v) => self.emit_u8(v), + Operand::I16(v) => self.emit_i16(v), + Operand::U16(v) => self.emit_u16(v), + Operand::I32(v) => self.emit_i32(v), + Operand::U32(v) => self.emit_u32(v), + Operand::I64(v) => self.emit_i64(v), + Operand::U64(v) => self.emit_u64(v), + Operand::Varying(v) => match varying_kind { + VaryingOperandKind::U8 => self.emit_u8(v as u8), + VaryingOperandKind::U16 => self.emit_u16(v as u16), + VaryingOperandKind::U32 => self.emit_u32(v), }, - Operand2::Register(reg) | Operand2::Operand(InstructionOperand::Register(reg)) => { + Operand::Register(reg) => { let v = reg.index(); match varying_kind { VaryingOperandKind::U8 => self.emit_u8(v as u8), @@ -855,29 +746,19 @@ impl<'ctx> ByteCompiler<'ctx> { } pub(crate) fn emit_get_function(&mut self, dst: &Register, index: u32) { - self.emit2( + self.emit( Opcode::GetFunction, - &[Operand2::Register(dst), Operand2::Varying(index)], + &[Operand::Register(dst), Operand::Varying(index)], ); } /// TODO: Temporary function, remove once transition is complete. fn pop_into_register(&mut self, dst: &Register) { - self.emit2(Opcode::PopIntoRegister, &[Operand2::Register(dst)]); - } - /// TODO: Temporary function, remove once transition is complete. - fn push_from_register(&mut self, src: &Register) { - self.emit2(Opcode::PushFromRegister, &[Operand2::Register(src)]); + self.emit(Opcode::PopIntoRegister, &[Operand::Register(dst)]); } /// TODO: Temporary function, remove once transition is complete. - fn push_from_operand(&mut self, src: InstructionOperand<'_>) { - match src { - InstructionOperand::Register(reg) => self.push_from_register(reg), - InstructionOperand::Argument(index) => { - self.emit_with_varying_operand(Opcode::GetArgument, index); - } - InstructionOperand::Constant(value) => self.emit_push_integer(value), - } + pub(crate) fn push_from_register(&mut self, src: &Register) { + self.emit(Opcode::PushFromRegister, &[Operand::Register(src)]); } /// Emits an opcode with one varying operand. @@ -898,69 +779,54 @@ impl<'ctx> ByteCompiler<'ctx> { } } - pub(crate) fn emit_binding_access(&mut self, opcode: Opcode, binding: &BindingKind) { + pub(crate) fn emit_binding_access( + &mut self, + opcode: Opcode, + binding: &BindingKind, + value: &Register, + ) { match binding { BindingKind::Stack(index) => match opcode { - Opcode::SetNameByLocator => self.emit_opcode(opcode), - _ => self.emit_with_varying_operand(opcode, *index), + Opcode::SetNameByLocator => self.emit(opcode, &[Operand::Register(value)]), + Opcode::GetLocator | Opcode::DefVar => { + self.emit(opcode, &[Operand::Varying(*index)]); + } + _ => self.emit( + opcode, + &[Operand::Register(value), Operand::Varying(*index)], + ), }, BindingKind::Local(index) => match opcode { - Opcode::GetName | Opcode::GetNameOrUndefined | Opcode::GetNameAndLocator => { - self.emit_with_varying_operand(Opcode::PushFromLocal, *index); - } + Opcode::GetName | Opcode::GetNameOrUndefined | Opcode::GetNameAndLocator => self + .emit( + Opcode::PushFromLocal, + &[Operand::Varying(*index), Operand::Register(value)], + ), Opcode::GetLocator | Opcode::DefVar => {} Opcode::SetName | Opcode::DefInitVar | Opcode::PutLexicalValue - | Opcode::SetNameByLocator => { - self.emit_with_varying_operand(Opcode::PopIntoLocal, *index); - } - Opcode::DeleteName => self.emit_opcode(Opcode::PushFalse), + | Opcode::SetNameByLocator => self.emit( + Opcode::PopIntoLocal, + &[Operand::Register(value), Operand::Varying(*index)], + ), + Opcode::DeleteName => self.push_false(value), _ => unreachable!("invalid opcode for binding access"), }, } } - pub(crate) fn emit_operand(&mut self, operand: Operand, varying_kind: VaryingOperandKind) { - match operand { - Operand::Bool(v) => self.emit_u8(v.into()), - Operand::I8(v) => self.emit_i8(v), - Operand::U8(v) => self.emit_u8(v), - Operand::I16(v) => self.emit_i16(v), - Operand::U16(v) => self.emit_u16(v), - Operand::I32(v) => self.emit_i32(v), - Operand::U32(v) => self.emit_u32(v), - Operand::I64(v) => self.emit_i64(v), - Operand::U64(v) => self.emit_u64(v), - Operand::Varying(v) => match varying_kind { - VaryingOperandKind::U8 => self.emit_u8(v as u8), - VaryingOperandKind::U16 => self.emit_u16(v as u16), - VaryingOperandKind::U32 => self.emit_u32(v), - }, - } - } - fn emit_i64(&mut self, value: i64) { self.emit_u64(value as u64); } - fn emit_get_property_by_name(&mut self, ident: Sym) { - let dst = self.register_allocator.alloc(); - let receiver = self.register_allocator.alloc(); - let value = self.register_allocator.alloc(); - - self.pop_into_register(&receiver); - self.pop_into_register(&value); - - self.emit_get_property_by_name2(&dst, &receiver, &value, ident); - - self.push_from_register(&dst); - - self.register_allocator.dealloc(dst); - self.register_allocator.dealloc(receiver); - self.register_allocator.dealloc(value); - } - fn emit_get_property_by_name2(&mut self, dst: &Register, receiver: &Register, value: &Register, ident: Sym) { + fn emit_get_property_by_name( + &mut self, + dst: &Register, + receiver: &Register, + value: &Register, + ident: Sym, + ) { let ic_index = self.ic.len() as u32; let name_index = self.get_or_insert_name(Identifier::new(ident)); @@ -969,18 +835,24 @@ impl<'ctx> ByteCompiler<'ctx> { }; self.ic.push(InlineCache::new(name.clone())); - self.emit2( + self.emit( Opcode::GetPropertyByName, &[ - Operand2::Register(dst), - Operand2::Operand(InstructionOperand::Register(receiver)), - Operand2::Operand(InstructionOperand::Register(value)), - Operand2::Varying(ic_index), + Operand::Register(dst), + Operand::Register(receiver), + Operand::Register(value), + Operand::Varying(ic_index), ], ); } - fn emit_set_property_by_name(&mut self, ident: Sym) { + fn emit_set_property_by_name( + &mut self, + value: &Register, + receiver: &Register, + object: &Register, + ident: Sym, + ) { let ic_index = self.ic.len() as u32; let name_index = self.get_or_insert_name(Identifier::new(ident)); @@ -989,7 +861,15 @@ impl<'ctx> ByteCompiler<'ctx> { }; self.ic.push(InlineCache::new(name.clone())); - self.emit_with_varying_operand(Opcode::SetPropertyByName, ic_index); + self.emit( + Opcode::SetPropertyByName, + &[ + Operand::Register(value), + Operand::Register(receiver), + Operand::Register(object), + Operand::Varying(ic_index), + ], + ); } fn emit_type_error(&mut self, message: &str) { @@ -1033,84 +913,254 @@ impl<'ctx> ByteCompiler<'ctx> { self.emit_u8(opcode as u8); } - fn emit_push_integer(&mut self, value: i32) { + fn emit_push_integer(&mut self, value: i32, dst: &Register) { match value { - 0 => self.emit_opcode(Opcode::PushZero), - 1 => self.emit_opcode(Opcode::PushOne), - x if i32::from(x as i8) == x => { - self.emit(Opcode::PushInt8, &[Operand::I8(x as i8)]); - } - x if i32::from(x as i16) == x => { - self.emit(Opcode::PushInt16, &[Operand::I16(x as i16)]); - } - x => self.emit(Opcode::PushInt32, &[Operand::I32(x)]), + 0 => self.push_zero(dst), + 1 => self.push_one(dst), + x if i32::from(x as i8) == x => self.push_int8(x as i8, dst), + x if i32::from(x as i16) == x => self.push_int16(x as i16, dst), + x => self.push_int32(x, dst), } } - fn emit_push_literal(&mut self, literal: Literal) { + fn emit_push_literal(&mut self, literal: Literal, dst: &Register) { let index = self.get_or_insert_literal(literal); - self.emit_with_varying_operand(Opcode::PushLiteral, index); + self.emit( + Opcode::PushLiteral, + &[Operand::Register(dst), Operand::Varying(index)], + ); } - fn emit_push_rational(&mut self, value: f64) { + fn emit_push_rational(&mut self, value: f64, dst: &Register) { if value.is_nan() { - return self.emit_opcode(Opcode::PushNaN); + return self.push_nan(dst); } if value.is_infinite() { if value.is_sign_positive() { - return self.emit_opcode(Opcode::PushPositiveInfinity); + return self.push_positive_infinity(dst); } - return self.emit_opcode(Opcode::PushNegativeInfinity); + return self.push_negative_infinity(dst); } // Check if the f64 value can fit in an i32. if f64::from(value as i32).to_bits() == value.to_bits() { - self.emit_push_integer(value as i32); + self.emit_push_integer(value as i32, dst); } else { let f32_value = value as f32; #[allow(clippy::float_cmp)] if f64::from(f32_value) == value { - self.emit(Opcode::PushFloat, &[Operand::U32(f32_value.to_bits())]); + self.push_float(f32_value, dst); } else { - self.emit(Opcode::PushDouble, &[Operand::U64(value.to_bits())]); + self.push_double(value, dst); } } } - #[allow(dead_code)] - fn emit_move(&mut self, dst: &Register, src: InstructionOperand<'_>) { - self.emit2( + pub(crate) fn push_zero(&mut self, dst: &Register) { + self.emit(Opcode::PushZero, &[Operand::Register(dst)]); + } + + pub(crate) fn push_one(&mut self, dst: &Register) { + self.emit(Opcode::PushOne, &[Operand::Register(dst)]); + } + + pub(crate) fn push_int8(&mut self, value: i8, dst: &Register) { + self.emit( + Opcode::PushInt8, + &[Operand::Register(dst), Operand::I8(value)], + ); + } + + pub(crate) fn push_int16(&mut self, value: i16, dst: &Register) { + self.emit( + Opcode::PushInt16, + &[Operand::Register(dst), Operand::I16(value)], + ); + } + + pub(crate) fn push_int32(&mut self, value: i32, dst: &Register) { + self.emit( + Opcode::PushInt32, + &[Operand::Register(dst), Operand::I32(value)], + ); + } + + pub(crate) fn push_float(&mut self, value: f32, dst: &Register) { + self.emit( + Opcode::PushFloat, + &[Operand::Register(dst), Operand::U32(value.to_bits())], + ); + } + + pub(crate) fn push_double(&mut self, value: f64, dst: &Register) { + self.emit( + Opcode::PushDouble, + &[Operand::Register(dst), Operand::U64(value.to_bits())], + ); + } + + pub(crate) fn push_nan(&mut self, dst: &Register) { + self.emit(Opcode::PushNaN, &[Operand::Register(dst)]); + } + + pub(crate) fn push_positive_infinity(&mut self, dst: &Register) { + self.emit(Opcode::PushPositiveInfinity, &[Operand::Register(dst)]); + } + + pub(crate) fn push_negative_infinity(&mut self, dst: &Register) { + self.emit(Opcode::PushNegativeInfinity, &[Operand::Register(dst)]); + } + + pub(crate) fn push_null(&mut self, dst: &Register) { + self.emit(Opcode::PushNull, &[Operand::Register(dst)]); + } + + pub(crate) fn push_true(&mut self, dst: &Register) { + self.emit(Opcode::PushTrue, &[Operand::Register(dst)]); + } + + pub(crate) fn push_false(&mut self, dst: &Register) { + self.emit(Opcode::PushFalse, &[Operand::Register(dst)]); + } + + pub(crate) fn push_undefined(&mut self, dst: &Register) { + self.emit(Opcode::PushUndefined, &[Operand::Register(dst)]); + } + + fn emit_move(&mut self, dst: &Register, src: &Register) { + self.emit( Opcode::Move, - &[Operand2::Register(dst), Operand2::Operand(src)], + &[Operand::Register(dst), Operand::Register(src)], ); } fn jump(&mut self) -> Label { - self.emit_opcode_with_operand(Opcode::Jump) + self.emit_with_label(Opcode::Jump, &[]) } - fn jump_if_true(&mut self) -> Label { - self.emit_opcode_with_operand(Opcode::JumpIfTrue) + pub(crate) fn jump_if_true(&mut self, value: &Register) -> Label { + let index = self.next_opcode_location(); + self.emit( + Opcode::JumpIfTrue, + &[Operand::U32(Self::DUMMY_ADDRESS), Operand::Register(value)], + ); + Label { index } } - fn jump_if_false(&mut self) -> Label { - self.emit_opcode_with_operand(Opcode::JumpIfFalse) + pub(crate) fn jump_if_false(&mut self, value: &Register) -> Label { + let index = self.next_opcode_location(); + self.emit( + Opcode::JumpIfFalse, + &[Operand::U32(Self::DUMMY_ADDRESS), Operand::Register(value)], + ); + Label { index } } - fn jump_if_null_or_undefined(&mut self) -> Label { - self.emit_opcode_with_operand(Opcode::JumpIfNullOrUndefined) + pub(crate) fn jump_if_null_or_undefined(&mut self, value: &Register) -> Label { + let index = self.next_opcode_location(); + self.emit( + Opcode::JumpIfNullOrUndefined, + &[Operand::U32(Self::DUMMY_ADDRESS), Operand::Register(value)], + ); + Label { index } + } + + pub(crate) fn emit_jump_if_not_undefined(&mut self, value: &Register) -> Label { + let index = self.next_opcode_location(); + self.emit( + Opcode::JumpIfNotUndefined, + &[Operand::U32(Self::DUMMY_ADDRESS), Operand::Register(value)], + ); + Label { index } + } + + pub(crate) fn case(&mut self, value: &Register, condition: &Register) -> Label { + let index = self.next_opcode_location(); + self.emit( + Opcode::Case, + &[ + Operand::U32(Self::DUMMY_ADDRESS), + Operand::Register(value), + Operand::Register(condition), + ], + ); + Label { index } + } + + pub(crate) fn generator_delegate_next( + &mut self, + value: &Register, + resume_kind: &Register, + is_return: &Register, + ) -> (Label, Label) { + let index = self.next_opcode_location(); + self.emit( + Opcode::GeneratorDelegateNext, + &[ + Operand::U32(Self::DUMMY_ADDRESS), + Operand::U32(Self::DUMMY_ADDRESS), + Operand::Register(value), + Operand::Register(resume_kind), + Operand::Register(is_return), + ], + ); + (Label { index }, Label { index: index + 4 }) + } + + pub(crate) fn generator_delegate_resume( + &mut self, + value: &Register, + resume_kind: &Register, + is_return: &Register, + ) -> (Label, Label) { + let index = self.next_opcode_location(); + self.emit( + Opcode::GeneratorDelegateResume, + &[ + Operand::U32(Self::DUMMY_ADDRESS), + Operand::U32(Self::DUMMY_ADDRESS), + Operand::Register(value), + Operand::Register(resume_kind), + Operand::Register(is_return), + ], + ); + (Label { index }, Label { index: index + 4 }) + } + + pub(crate) fn template_lookup(&mut self, dst: &Register, site: u64) -> Label { + let index = self.next_opcode_location(); + self.emit( + Opcode::TemplateLookup, + &[ + Operand::U32(Self::DUMMY_ADDRESS), + Operand::U64(site), + Operand::Register(dst), + ], + ); + Label { index } } - fn emit_resume_kind(&mut self, resume_kind: GeneratorResumeKind) { - self.emit_push_integer(resume_kind as i32); + fn emit_resume_kind(&mut self, resume_kind: GeneratorResumeKind, dst: &Register) { + self.emit_push_integer(resume_kind as i32, dst); } - fn jump_if_not_resume_kind(&mut self, resume_kind: GeneratorResumeKind) -> Label { - let label = self.emit_opcode_with_operand(Opcode::JumpIfNotResumeKind); - self.emit_u8(resume_kind as u8); - label + fn jump_if_not_resume_kind( + &mut self, + resume_kind: GeneratorResumeKind, + value: &Register, + ) -> Label { + let index = self.next_opcode_location(); + self.emit( + Opcode::JumpIfNotResumeKind, + &[ + Operand::U32(Self::DUMMY_ADDRESS), + Operand::U8(resume_kind as u8), + Operand::Register(value), + ], + ); + Label { index } } /// Push a jump table with `count` of entries. @@ -1134,49 +1184,14 @@ impl<'ctx> ByteCompiler<'ctx> { (labels, default) } - /// Emit an opcode with a dummy operand. - /// Return the `Label` of the operand. - pub(crate) fn emit_opcode_with_operand(&mut self, opcode: Opcode) -> Label { - let index = self.next_opcode_location(); - self.emit(opcode, &[Operand::U32(Self::DUMMY_ADDRESS)]); - Label { index } - } - - pub(crate) fn emit_opcode_with_operand2( - &mut self, - opcode: Opcode, - src: InstructionOperand<'_>, - ) -> Label { - let index = self.next_opcode_location(); - self.emit2( - opcode, - &[Operand2::U32(Self::DUMMY_ADDRESS), Operand2::Operand(src)], - ); - // NOTE: Plus one because the `operand_types` is emited - Label { index: index + 1 } - } - - pub(crate) fn emit_push_private_environment(&mut self, class: InstructionOperand<'_>) -> Label { - self.emit2(Opcode::PushPrivateEnvironment, &[Operand2::Operand(class)]); + pub(crate) fn emit_push_private_environment(&mut self, class: &Register) -> Label { + self.emit(Opcode::PushPrivateEnvironment, &[Operand::Register(class)]); let index = self.next_opcode_location(); self.emit_u32(Self::DUMMY_ADDRESS); Label { index: index - 1 } } - /// Emit an opcode with two dummy operands. - /// Return the `Label`s of the two operands. - pub(crate) fn emit_opcode_with_two_operands(&mut self, opcode: Opcode) -> (Label, Label) { - let index = self.next_opcode_location(); - self.emit( - opcode, - &[ - Operand::U32(Self::DUMMY_ADDRESS), - Operand::U32(Self::DUMMY_ADDRESS), - ], - ); - (Label { index }, Label { index: index + 4 }) - } - + #[track_caller] pub(crate) fn patch_jump_with_target(&mut self, label: Label, target: u32) { const U32_SIZE: usize = size_of::(); @@ -1199,71 +1214,93 @@ impl<'ctx> ByteCompiler<'ctx> { identifier.to_js_string(self.interner()) } - fn access_get(&mut self, access: Access<'_>, use_expr: bool) { + fn access_get(&mut self, access: Access<'_>, dst: &Register) { match access { Access::Variable { name } => { let name = self.resolve_identifier_expect(name); let binding = self.lexical_scope.get_identifier_reference(name); let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::GetName, &index); + self.emit_binding_access(Opcode::GetName, &index, dst); } Access::Property { access } => match access { - PropertyAccess::Simple(access) => match access.field() { - PropertyAccessField::Const(name) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.emit_get_property_by_name(*name); - } - PropertyAccessField::Expr(expr) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.compile_expr(expr, true); - self.emit_opcode(Opcode::GetPropertyByValue); + PropertyAccess::Simple(access) => { + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); + + match access.field() { + PropertyAccessField::Const(ident) => { + self.emit_get_property_by_name(dst, &object, &object, *ident); + } + PropertyAccessField::Expr(expr) => { + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + + self.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&object), + Operand::Register(&object), + ], + ); + self.register_allocator.dealloc(key); + } } - }, + self.register_allocator.dealloc(object); + } PropertyAccess::Private(access) => { let index = self.get_or_insert_private_name(access.field()); - self.compile_expr(access.target(), true); - self.emit_with_varying_operand(Opcode::GetPrivateField, index); + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); + self.emit( + Opcode::GetPrivateField, + &[ + Operand::Register(dst), + Operand::Register(&object), + Operand::Varying(index), + ], + ); + self.register_allocator.dealloc(object); } - PropertyAccess::Super(access) => match access.field() { - PropertyAccessField::Const(field) => { - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::This); - - self.emit_get_property_by_name(*field); - } - PropertyAccessField::Expr(expr) => { - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::This); - self.compile_expr(expr, true); - self.emit_opcode(Opcode::GetPropertyByValue); + PropertyAccess::Super(access) => { + let value = self.register_allocator.alloc(); + let receiver = self.register_allocator.alloc(); + self.emit(Opcode::Super, &[Operand::Register(&value)]); + self.emit(Opcode::This, &[Operand::Register(&receiver)]); + match access.field() { + PropertyAccessField::Const(ident) => { + self.emit_get_property_by_name(dst, &receiver, &value, *ident); + } + PropertyAccessField::Expr(expr) => { + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + + self.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(&receiver), + Operand::Register(&value), + ], + ); + self.register_allocator.dealloc(key); + } } - }, + self.register_allocator.dealloc(receiver); + self.register_allocator.dealloc(value); + } }, Access::This => { - self.emit_opcode(Opcode::This); + self.emit(Opcode::This, &[Operand::Register(dst)]); } } - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } } - fn access_set_top_of_stack_expr_fn(compiler: &mut ByteCompiler<'_>, level: u8) { - match level { - 0 => {} - 1 => compiler.emit_opcode(Opcode::Swap), - _ => { - compiler.emit(Opcode::RotateLeft, &[Operand::U8(level + 1)]); - } - } - } - - fn access_set(&mut self, access: Access<'_>, use_expr: bool, expr_fn: F) + fn access_set<'a, F>(&mut self, access: Access<'_>, expr_fn: F) where - F: FnOnce(&mut ByteCompiler<'_>, u8) -> R, + F: FnOnce(&mut ByteCompiler<'_>) -> &'a Register, { match access { Access::Variable { name } => { @@ -1272,84 +1309,121 @@ impl<'ctx> ByteCompiler<'ctx> { let is_lexical = binding.is_lexical(); let index = self.get_or_insert_binding(binding); + let value = self.register_allocator.alloc(); if !is_lexical { - self.emit_binding_access(Opcode::GetLocator, &index); + self.emit_binding_access(Opcode::GetLocator, &index, &value); } + self.register_allocator.dealloc(value); - expr_fn(self, 0); - if use_expr { - self.emit(Opcode::Dup, &[]); - } + let value = expr_fn(self); if is_lexical { match self.lexical_scope.set_mutable_binding(name.clone()) { Ok(binding) => { let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::SetName, &index); + self.emit_binding_access(Opcode::SetName, &index, value); } Err(BindingLocatorError::MutateImmutable) => { let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } + Err(BindingLocatorError::Silent) => {} } } else { - self.emit_binding_access(Opcode::SetNameByLocator, &index); + self.emit_binding_access(Opcode::SetNameByLocator, &index, value); } } Access::Property { access } => match access { PropertyAccess::Simple(access) => match access.field() { PropertyAccessField::Const(name) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - expr_fn(self, 2); - - self.emit_set_property_by_name(*name); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); + let value = expr_fn(self); + self.emit_set_property_by_name(value, &object, &object, *name); + self.register_allocator.dealloc(object); } PropertyAccessField::Expr(expr) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.compile_expr(expr, true); - expr_fn(self, 3); - self.emit_opcode(Opcode::SetPropertyByValue); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); + + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + + let value = expr_fn(self); + + self.emit( + Opcode::SetPropertyByValue, + &[ + Operand::Register(value), + Operand::Register(&key), + Operand::Register(&object), + Operand::Register(&object), + ], + ); + + self.register_allocator.dealloc(object); + self.register_allocator.dealloc(key); } }, PropertyAccess::Private(access) => { - self.compile_expr(access.target(), true); - expr_fn(self, 1); let index = self.get_or_insert_private_name(access.field()); - self.emit_with_varying_operand(Opcode::SetPrivateField, index); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + + let object = self.register_allocator.alloc(); + self.compile_expr(access.target(), &object); + + let value = expr_fn(self); + + self.emit( + Opcode::SetPrivateField, + &[ + Operand::Register(value), + Operand::Register(&object), + Operand::Varying(index), + ], + ); + + self.register_allocator.dealloc(object); } PropertyAccess::Super(access) => match access.field() { PropertyAccessField::Const(name) => { - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::This); - expr_fn(self, 1); - self.emit_set_property_by_name(*name); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let object = self.register_allocator.alloc(); + self.emit(Opcode::Super, &[Operand::Register(&object)]); + + let receiver = self.register_allocator.alloc(); + self.emit(Opcode::This, &[Operand::Register(&receiver)]); + + let value = expr_fn(self); + + self.emit_set_property_by_name(value, &receiver, &object, *name); + + self.register_allocator.dealloc(receiver); + self.register_allocator.dealloc(object); } PropertyAccessField::Expr(expr) => { - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::This); - self.compile_expr(expr, true); - expr_fn(self, 1); - self.emit_opcode(Opcode::SetPropertyByValue); - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let object = self.register_allocator.alloc(); + self.emit(Opcode::Super, &[Operand::Register(&object)]); + + let receiver = self.register_allocator.alloc(); + self.emit(Opcode::This, &[Operand::Register(&receiver)]); + + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + + let value = expr_fn(self); + + self.emit( + Opcode::SetPropertyByValue, + &[ + Operand::Register(value), + Operand::Register(&key), + Operand::Register(&receiver), + Operand::Register(&object), + ], + ); + + self.register_allocator.dealloc(key); + self.register_allocator.dealloc(receiver); + self.register_allocator.dealloc(object); } }, }, @@ -1357,22 +1431,29 @@ impl<'ctx> ByteCompiler<'ctx> { } } - fn access_delete(&mut self, access: Access<'_>) { + fn access_delete(&mut self, access: Access<'_>, dst: &Register) { match access { Access::Property { access } => match access { PropertyAccess::Simple(access) => match access.field() { PropertyAccessField::Const(name) => { let index = self.get_or_insert_name((*name).into()); - self.compile_expr(access.target(), true); - self.emit_with_varying_operand(Opcode::DeletePropertyByName, index); + self.compile_expr(access.target(), dst); + self.emit( + Opcode::DeletePropertyByName, + &[Operand::Register(dst), Operand::Varying(index)], + ); } PropertyAccessField::Expr(expr) => { - self.compile_expr(access.target(), true); - self.compile_expr(expr, true); - self.emit_opcode(Opcode::DeletePropertyByValue); + self.compile_expr(access.target(), dst); + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + self.emit( + Opcode::DeletePropertyByValue, + &[Operand::Register(dst), Operand::Register(&key)], + ); + self.register_allocator.dealloc(key); } }, - // TODO: throw ReferenceError on super deletion. PropertyAccess::Super(_) => self.emit_opcode(Opcode::DeleteSuperThrow), PropertyAccess::Private(_) => { unreachable!("deleting private properties should always throw early errors.") @@ -1382,11 +1463,9 @@ impl<'ctx> ByteCompiler<'ctx> { let name = name.to_js_string(self.interner()); let binding = self.lexical_scope.get_identifier_reference(name); let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::DeleteName, &index); - } - Access::This => { - self.emit_opcode(Opcode::PushTrue); + self.emit_binding_access(Opcode::DeleteName, &index, dst); } + Access::This => self.push_true(dst), } } @@ -1421,23 +1500,8 @@ impl<'ctx> ByteCompiler<'ctx> { /// Compile an [`Expression`]. #[inline] - pub fn compile_expr(&mut self, expr: &Expression, use_expr: bool) { - self.compile_expr_impl(expr, use_expr); - } - - // The function should take an optional prefered reg - // Should output - - /// Compile an [`Expression`]. - #[inline] - pub(crate) fn compile_expr2<'a>( - &mut self, - expr: &Expression, - reg: &'a Register, - ) -> InstructionOperand<'a> { - self.compile_expr_impl(expr, true); - self.pop_into_register(reg); - InstructionOperand::Register(reg) + pub(crate) fn compile_expr(&mut self, expr: &Expression, dst: &'_ Register) { + self.compile_expr_impl(expr, dst); } /// Compile a property access expression, prepending `this` to the property value in the stack. @@ -1450,41 +1514,74 @@ impl<'ctx> ByteCompiler<'ctx> { /// with calls (`a.b()`), since both of them must have `a` be the value of `this` for the function /// call `b()`, but a regular compilation of the access would lose the `this` value after accessing /// `b`. - fn compile_access_preserve_this(&mut self, access: &PropertyAccess) { + fn compile_access_preserve_this( + &mut self, + access: &PropertyAccess, + this: &Register, + dst: &Register, + ) { match access { PropertyAccess::Simple(access) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + self.compile_expr(access.target(), this); + match access.field() { - PropertyAccessField::Const(field) => { - self.emit_get_property_by_name(*field); + PropertyAccessField::Const(ident) => { + self.emit_get_property_by_name(dst, this, this, *ident); } PropertyAccessField::Expr(field) => { - self.compile_expr(field, true); - self.emit_opcode(Opcode::GetPropertyByValue); + let key = self.register_allocator.alloc(); + self.compile_expr(field, &key); + self.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(this), + Operand::Register(this), + ], + ); + self.register_allocator.dealloc(key); } } } PropertyAccess::Private(access) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); + self.compile_expr(access.target(), this); + let index = self.get_or_insert_private_name(access.field()); - self.emit_with_varying_operand(Opcode::GetPrivateField, index); + self.emit( + Opcode::GetPrivateField, + &[ + Operand::Register(dst), + Operand::Register(this), + Operand::Varying(index), + ], + ); } PropertyAccess::Super(access) => { - self.emit_opcode(Opcode::This); - self.emit_opcode(Opcode::Super); - self.emit_opcode(Opcode::This); + let object = self.register_allocator.alloc(); + self.emit(Opcode::This, &[Operand::Register(this)]); + self.emit(Opcode::Super, &[Operand::Register(&object)]); + match access.field() { - PropertyAccessField::Const(field) => { - self.emit_get_property_by_name(*field); + PropertyAccessField::Const(ident) => { + self.emit_get_property_by_name(dst, this, &object, *ident); } PropertyAccessField::Expr(expr) => { - self.compile_expr(expr, true); - self.emit_opcode(Opcode::GetPropertyByValue); + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + self.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(dst), + Operand::Register(&key), + Operand::Register(this), + Operand::Register(&object), + ], + ); + self.register_allocator.dealloc(key); } } + self.register_allocator.dealloc(object); } } } @@ -1500,20 +1597,26 @@ impl<'ctx> ByteCompiler<'ctx> { /// would only return the result of the chain without preserving the `this` value. In other words, /// `this` would be set to `undefined` for that call, which is incorrect since `a` should be the /// `this` value of the call. - fn compile_optional_preserve_this(&mut self, optional: &Optional) { + fn compile_optional_preserve_this( + &mut self, + optional: &Optional, + this: &Register, + value: &Register, + ) { let mut jumps = Vec::with_capacity(optional.chain().len()); match optional.target().flatten() { Expression::PropertyAccess(access) => { - self.compile_access_preserve_this(access); + self.compile_access_preserve_this(access, this, value); } - Expression::Optional(opt) => self.compile_optional_preserve_this(opt), + Expression::Optional(opt) => self.compile_optional_preserve_this(opt, this, value), expr => { - self.emit(Opcode::PushUndefined, &[]); - self.compile_expr(expr, true); + self.push_undefined(this); + self.compile_expr(expr, value); } } - jumps.push(self.jump_if_null_or_undefined()); + + jumps.push(self.jump_if_null_or_undefined(value)); let (first, rest) = optional .chain() @@ -1521,22 +1624,22 @@ impl<'ctx> ByteCompiler<'ctx> { .expect("chain must have at least one element"); assert!(first.shorted()); - self.compile_optional_item_kind(first.kind()); + self.compile_optional_item_kind(first.kind(), this, value); for item in rest { if item.shorted() { - jumps.push(self.jump_if_null_or_undefined()); + jumps.push(self.jump_if_null_or_undefined(value)); } - self.compile_optional_item_kind(item.kind()); + self.compile_optional_item_kind(item.kind(), this, value); } + let skip_undef = self.jump(); for label in jumps { self.patch_jump(label); + self.push_undefined(value); } - self.emit_opcode(Opcode::PushUndefined); - self.patch_jump(skip_undef); } @@ -1556,55 +1659,91 @@ impl<'ctx> ByteCompiler<'ctx> { /// is not null or undefined (if the operator `?.` was used). /// - This assumes that the state of the stack before compiling is `...rest, this, value`, /// since the operation compiled by this function could be a call. - fn compile_optional_item_kind(&mut self, kind: &OptionalOperationKind) { + fn compile_optional_item_kind( + &mut self, + kind: &OptionalOperationKind, + this: &Register, + value: &Register, + ) { match kind { OptionalOperationKind::SimplePropertyAccess { field } => { - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + self.emit_move(this, value); match field { PropertyAccessField::Const(name) => { - self.emit_get_property_by_name(*name); + self.emit_get_property_by_name(value, value, value, *name); } PropertyAccessField::Expr(expr) => { - self.compile_expr(expr, true); - self.emit_opcode(Opcode::GetPropertyByValue); + let key = self.register_allocator.alloc(); + self.compile_expr(expr, &key); + self.emit( + Opcode::GetPropertyByValue, + &[ + Operand::Register(value), + Operand::Register(&key), + Operand::Register(value), + Operand::Register(value), + ], + ); + self.register_allocator.dealloc(key); } } - self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); - self.emit_opcode(Opcode::Pop); } OptionalOperationKind::PrivatePropertyAccess { field } => { - self.emit_opcode(Opcode::Dup); + self.emit_move(this, value); let index = self.get_or_insert_private_name(*field); - self.emit_with_varying_operand(Opcode::GetPrivateField, index); - self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); - self.emit_opcode(Opcode::Pop); + self.emit( + Opcode::GetPrivateField, + &[ + Operand::Register(value), + Operand::Register(value), + Operand::Varying(index), + ], + ); } OptionalOperationKind::Call { args } => { + self.push_from_register(this); + self.push_from_register(value); + let args = &**args; let contains_spread = args.iter().any(|arg| matches!(arg, Expression::Spread(_))); if contains_spread { - self.emit_opcode(Opcode::PushNewArray); + let array = self.register_allocator.alloc(); + let value = self.register_allocator.alloc(); + + self.emit(Opcode::PushNewArray, &[Operand::Register(&array)]); + for arg in args { - self.compile_expr(arg, true); + self.compile_expr(arg, &value); if let Expression::Spread(_) = arg { - self.emit_opcode(Opcode::GetIterator); - self.emit_opcode(Opcode::PushIteratorToArray); + self.emit(Opcode::GetIterator, &[Operand::Register(&value)]); + self.emit(Opcode::PushIteratorToArray, &[Operand::Register(&array)]); } else { - self.emit_opcode(Opcode::PushValueToArray); + self.emit( + Opcode::PushValueToArray, + &[Operand::Register(&value), Operand::Register(&array)], + ); } } + + self.push_from_register(&array); + + self.register_allocator.dealloc(value); + self.register_allocator.dealloc(array); + self.emit_opcode(Opcode::CallSpread); } else { for arg in args { - self.compile_expr(arg, true); + let value = self.register_allocator.alloc(); + self.compile_expr(arg, &value); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } self.emit_with_varying_operand(Opcode::Call, args.len() as u32); } - self.emit_opcode(Opcode::PushUndefined); - self.emit_opcode(Opcode::Swap); + self.pop_into_register(value); + self.push_undefined(this); } } } @@ -1618,21 +1757,26 @@ impl<'ctx> ByteCompiler<'ctx> { if let Some(expr) = variable.init() { let binding = self.lexical_scope.get_identifier_reference(ident.clone()); let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::GetLocator, &index); - self.compile_expr(expr, true); - self.emit_binding_access(Opcode::SetNameByLocator, &index); + let value = self.register_allocator.alloc(); + self.emit_binding_access(Opcode::GetLocator, &index, &value); + self.compile_expr(expr, &value); + self.emit_binding_access(Opcode::SetNameByLocator, &index, &value); + self.register_allocator.dealloc(value); } else { - self.emit_binding(BindingOpcode::Var, ident); + let value = self.register_allocator.alloc(); + self.emit_binding(BindingOpcode::Var, ident, &value); + self.register_allocator.dealloc(value); } } Binding::Pattern(pattern) => { + let value = self.register_allocator.alloc(); if let Some(init) = variable.init() { - self.compile_expr(init, true); + self.compile_expr(init, &value); } else { - self.emit_opcode(Opcode::PushUndefined); + self.push_undefined(&value); }; - - self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); + self.compile_declaration_pattern(pattern, BindingOpcode::InitVar, &value); + self.register_allocator.dealloc(value); } } } @@ -1646,21 +1790,28 @@ impl<'ctx> ByteCompiler<'ctx> { match variable.binding() { Binding::Identifier(ident) => { let ident = ident.to_js_string(self.interner()); - if let Some(expr) = variable.init() { - self.compile_expr(expr, true); + let value = self.register_allocator.alloc(); + if let Some(init) = variable.init() { + self.compile_expr(init, &value); } else { - self.emit_opcode(Opcode::PushUndefined); - } - self.emit_binding(BindingOpcode::InitLexical, ident); + self.push_undefined(&value); + }; + self.emit_binding(BindingOpcode::InitLexical, ident, &value); + self.register_allocator.dealloc(value); } Binding::Pattern(pattern) => { + let value = self.register_allocator.alloc(); if let Some(init) = variable.init() { - self.compile_expr(init, true); + self.compile_expr(init, &value); } else { - self.emit_opcode(Opcode::PushUndefined); + self.push_undefined(&value); }; - - self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); + self.compile_declaration_pattern( + pattern, + BindingOpcode::InitLexical, + &value, + ); + self.register_allocator.dealloc(value); } } } @@ -1673,17 +1824,24 @@ impl<'ctx> ByteCompiler<'ctx> { let init = variable .init() .expect("const declaration must have initializer"); - self.compile_expr(init, true); - self.emit_binding(BindingOpcode::InitLexical, ident); + let value = self.register_allocator.alloc(); + self.compile_expr(init, &value); + self.emit_binding(BindingOpcode::InitLexical, ident, &value); + self.register_allocator.dealloc(value); } Binding::Pattern(pattern) => { + let value = self.register_allocator.alloc(); if let Some(init) = variable.init() { - self.compile_expr(init, true); + self.compile_expr(init, &value); } else { - self.emit_opcode(Opcode::PushUndefined); + self.push_undefined(&value); }; - - self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); + self.compile_declaration_pattern( + pattern, + BindingOpcode::InitLexical, + &value, + ); + self.register_allocator.dealloc(value); } } } @@ -1712,24 +1870,24 @@ impl<'ctx> ByteCompiler<'ctx> { let name = name.to_js_string(self.interner()); let binding = self.lexical_scope.get_identifier_reference(name.clone()); let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::GetName, &index); + let value = self.register_allocator.alloc(); + self.emit_binding_access(Opcode::GetName, &index, &value); match self.variable_scope.set_mutable_binding_var(name.clone()) { Ok(binding) => { let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::SetName, &index); + self.emit_binding_access(Opcode::SetName, &index, &value); } Err(BindingLocatorError::MutateImmutable) => { let index = self.get_or_insert_string(name); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } + Err(BindingLocatorError::Silent) => {} } + self.register_allocator.dealloc(value); } } - Declaration::ClassDeclaration(class) => self.class(class.into(), false), + Declaration::ClassDeclaration(class) => self.compile_class(class.into(), None), Declaration::Lexical(lexical) => self.compile_lexical_decl(lexical), _ => {} } @@ -1785,34 +1943,30 @@ impl<'ctx> ByteCompiler<'ctx> { &mut self, function: FunctionSpec<'_>, node_kind: NodeKind, - use_expr: bool, + dst: &Register, ) { let name = function.name; - let index = self.function(function); - let dst = self.register_allocator.alloc(); - self.emit_get_function(&dst, index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); - + self.emit_get_function(dst, index); match node_kind { NodeKind::Declaration => { self.emit_binding( BindingOpcode::InitVar, name.expect("function declaration must have a name") .to_js_string(self.interner()), + dst, ); } - NodeKind::Expression => { - if !use_expr { - self.emit_opcode(Opcode::Pop); - } - } + NodeKind::Expression => {} } } /// Compile an object method AST Node into bytecode. - pub(crate) fn object_method(&mut self, function: FunctionSpec<'_>, kind: MethodKind) { + pub(crate) fn object_method( + &mut self, + function: FunctionSpec<'_>, + kind: MethodKind, + ) -> Register { let (generator, r#async, arrow) = ( function.kind.is_generator(), function.kind.is_async(), @@ -1860,12 +2014,11 @@ impl<'ctx> ByteCompiler<'ctx> { let index = self.push_function_to_constants(code); let dst = self.register_allocator.alloc(); self.emit_get_function(&dst, index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); + dst } /// Compile a class method AST Node into bytecode. - fn method(&mut self, function: FunctionSpec<'_>) { + fn method(&mut self, function: FunctionSpec<'_>) -> Register { let (generator, r#async, arrow) = ( function.kind.is_generator(), function.kind.is_async(), @@ -1907,11 +2060,10 @@ impl<'ctx> ByteCompiler<'ctx> { let index = self.push_function_to_constants(code); let dst = self.register_allocator.alloc(); self.emit_get_function(&dst, index); - self.push_from_register(&dst); - self.register_allocator.dealloc(dst); + dst } - fn call(&mut self, callable: Callable<'_>, use_expr: bool) { + fn call(&mut self, callable: Callable<'_>, dst: &Register) { #[derive(PartialEq)] enum CallKind { CallEval, @@ -1926,11 +2078,23 @@ impl<'ctx> ByteCompiler<'ctx> { match call.function().flatten() { Expression::PropertyAccess(access) if kind == CallKind::Call => { - self.compile_access_preserve_this(access); + let this = self.register_allocator.alloc(); + let dst = self.register_allocator.alloc(); + self.compile_access_preserve_this(access, &this, &dst); + self.push_from_register(&this); + self.push_from_register(&dst); + self.register_allocator.dealloc(this); + self.register_allocator.dealloc(dst); } Expression::Optional(opt) if kind == CallKind::Call => { - self.compile_optional_preserve_this(opt); + let this = self.register_allocator.alloc(); + let dst = self.register_allocator.alloc(); + self.compile_optional_preserve_this(opt, &this, &dst); + self.push_from_register(&this); + self.push_from_register(&dst); + self.register_allocator.dealloc(this); + self.register_allocator.dealloc(dst); } expr if kind == CallKind::Call => { if let Expression::Identifier(ident) = expr { @@ -1942,18 +2106,39 @@ impl<'ctx> ByteCompiler<'ctx> { let name = self.resolve_identifier_expect(*ident); let binding = self.lexical_scope.get_identifier_reference(name); let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::ThisForObjectEnvironmentName, &index); + let BindingKind::Stack(index) = index else { + unreachable!("with binding cannot be local") + }; + let value = self.register_allocator.alloc(); + self.emit( + Opcode::ThisForObjectEnvironmentName, + &[Operand::Register(&value), Operand::Varying(index)], + ); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } else { - self.emit_opcode(Opcode::PushUndefined); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } } else { - self.emit_opcode(Opcode::PushUndefined); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } - self.compile_expr(expr, true); + let value = self.register_allocator.alloc(); + self.compile_expr(expr, &value); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } expr => { - self.compile_expr(expr, true); + let value = self.register_allocator.alloc(); + self.compile_expr(expr, &value); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } } @@ -1963,19 +2148,34 @@ impl<'ctx> ByteCompiler<'ctx> { .any(|arg| matches!(arg, Expression::Spread(_))); if contains_spread { - self.emit_opcode(Opcode::PushNewArray); + let array = self.register_allocator.alloc(); + let value = self.register_allocator.alloc(); + + self.emit(Opcode::PushNewArray, &[Operand::Register(&array)]); + for arg in call.args() { - self.compile_expr(arg, true); + self.compile_expr(arg, &value); if let Expression::Spread(_) = arg { - self.emit_opcode(Opcode::GetIterator); - self.emit_opcode(Opcode::PushIteratorToArray); + self.emit(Opcode::GetIterator, &[Operand::Register(&value)]); + self.emit(Opcode::PushIteratorToArray, &[Operand::Register(&array)]); } else { - self.emit_opcode(Opcode::PushValueToArray); + self.emit( + Opcode::PushValueToArray, + &[Operand::Register(&value), Operand::Register(&array)], + ); } } + + self.push_from_register(&array); + + self.register_allocator.dealloc(array); + self.register_allocator.dealloc(value); } else { for arg in call.args() { - self.compile_expr(arg, true); + let value = self.register_allocator.alloc(); + self.compile_expr(arg, &value); + self.push_from_register(&value); + self.register_allocator.dealloc(value); } } @@ -2003,10 +2203,7 @@ impl<'ctx> ByteCompiler<'ctx> { CallKind::New if contains_spread => self.emit_opcode(Opcode::NewSpread), CallKind::New => self.emit_with_varying_operand(Opcode::New, call.args().len() as u32), } - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.pop_into_register(dst); } /// Finish compiling code with the [`ByteCompiler`] and return the generated [`CodeBlock`]. @@ -2034,12 +2231,6 @@ impl<'ctx> ByteCompiler<'ctx> { let register_count = self.register_allocator.finish(); - // NOTE: Offset the handlers stack count so we don't pop the registers - // when a exception is thrown. - for handler in &mut self.handlers { - handler.stack_count += register_count; - } - CodeBlock { name: self.function_name, length: self.length, @@ -2057,11 +2248,12 @@ impl<'ctx> ByteCompiler<'ctx> { } } - fn compile_declaration_pattern(&mut self, pattern: &Pattern, def: BindingOpcode) { - self.compile_declaration_pattern_impl(pattern, def); - } - - fn class(&mut self, class: ClassSpec<'_>, expression: bool) { - self.compile_class(class, expression); + fn compile_declaration_pattern( + &mut self, + pattern: &Pattern, + def: BindingOpcode, + object: &Register, + ) { + self.compile_declaration_pattern_impl(pattern, def, object); } } diff --git a/core/engine/src/bytecompiler/module.rs b/core/engine/src/bytecompiler/module.rs index f29d473d71b..cdf589d431e 100644 --- a/core/engine/src/bytecompiler/module.rs +++ b/core/engine/src/bytecompiler/module.rs @@ -42,22 +42,34 @@ impl ByteCompiler<'_> { } ExportDeclaration::VarStatement(var) => self.compile_var_decl(var), ExportDeclaration::Declaration(decl) => self.compile_decl(decl, false), - ExportDeclaration::DefaultClassDeclaration(cl) => self.class(cl.into(), false), + ExportDeclaration::DefaultClassDeclaration(cl) => { + self.compile_class(cl.into(), None); + } ExportDeclaration::DefaultAssignmentExpression(expr) => { - let name = Sym::DEFAULT_EXPORT.to_js_string(self.interner()); - self.compile_expr(expr, true); + let function = self.register_allocator.alloc(); + self.compile_expr(expr, &function); if expr.is_anonymous_function_definition() { let default = self .interner() .resolve_expect(Sym::DEFAULT) .into_common(false); - self.emit_push_literal(Literal::String(default)); - self.emit_opcode(Opcode::Swap); - self.emit(Opcode::SetFunctionName, &[Operand::U8(0)]); + let key = self.register_allocator.alloc(); + self.emit_push_literal(Literal::String(default), &key); + self.emit( + Opcode::SetFunctionName, + &[ + Operand::Register(&function), + Operand::Register(&key), + Operand::U8(0), + ], + ); + self.register_allocator.dealloc(key); } - self.emit_binding(BindingOpcode::InitLexical, name); + let name = Sym::DEFAULT_EXPORT.to_js_string(self.interner()); + self.emit_binding(BindingOpcode::InitLexical, name, &function); + self.register_allocator.dealloc(function); } } } diff --git a/core/engine/src/bytecompiler/register.rs b/core/engine/src/bytecompiler/register.rs index ba4ebe44c67..5f223ed1b63 100644 --- a/core/engine/src/bytecompiler/register.rs +++ b/core/engine/src/bytecompiler/register.rs @@ -74,8 +74,7 @@ impl RegisterAllocator { .registers .iter_mut() .enumerate() - .filter(|(_, reg)| !reg.flags.is_used()) - .next() + .find(|(_, reg)| !reg.flags.is_used()) { assert!(!register.flags.is_persistent()); diff --git a/core/engine/src/bytecompiler/statement/break.rs b/core/engine/src/bytecompiler/statement/break.rs index c1c80c67e89..bc4422cba66 100644 --- a/core/engine/src/bytecompiler/statement/break.rs +++ b/core/engine/src/bytecompiler/statement/break.rs @@ -18,11 +18,14 @@ impl ByteCompiler<'_> { let count = self.jump_info_open_environment_count(i); actions.push(JumpRecordAction::PopEnvironments { count }); - if info.is_try_with_finally_block() && !info.in_finally() { - actions.push(JumpRecordAction::HandleFinally { - index: info.jumps.len() as u32, - }); - actions.push(JumpRecordAction::Transfer { index: i as u32 }); + if !info.in_finally() { + if let Some(finally_throw) = info.finally_throw { + actions.push(JumpRecordAction::HandleFinally { + index: info.jumps.len() as u32, + finally_throw, + }); + actions.push(JumpRecordAction::Transfer { index: i as u32 }); + } } if let Some(label) = node.label() { diff --git a/core/engine/src/bytecompiler/statement/continue.rs b/core/engine/src/bytecompiler/statement/continue.rs index d05611da266..b0e8700a071 100644 --- a/core/engine/src/bytecompiler/statement/continue.rs +++ b/core/engine/src/bytecompiler/statement/continue.rs @@ -19,11 +19,14 @@ impl ByteCompiler<'_> { let count = self.jump_info_open_environment_count(i); actions.push(JumpRecordAction::PopEnvironments { count }); - if info.is_try_with_finally_block() && !info.in_finally() { - actions.push(JumpRecordAction::HandleFinally { - index: info.jumps.len() as u32, - }); - actions.push(JumpRecordAction::Transfer { index: i as u32 }); + if !info.in_finally() { + if let Some(finally_throw) = info.finally_throw { + actions.push(JumpRecordAction::HandleFinally { + index: info.jumps.len() as u32, + finally_throw, + }); + actions.push(JumpRecordAction::Transfer { index: i as u32 }); + } } if let Some(label) = node.label() { diff --git a/core/engine/src/bytecompiler/statement/if.rs b/core/engine/src/bytecompiler/statement/if.rs index 095cf5fa6b2..775f5267497 100644 --- a/core/engine/src/bytecompiler/statement/if.rs +++ b/core/engine/src/bytecompiler/statement/if.rs @@ -3,11 +3,11 @@ use boa_ast::statement::If; impl ByteCompiler<'_> { pub(crate) fn compile_if(&mut self, node: &If, use_expr: bool) { - self.compile_expr(node.cond(), true); - let jelse = self.jump_if_false(); - + let value = self.register_allocator.alloc(); + self.compile_expr(node.cond(), &value); + let jelse = self.jump_if_false(&value); + self.register_allocator.dealloc(value); self.compile_stmt(node.body(), use_expr, true); - let exit = self.jump(); self.patch_jump(jelse); if let Some(else_body) = node.else_node() { diff --git a/core/engine/src/bytecompiler/statement/labelled.rs b/core/engine/src/bytecompiler/statement/labelled.rs index 3041c996a9c..89f1d520fb1 100644 --- a/core/engine/src/bytecompiler/statement/labelled.rs +++ b/core/engine/src/bytecompiler/statement/labelled.rs @@ -30,7 +30,9 @@ impl ByteCompiler<'_> { stmt => self.compile_stmt(stmt, use_expr, true), }, LabelledItem::FunctionDeclaration(f) => { - self.function_with_binding(f.into(), NodeKind::Declaration, false); + let dst = self.register_allocator.alloc(); + self.function_with_binding(f.into(), NodeKind::Declaration, &dst); + self.register_allocator.dealloc(dst); } } diff --git a/core/engine/src/bytecompiler/statement/loop.rs b/core/engine/src/bytecompiler/statement/loop.rs index b02c93bcc1f..93c0ba6b4d5 100644 --- a/core/engine/src/bytecompiler/statement/loop.rs +++ b/core/engine/src/bytecompiler/statement/loop.rs @@ -27,7 +27,11 @@ impl ByteCompiler<'_> { if let Some(init) = for_loop.init() { match init { - ForLoopInitializer::Expression(expr) => self.compile_expr(expr, false), + ForLoopInitializer::Expression(expr) => { + let value = self.register_allocator.alloc(); + self.compile_expr(expr, &value); + self.register_allocator.dealloc(value); + } ForLoopInitializer::Var(decl) => { self.compile_var_decl(decl); } @@ -66,8 +70,11 @@ impl ByteCompiler<'_> { self.push_empty_loop_jump_control(use_expr); if let Some((let_binding_indices, scope_index)) = &let_binding_indices { + let mut values = Vec::with_capacity(let_binding_indices.len()); for index in let_binding_indices { - self.emit_binding_access(Opcode::GetName, index); + let value = self.register_allocator.alloc(); + self.emit_binding_access(Opcode::GetName, index, &value); + values.push((index, value)); } if let Some(index) = scope_index { @@ -75,8 +82,9 @@ impl ByteCompiler<'_> { self.emit_with_varying_operand(Opcode::PushScope, *index); } - for index in let_binding_indices.iter().rev() { - self.emit_binding_access(Opcode::PutLexicalValue, index); + for (index, value) in values { + self.emit_binding_access(Opcode::PutLexicalValue, index, &value); + self.register_allocator.dealloc(value); } } @@ -91,8 +99,11 @@ impl ByteCompiler<'_> { .set_start_address(start_address); if let Some((let_binding_indices, scope_index)) = &let_binding_indices { + let mut values = Vec::with_capacity(let_binding_indices.len()); for index in let_binding_indices { - self.emit_binding_access(Opcode::GetName, index); + let value = self.register_allocator.alloc(); + self.emit_binding_access(Opcode::GetName, index, &value); + values.push((index, value)); } if let Some(index) = scope_index { @@ -100,25 +111,30 @@ impl ByteCompiler<'_> { self.emit_with_varying_operand(Opcode::PushScope, *index); } - for index in let_binding_indices.iter().rev() { - self.emit_binding_access(Opcode::PutLexicalValue, index); + for (index, value) in values { + self.emit_binding_access(Opcode::PutLexicalValue, index, &value); + self.register_allocator.dealloc(value); } } self.emit_opcode(Opcode::IncrementLoopIteration); if let Some(final_expr) = for_loop.final_expr() { - self.compile_expr(final_expr, false); + let value = self.register_allocator.alloc(); + self.compile_expr(final_expr, &value); + self.register_allocator.dealloc(value); } self.patch_jump(initial_jump); + let value = self.register_allocator.alloc(); if let Some(condition) = for_loop.condition() { - self.compile_expr(condition, true); + self.compile_expr(condition, &value); } else { - self.emit_opcode(Opcode::PushTrue); + self.push_true(&value); } - let exit = self.jump_if_false(); + let exit = self.jump_if_false(&value); + self.register_allocator.dealloc(value); self.compile_stmt(for_loop.body(), use_expr, true); @@ -144,66 +160,71 @@ impl ByteCompiler<'_> { if let Binding::Identifier(ident) = var.binding() { let ident = ident.to_js_string(self.interner()); if let Some(init) = var.init() { - self.compile_expr(init, true); - self.emit_binding(BindingOpcode::InitVar, ident); + let value = self.register_allocator.alloc(); + self.compile_expr(init, &value); + self.emit_binding(BindingOpcode::InitVar, ident, &value); + self.register_allocator.dealloc(value); } } } let outer_scope = self.push_declarative_scope(for_in_loop.target_scope()); - self.compile_expr(for_in_loop.target(), true); + let value = self.register_allocator.alloc(); + self.compile_expr(for_in_loop.target(), &value); self.pop_declarative_scope(outer_scope); - let early_exit = self.jump_if_null_or_undefined(); - self.emit_opcode(Opcode::CreateForInIterator); + let early_exit = self.jump_if_null_or_undefined(&value); + + self.emit(Opcode::CreateForInIterator, &[Operand::Register(&value)]); + + self.register_allocator.dealloc(value); let start_address = self.next_opcode_location(); self.push_loop_control_info_for_of_in_loop(label, start_address, use_expr); self.emit_opcode(Opcode::IncrementLoopIteration); self.emit_opcode(Opcode::IteratorNext); - self.emit_opcode(Opcode::IteratorDone); - let exit = self.jump_if_true(); - self.emit_opcode(Opcode::IteratorValue); + let value = self.register_allocator.alloc(); + self.emit(Opcode::IteratorDone, &[Operand::Register(&value)]); + let exit = self.jump_if_true(&value); + self.emit(Opcode::IteratorValue, &[Operand::Register(&value)]); let outer_scope = self.push_declarative_scope(for_in_loop.scope()); match for_in_loop.initializer() { IterableLoopInitializer::Identifier(ident) => { let ident = ident.to_js_string(self.interner()); - self.emit_binding(BindingOpcode::InitVar, ident); + self.emit_binding(BindingOpcode::InitVar, ident, &value); } IterableLoopInitializer::Access(access) => { - self.access_set( - Access::Property { access }, - false, - ByteCompiler::access_set_top_of_stack_expr_fn, - ); + self.access_set(Access::Property { access }, |_| &value); } IterableLoopInitializer::Var(declaration) => match declaration.binding() { Binding::Identifier(ident) => { let ident = ident.to_js_string(self.interner()); - self.emit_binding(BindingOpcode::InitVar, ident); + self.emit_binding(BindingOpcode::InitVar, ident, &value); } Binding::Pattern(pattern) => { - self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); + self.compile_declaration_pattern(pattern, BindingOpcode::InitVar, &value); } }, IterableLoopInitializer::Let(declaration) | IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { let ident = ident.to_js_string(self.interner()); - self.emit_binding(BindingOpcode::InitLexical, ident); + self.emit_binding(BindingOpcode::InitLexical, ident, &value); } Binding::Pattern(pattern) => { - self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical, &value); } }, IterableLoopInitializer::Pattern(pattern) => { - self.compile_declaration_pattern(pattern, BindingOpcode::SetName); + self.compile_declaration_pattern(pattern, BindingOpcode::SetName, &value); } } + self.register_allocator.dealloc(value); + self.compile_stmt(for_in_loop.body(), use_expr, true); self.pop_declarative_scope(outer_scope); @@ -216,7 +237,9 @@ impl ByteCompiler<'_> { let skip_early_exit = self.jump(); self.patch_jump(early_exit); - self.emit_opcode(Opcode::PushUndefined); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.register_allocator.dealloc(value); self.patch_jump(skip_early_exit); } @@ -227,15 +250,18 @@ impl ByteCompiler<'_> { use_expr: bool, ) { let outer_scope = self.push_declarative_scope(for_of_loop.iterable_scope()); - self.compile_expr(for_of_loop.iterable(), true); + let object = self.register_allocator.alloc(); + self.compile_expr(for_of_loop.iterable(), &object); self.pop_declarative_scope(outer_scope); if for_of_loop.r#await() { - self.emit_opcode(Opcode::GetAsyncIterator); + self.emit(Opcode::GetAsyncIterator, &[Operand::Register(&object)]); } else { - self.emit_opcode(Opcode::GetIterator); + self.emit(Opcode::GetIterator, &[Operand::Register(&object)]); } + self.register_allocator.dealloc(object); + let start_address = self.next_opcode_location(); if for_of_loop.r#await() { self.push_loop_control_info_for_await_of_loop(label, start_address, use_expr); @@ -246,14 +272,29 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::IteratorNext); if for_of_loop.r#await() { - self.emit_opcode(Opcode::IteratorResult); - self.emit_opcode(Opcode::Await); - self.emit_opcode(Opcode::IteratorFinishAsyncNext); - self.emit_opcode(Opcode::GeneratorNext); + let value = self.register_allocator.alloc(); + self.emit(Opcode::IteratorResult, &[Operand::Register(&value)]); + self.emit(Opcode::Await, &[Operand::Register(&value)]); + let resume_kind = self.register_allocator.alloc(); + self.pop_into_register(&resume_kind); + self.pop_into_register(&value); + + self.emit( + Opcode::IteratorFinishAsyncNext, + &[Operand::Register(&resume_kind), Operand::Register(&value)], + ); + self.emit( + Opcode::GeneratorNext, + &[Operand::Register(&resume_kind), Operand::Register(&value)], + ); + self.register_allocator.dealloc(value); + self.register_allocator.dealloc(resume_kind); } - self.emit_opcode(Opcode::IteratorDone); - let exit = self.jump_if_true(); - self.emit_opcode(Opcode::IteratorValue); + + let value = self.register_allocator.alloc(); + self.emit(Opcode::IteratorDone, &[Operand::Register(&value)]); + let exit = self.jump_if_true(&value); + self.emit(Opcode::IteratorValue, &[Operand::Register(&value)]); let outer_scope = self.push_declarative_scope(for_of_loop.scope()); let handler_index = self.push_handler(); @@ -264,23 +305,17 @@ impl ByteCompiler<'_> { match self.lexical_scope.set_mutable_binding(ident.clone()) { Ok(binding) => { let index = self.get_or_insert_binding(binding); - self.emit_binding_access(Opcode::DefInitVar, &index); + self.emit_binding_access(Opcode::DefInitVar, &index, &value); } Err(BindingLocatorError::MutateImmutable) => { let index = self.get_or_insert_string(ident); self.emit_with_varying_operand(Opcode::ThrowMutateImmutable, index); } - Err(BindingLocatorError::Silent) => { - self.emit_opcode(Opcode::Pop); - } + Err(BindingLocatorError::Silent) => {} } } IterableLoopInitializer::Access(access) => { - self.access_set( - Access::Property { access }, - false, - ByteCompiler::access_set_top_of_stack_expr_fn, - ); + self.access_set(Access::Property { access }, |_| &value); } IterableLoopInitializer::Var(declaration) => { // ignore initializers since those aren't allowed on for-of loops. @@ -288,10 +323,10 @@ impl ByteCompiler<'_> { match declaration.binding() { Binding::Identifier(ident) => { let ident = ident.to_js_string(self.interner()); - self.emit_binding(BindingOpcode::InitVar, ident); + self.emit_binding(BindingOpcode::InitVar, ident, &value); } Binding::Pattern(pattern) => { - self.compile_declaration_pattern(pattern, BindingOpcode::InitVar); + self.compile_declaration_pattern(pattern, BindingOpcode::InitVar, &value); } } } @@ -299,35 +334,35 @@ impl ByteCompiler<'_> { | IterableLoopInitializer::Const(declaration) => match declaration { Binding::Identifier(ident) => { let ident = ident.to_js_string(self.interner()); - self.emit_binding(BindingOpcode::InitLexical, ident); + self.emit_binding(BindingOpcode::InitLexical, ident, &value); } Binding::Pattern(pattern) => { - self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical, &value); } }, IterableLoopInitializer::Pattern(pattern) => { - self.compile_declaration_pattern(pattern, BindingOpcode::SetName); + self.compile_declaration_pattern(pattern, BindingOpcode::SetName, &value); } } + self.register_allocator.dealloc(value); + self.compile_stmt(for_of_loop.body(), use_expr, true); { let exit = self.jump(); self.patch_handler(handler_index); - self.emit_opcode(Opcode::Exception); + let error = self.register_allocator.alloc(); + self.emit(Opcode::Exception, &[Operand::Register(&error)]); - self.current_stack_value_count += 1; // NOTE: Capture throw of the iterator close and ignore it. - { - let handler_index = self.push_handler(); - self.iterator_close(for_of_loop.r#await()); - self.patch_handler(handler_index); - } - self.current_stack_value_count -= 1; + let handler_index = self.push_handler(); + self.iterator_close(for_of_loop.r#await()); + self.patch_handler(handler_index); - self.emit_opcode(Opcode::Throw); + self.emit(Opcode::Throw, &[Operand::Register(&error)]); + self.register_allocator.dealloc(error); self.patch_jump(exit); } @@ -350,8 +385,10 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::IncrementLoopIteration); self.push_loop_control_info(label, start_address, use_expr); - self.compile_expr(while_loop.condition(), true); - let exit = self.jump_if_false(); + let value = self.register_allocator.alloc(); + self.compile_expr(while_loop.condition(), &value); + let exit = self.jump_if_false(&value); + self.register_allocator.dealloc(value); self.compile_stmt(while_loop.body(), use_expr, true); @@ -375,8 +412,11 @@ impl ByteCompiler<'_> { let condition_label_address = self.next_opcode_location(); self.emit_opcode(Opcode::IncrementLoopIteration); - self.compile_expr(do_while_loop.cond(), true); - let exit = self.jump_if_false(); + + let value = self.register_allocator.alloc(); + self.compile_expr(do_while_loop.cond(), &value); + let exit = self.jump_if_false(&value); + self.register_allocator.dealloc(value); self.patch_jump(initial_label); diff --git a/core/engine/src/bytecompiler/statement/mod.rs b/core/engine/src/bytecompiler/statement/mod.rs index 17ca6631dfd..239b5d6adc1 100644 --- a/core/engine/src/bytecompiler/statement/mod.rs +++ b/core/engine/src/bytecompiler/statement/mod.rs @@ -2,7 +2,10 @@ use crate::{bytecompiler::ByteCompiler, vm::Opcode}; use boa_ast::Statement; -use super::jump_control::{JumpRecord, JumpRecordAction, JumpRecordKind}; +use super::{ + jump_control::{JumpRecord, JumpRecordAction, JumpRecordKind}, + Operand, +}; mod block; mod r#break; @@ -43,44 +46,63 @@ impl ByteCompiler<'_> { } Statement::Continue(node) => { if root_statement && (use_expr || self.jump_control_info_has_use_expr()) { - self.emit_opcode(Opcode::PushUndefined); - self.emit_opcode(Opcode::SetAccumulatorFromStack); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + self.register_allocator.dealloc(value); } self.compile_continue(*node, use_expr); } Statement::Break(node) => { if root_statement && (use_expr || self.jump_control_info_has_use_expr()) { - self.emit_opcode(Opcode::PushUndefined); - self.emit_opcode(Opcode::SetAccumulatorFromStack); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + self.register_allocator.dealloc(value); } self.compile_break(*node, use_expr); } Statement::Throw(throw) => { - self.compile_expr(throw.target(), true); - self.emit(Opcode::Throw, &[]); + let error = self.register_allocator.alloc(); + self.compile_expr(throw.target(), &error); + self.emit(Opcode::Throw, &[Operand::Register(&error)]); + self.register_allocator.dealloc(error); } Statement::Switch(switch) => { self.compile_switch(switch, use_expr); } Statement::Return(ret) => { + let value = self.register_allocator.alloc(); if let Some(expr) = ret.target() { - self.compile_expr(expr, true); + self.compile_expr(expr, &value); + if self.is_async_generator() { - self.emit_opcode(Opcode::Await); - self.emit_opcode(Opcode::GeneratorNext); + self.emit(Opcode::Await, &[Operand::Register(&value)]); + let resume_kind = self.register_allocator.alloc(); + self.pop_into_register(&resume_kind); + self.pop_into_register(&value); + self.emit( + Opcode::GeneratorNext, + &[Operand::Register(&resume_kind), Operand::Register(&value)], + ); + self.register_allocator.dealloc(resume_kind); } } else { - self.emit_opcode(Opcode::PushUndefined); + self.push_undefined(&value); } + self.push_from_register(&value); + self.register_allocator.dealloc(value); self.r#return(true); } Statement::Try(t) => self.compile_try(t, use_expr), Statement::Expression(expr) => { - self.compile_expr(expr, use_expr); + let value = self.register_allocator.alloc(); + self.compile_expr(expr, &value); if use_expr { - self.emit_opcode(Opcode::SetAccumulatorFromStack); + self.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); } + self.register_allocator.dealloc(value); } Statement::With(with) => self.compile_with(with, use_expr), Statement::Empty => {} @@ -105,11 +127,14 @@ impl ByteCompiler<'_> { let count = self.jump_info_open_environment_count(i); actions.push(JumpRecordAction::PopEnvironments { count }); - if info.is_try_with_finally_block() && !info.in_finally() { - actions.push(JumpRecordAction::HandleFinally { - index: info.jumps.len() as u32, - }); - actions.push(JumpRecordAction::Transfer { index: i as u32 }); + if !info.in_finally() { + if let Some(finally_throw) = info.finally_throw { + actions.push(JumpRecordAction::HandleFinally { + index: info.jumps.len() as u32, + finally_throw, + }); + actions.push(JumpRecordAction::Transfer { index: i as u32 }); + } } if info.iterator_loop() { diff --git a/core/engine/src/bytecompiler/statement/switch.rs b/core/engine/src/bytecompiler/statement/switch.rs index 3a53d9cc39a..35e442265a9 100644 --- a/core/engine/src/bytecompiler/statement/switch.rs +++ b/core/engine/src/bytecompiler/statement/switch.rs @@ -1,10 +1,11 @@ -use crate::{bytecompiler::ByteCompiler, vm::Opcode}; +use crate::bytecompiler::ByteCompiler; use boa_ast::statement::Switch; impl ByteCompiler<'_> { /// Compile a [`Switch`] `boa_ast` node pub(crate) fn compile_switch(&mut self, switch: &Switch, use_expr: bool) { - self.compile_expr(switch.val(), true); + let value = self.register_allocator.alloc(); + self.compile_expr(switch.val(), &value); let outer_scope = self.push_declarative_scope(switch.scope()); self.block_declaration_instantiation(switch); @@ -13,12 +14,14 @@ impl ByteCompiler<'_> { self.push_switch_control_info(None, start_address, use_expr); let mut labels = Vec::with_capacity(switch.cases().len()); + + let condition = self.register_allocator.alloc(); + for case in switch.cases() { // If it does not have a condition it is the default case. - let label = if let Some(condition) = case.condition() { - self.compile_expr(condition, true); - - self.emit_opcode_with_operand(Opcode::Case) + let label = if let Some(cond) = case.condition() { + self.compile_expr(cond, &condition); + self.case(&value, &condition) } else { Self::DUMMY_LABEL }; @@ -26,7 +29,10 @@ impl ByteCompiler<'_> { labels.push(label); } - let default_label = self.emit_opcode_with_operand(Opcode::Default); + self.register_allocator.dealloc(condition); + self.register_allocator.dealloc(value); + + let default_label = self.jump(); let mut default_label_set = false; for (label, case) in labels.into_iter().zip(switch.cases()) { diff --git a/core/engine/src/bytecompiler/statement/try.rs b/core/engine/src/bytecompiler/statement/try.rs index fea86b5486c..ce8f2108bb2 100644 --- a/core/engine/src/bytecompiler/statement/try.rs +++ b/core/engine/src/bytecompiler/statement/try.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{jump_control::JumpControlInfoFlags, ByteCompiler, ToJsString}, + bytecompiler::{jump_control::JumpControlInfoFlags, ByteCompiler, Register, ToJsString}, vm::{BindingOpcode, Opcode}, }; use boa_ast::{ @@ -8,120 +8,169 @@ use boa_ast::{ Statement, StatementListItem, }; +use super::Operand; + +enum TryVariant<'a> { + Catch(&'a Catch), + Finally((&'a Finally, Register)), + CatchFinally((&'a Catch, &'a Finally, Register)), +} + +impl TryVariant<'_> { + fn finaly_re_throw_register(&self) -> Option<&Register> { + match self { + TryVariant::Catch(_) => None, + TryVariant::Finally((_, r)) | TryVariant::CatchFinally((_, _, r)) => Some(r), + } + } +} + impl ByteCompiler<'_> { /// Compile try statement. pub(crate) fn compile_try(&mut self, t: &Try, use_expr: bool) { - let has_catch = t.catch().is_some(); - let has_finally = t.finally().is_some(); - - // stack: - if has_finally { - self.push_try_with_finally_control_info(use_expr); - } + let variant = match (t.catch(), t.finally()) { + (Some(catch), Some(finally)) => { + let finally_re_throw = self.register_allocator.alloc(); + self.push_try_with_finally_control_info(&finally_re_throw, use_expr); + TryVariant::CatchFinally((catch, finally, finally_re_throw)) + } + (Some(catch), None) => TryVariant::Catch(catch), + (None, Some(finally)) => { + let finally_re_throw = self.register_allocator.alloc(); + self.push_try_with_finally_control_info(&finally_re_throw, use_expr); + TryVariant::Finally((finally, finally_re_throw)) + } + (None, None) => unreachable!("try statement must have either catch or finally"), + }; let try_handler = self.push_handler(); // Compile try block self.compile_block(t.block(), use_expr); - if has_finally { - self.emit_opcode(Opcode::PushZero); - self.emit_opcode(Opcode::PushFalse); - - // stack: false, 0 + if let Some(finally_re_throw) = variant.finaly_re_throw_register() { + self.push_false(finally_re_throw); } let finally = self.jump(); self.patch_handler(try_handler); - // If it has a finally but no catch and we are in a generator, then we still need it - // to handle `return()` call on generators. - let catch_handler = if has_finally && (self.is_generator() || has_catch) { - self.current_stack_value_count += 2; - Some(self.push_handler()) - } else { - None - }; - - self.emit_opcode(Opcode::Exception); - if let Some(catch) = t.catch() { - self.compile_catch_stmt(catch, has_finally, use_expr); - } else { - // Note: implicit !has_catch - if self.is_generator() && has_finally { + match variant { + TryVariant::Catch(c) => { + let error = self.register_allocator.alloc(); + self.emit(Opcode::Exception, &[Operand::Register(&error)]); + self.compile_catch_stmt(c, &error, use_expr); + self.register_allocator.dealloc(error); + self.patch_jump(finally); + } + TryVariant::CatchFinally((c, f, finally_re_throw)) => { + let catch_handler = self.push_handler(); + let error = self.register_allocator.alloc(); + self.emit(Opcode::Exception, &[Operand::Register(&error)]); + self.compile_catch_stmt(c, &error, use_expr); + self.push_false(&finally_re_throw); + + let no_throw = self.jump(); + self.patch_handler(catch_handler); + self.push_true(&finally_re_throw); + + self.patch_jump(no_throw); + self.patch_jump(finally); + + let finally_start = self.next_opcode_location(); + self.jump_info + .last_mut() + .expect("there should be a try block") + .flags |= JumpControlInfoFlags::IN_FINALLY; + self.compile_finally_stmt(f); + self.register_allocator.dealloc(error); + let do_not_throw_exit = self.jump_if_false(&finally_re_throw); + self.emit_opcode(Opcode::ReThrow); + self.patch_jump(do_not_throw_exit); + self.pop_try_with_finally_control_info(finally_start); + self.register_allocator.dealloc(finally_re_throw); + } + TryVariant::Finally((f, finally_re_throw)) if self.is_generator() => { + let catch_handler = self.push_handler(); + let error = self.register_allocator.alloc(); + self.emit(Opcode::Exception, &[Operand::Register(&error)]); // Is this a generator `return()` empty exception? // // This is false because when the `Exception` opcode is executed, // it rethrows the empty exception, so if we reached this section, // that means it's not an `return()` generator exception. - self.emit_opcode(Opcode::PushFalse); - } + let re_throw_generator = self.register_allocator.alloc(); + self.push_false(&re_throw_generator); - // Should we rethrow the exception? - self.emit_opcode(Opcode::PushTrue); - } + // Should we rethrow the exception? + self.push_true(&finally_re_throw); - if has_finally { - if has_catch { - self.emit_opcode(Opcode::PushZero); - self.emit_opcode(Opcode::PushFalse); + let no_throw = self.jump(); + self.patch_handler(catch_handler); + self.push_true(&re_throw_generator); + + self.patch_jump(no_throw); + self.patch_jump(finally); + + let finally_start = self.next_opcode_location(); + self.jump_info + .last_mut() + .expect("there should be a try block") + .flags |= JumpControlInfoFlags::IN_FINALLY; + self.compile_finally_stmt(f); + let do_not_throw_exit = self.jump_if_false(&finally_re_throw); + let is_generator_exit = self.jump_if_true(&re_throw_generator); + self.emit(Opcode::Throw, &[Operand::Register(&error)]); + self.register_allocator.dealloc(error); + self.patch_jump(is_generator_exit); + self.emit_opcode(Opcode::ReThrow); + self.patch_jump(do_not_throw_exit); + self.register_allocator.dealloc(re_throw_generator); + self.pop_try_with_finally_control_info(finally_start); + self.register_allocator.dealloc(finally_re_throw); } + TryVariant::Finally((f, finally_re_throw)) => { + let catch_handler = self.push_handler(); + let error = self.register_allocator.alloc(); + self.emit(Opcode::Exception, &[Operand::Register(&error)]); + self.push_true(&finally_re_throw); - let exit = self.jump(); - - if let Some(catch_handler) = catch_handler { - self.current_stack_value_count -= 2; + let no_throw = self.jump(); self.patch_handler(catch_handler); - } - // Note: implicit has_finally - if !has_catch && self.is_generator() { - // Is this a generator `return()` empty exception? - self.emit_opcode(Opcode::PushTrue); + self.patch_jump(no_throw); + self.patch_jump(finally); + + let finally_start = self.next_opcode_location(); + self.jump_info + .last_mut() + .expect("there should be a try block") + .flags |= JumpControlInfoFlags::IN_FINALLY; + self.compile_finally_stmt(f); + let do_not_throw_exit = self.jump_if_false(&finally_re_throw); + self.emit(Opcode::Throw, &[Operand::Register(&error)]); + self.register_allocator.dealloc(error); + self.patch_jump(do_not_throw_exit); + self.pop_try_with_finally_control_info(finally_start); + self.register_allocator.dealloc(finally_re_throw); } - - // Should we rethrow the exception? - self.emit_opcode(Opcode::PushTrue); - - self.patch_jump(exit); - } - - self.patch_jump(finally); - - let finally_start = self.next_opcode_location(); - if let Some(finally) = t.finally() { - self.jump_info - .last_mut() - .expect("there should be a try block") - .flags |= JumpControlInfoFlags::IN_FINALLY; - - self.current_stack_value_count += 2; - // Compile finally statement body - self.compile_finally_stmt(finally, has_catch); - self.current_stack_value_count -= 2; - } - - if has_finally { - self.pop_try_with_finally_control_info(finally_start); } } - pub(crate) fn compile_catch_stmt(&mut self, catch: &Catch, _has_finally: bool, use_expr: bool) { + pub(crate) fn compile_catch_stmt(&mut self, catch: &Catch, error: &Register, use_expr: bool) { let outer_scope = self.push_declarative_scope(Some(catch.scope())); if let Some(binding) = catch.parameter() { match binding { Binding::Identifier(ident) => { let ident = ident.to_js_string(self.interner()); - self.emit_binding(BindingOpcode::InitLexical, ident); + self.emit_binding(BindingOpcode::InitLexical, ident, error); } Binding::Pattern(pattern) => { - self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical); + self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical, error); } } - } else { - self.emit_opcode(Opcode::Pop); } self.compile_catch_finally_block(catch.block(), use_expr); @@ -129,30 +178,16 @@ impl ByteCompiler<'_> { self.pop_declarative_scope(outer_scope); } - pub(crate) fn compile_finally_stmt(&mut self, finally: &Finally, has_catch: bool) { + pub(crate) fn compile_finally_stmt(&mut self, finally: &Finally) { // TODO: We could probably remove the Get/SetAccumulatorFromStack if we check that there is no break/continues statements. - self.current_stack_value_count += 1; - self.emit_opcode(Opcode::GetAccumulator); - self.compile_catch_finally_block(finally.block(), true); - self.emit_opcode(Opcode::SetAccumulatorFromStack); - self.current_stack_value_count -= 1; - - // Rethrow error if error happend! - let do_not_throw_exit = self.jump_if_false(); - - if has_catch { - self.emit_opcode(Opcode::ReThrow); - } else if self.is_generator() { - let is_generator_exit = self.jump_if_true(); - self.emit_opcode(Opcode::Throw); - self.patch_jump(is_generator_exit); - - self.emit_opcode(Opcode::ReThrow); - } else { - self.emit_opcode(Opcode::Throw); - } - - self.patch_jump(do_not_throw_exit); + let value = self.register_allocator.alloc(); + self.emit( + Opcode::SetRegisterFromAccumulator, + &[Operand::Register(&value)], + ); + self.compile_catch_finally_block(finally.block(), false); + self.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + self.register_allocator.dealloc(value); } /// Compile a catch or finally block. @@ -170,8 +205,10 @@ impl ByteCompiler<'_> { Some(StatementListItem::Statement( Statement::Break(_) | Statement::Continue(_), )) => { - self.emit_opcode(Opcode::PushUndefined); - self.emit_opcode(Opcode::SetAccumulatorFromStack); + let value = self.register_allocator.alloc(); + self.push_undefined(&value); + self.emit(Opcode::SetAccumulator, &[Operand::Register(&value)]); + self.register_allocator.dealloc(value); break; } Some(StatementListItem::Statement(Statement::Block(block))) => { diff --git a/core/engine/src/bytecompiler/statement/with.rs b/core/engine/src/bytecompiler/statement/with.rs index dcbf80d0260..01dd60af6e0 100644 --- a/core/engine/src/bytecompiler/statement/with.rs +++ b/core/engine/src/bytecompiler/statement/with.rs @@ -1,14 +1,19 @@ -use crate::{bytecompiler::ByteCompiler, vm::Opcode}; +use crate::{ + bytecompiler::{ByteCompiler, Operand}, + vm::Opcode, +}; use boa_ast::statement::With; impl ByteCompiler<'_> { /// Compile a [`With`] `boa_ast` node pub(crate) fn compile_with(&mut self, with: &With, use_expr: bool) { - self.compile_expr(with.expression(), true); + let object = self.register_allocator.alloc(); + self.compile_expr(with.expression(), &object); let outer_scope = self.lexical_scope.clone(); let _ = self.push_scope(with.scope()); - self.emit_opcode(Opcode::PushObjectEnvironment); + self.emit(Opcode::PushObjectEnvironment, &[Operand::Register(&object)]); + self.register_allocator.dealloc(object); let in_with = self.in_with; self.in_with = true; diff --git a/core/engine/src/bytecompiler/utils.rs b/core/engine/src/bytecompiler/utils.rs index f189d55d089..e96a1f98663 100644 --- a/core/engine/src/bytecompiler/utils.rs +++ b/core/engine/src/bytecompiler/utils.rs @@ -3,7 +3,7 @@ use crate::{ vm::{GeneratorResumeKind, Opcode}, }; -use super::{ByteCompiler, Literal, Operand}; +use super::{ByteCompiler, Literal, Operand, Register}; impl ByteCompiler<'_> { /// Closes an iterator @@ -17,16 +17,34 @@ impl ByteCompiler<'_> { /// [iter]: https://tc39.es/ecma262/#sec-iteratorclose /// [async]: https://tc39.es/ecma262/#sec-asynciteratorclose pub(super) fn iterator_close(&mut self, async_: bool) { - self.emit_opcode(Opcode::IteratorReturn); + let value = self.register_allocator.alloc(); + let called = self.register_allocator.alloc(); + + self.emit( + Opcode::IteratorReturn, + &[Operand::Register(&value), Operand::Register(&called)], + ); // `iterator` didn't have a `return` method, is already done or is not on the iterator stack. - let early_exit = self.jump_if_false(); + let early_exit = self.jump_if_false(&called); + self.register_allocator.dealloc(called); + if async_ { - self.emit_opcode(Opcode::Await); - self.emit_opcode(Opcode::GeneratorNext); + self.emit(Opcode::Await, &[Operand::Register(&value)]); + let resume_kind = self.register_allocator.alloc(); + self.pop_into_register(&resume_kind); + self.pop_into_register(&value); + self.emit( + Opcode::GeneratorNext, + &[Operand::Register(&resume_kind), Operand::Register(&value)], + ); + self.register_allocator.dealloc(resume_kind); } - self.emit_opcode(Opcode::IsObject); - let skip_throw = self.jump_if_true(); + + self.emit(Opcode::IsObject, &[Operand::Register(&value)]); + let skip_throw = self.jump_if_true(&value); + + self.register_allocator.dealloc(value); let error_msg = self.get_or_insert_literal(Literal::String(js_string!( "inner result was not an object" @@ -40,11 +58,15 @@ impl ByteCompiler<'_> { /// Closes all active iterators in the current [`CallFrame`][crate::vm::CallFrame]. pub(super) fn close_active_iterators(&mut self) { let start = self.next_opcode_location(); - self.emit_opcode(Opcode::IteratorStackEmpty); - let empty = self.jump_if_true(); + + let empty = self.register_allocator.alloc(); + self.emit(Opcode::IteratorStackEmpty, &[Operand::Register(&empty)]); + let exit = self.jump_if_true(&empty); + self.register_allocator.dealloc(empty); + self.iterator_close(self.is_async_generator()); self.emit(Opcode::Jump, &[Operand::U32(start)]); - self.patch_jump(empty); + self.patch_jump(exit); } /// Yields from the current generator. @@ -55,20 +77,36 @@ impl ByteCompiler<'_> { /// - value **=>** received /// /// [yield]: https://tc39.es/ecma262/#sec-yield - pub(super) fn r#yield(&mut self) { + pub(super) fn r#yield(&mut self, value: &Register) { + let resume_kind = self.register_allocator.alloc(); + // 1. Let generatorKind be GetGeneratorKind(). if self.is_async() { // 2. If generatorKind is async, return ? AsyncGeneratorYield(? Await(value)). - self.emit_opcode(Opcode::Await); - self.emit_opcode(Opcode::GeneratorNext); - self.async_generator_yield(); + self.emit(Opcode::Await, &[Operand::Register(value)]); + self.pop_into_register(&resume_kind); + self.pop_into_register(value); + self.emit( + Opcode::GeneratorNext, + &[Operand::Register(&resume_kind), Operand::Register(value)], + ); + self.async_generator_yield(value, &resume_kind); } else { // 3. Otherwise, return ? GeneratorYield(CreateIterResultObject(value, false)). - self.emit(Opcode::CreateIteratorResult, &[Operand::Bool(false)]); - self.emit_opcode(Opcode::GeneratorYield); + self.emit( + Opcode::CreateIteratorResult, + &[Operand::Register(value), Operand::Bool(false)], + ); + self.emit(Opcode::GeneratorYield, &[Operand::Register(value)]); + self.pop_into_register(&resume_kind); + self.pop_into_register(value); } - self.emit_opcode(Opcode::GeneratorNext); + self.emit( + Opcode::GeneratorNext, + &[Operand::Register(&resume_kind), Operand::Register(value)], + ); + self.register_allocator.dealloc(resume_kind); } /// Yields from the current async generator. @@ -76,37 +114,27 @@ impl ByteCompiler<'_> { /// This is equivalent to the [`AsyncGeneratorYield ( value )`][async_yield] operation from the spec. /// /// stack: - /// - value **=>** received + /// - value **=>** `resume_kind`, received /// /// [async_yield]: https://tc39.es/ecma262/#sec-asyncgeneratoryield - pub(super) fn async_generator_yield(&mut self) { - // Stack: value - self.emit_opcode(Opcode::AsyncGeneratorYield); - - // Stack: resume_kind, received - let non_return_resume = self.jump_if_not_resume_kind(GeneratorResumeKind::Return); + pub(super) fn async_generator_yield(&mut self, value: &Register, resume_kind: &Register) { + self.emit(Opcode::AsyncGeneratorYield, &[Operand::Register(value)]); + self.pop_into_register(resume_kind); + self.pop_into_register(value); - // Stack: resume_kind(Return), received - self.emit_opcode(Opcode::Pop); + let non_return_resume = + self.jump_if_not_resume_kind(GeneratorResumeKind::Return, resume_kind); - // Stack: received - self.emit_opcode(Opcode::Await); + self.emit(Opcode::Await, &[Operand::Register(value)]); + self.pop_into_register(resume_kind); + self.pop_into_register(value); - // Stack: resume_kind, received - let non_normal_resume = self.jump_if_not_resume_kind(GeneratorResumeKind::Normal); + let non_normal_resume = + self.jump_if_not_resume_kind(GeneratorResumeKind::Normal, resume_kind); - // Stack: resume_kind(Normal), received - self.emit_opcode(Opcode::Pop); + self.emit_resume_kind(GeneratorResumeKind::Return, resume_kind); - // Stack: received - self.emit_resume_kind(GeneratorResumeKind::Return); - - // Stack: resume_kind(Return) received self.patch_jump(non_normal_resume); - - // Stack: resume_kind, received self.patch_jump(non_return_resume); - - // Stack: resume_kind, received } } diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index e7f10c7f102..30ef6e43bdc 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -27,7 +27,7 @@ use crate::{ realm::Realm, vm::{ create_function_object_fast, ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock, - CompletionRecord, Opcode, + CompletionRecord, Opcode, Registers, }, Context, JsArgs, JsError, JsNativeError, JsObject, JsResult, JsString, JsValue, NativeFunction, }; @@ -1522,8 +1522,10 @@ impl SourceTextModule { .get_binding_reference(&name) .expect("binding must exist"); let index = compiler.get_or_insert_binding(binding); - compiler.emit_opcode(Opcode::PushUndefined); - compiler.emit_binding_access(Opcode::DefInitVar, &index); + let value = compiler.register_allocator.alloc(); + compiler.push_undefined(&value); + compiler.emit_binding_access(Opcode::DefInitVar, &index, &value); + compiler.register_allocator.dealloc(value); // 3. Append dn to declaredVarNames. declared_var_names.push(name); @@ -1740,10 +1742,13 @@ impl SourceTextModule { .vm .push_frame_with_stack(callframe, JsValue::undefined(), JsValue::null()); + let register_count = context.vm.frame().code_block().register_count; + let registers = &mut Registers::new(register_count as usize); + context .vm .frame - .set_promise_capability(&mut context.vm.stack, capability); + .set_promise_capability(registers, capability); // 9. If module.[[HasTLA]] is false, then // a. Assert: capability is not present. @@ -1754,7 +1759,7 @@ impl SourceTextModule { // 10. Else, // a. Assert: capability is a PromiseCapability Record. // b. Perform AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext). - let result = context.run(); + let result = context.run(registers); context.vm.pop_frame(); diff --git a/core/engine/src/object/internal_methods/mod.rs b/core/engine/src/object/internal_methods/mod.rs index ac671752f3c..c31b8b9a5e8 100644 --- a/core/engine/src/object/internal_methods/mod.rs +++ b/core/engine/src/object/internal_methods/mod.rs @@ -392,11 +392,12 @@ pub struct InternalObjectMethods { /// The return value of an internal method (`[[Call]]` or `[[Construct]]`). /// /// This is done to avoid recursion. +#[allow(variant_size_differences)] pub(crate) enum CallValue { /// Calling is ready, the frames have been setup. /// /// Requires calling [`Context::run()`]. - Ready, + Ready { register_count: usize }, /// Further processing is needed. Pending { @@ -411,7 +412,7 @@ pub(crate) enum CallValue { impl CallValue { /// Resolves the [`CallValue`], and return if the value is complete. - pub(crate) fn resolve(mut self, context: &mut Context) -> JsResult { + pub(crate) fn resolve(mut self, context: &mut Context) -> JsResult> { while let Self::Pending { func, object, @@ -421,7 +422,11 @@ impl CallValue { self = func(&object, argument_count, context)?; } - Ok(matches!(self, Self::Complete)) + match self { + Self::Ready { register_count } => Ok(Some(register_count)), + Self::Complete => Ok(None), + Self::Pending { .. } => unreachable!(), + } } } diff --git a/core/engine/src/object/operations.rs b/core/engine/src/object/operations.rs index 9a989b5cc28..1fd14dffa7a 100644 --- a/core/engine/src/object/operations.rs +++ b/core/engine/src/object/operations.rs @@ -11,6 +11,7 @@ use crate::{ realm::Realm, string::StaticJsStrings, value::Type, + vm::Registers, Context, JsResult, JsSymbol, JsValue, }; @@ -405,11 +406,9 @@ impl JsObject { // 3. Return ? F.[[Call]](V, argumentsList). let frame_index = context.vm.frames.len(); - let is_complete = self.__call__(argument_count).resolve(context)?; - - if is_complete { + let Some(register_count) = self.__call__(argument_count).resolve(context)? else { return Ok(context.vm.pop()); - } + }; if frame_index + 1 == context.vm.frames.len() { context.vm.frame.set_exit_early(true); @@ -417,7 +416,7 @@ impl JsObject { context.vm.frames[frame_index + 1].set_exit_early(true); } - let result = context.run().consume(); + let result = context.run(&mut Registers::new(register_count)).consume(); context.vm.pop_frame().expect("frame must exist"); @@ -455,15 +454,14 @@ impl JsObject { // 2. If argumentsList is not present, set argumentsList to a new empty List. // 3. Return ? F.[[Construct]](argumentsList, newTarget). let frame_index = context.vm.frames.len(); - let is_complete = self.__construct__(argument_count).resolve(context)?; - if is_complete { + let Some(register_count) = self.__construct__(argument_count).resolve(context)? else { let result = context.vm.pop(); return Ok(result .as_object() .expect("construct value should be an object") .clone()); - } + }; if frame_index + 1 == context.vm.frames.len() { context.vm.frame.set_exit_early(true); @@ -471,7 +469,7 @@ impl JsObject { context.vm.frames[frame_index + 1].set_exit_early(true); } - let result = context.run().consume(); + let result = context.run(&mut Registers::new(register_count)).consume(); context.vm.pop_frame().expect("frame must exist"); diff --git a/core/engine/src/script.rs b/core/engine/src/script.rs index 52c59110e22..8c3f5130748 100644 --- a/core/engine/src/script.rs +++ b/core/engine/src/script.rs @@ -20,7 +20,7 @@ use crate::{ bytecompiler::{global_declaration_instantiation_context, ByteCompiler}, js_string, realm::Realm, - vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock}, + vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock, Registers}, Context, HostDefined, JsResult, JsString, JsValue, Module, }; @@ -167,7 +167,8 @@ impl Script { let _timer = Profiler::global().start_event("Execution", "Main"); self.prepare_run(context)?; - let record = context.run(); + let register_count = self.codeblock(context)?.register_count; + let record = context.run(&mut Registers::new(register_count as usize)); context.vm.pop_frame(); context.clear_kept_objects(); @@ -203,7 +204,10 @@ impl Script { self.prepare_run(context)?; - let record = context.run_async_with_budget(budget).await; + let register_count = self.codeblock(context)?.register_count; + let record = context + .run_async_with_budget(budget, &mut Registers::new(register_count as usize)) + .await; context.vm.pop_frame(); context.clear_kept_objects(); diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index 38a4c41a7c4..1a80c3ae38e 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -17,7 +17,7 @@ use boa_ast::scope::BindingLocator; use boa_gc::{Finalize, Gc, Trace}; use thin_vec::ThinVec; -use super::{ActiveRunnable, Vm}; +use super::{ActiveRunnable, Registers, Vm}; bitflags::bitflags! { /// Flags associated with a [`CallFrame`]. @@ -225,32 +225,33 @@ impl CallFrame { } /// Returns the async generator object, if the function that this [`CallFrame`] is from an async generator, [`None`] otherwise. - pub(crate) fn async_generator_object(&self, stack: &[JsValue]) -> Option { + pub(crate) fn async_generator_object(&self, registers: &Registers) -> Option { if !self.code_block().is_async_generator() { return None; } - self.register(Self::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX, stack) + registers + .get(Self::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX) .as_object() .cloned() } - pub(crate) fn promise_capability(&self, stack: &[JsValue]) -> Option { + pub(crate) fn promise_capability(&self, registers: &Registers) -> Option { if !self.code_block().is_async() { return None; } - let promise = self - .register(Self::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX, stack) + let promise = registers + .get(Self::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX) .as_object() .cloned()?; - let resolve = self - .register(Self::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX, stack) + let resolve = registers + .get(Self::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX) .as_object() .cloned() .and_then(JsFunction::from_object)?; - let reject = self - .register(Self::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX, stack) + let reject = registers + .get(Self::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX) .as_object() .cloned() .and_then(JsFunction::from_object)?; @@ -263,7 +264,7 @@ impl CallFrame { pub(crate) fn set_promise_capability( &self, - stack: &mut [JsValue], + registers: &mut Registers, promise_capability: Option<&PromiseCapability>, ) { debug_assert!( @@ -271,55 +272,29 @@ impl CallFrame { "Only async functions have a promise capability" ); - self.set_register( + registers.set( Self::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX, promise_capability .map(PromiseCapability::promise) .cloned() .map_or_else(JsValue::undefined, Into::into), - stack, ); - self.set_register( + registers.set( Self::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX, promise_capability .map(PromiseCapability::resolve) .cloned() .map_or_else(JsValue::undefined, Into::into), - stack, ); - self.set_register( + registers.set( Self::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX, promise_capability .map(PromiseCapability::reject) .cloned() .map_or_else(JsValue::undefined, Into::into), - stack, ); } - /// Returns the register at the given index. - /// - /// # Panics - /// - /// If the index is out of bounds. - #[track_caller] - pub(crate) fn register<'stack>(&self, index: u32, stack: &'stack [JsValue]) -> &'stack JsValue { - debug_assert!(index < self.code_block().register_count); - let at = self.rp + index; - &stack[at as usize] - } - - /// Sets the register at the given index. - /// - /// # Panics - /// - /// If the index is out of bounds. - pub(crate) fn set_register(&self, index: u32, value: JsValue, stack: &mut [JsValue]) { - debug_assert!(index < self.code_block().register_count); - let at = self.rp + index; - stack[at as usize] = value; - } - /// Does this have the [`CallFrameFlags::EXIT_EARLY`] flag. pub(crate) fn exit_early(&self) -> bool { self.flags.contains(CallFrameFlags::EXIT_EARLY) @@ -343,26 +318,6 @@ impl CallFrame { pub(crate) fn has_this_value_cached(&self) -> bool { self.flags.contains(CallFrameFlags::THIS_VALUE_CACHED) } - - pub(crate) fn read_value( - &self, - operand_types: u8, - operand: u32, - vm: &Vm, - ) -> JsValue { - assert!(N <= 4, "operand type index ({N}) must be less than 4"); - - let type_ = (operand_types >> (N * 2)) & 0x0000_0011; - match type_ { - 0 => vm.stack[(self.rp + operand) as usize].clone(), - 1 => self - .argument(operand as usize, vm) - .expect("should be argument") - .clone(), - 2 => operand.into(), - _ => unreachable!(), - } - } } /// ---- `CallFrame` stack methods ---- diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index 150c85f49dd..b0c00a633cc 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -91,8 +91,6 @@ unsafe impl Trace for CodeBlockFlags { pub(crate) struct Handler { pub(crate) start: u32, pub(crate) end: u32, - - pub(crate) stack_count: u32, pub(crate) environment_count: u32, } @@ -378,346 +376,610 @@ impl CodeBlock { /// Returns an empty `String` if no operands are present. pub(crate) fn instruction_operands(&self, instruction: &Instruction) -> String { match instruction { - Instruction::SetRegisterFromAccumulator { register } - | Instruction::SetAccumulator { register } => format!("R{}", register.value()), - Instruction::Add { .. } - | Instruction::Sub { .. } - | Instruction::Div { .. } - | Instruction::Mul { .. } - | Instruction::Mod { .. } - | Instruction::Pow { .. } - | Instruction::ShiftRight { .. } - | Instruction::ShiftLeft { .. } - | Instruction::UnsignedShiftRight { .. } - | Instruction::BitOr { .. } - | Instruction::BitAnd { .. } - | Instruction::BitXor { .. } - | Instruction::BitNot { .. } - | Instruction::In { .. } - | Instruction::Eq { .. } - | Instruction::NotEq { .. } - | Instruction::GreaterThan { .. } - | Instruction::GreaterThanOrEq { .. } - | Instruction::LessThan { .. } - | Instruction::LessThanOrEq { .. } - | Instruction::InstanceOf { .. } - | Instruction::StrictNotEq { .. } - | Instruction::StrictEq { .. } - | Instruction::InPrivate { .. } - | Instruction::Inc { .. } - | Instruction::Dec { .. } - | Instruction::ToNumeric { .. } => "TODO: fix".to_string(), - Instruction::PopIntoRegister { dst } => format!("R{}", dst.value()), - Instruction::PushFromRegister { src } => format!("R{}", src.value()), - Instruction::Move { - operand_types, - dst: r1, - src: r2, - } => { + Instruction::SetRegisterFromAccumulator { dst } + | Instruction::PopIntoRegister { dst } + | Instruction::PushZero { dst } + | Instruction::PushOne { dst } + | Instruction::PushNaN { dst } + | Instruction::PushPositiveInfinity { dst } + | Instruction::PushNegativeInfinity { dst } + | Instruction::PushNull { dst } + | Instruction::PushTrue { dst } + | Instruction::PushFalse { dst } + | Instruction::PushUndefined { dst } + | Instruction::Exception { dst } + | Instruction::This { dst } + | Instruction::Super { dst } + | Instruction::SuperCallPrepare { dst } + | Instruction::NewTarget { dst } + | Instruction::ImportMeta { dst } + | Instruction::CreateMappedArgumentsObject { dst } + | Instruction::CreateUnmappedArgumentsObject { dst } + | Instruction::RestParameterInit { dst } + | Instruction::PushNewArray { dst } => format!("dst:{}", dst.value()), + Instruction::Add { lhs, rhs, dst } + | Instruction::Sub { lhs, rhs, dst } + | Instruction::Div { lhs, rhs, dst } + | Instruction::Mul { lhs, rhs, dst } + | Instruction::Mod { lhs, rhs, dst } + | Instruction::Pow { lhs, rhs, dst } + | Instruction::ShiftRight { lhs, rhs, dst } + | Instruction::ShiftLeft { lhs, rhs, dst } + | Instruction::UnsignedShiftRight { lhs, rhs, dst } + | Instruction::BitOr { lhs, rhs, dst } + | Instruction::BitAnd { lhs, rhs, dst } + | Instruction::BitXor { lhs, rhs, dst } + | Instruction::BitNot { lhs, rhs, dst } + | Instruction::In { lhs, rhs, dst } + | Instruction::Eq { lhs, rhs, dst } + | Instruction::StrictEq { lhs, rhs, dst } + | Instruction::NotEq { lhs, rhs, dst } + | Instruction::StrictNotEq { lhs, rhs, dst } + | Instruction::GreaterThan { lhs, rhs, dst } + | Instruction::GreaterThanOrEq { lhs, rhs, dst } + | Instruction::LessThan { lhs, rhs, dst } + | Instruction::LessThanOrEq { lhs, rhs, dst } + | Instruction::InstanceOf { lhs, rhs, dst } => { + format!( + "lhs:{}, rhs:{}, dst:{}", + lhs.value(), + rhs.value(), + dst.value() + ) + } + Instruction::InPrivate { dst, index, rhs } => { format!( - "dst:reg{}, src:{}", - r1.value(), - r2.to_string::<0>(*operand_types) + "rhs:{}, index:{}, dst:{}", + rhs.value(), + index.value(), + dst.value() ) } - Instruction::SetFunctionName { prefix } => match prefix { - 0 => "prefix: none", - 1 => "prefix: get", - 2 => "prefix: set", - _ => unreachable!(), + Instruction::Inc { src, dst } + | Instruction::Dec { src, dst } + | Instruction::Move { src, dst } + | Instruction::ToPropertyKey { src, dst } + | Instruction::PopIntoLocal { src, dst } + | Instruction::PushFromLocal { src, dst } => { + format!("src:{}, dst:{}", src.value(), dst.value()) + } + Instruction::SetFunctionName { + function, + name, + prefix, + } => { + format!( + "function:{}, name:{}, prefix:{}", + function.value(), + name.value(), + match prefix { + 0 => "prefix:", + 1 => "prefix: get", + 2 => "prefix: set", + _ => unreachable!(), + } + ) } - .to_owned(), - Instruction::RotateLeft { n } | Instruction::RotateRight { n } => n.to_string(), Instruction::Generator { r#async } => { format!("async: {async}") } - Instruction::PushInt8 { value } => value.to_string(), - Instruction::PushInt16 { value } => value.to_string(), - Instruction::PushInt32 { value } => value.to_string(), - Instruction::PushFloat { value } => ryu_js::Buffer::new().format(*value).to_string(), - Instruction::PushDouble { value } => ryu_js::Buffer::new().format(*value).to_string(), - Instruction::PushLiteral { index } - | Instruction::ThrowNewTypeError { message: index } - | Instruction::ThrowNewSyntaxError { message: index } - | Instruction::HasRestrictedGlobalProperty { index } - | Instruction::CanDeclareGlobalFunction { index } - | Instruction::CanDeclareGlobalVar { index } => index.value().to_string(), + Instruction::PushInt8 { value, dst } => { + format!("value:{}, dst:{}", value, dst.value()) + } + Instruction::PushInt16 { value, dst } => { + format!("value:{}, dst:{}", value, dst.value()) + } + Instruction::PushInt32 { value, dst } => { + format!("value:{}, dst:{}", value, dst.value()) + } + Instruction::PushFloat { value, dst } => { + format!("value:{}, dst:{}", value, dst.value()) + } + Instruction::PushDouble { value, dst } => { + format!("value:{}, dst:{}", value, dst.value()) + } + Instruction::PushLiteral { index, dst } + | Instruction::ThisForObjectEnvironmentName { index, dst } + | Instruction::GetFunction { index, dst } + | Instruction::HasRestrictedGlobalProperty { index, dst } + | Instruction::CanDeclareGlobalFunction { index, dst } + | Instruction::CanDeclareGlobalVar { index, dst } + | Instruction::GetArgument { index, dst } => { + format!("index:{}, dst:{}", index.value(), dst.value()) + } + Instruction::ThrowNewTypeError { message: index } + | Instruction::ThrowNewSyntaxError { message: index } => index.value().to_string(), Instruction::PushRegExp { - pattern_index: source_index, - flags_index: flag_index, + pattern_index, + flags_index, + dst, } => { - let pattern = self - .constant_string(source_index.value() as usize) - .to_std_string_escaped(); - let flags = self - .constant_string(flag_index.value() as usize) - .to_std_string_escaped(); - format!("/{pattern}/{flags}") - } - Instruction::Jump { address: value } - | Instruction::JumpIfTrue { address: value } - | Instruction::JumpIfFalse { address: value } - | Instruction::JumpIfNotUndefined { address: value } - | Instruction::JumpIfNullOrUndefined { address: value } - | Instruction::Case { address: value } - | Instruction::Default { address: value } => value.to_string(), - Instruction::LogicalAnd { - exit, - lhs, - operand_types, + format!( + "pattern:{}, flags:{}, dst:{}", + pattern_index.value(), + flags_index.value(), + dst.value() + ) } - | Instruction::LogicalOr { - exit, - lhs, - operand_types, + Instruction::Jump { address } => address.to_string(), + Instruction::JumpIfTrue { address, value } + | Instruction::JumpIfFalse { address, value } + | Instruction::JumpIfNotUndefined { address, value } + | Instruction::JumpIfNullOrUndefined { address, value } + | Instruction::LogicalAnd { address, value } + | Instruction::LogicalOr { address, value } + | Instruction::Coalesce { address, value } => { + format!("value:{}, address:{}", value.value(), address) } - | Instruction::Coalesce { - exit, - lhs, - operand_types, + Instruction::Case { + address, + value, + condition, } => { - format!("lhs:{} exit:{exit}", lhs.to_string::<0>(*operand_types)) + format!( + "value:{}, condition:{}, address:{}", + value.value(), + condition.value(), + address + ) } Instruction::CallEval { - argument_count: value, + argument_count, scope_index, } => { - format!("{}, {}", value.value(), scope_index.value()) + format!( + "argument_count:{}, scope_index:{}", + argument_count.value(), + scope_index.value() + ) + } + Instruction::CallEvalSpread { scope_index } + | Instruction::PushScope { scope_index } => { + format!("scope_index:{}", scope_index.value()) } - Instruction::Call { - argument_count: value, + Instruction::Call { argument_count } + | Instruction::New { argument_count } + | Instruction::SuperCall { argument_count } => { + format!("argument_count:{}", argument_count.value()) } - | Instruction::New { - argument_count: value, + Instruction::DefVar { binding_index } | Instruction::GetLocator { binding_index } => { + format!("binding_index:{}", binding_index.value()) } - | Instruction::SuperCall { - argument_count: value, + Instruction::DefInitVar { src, binding_index } + | Instruction::PutLexicalValue { src, binding_index } + | Instruction::SetName { src, binding_index } => { + format!( + "src:{}, binding_index:{}", + src.value(), + binding_index.value() + ) } - | Instruction::ConcatToString { value_count: value } - | Instruction::GetArgument { index: value } => value.value().to_string(), - Instruction::PushScope { index } | Instruction::CallEvalSpread { index } => { - index.value().to_string() + Instruction::GetName { dst, binding_index } + | Instruction::GetNameAndLocator { dst, binding_index } + | Instruction::GetNameOrUndefined { dst, binding_index } + | Instruction::DeleteName { dst, binding_index } => { + format!( + "dst:{}, binding_index:{}", + dst.value(), + binding_index.value() + ) } - Instruction::CopyDataProperties { - excluded_key_count: value1, - excluded_key_count_computed: value2, - } => format!("{}, {}", value1.value(), value2.value()), Instruction::GeneratorDelegateNext { - return_method_undefined: value1, - throw_method_undefined: value2, + return_method_undefined, + throw_method_undefined, + value, + resume_kind, + is_return, + } => { + format!( + "return_method_undefined:{}, throw_method_undefined:{}, value:{}, resume_kind:{}, is_return:{}", + return_method_undefined, throw_method_undefined, value.value(), resume_kind.value(), is_return.value() + ) } - | Instruction::GeneratorDelegateResume { - exit: value1, - r#return: value2, + Instruction::GeneratorDelegateResume { + r#return, + exit, + value, + resume_kind, + is_return, } => { - format!("{value1}, {value2}") + format!( + "return:{}, exit:{}, value:{}, resume_kind:{}, is_return:{}", + r#return, + exit, + value.value(), + resume_kind.value(), + is_return.value() + ) + } + Instruction::DefineOwnPropertyByName { + object, + value, + name_index, + } + | Instruction::SetPropertyGetterByName { + object, + value, + name_index, + } + | Instruction::SetPropertySetterByName { + object, + value, + name_index, + } + | Instruction::DefinePrivateField { + object, + value, + name_index, + } + | Instruction::SetPrivateMethod { + object, + value, + name_index, + } + | Instruction::SetPrivateSetter { + object, + value, + name_index, + } + | Instruction::SetPrivateGetter { + object, + value, + name_index, + } + | Instruction::PushClassPrivateGetter { + object, + value, + name_index, + } + | Instruction::PushClassPrivateSetter { + object, + value, + name_index, + } + | Instruction::DefineClassStaticMethodByName { + object, + value, + name_index, + } + | Instruction::DefineClassMethodByName { + object, + value, + name_index, + } + | Instruction::DefineClassStaticGetterByName { + object, + value, + name_index, + } + | Instruction::DefineClassGetterByName { + object, + value, + name_index, } - Instruction::TemplateLookup { exit: value, site } => format!("{value}, {site}"), - Instruction::TemplateCreate { count, site } => { - format!("{}, {site}", count.value()) + | Instruction::DefineClassStaticSetterByName { + object, + value, + name_index, + } + | Instruction::DefineClassSetterByName { + object, + value, + name_index, + } + | Instruction::SetPrivateField { + object, + value, + name_index, + } + | Instruction::PushClassFieldPrivate { + object, + value, + name_index, + } => { + format!( + "object:{}, value:{}, name_index:{}", + object.value(), + value.value(), + name_index.value() + ) } - Instruction::GetFunction { dst, index } => { - let index = index.value() as usize; + Instruction::GetPrivateField { + dst, + object, + name_index, + } => { format!( - "R{} = {index:04}: '{}' (length: {})", + "dst:{}, object:{}, name_index:{}", dst.value(), - self.constant_function(index).name().to_std_string_escaped(), - self.constant_function(index).length + object.value(), + name_index.value() ) } - Instruction::DefVar { index } - | Instruction::DefInitVar { index } - | Instruction::PutLexicalValue { index } - | Instruction::GetName { index } - | Instruction::GetLocator { index } - | Instruction::GetNameAndLocator { index } - | Instruction::GetNameOrUndefined { index } - | Instruction::SetName { index } - | Instruction::DeleteName { index } => { + Instruction::PushClassPrivateMethod { + object, + proto, + value, + name_index, + } => { format!( - "{:04}: '{}'", - index.value(), - self.bindings[index.value() as usize] - .name() - .to_std_string_escaped() + "object:{}, proto:{}, value:{}, name_index:{}", + object.value(), + proto.value(), + value.value(), + name_index.value() ) } - Instruction::DefineOwnPropertyByName { index } - | Instruction::DefineClassStaticMethodByName { index } - | Instruction::DefineClassMethodByName { index } - | Instruction::SetPropertyGetterByName { index } - | Instruction::DefineClassStaticGetterByName { index } - | Instruction::DefineClassGetterByName { index } - | Instruction::SetPropertySetterByName { index } - | Instruction::DefineClassStaticSetterByName { index } - | Instruction::DefineClassSetterByName { index } - | Instruction::ThrowMutateImmutable { index } - | Instruction::DeletePropertyByName { index } - | Instruction::SetPrivateField { index } - | Instruction::DefinePrivateField { index } - | Instruction::SetPrivateMethod { index } - | Instruction::SetPrivateSetter { index } - | Instruction::SetPrivateGetter { index } - | Instruction::GetPrivateField { index } - | Instruction::PushClassFieldPrivate { index } - | Instruction::PushClassPrivateGetter { index } - | Instruction::PushClassPrivateSetter { index } - | Instruction::PushClassPrivateMethod { index } => { + Instruction::ThrowMutateImmutable { index } => { + format!("index:{}", index.value()) + } + + Instruction::DeletePropertyByName { object, name_index } => { format!( - "{:04}: '{}'", - index.value(), - self.constant_string(index.value() as usize) - .to_std_string_escaped(), + "object:{}, name_index:{}", + object.value(), + name_index.value() ) } Instruction::GetPropertyByName { - operand_types, dst, receiver, value, - index, + ic_index, } => { - let ic = &self.ic[index.value() as usize]; - let slot = ic.slot(); + let ic = &self.ic[ic_index.value() as usize]; format!( - "dst:reg{}, receiver:{}, value:{}, {:04}: '{}', Shape: 0x{:x}, Slot: index: {}, attributes {:?}", + "dst:{}, receiver:{}, value:{}, ic:shape:0x{:x}", dst.value(), - receiver.to_string::<0>(*operand_types), - value.to_string::<1>(*operand_types), - index.value(), - ic.name.to_std_string_escaped(), + receiver.value(), + value.value(), ic.shape.borrow().to_addr_usize(), - slot.index, - slot.attributes, ) } - Instruction::SetPropertyByName { index } => { - let ic = &self.ic[index.value() as usize]; - let slot = ic.slot(); + Instruction::SetPropertyByName { + value, + receiver, + object, + ic_index, + } => { + let ic = &self.ic[ic_index.value() as usize]; format!( - "{:04}: '{}', Shape: 0x{:x}, Slot: index: {}, attributes {:?}", - index.value(), - ic.name.to_std_string_escaped(), + "object:{}, receiver:{}, value:{}, ic:shape:0x{:x}", + object.value(), + receiver.value(), + value.value(), ic.shape.borrow().to_addr_usize(), - slot.index, - slot.attributes, ) } - Instruction::PushPrivateEnvironment { - class, - name_indices, - operand_types, + Instruction::GetPropertyByValue { + dst, + key, + receiver, + object, + } + | Instruction::GetPropertyByValuePush { + dst, + key, + receiver, + object, } => { format!( - "class:{}, names:{name_indices:?}", - class.to_string::<0>(*operand_types) + "dst:{}, object:{}, receiver:{}, key:{}", + dst.value(), + object.value(), + receiver.value(), + key.value(), ) } - Instruction::JumpTable { default, addresses } => { - let mut operands = format!("#{}: Default: {default:4}", addresses.len()); - for (i, address) in addresses.iter().enumerate() { - operands += &format!(", {i}: {address}"); - } - operands - } - Instruction::JumpIfNotResumeKind { exit, resume_kind } => { - format!("ResumeKind: {resume_kind:?}, exit: {exit}") - } - Instruction::CreateIteratorResult { done } => { - format!("done: {done}") - } - Instruction::CreateGlobalFunctionBinding { - index, - configurable, - } - | Instruction::CreateGlobalVarBinding { - index, - configurable, + Instruction::SetPropertyByValue { + value, + key, + receiver, + object, } => { - let name = self - .constant_string(index.value() as usize) - .to_std_string_escaped(); - format!("name: {name}, configurable: {configurable}") + format!( + "object:{}, receiver:{}, key:{}, value:{}", + object.value(), + receiver.value(), + key.value(), + value.value(), + ) } - Instruction::PushClassField { - is_anonymous_function, - } => { - format!("is_anonymous_function: {is_anonymous_function}") + Instruction::DefineOwnPropertyByValue { value, key, object } + | Instruction::DefineClassStaticMethodByValue { value, key, object } + | Instruction::DefineClassMethodByValue { value, key, object } + | Instruction::SetPropertyGetterByValue { value, key, object } + | Instruction::DefineClassStaticGetterByValue { value, key, object } + | Instruction::DefineClassGetterByValue { value, key, object } + | Instruction::SetPropertySetterByValue { value, key, object } + | Instruction::DefineClassStaticSetterByValue { value, key, object } + | Instruction::DefineClassSetterByValue { value, key, object } => { + format!( + "object:{}, key:{}, value:{}", + object.value(), + key.value(), + value.value() + ) } - Instruction::PopIntoRegister { dst } | Instruction::PopIntoLocal { dst } => { - format!("dst:reg{}", dst.value()) + Instruction::DeletePropertyByValue { key, object } => { + format!("object:{}, key:{}", object.value(), key.value()) } - Instruction::PushFromRegister { src } | Instruction::PushFromLocal { src } => { - format!("src:reg{}", src.value()) + Instruction::CreateIteratorResult { value, done } => { + format!("value:{}, done:{}", value.value(), done) } Instruction::PushClassPrototype { - operand_types, dst, class, superclass, } => { format!( - "dst:reg{}, class:{}, superclass:{}", + "dst:{}, class:{}, superclass:{}", dst.value(), - class.to_string::<0>(*operand_types), - superclass.to_string::<1>(*operand_types) + class.value(), + superclass.value(), ) } Instruction::SetClassPrototype { - operand_types, dst, prototype, class, } => { format!( - "dst:reg{}, prototype:{}, class:{}", + "dst:{}, prototype:{}, class:{}", dst.value(), - prototype.to_string::<0>(*operand_types), - class.to_string::<1>(*operand_types) + prototype.value(), + class.value() ) } + Instruction::SetHomeObject { function, home } => { + format!("function:{}, home:{}", function.value(), home.value()) + } + Instruction::SetPrototype { object, prototype } => { + format!("object:{}, prototype:{}", object.value(), prototype.value()) + } + Instruction::PushValueToArray { value, array } => { + format!("value:{}, array:{}", value.value(), array.value()) + } + Instruction::PushElisionToArray { array } + | Instruction::PushIteratorToArray { array } => { + format!("array:{}", array.value()) + } + Instruction::TypeOf { value } + | Instruction::LogicalNot { value } + | Instruction::Pos { value } + | Instruction::Neg { value } + | Instruction::IsObject { value } + | Instruction::ImportCall { value } + | Instruction::BindThisValue { value } => { + format!("value:{}", value.value()) + } + Instruction::PushClassField { + object, + name_index, + value, + is_anonymous_function, + } => { + format!( + "object:{}, value:{}, name_index:{}, is_anonymous_function:{}", + object.value(), + value.value(), + name_index.value(), + is_anonymous_function + ) + } + Instruction::MaybeException { + has_exception, + exception, + } => { + format!( + "has_exception:{}, exception:{}", + has_exception.value(), + exception.value() + ) + } + Instruction::SetAccumulator { src } + | Instruction::PushFromRegister { src } + | Instruction::Throw { src } + | Instruction::SetNameByLocator { src } + | Instruction::PushObjectEnvironment { src } + | Instruction::CreateForInIterator { src } + | Instruction::GetIterator { src } + | Instruction::GetAsyncIterator { src } + | Instruction::ValueNotNullOrUndefined { src } + | Instruction::GeneratorYield { src } + | Instruction::AsyncGeneratorYield { src } + | Instruction::Await { src } => { + format!("src:{}", src.value()) + } + Instruction::IteratorDone { dst } + | Instruction::IteratorValue { dst } + | Instruction::IteratorResult { dst } + | Instruction::IteratorToArray { dst } + | Instruction::IteratorStackEmpty { dst } + | Instruction::PushEmptyObject { dst } => { + format!("dst:{}", dst.value()) + } + Instruction::IteratorFinishAsyncNext { resume_kind, value } + | Instruction::GeneratorNext { resume_kind, value } => { + format!( + "resume_kind:{}, value:{}", + resume_kind.value(), + value.value() + ) + } + Instruction::IteratorReturn { value, called } => { + format!("value:{}, called:{}", value.value(), called.value()) + } + Instruction::JumpIfNotResumeKind { + address, + resume_kind, + src, + } => { + format!( + "address:{}, resume_kind:{}, src:{}", + address, + *resume_kind as u8, + src.value() + ) + } + Instruction::CreateGlobalFunctionBinding { + src, + configurable, + name_index, + } => { + format!( + "src:{}, configurable:{}, name_index:{}", + src.value(), + configurable, + name_index.value() + ) + } + Instruction::CreateGlobalVarBinding { + configurable, + name_index, + } => { + format!( + "configurable:{}, name_index:{}", + configurable, + name_index.value() + ) + } + Instruction::PushPrivateEnvironment { + class, + name_indices, + } => { + format!("class:{}, names:{name_indices:?}", class.value()) + } + Instruction::TemplateLookup { address, site, dst } => { + format!("address:{}, site:{}, dst:{}", address, site, dst.value()) + } + Instruction::JumpTable { default, addresses } => { + let mut operands = format!("#{}: Default: {default:4}", addresses.len()); + for (i, address) in addresses.iter().enumerate() { + operands += &format!(", {i}: {address}"); + } + operands + } + Instruction::ConcatToString { dst, values } => { + format!("dst:{}, values:{values:?}", dst.value()) + } + Instruction::CopyDataProperties { + object, + source, + excluded_keys, + } => { + format!( + "object:{}, source:{}, excluded_keys:{excluded_keys:?}", + object.value(), + source.value() + ) + } + Instruction::TemplateCreate { site, dst, values } => { + format!("site:{}, dst:{}, values:{values:?}", site, dst.value()) + } Instruction::Pop - | Instruction::Dup - | Instruction::Swap - | Instruction::PushZero - | Instruction::PushOne - | Instruction::PushNaN - | Instruction::PushPositiveInfinity - | Instruction::PushNegativeInfinity - | Instruction::PushNull - | Instruction::PushTrue - | Instruction::PushFalse - | Instruction::PushUndefined - | Instruction::PushEmptyObject - | Instruction::SetHomeObject - | Instruction::TypeOf - | Instruction::Void - | Instruction::LogicalNot - | Instruction::Pos - | Instruction::Neg - | Instruction::GetPropertyByValue - | Instruction::GetPropertyByValuePush - | Instruction::SetPropertyByValue - | Instruction::DefineOwnPropertyByValue - | Instruction::DefineClassStaticMethodByValue - | Instruction::DefineClassMethodByValue - | Instruction::SetPropertyGetterByValue - | Instruction::DefineClassStaticGetterByValue - | Instruction::DefineClassGetterByValue - | Instruction::SetPropertySetterByValue - | Instruction::DefineClassStaticSetterByValue - | Instruction::DefineClassSetterByValue - | Instruction::DeletePropertyByValue | Instruction::DeleteSuperThrow - | Instruction::ToPropertyKey - | Instruction::ToBoolean - | Instruction::Throw | Instruction::ReThrow - | Instruction::Exception - | Instruction::MaybeException - | Instruction::This - | Instruction::ThisForObjectEnvironmentName { .. } - | Instruction::Super | Instruction::CheckReturn | Instruction::Return | Instruction::AsyncGeneratorClose @@ -725,50 +987,12 @@ impl CodeBlock { | Instruction::CompletePromiseCapability | Instruction::PopEnvironment | Instruction::IncrementLoopIteration - | Instruction::CreateForInIterator - | Instruction::GetIterator - | Instruction::GetAsyncIterator | Instruction::IteratorNext - | Instruction::IteratorNextWithoutPop - | Instruction::IteratorFinishAsyncNext - | Instruction::IteratorValue - | Instruction::IteratorValueWithoutPop - | Instruction::IteratorResult - | Instruction::IteratorDone - | Instruction::IteratorToArray - | Instruction::IteratorReturn - | Instruction::IteratorStackEmpty - | Instruction::RequireObjectCoercible - | Instruction::ValueNotNullOrUndefined - | Instruction::RestParameterInit - | Instruction::PushValueToArray - | Instruction::PushElisionToArray - | Instruction::PushIteratorToArray - | Instruction::PushNewArray - | Instruction::GeneratorYield - | Instruction::AsyncGeneratorYield - | Instruction::GeneratorNext | Instruction::SuperCallDerived - | Instruction::Await - | Instruction::NewTarget - | Instruction::ImportMeta - | Instruction::SuperCallPrepare | Instruction::CallSpread | Instruction::NewSpread | Instruction::SuperCallSpread - | Instruction::SetPrototype - | Instruction::PushObjectEnvironment - | Instruction::IsObject - | Instruction::SetNameByLocator - | Instruction::PopPrivateEnvironment - | Instruction::ImportCall - | Instruction::GetAccumulator - | Instruction::SetAccumulatorFromStack - | Instruction::BindThisValue - | Instruction::CreateMappedArgumentsObject - | Instruction::CreateUnmappedArgumentsObject - | Instruction::Nop => String::new(), - + | Instruction::PopPrivateEnvironment => String::new(), Instruction::U16Operands | Instruction::U32Operands | Instruction::Reserved1 @@ -817,7 +1041,21 @@ impl CodeBlock { | Instruction::Reserved44 | Instruction::Reserved45 | Instruction::Reserved46 - | Instruction::Reserved47 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved47 + | Instruction::Reserved48 + | Instruction::Reserved49 + | Instruction::Reserved50 + | Instruction::Reserved51 + | Instruction::Reserved52 + | Instruction::Reserved53 + | Instruction::Reserved54 + | Instruction::Reserved55 + | Instruction::Reserved56 + | Instruction::Reserved57 + | Instruction::Reserved58 + | Instruction::Reserved59 + | Instruction::Reserved60 + | Instruction::Reserved61 => unreachable!("Reserved opcodes are unreachable"), } } } @@ -921,12 +1159,12 @@ impl Display for CodeBlock { f.write_str(" \n")?; } else { for (i, handler) in self.handlers.iter().enumerate() { - writeln!(f, - " {i:04}: Range: [{:04}, {:04}): Handler: {:04}, Stack: {:02}, Environment: {:02}", + writeln!( + f, + " {i:04}: Range: [{:04}, {:04}): Handler: {:04}, Environment: {:02}", handler.start, handler.end, handler.handler(), - handler.stack_count, handler.environment_count, )?; } diff --git a/core/engine/src/vm/flowgraph/mod.rs b/core/engine/src/vm/flowgraph/mod.rs index 88b34b065e5..1072d5fb2ce 100644 --- a/core/engine/src/vm/flowgraph/mod.rs +++ b/core/engine/src/vm/flowgraph/mod.rs @@ -67,15 +67,12 @@ impl CodeBlock { | Instruction::InstanceOf { .. } | Instruction::SetAccumulator { .. } | Instruction::SetFunctionName { .. } - | Instruction::ToNumeric { .. } | Instruction::Inc { .. } | Instruction::Dec { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Instruction::RotateLeft { .. } - | Instruction::RotateRight { .. } - | Instruction::CreateIteratorResult { .. } => { + Instruction::CreateIteratorResult { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } @@ -121,10 +118,10 @@ impl CodeBlock { EdgeStyle::Line, ); } - Instruction::JumpIfFalse { address } - | Instruction::JumpIfTrue { address } - | Instruction::JumpIfNotUndefined { address } - | Instruction::JumpIfNullOrUndefined { address } => { + Instruction::JumpIfFalse { address, .. } + | Instruction::JumpIfTrue { address, .. } + | Instruction::JumpIfNotUndefined { address, .. } + | Instruction::JumpIfNullOrUndefined { address, .. } => { graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None); graph.add_edge( previous_pc, @@ -145,20 +142,20 @@ impl CodeBlock { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Instruction::LogicalAnd { exit, .. } - | Instruction::LogicalOr { exit, .. } - | Instruction::Coalesce { exit, .. } => { + Instruction::LogicalAnd { address, .. } + | Instruction::LogicalOr { address, .. } + | Instruction::Coalesce { address, .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge( previous_pc, - exit as usize, + address as usize, Some("SHORT CIRCUIT".into()), Color::Red, EdgeStyle::Line, ); } - Instruction::Case { address } => { + Instruction::Case { address, .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge( previous_pc, @@ -175,19 +172,10 @@ impl CodeBlock { EdgeStyle::Line, ); } - Instruction::Default { address } => { - graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); - graph.add_edge( - previous_pc, - address as usize, - None, - Color::None, - EdgeStyle::Line, - ); - } Instruction::GeneratorDelegateNext { return_method_undefined, throw_method_undefined, + .. } => { graph.add_node( previous_pc, @@ -211,7 +199,7 @@ impl CodeBlock { EdgeStyle::Line, ); } - Instruction::GeneratorDelegateResume { r#return, exit } => { + Instruction::GeneratorDelegateResume { r#return, exit, .. } => { graph.add_node( previous_pc, NodeShape::Diamond, @@ -243,11 +231,11 @@ impl CodeBlock { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Instruction::JumpIfNotResumeKind { exit, .. } => { + Instruction::JumpIfNotResumeKind { address, .. } => { graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None); graph.add_edge( previous_pc, - exit as usize, + address as usize, Some("EXIT".into()), Color::Red, EdgeStyle::Line, @@ -290,6 +278,8 @@ impl CodeBlock { graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } Instruction::GetPropertyByName { .. } + | Instruction::GetPropertyByValue { .. } + | Instruction::GetPropertyByValuePush { .. } | Instruction::SetPropertyByName { .. } | Instruction::DefineOwnPropertyByName { .. } | Instruction::DefineClassStaticMethodByName { .. } @@ -328,7 +318,7 @@ impl CodeBlock { ); } } - Instruction::Throw | Instruction::ReThrow => { + Instruction::Throw { .. } | Instruction::ReThrow => { if let Some((i, handler)) = self.find_handler(previous_pc as u32) { graph.add_node(previous_pc, NodeShape::Record, label.into(), Color::None); graph.add_edge( @@ -367,103 +357,89 @@ impl CodeBlock { } } Instruction::Pop - | Instruction::Dup - | Instruction::Swap - | Instruction::PushZero - | Instruction::PushOne - | Instruction::PushNaN - | Instruction::PushPositiveInfinity - | Instruction::PushNegativeInfinity - | Instruction::PushNull - | Instruction::PushTrue - | Instruction::PushFalse - | Instruction::PushUndefined - | Instruction::PushEmptyObject + | Instruction::PushZero { .. } + | Instruction::PushOne { .. } + | Instruction::PushNaN { .. } + | Instruction::PushPositiveInfinity { .. } + | Instruction::PushNegativeInfinity { .. } + | Instruction::PushNull { .. } + | Instruction::PushTrue { .. } + | Instruction::PushFalse { .. } + | Instruction::PushUndefined { .. } + | Instruction::PushEmptyObject { .. } | Instruction::PushClassPrototype { .. } | Instruction::SetClassPrototype { .. } - | Instruction::SetHomeObject - | Instruction::TypeOf - | Instruction::Void - | Instruction::LogicalNot - | Instruction::Pos - | Instruction::Neg - | Instruction::GetPropertyByValue - | Instruction::GetPropertyByValuePush - | Instruction::SetPropertyByValue - | Instruction::DefineOwnPropertyByValue - | Instruction::DefineClassStaticMethodByValue - | Instruction::DefineClassMethodByValue - | Instruction::SetPropertyGetterByValue - | Instruction::DefineClassStaticGetterByValue - | Instruction::DefineClassGetterByValue - | Instruction::SetPropertySetterByValue - | Instruction::DefineClassStaticSetterByValue - | Instruction::DefineClassSetterByValue - | Instruction::DeletePropertyByValue + | Instruction::SetHomeObject { .. } + | Instruction::TypeOf { .. } + | Instruction::LogicalNot { .. } + | Instruction::Pos { .. } + | Instruction::Neg { .. } + | Instruction::SetPropertyByValue { .. } + | Instruction::DefineOwnPropertyByValue { .. } + | Instruction::DefineClassStaticMethodByValue { .. } + | Instruction::DefineClassMethodByValue { .. } + | Instruction::SetPropertyGetterByValue { .. } + | Instruction::DefineClassStaticGetterByValue { .. } + | Instruction::DefineClassGetterByValue { .. } + | Instruction::SetPropertySetterByValue { .. } + | Instruction::DefineClassStaticSetterByValue { .. } + | Instruction::DefineClassSetterByValue { .. } + | Instruction::DeletePropertyByValue { .. } | Instruction::DeleteSuperThrow - | Instruction::ToPropertyKey - | Instruction::ToBoolean - | Instruction::This + | Instruction::ToPropertyKey { .. } + | Instruction::This { .. } | Instruction::ThisForObjectEnvironmentName { .. } - | Instruction::Super + | Instruction::Super { .. } | Instruction::IncrementLoopIteration - | Instruction::CreateForInIterator - | Instruction::GetIterator - | Instruction::GetAsyncIterator + | Instruction::CreateForInIterator { .. } + | Instruction::GetIterator { .. } + | Instruction::GetAsyncIterator { .. } | Instruction::IteratorNext - | Instruction::IteratorNextWithoutPop - | Instruction::IteratorFinishAsyncNext - | Instruction::IteratorValue - | Instruction::IteratorValueWithoutPop - | Instruction::IteratorResult - | Instruction::IteratorDone - | Instruction::IteratorToArray - | Instruction::IteratorReturn - | Instruction::IteratorStackEmpty - | Instruction::RequireObjectCoercible - | Instruction::ValueNotNullOrUndefined - | Instruction::RestParameterInit - | Instruction::PushValueToArray - | Instruction::PushElisionToArray - | Instruction::PushIteratorToArray - | Instruction::PushNewArray - | Instruction::GeneratorYield - | Instruction::AsyncGeneratorYield + | Instruction::IteratorFinishAsyncNext { .. } + | Instruction::IteratorValue { .. } + | Instruction::IteratorResult { .. } + | Instruction::IteratorDone { .. } + | Instruction::IteratorToArray { .. } + | Instruction::IteratorReturn { .. } + | Instruction::IteratorStackEmpty { .. } + | Instruction::ValueNotNullOrUndefined { .. } + | Instruction::RestParameterInit { .. } + | Instruction::PushValueToArray { .. } + | Instruction::PushElisionToArray { .. } + | Instruction::PushIteratorToArray { .. } + | Instruction::PushNewArray { .. } + | Instruction::GeneratorYield { .. } + | Instruction::AsyncGeneratorYield { .. } | Instruction::AsyncGeneratorClose | Instruction::CreatePromiseCapability | Instruction::CompletePromiseCapability - | Instruction::GeneratorNext + | Instruction::GeneratorNext { .. } | Instruction::PushClassField { .. } | Instruction::SuperCallDerived - | Instruction::Await - | Instruction::NewTarget - | Instruction::ImportMeta + | Instruction::Await { .. } + | Instruction::NewTarget { .. } + | Instruction::ImportMeta { .. } | Instruction::CallEvalSpread { .. } | Instruction::CallSpread | Instruction::NewSpread | Instruction::SuperCallSpread - | Instruction::SuperCallPrepare - | Instruction::SetPrototype - | Instruction::IsObject - | Instruction::SetNameByLocator - | Instruction::PushObjectEnvironment + | Instruction::SuperCallPrepare { .. } + | Instruction::SetPrototype { .. } + | Instruction::IsObject { .. } + | Instruction::SetNameByLocator { .. } + | Instruction::PushObjectEnvironment { .. } | Instruction::PopPrivateEnvironment - | Instruction::ImportCall - | Instruction::GetAccumulator - | Instruction::SetAccumulatorFromStack - | Instruction::Exception - | Instruction::MaybeException + | Instruction::ImportCall { .. } + | Instruction::Exception { .. } + | Instruction::MaybeException { .. } | Instruction::CheckReturn - | Instruction::BindThisValue - | Instruction::CreateMappedArgumentsObject - | Instruction::CreateUnmappedArgumentsObject + | Instruction::BindThisValue { .. } + | Instruction::CreateMappedArgumentsObject { .. } + | Instruction::CreateUnmappedArgumentsObject { .. } | Instruction::CreateGlobalFunctionBinding { .. } | Instruction::CreateGlobalVarBinding { .. } - | Instruction::PopIntoRegister { .. } - | Instruction::PushFromRegister { .. } | Instruction::PopIntoLocal { .. } - | Instruction::PushFromLocal { .. } - | Instruction::Nop => { + | Instruction::PushFromLocal { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } @@ -518,7 +494,21 @@ impl CodeBlock { | Instruction::Reserved44 | Instruction::Reserved45 | Instruction::Reserved46 - | Instruction::Reserved47 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved47 + | Instruction::Reserved48 + | Instruction::Reserved49 + | Instruction::Reserved50 + | Instruction::Reserved51 + | Instruction::Reserved52 + | Instruction::Reserved53 + | Instruction::Reserved54 + | Instruction::Reserved55 + | Instruction::Reserved56 + | Instruction::Reserved57 + | Instruction::Reserved58 + | Instruction::Reserved59 + | Instruction::Reserved60 + | Instruction::Reserved61 => unreachable!("Reserved opcodes are unreachable"), } } diff --git a/core/engine/src/vm/mod.rs b/core/engine/src/vm/mod.rs index 84525665fcf..6402ca048fe 100644 --- a/core/engine/src/vm/mod.rs +++ b/core/engine/src/vm/mod.rs @@ -178,11 +178,8 @@ impl Vm { // since generator-like functions push the same call // frame with pre-built stack. if !frame.registers_already_pushed() { - let register_count = frame.code_block().register_count; - self.stack.resize_with( - current_stack_length + register_count as usize, - JsValue::undefined, - ); + self.stack + .resize_with(current_stack_length, JsValue::undefined); } // Keep carrying the last active runnable in case the current callframe @@ -232,13 +229,11 @@ impl Vm { let catch_address = handler.handler(); let environment_sp = frame.env_fp + handler.environment_count; - let sp = frame.rp + handler.stack_count; // Go to handler location. frame.pc = catch_address; self.environments.truncate(environment_sp as usize); - self.stack.truncate(sp as usize); true } @@ -313,9 +308,13 @@ impl Context { ); } - fn trace_execute_instruction(&mut self, f: F) -> JsResult + fn trace_execute_instruction( + &mut self, + f: F, + registers: &mut Registers, + ) -> JsResult where - F: FnOnce(Opcode, &mut Context) -> JsResult, + F: FnOnce(Opcode, &mut Registers, &mut Context) -> JsResult, { let frame = self.vm.frame(); let bytecodes = &frame.code_block.bytecode; @@ -343,7 +342,7 @@ impl Context { } let instant = Instant::now(); - let result = self.execute_instruction(f); + let result = self.execute_instruction(f, registers); let duration = instant.elapsed(); let fp = if self.vm.frames.is_empty() { @@ -395,9 +394,13 @@ impl Context { } impl Context { - fn execute_instruction(&mut self, f: F) -> JsResult + fn execute_instruction( + &mut self, + f: F, + registers: &mut Registers, + ) -> JsResult where - F: FnOnce(Opcode, &mut Context) -> JsResult, + F: FnOnce(Opcode, &mut Registers, &mut Context) -> JsResult, { let opcode: Opcode = { let _timer = Profiler::global().start_event("Opcode retrieval", "vm"); @@ -412,12 +415,12 @@ impl Context { let _timer = Profiler::global().start_event(opcode.as_instruction_str(), "vm"); - f(opcode, self) + f(opcode, registers, self) } - fn execute_one(&mut self, f: F) -> ControlFlow + fn execute_one(&mut self, f: F, registers: &mut Registers) -> ControlFlow where - F: FnOnce(Opcode, &mut Context) -> JsResult, + F: FnOnce(Opcode, &mut Registers, &mut Context) -> JsResult, { #[cfg(feature = "fuzz")] { @@ -431,13 +434,13 @@ impl Context { #[cfg(feature = "trace")] let result = if self.vm.trace || self.vm.frame().code_block.traceable() { - self.trace_execute_instruction(f) + self.trace_execute_instruction(f, registers) } else { - self.execute_instruction(f) + self.execute_instruction(f, registers) }; #[cfg(not(feature = "trace"))] - let result = self.execute_instruction(f); + let result = self.execute_instruction(f, registers); let result = match result { Ok(result) => result, @@ -454,7 +457,11 @@ impl Context { fp = self.vm.frame.fp() as usize; env_fp = self.vm.frame.env_fp as usize; - if self.vm.pop_frame().is_none() { + + if self.vm.pop_frame().is_some() { + registers + .pop_function(self.vm.frame().code_block().register_count as usize); + } else { break; } } @@ -492,7 +499,8 @@ impl Context { } self.vm.push(result); - self.vm.pop_frame(); + self.vm.pop_frame().expect("frame must exist"); + registers.pop_function(self.vm.frame().code_block().register_count as usize); } CompletionType::Throw => { let frame = self.vm.frame(); @@ -509,7 +517,8 @@ impl Context { )); } - self.vm.pop_frame(); + self.vm.pop_frame().expect("frame must exist"); + registers.pop_function(self.vm.frame().code_block().register_count as usize); loop { fp = self.vm.frame.fp(); @@ -530,7 +539,10 @@ impl Context { )); } - if self.vm.pop_frame().is_none() { + if self.vm.pop_frame().is_some() { + registers + .pop_function(self.vm.frame().code_block().register_count as usize); + } else { break; } } @@ -545,7 +557,8 @@ impl Context { } self.vm.push(result); - self.vm.pop_frame(); + self.vm.pop_frame().expect("frame must exist"); + registers.pop_function(self.vm.frame().code_block().register_count as usize); } } @@ -555,7 +568,11 @@ impl Context { /// Runs the current frame to completion, yielding to the caller each time `budget` /// "clock cycles" have passed. #[allow(clippy::future_not_send)] - pub(crate) async fn run_async_with_budget(&mut self, budget: u32) -> CompletionRecord { + pub(crate) async fn run_async_with_budget( + &mut self, + budget: u32, + registers: &mut Registers, + ) -> CompletionRecord { let _timer = Profiler::global().start_event("run_async_with_budget", "vm"); #[cfg(feature = "trace")] @@ -566,9 +583,12 @@ impl Context { let mut runtime_budget: u32 = budget; loop { - match self.execute_one(|opcode, context| { - opcode.spend_budget_and_execute(context, &mut runtime_budget) - }) { + match self.execute_one( + |opcode, registers, context| { + opcode.spend_budget_and_execute(registers, context, &mut runtime_budget) + }, + registers, + ) { ControlFlow::Continue(()) => {} ControlFlow::Break(record) => return record, } @@ -580,7 +600,7 @@ impl Context { } } - pub(crate) fn run(&mut self) -> CompletionRecord { + pub(crate) fn run(&mut self, registers: &mut Registers) -> CompletionRecord { let _timer = Profiler::global().start_event("run", "vm"); #[cfg(feature = "trace")] @@ -589,7 +609,7 @@ impl Context { } loop { - match self.execute_one(Opcode::execute) { + match self.execute_one(Opcode::execute, registers) { ControlFlow::Continue(()) => {} ControlFlow::Break(value) => return value, } @@ -635,3 +655,49 @@ fn yield_now() -> impl Future { YieldNow(false) } + +#[derive(Debug, Default, Trace, Finalize)] +pub(crate) struct Registers { + rp: usize, + registers: Vec, +} + +impl Registers { + pub(crate) fn new(register_count: usize) -> Self { + let mut registers = Vec::with_capacity(register_count); + registers.resize(register_count, JsValue::undefined()); + + Self { rp: 0, registers } + } + + pub(crate) fn push_function(&mut self, register_count: usize) { + self.rp = self.registers.len(); + self.registers + .resize(self.rp + register_count, JsValue::undefined()); + } + + #[track_caller] + pub(crate) fn pop_function(&mut self, register_count: usize) { + self.registers.truncate(self.rp); + self.rp -= register_count; + } + + #[track_caller] + pub(crate) fn set(&mut self, index: u32, value: JsValue) { + self.registers[self.rp + index as usize] = value; + } + + #[track_caller] + pub(crate) fn get(&self, index: u32) -> &JsValue { + self.registers + .get(self.rp + index as usize) + .expect("registers must be initialized") + } + + pub(crate) fn clone_current_frame(&self) -> Self { + Self { + rp: 0, + registers: self.registers[self.rp..].to_vec(), + } + } +} diff --git a/core/engine/src/vm/opcode/arguments.rs b/core/engine/src/vm/opcode/arguments.rs index 957d30b1dc7..3f0155258b4 100644 --- a/core/engine/src/vm/opcode/arguments.rs +++ b/core/engine/src/vm/opcode/arguments.rs @@ -4,7 +4,7 @@ use crate::{ Context, JsResult, }; -use super::Operation; +use super::{Operation, Registers}; /// `CreateMappedArgumentsObject` implements the Opcode Operation for `Opcode::CreateMappedArgumentsObject` /// @@ -13,12 +13,13 @@ use super::Operation; #[derive(Debug, Clone, Copy)] pub(crate) struct CreateMappedArgumentsObject; -impl Operation for CreateMappedArgumentsObject { - const NAME: &'static str = "CreateMappedArgumentsObject"; - const INSTRUCTION: &'static str = "INST - CreateMappedArgumentsObject"; - const COST: u8 = 8; - - fn execute(context: &mut Context) -> JsResult { +impl CreateMappedArgumentsObject { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let frame = context.vm.frame(); let function_object = frame .function(&context.vm) @@ -26,7 +27,6 @@ impl Operation for CreateMappedArgumentsObject { .expect("there should be a function object"); let code = frame.code_block().clone(); let args = frame.arguments(&context.vm).to_vec(); - let env = context .vm .environments @@ -39,11 +39,32 @@ impl Operation for CreateMappedArgumentsObject { env, context, ); - context.vm.push(arguments); + registers.set(value, arguments.into()); Ok(CompletionType::Normal) } } +impl Operation for CreateMappedArgumentsObject { + const NAME: &'static str = "CreateMappedArgumentsObject"; + const INSTRUCTION: &'static str = "INST - CreateMappedArgumentsObject"; + const COST: u8 = 8; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} + /// `CreateUnmappedArgumentsObject` implements the Opcode Operation for `Opcode::CreateUnmappedArgumentsObject` /// /// Operation: @@ -51,15 +72,37 @@ impl Operation for CreateMappedArgumentsObject { #[derive(Debug, Clone, Copy)] pub(crate) struct CreateUnmappedArgumentsObject; +impl CreateUnmappedArgumentsObject { + #[allow(clippy::unnecessary_wraps)] + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let args = context.vm.frame().arguments(&context.vm).to_vec(); + let arguments = UnmappedArguments::new(&args, context); + registers.set(dst, arguments.into()); + Ok(CompletionType::Normal) + } +} + impl Operation for CreateUnmappedArgumentsObject { const NAME: &'static str = "CreateUnmappedArgumentsObject"; const INSTRUCTION: &'static str = "INST - CreateUnmappedArgumentsObject"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let args = context.vm.frame().arguments(&context.vm).to_vec(); - let arguments = UnmappedArguments::new(&args, context); - context.vm.push(arguments); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) } } diff --git a/core/engine/src/vm/opcode/await/mod.rs b/core/engine/src/vm/opcode/await/mod.rs index 45f2df900fb..dc04e7d3740 100644 --- a/core/engine/src/vm/opcode/await/mod.rs +++ b/core/engine/src/vm/opcode/await/mod.rs @@ -10,7 +10,7 @@ use crate::{ js_string, native_function::NativeFunction, object::FunctionObjectBuilder, - vm::{opcode::Operation, CompletionType, GeneratorResumeKind}, + vm::{opcode::Operation, CompletionType, GeneratorResumeKind, Registers}, Context, JsArgs, JsResult, JsValue, }; @@ -21,32 +21,32 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct Await; -impl Operation for Await { - const NAME: &'static str = "Await"; - const INSTRUCTION: &'static str = "INST - Await"; - const COST: u8 = 5; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); +impl Await { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); // 2. Let promise be ? PromiseResolve(%Promise%, value). let promise = Promise::promise_resolve( &context.intrinsics().constructors().promise().constructor(), - value, + value.clone(), context, )?; let return_value = context .vm .frame() - .promise_capability(&context.vm.stack) + .promise_capability(registers) .as_ref() .map(PromiseCapability::promise) .cloned() .map(JsValue::from) .unwrap_or_default(); - let gen = GeneratorContext::from_current(context); + let gen = GeneratorContext::from_current(context, registers.clone_current_frame(), None); let captures = Gc::new(Cell::new(Some(gen))); @@ -142,6 +142,27 @@ impl Operation for Await { } } +impl Operation for Await { + const NAME: &'static str = "Await"; + const INSTRUCTION: &'static str = "INST - Await"; + const COST: u8 = 5; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} + /// `CreatePromiseCapability` implements the Opcode Operation for `Opcode::CreatePromiseCapability` /// /// Operation: @@ -154,13 +175,8 @@ impl Operation for CreatePromiseCapability { const INSTRUCTION: &'static str = "INST - CreatePromiseCapability"; const COST: u8 = 8; - fn execute(context: &mut Context) -> JsResult { - if context - .vm - .frame() - .promise_capability(&context.vm.stack) - .is_some() - { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + if context.vm.frame().promise_capability(registers).is_some() { return Ok(CompletionType::Normal); } @@ -173,7 +189,7 @@ impl Operation for CreatePromiseCapability { context .vm .frame - .set_promise_capability(&mut context.vm.stack, Some(&promise_capability)); + .set_promise_capability(registers, Some(&promise_capability)); Ok(CompletionType::Normal) } } @@ -190,11 +206,10 @@ impl Operation for CompletePromiseCapability { const INSTRUCTION: &'static str = "INST - CompletePromiseCapability"; const COST: u8 = 8; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { // If the current executing function is an async function we have to resolve/reject it's promise at the end. // The relevant spec section is 3. in [AsyncBlockStart](https://tc39.es/ecma262/#sec-asyncblockstart). - let Some(promise_capability) = context.vm.frame().promise_capability(&context.vm.stack) - else { + let Some(promise_capability) = context.vm.frame().promise_capability(registers) else { return if context.vm.pending_exception.is_some() { Ok(CompletionType::Throw) } else { diff --git a/core/engine/src/vm/opcode/binary_ops/logical.rs b/core/engine/src/vm/opcode/binary_ops/logical.rs index b0892ec34ec..d3689f96e98 100644 --- a/core/engine/src/vm/opcode/binary_ops/logical.rs +++ b/core/engine/src/vm/opcode/binary_ops/logical.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -16,14 +16,10 @@ impl LogicalAnd { fn operation( exit: u32, lhs: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let lhs = context - .vm - .frame() - .read_value::<0>(operand_types, lhs, &context.vm); - + let lhs = registers.get(lhs); if !lhs.to_boolean() { context.vm.frame_mut().pc = exit; } @@ -36,23 +32,22 @@ impl Operation for LogicalAnd { const INSTRUCTION: &'static str = "INST - LogicalAnd"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = context.vm.read::().into(); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = context.vm.read::().into(); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = context.vm.read::(); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } } @@ -69,14 +64,10 @@ impl LogicalOr { fn operation( exit: u32, lhs: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let lhs = context - .vm - .frame() - .read_value::<0>(operand_types, lhs, &context.vm); - + let lhs = registers.get(lhs); if lhs.to_boolean() { context.vm.frame_mut().pc = exit; } @@ -89,23 +80,22 @@ impl Operation for LogicalOr { const INSTRUCTION: &'static str = "INST - LogicalOr"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = u32::from(context.vm.read::()); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = u32::from(context.vm.read::()); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = context.vm.read::(); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } } @@ -122,14 +112,10 @@ impl Coalesce { fn operation( exit: u32, lhs: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let lhs = context - .vm - .frame() - .read_value::<0>(operand_types, lhs, &context.vm); - + let lhs = registers.get(lhs); if !lhs.is_null_or_undefined() { context.vm.frame_mut().pc = exit; } @@ -142,22 +128,21 @@ impl Operation for Coalesce { const INSTRUCTION: &'static str = "INST - Coalesce"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = u32::from(context.vm.read::()); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = u32::from(context.vm.read::()); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let lhs = context.vm.read::(); - Self::operation(exit, lhs, operand_types, context) + Self::operation(exit, lhs, registers, context) } } diff --git a/core/engine/src/vm/opcode/binary_ops/macro_defined.rs b/core/engine/src/vm/opcode/binary_ops/macro_defined.rs index 75e416505dc..3d2c0d18773 100644 --- a/core/engine/src/vm/opcode/binary_ops/macro_defined.rs +++ b/core/engine/src/vm/opcode/binary_ops/macro_defined.rs @@ -1,6 +1,6 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, - Context, JsResult, JsValue, + vm::{opcode::Operation, CompletionType, Registers}, + Context, JsResult, }; macro_rules! implement_bin_ops { @@ -15,20 +15,16 @@ macro_rules! implement_bin_ops { impl $name { #[allow(clippy::needless_pass_by_value)] fn operation( - output: u32, + dst: u32, lhs: u32, rhs: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - - let lhs = context.vm.frame().read_value::<0>(operand_types, lhs, &context.vm); - let rhs = context.vm.frame().read_value::<1>(operand_types, rhs, &context.vm); - + let lhs = registers.get(lhs); + let rhs = registers.get(rhs); let value = lhs.$op(&rhs, context)?; - - context.vm.stack[(rp + output) as usize] = JsValue::from(value); + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -38,28 +34,25 @@ macro_rules! implement_bin_ops { const INSTRUCTION: &'static str = stringify!("INST - " + $name); const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::().into(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::().into(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let lhs = context.vm.read::(); let rhs = context.vm.read::(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } } }; diff --git a/core/engine/src/vm/opcode/binary_ops/mod.rs b/core/engine/src/vm/opcode/binary_ops/mod.rs index 947cd47194a..9ef90833404 100644 --- a/core/engine/src/vm/opcode/binary_ops/mod.rs +++ b/core/engine/src/vm/opcode/binary_ops/mod.rs @@ -1,7 +1,7 @@ use crate::{ error::JsNativeError, - vm::{opcode::Operation, CompletionType}, - Context, JsResult, JsValue, + vm::{opcode::Operation, CompletionType, Registers}, + Context, JsResult, }; pub(crate) mod logical; @@ -20,26 +20,16 @@ pub(crate) struct NotEq; impl NotEq { #[allow(clippy::needless_pass_by_value)] fn operation( - output: u32, + dst: u32, lhs: u32, rhs: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - - let lhs = context - .vm - .frame() - .read_value::<0>(operand_types, lhs, &context.vm); - let rhs = context - .vm - .frame() - .read_value::<1>(operand_types, rhs, &context.vm); - - let value = !lhs.equals(&rhs, context)?; - - context.vm.stack[(rp + output) as usize] = JsValue::from(value); + let lhs = registers.get(lhs); + let rhs = registers.get(rhs); + let value = !lhs.equals(rhs, context)?; + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -49,28 +39,25 @@ impl Operation for NotEq { const INSTRUCTION: &'static str = "INST - NotEq"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::().into(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::().into(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let lhs = context.vm.read::(); let rhs = context.vm.read::(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } } @@ -85,26 +72,16 @@ impl StrictEq { #[allow(clippy::unnecessary_wraps)] #[allow(clippy::needless_pass_by_value)] fn operation( - output: u32, + dst: u32, lhs: u32, rhs: u32, - operand_types: u8, - context: &mut Context, + registers: &mut Registers, + _: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - - let lhs = context - .vm - .frame() - .read_value::<0>(operand_types, lhs, &context.vm); - let rhs = context - .vm - .frame() - .read_value::<1>(operand_types, rhs, &context.vm); - - let value = lhs.strict_equals(&rhs); - - context.vm.stack[(rp + output) as usize] = JsValue::from(value); + let lhs = registers.get(lhs); + let rhs = registers.get(rhs); + let value = lhs.strict_equals(rhs); + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -114,28 +91,25 @@ impl Operation for StrictEq { const INSTRUCTION: &'static str = "INST - StrictEq"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = u32::from(context.vm.read::()); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = u32::from(context.vm.read::()); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let lhs = context.vm.read::(); let rhs = context.vm.read::(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } } @@ -150,26 +124,16 @@ impl StrictNotEq { #[allow(clippy::unnecessary_wraps)] #[allow(clippy::needless_pass_by_value)] fn operation( - output: u32, + dst: u32, lhs: u32, rhs: u32, - operand_types: u8, - context: &mut Context, + registers: &mut Registers, + _: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - - let lhs = context - .vm - .frame() - .read_value::<0>(operand_types, lhs, &context.vm); - let rhs = context - .vm - .frame() - .read_value::<1>(operand_types, rhs, &context.vm); - - let value = !lhs.strict_equals(&rhs); - - context.vm.stack[(rp + output) as usize] = JsValue::from(value); + let lhs = registers.get(lhs); + let rhs = registers.get(rhs); + let value = !lhs.strict_equals(rhs); + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -179,28 +143,25 @@ impl Operation for StrictNotEq { const INSTRUCTION: &'static str = "INST - StrictNotEq"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = u32::from(context.vm.read::()); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = u32::from(context.vm.read::()); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let lhs = context.vm.read::(); let rhs = context.vm.read::(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } } @@ -214,18 +175,13 @@ pub(crate) struct In; impl In { #[allow(clippy::needless_pass_by_value)] fn operation( - output: u32, + dst: u32, lhs: u32, rhs: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - let rhs = context - .vm - .frame() - .read_value::<1>(operand_types, rhs, &context.vm); - + let rhs = registers.get(rhs); let Some(rhs) = rhs.as_object() else { return Err(JsNativeError::typ() .with_message(format!( @@ -234,14 +190,10 @@ impl In { )) .into()); }; - - let lhs = context - .vm - .frame() - .read_value::<0>(operand_types, lhs, &context.vm); + let lhs = registers.get(lhs); let key = lhs.to_property_key(context)?; let value = rhs.has_property(key, context)?; - context.vm.stack[(rp + output) as usize] = JsValue::from(value); + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -251,28 +203,25 @@ impl Operation for In { const INSTRUCTION: &'static str = "INST - In"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = u32::from(context.vm.read::()); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = u32::from(context.vm.read::()); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); let lhs = context.vm.read::().into(); let rhs = context.vm.read::().into(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let output = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let lhs = context.vm.read::(); let rhs = context.vm.read::(); - Self::operation(output, lhs, rhs, operand_types, context) + Self::operation(dst, lhs, rhs, registers, context) } } @@ -289,15 +238,11 @@ impl InPrivate { dst: u32, index: usize, rhs: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { let name = context.vm.frame().code_block().constant_string(index); - let rp = context.vm.frame().rp; - let rhs = context - .vm - .frame() - .read_value::<0>(operand_types, rhs, &context.vm); + let rhs = registers.get(rhs); let Some(rhs) = rhs.as_object() else { return Err(JsNativeError::typ() @@ -316,7 +261,7 @@ impl InPrivate { let value = rhs.private_element_find(&name, true, true).is_some(); - context.vm.stack[(rp + dst) as usize] = JsValue::from(value); + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -326,27 +271,24 @@ impl Operation for InPrivate { const INSTRUCTION: &'static str = "INST - InPrivate"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); let index = context.vm.read::() as usize; let rhs = context.vm.read::().into(); - Self::operation(dst, index, rhs, operand_types, context) + Self::operation(dst, index, rhs, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); let index = context.vm.read::() as usize; let rhs = context.vm.read::().into(); - Self::operation(dst, index, rhs, operand_types, context) + Self::operation(dst, index, rhs, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); let index = context.vm.read::() as usize; let rhs = context.vm.read::(); - Self::operation(dst, index, rhs, operand_types, context) + Self::operation(dst, index, rhs, registers, context) } } diff --git a/core/engine/src/vm/opcode/call/mod.rs b/core/engine/src/vm/opcode/call/mod.rs index 7db445a0cd9..51869df728a 100644 --- a/core/engine/src/vm/opcode/call/mod.rs +++ b/core/engine/src/vm/opcode/call/mod.rs @@ -3,7 +3,7 @@ use crate::{ error::JsNativeError, module::{ModuleKind, Referrer}, object::FunctionObjectBuilder, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsObject, JsResult, JsValue, NativeFunction, }; @@ -16,6 +16,7 @@ pub(crate) struct CallEval; impl CallEval { fn operation( + registers: &mut Registers, context: &mut Context, argument_count: usize, scope_index: usize, @@ -66,7 +67,9 @@ impl CallEval { return Ok(CompletionType::Normal); } - object.__call__(argument_count).resolve(context)?; + if let Some(register_count) = object.__call__(argument_count).resolve(context)? { + registers.push_function(register_count); + } Ok(CompletionType::Normal) } } @@ -76,22 +79,32 @@ impl Operation for CallEval { const INSTRUCTION: &'static str = "INST - CallEval"; const COST: u8 = 5; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::(); let scope_index = context.vm.read::(); - Self::operation(context, argument_count as usize, scope_index as usize) + Self::operation( + registers, + context, + argument_count as usize, + scope_index as usize, + ) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::() as usize; let scope_index = context.vm.read::(); - Self::operation(context, argument_count, scope_index as usize) + Self::operation(registers, context, argument_count, scope_index as usize) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::(); let scope_index = context.vm.read::(); - Self::operation(context, argument_count as usize, scope_index as usize) + Self::operation( + registers, + context, + argument_count as usize, + scope_index as usize, + ) } } @@ -103,7 +116,11 @@ impl Operation for CallEval { pub(crate) struct CallEvalSpread; impl CallEvalSpread { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + registers: &mut Registers, + context: &mut Context, + index: usize, + ) -> JsResult { // Get the arguments that are stored as an array object on the stack. let arguments_array = context.vm.pop(); let arguments_array_object = arguments_array @@ -163,7 +180,9 @@ impl CallEvalSpread { let argument_count = arguments.len(); context.vm.push_values(&arguments); - object.__call__(argument_count).resolve(context)?; + if let Some(register_count) = object.__call__(argument_count).resolve(context)? { + registers.push_function(register_count); + } Ok(CompletionType::Normal) } } @@ -173,19 +192,19 @@ impl Operation for CallEvalSpread { const INSTRUCTION: &'static str = "INST - CallEvalSpread"; const COST: u8 = 5; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::(); - Self::operation(context, index as usize) + Self::operation(registers, context, index as usize) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::(); - Self::operation(context, index as usize) + Self::operation(registers, context, index as usize) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::(); - Self::operation(context, index as usize) + Self::operation(registers, context, index as usize) } } @@ -197,7 +216,11 @@ impl Operation for CallEvalSpread { pub(crate) struct Call; impl Call { - fn operation(context: &mut Context, argument_count: usize) -> JsResult { + fn operation( + registers: &mut Registers, + context: &mut Context, + argument_count: usize, + ) -> JsResult { let at = context.vm.stack.len() - argument_count; let func = &context.vm.stack[at - 1]; @@ -207,7 +230,10 @@ impl Call { .into()); }; - object.__call__(argument_count).resolve(context)?; + if let Some(register_count) = object.__call__(argument_count).resolve(context)? { + registers.push_function(register_count); + } + Ok(CompletionType::Normal) } } @@ -217,19 +243,19 @@ impl Operation for Call { const INSTRUCTION: &'static str = "INST - Call"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::(); - Self::operation(context, argument_count as usize) + Self::operation(registers, context, argument_count as usize) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::() as usize; - Self::operation(context, argument_count) + Self::operation(registers, context, argument_count) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::(); - Self::operation(context, argument_count as usize) + Self::operation(registers, context, argument_count as usize) } } @@ -241,7 +267,7 @@ impl Operation for CallSpread { const INSTRUCTION: &'static str = "INST - CallSpread"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { // Get the arguments that are stored as an array object on the stack. let arguments_array = context.vm.pop(); let arguments_array_object = arguments_array @@ -265,7 +291,9 @@ impl Operation for CallSpread { .into()); }; - object.__call__(argument_count).resolve(context)?; + if let Some(register_count) = object.__call__(argument_count).resolve(context)? { + registers.push_function(register_count); + } Ok(CompletionType::Normal) } } @@ -277,12 +305,12 @@ impl Operation for CallSpread { #[derive(Debug, Clone, Copy)] pub(crate) struct ImportCall; -impl Operation for ImportCall { - const NAME: &'static str = "ImportCall"; - const INSTRUCTION: &'static str = "INST - ImportCall"; - const COST: u8 = 15; - - fn execute(context: &mut Context) -> JsResult { +impl ImportCall { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { // Import Calls // Runtime Semantics: Evaluation // https://tc39.es/ecma262/#sec-import-call-runtime-semantics-evaluation @@ -295,7 +323,7 @@ impl Operation for ImportCall { // 3. Let argRef be ? Evaluation of AssignmentExpression. // 4. Let specifier be ? GetValue(argRef). - let arg = context.vm.pop(); + let arg = registers.get(value); // 5. Let promiseCapability be ! NewPromiseCapability(%Promise%). let cap = PromiseCapability::new( @@ -479,8 +507,28 @@ impl Operation for ImportCall { }; // 9. Return promiseCapability.[[Promise]]. - context.vm.push(promise); - + registers.set(value, promise.into()); Ok(CompletionType::Normal) } } + +impl Operation for ImportCall { + const NAME: &'static str = "ImportCall"; + const INSTRUCTION: &'static str = "INST - ImportCall"; + const COST: u8 = 15; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/concat/mod.rs b/core/engine/src/vm/opcode/concat/mod.rs index 2bcc3c55059..09b39862817 100644 --- a/core/engine/src/vm/opcode/concat/mod.rs +++ b/core/engine/src/vm/opcode/concat/mod.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsString, }; @@ -11,12 +11,17 @@ use crate::{ pub(crate) struct ConcatToString; impl ConcatToString { - fn operation(context: &mut Context, value_count: usize) -> JsResult { - let mut strings = Vec::with_capacity(value_count); - for _ in 0..value_count { - strings.push(context.vm.pop().to_string(context)?); + fn operation( + string: u32, + values: &[u32], + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let mut strings = Vec::with_capacity(values.len()); + for value in values { + let val = registers.get(*value); + strings.push(val.to_string(context)?); } - strings.reverse(); let s = JsString::concat_array( &strings .iter() @@ -24,7 +29,7 @@ impl ConcatToString { .map(Into::into) .collect::>(), ); - context.vm.push(s); + registers.set(string, s.into()); Ok(CompletionType::Normal) } } @@ -34,18 +39,33 @@ impl Operation for ConcatToString { const INSTRUCTION: &'static str = "INST - ConcatToString"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let string = context.vm.read::().into(); let value_count = context.vm.read::() as usize; - Self::operation(context, value_count) + let mut values = Vec::with_capacity(value_count); + for _ in 0..value_count { + values.push(context.vm.read::().into()); + } + Self::operation(string, &values, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let string = context.vm.read::().into(); let value_count = context.vm.read::() as usize; - Self::operation(context, value_count) + let mut values = Vec::with_capacity(value_count); + for _ in 0..value_count { + values.push(context.vm.read::().into()); + } + Self::operation(string, &values, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let string = context.vm.read::(); let value_count = context.vm.read::() as usize; - Self::operation(context, value_count) + let mut values = Vec::with_capacity(value_count); + for _ in 0..value_count { + values.push(context.vm.read::()); + } + Self::operation(string, &values, registers, context) } } diff --git a/core/engine/src/vm/opcode/control_flow/jump.rs b/core/engine/src/vm/opcode/control_flow/jump.rs index 5ed13643b5d..173e967e7b4 100644 --- a/core/engine/src/vm/opcode/control_flow/jump.rs +++ b/core/engine/src/vm/opcode/control_flow/jump.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -15,7 +15,7 @@ impl Operation for Jump { const INSTRUCTION: &'static str = "INST - Jump"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let address = context.vm.read::(); context.vm.frame_mut().pc = address; Ok(CompletionType::Normal) @@ -29,17 +29,43 @@ impl Operation for Jump { #[derive(Debug, Clone, Copy)] pub(crate) struct JumpIfTrue; +impl JumpIfTrue { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + address: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + if value.to_boolean() { + context.vm.frame_mut().pc = address; + } + Ok(CompletionType::Normal) + } +} + impl Operation for JumpIfTrue { const NAME: &'static str = "JumpIfTrue"; const INSTRUCTION: &'static str = "INST - JumpIfTrue"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let address = context.vm.read::(); - if context.vm.pop().to_boolean() { - context.vm.frame_mut().pc = address; - } - Ok(CompletionType::Normal) + let value = context.vm.read::().into(); + Self::operation(value, address, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::().into(); + Self::operation(value, address, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::(); + Self::operation(value, address, registers, context) } } @@ -50,17 +76,43 @@ impl Operation for JumpIfTrue { #[derive(Debug, Clone, Copy)] pub(crate) struct JumpIfFalse; +impl JumpIfFalse { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + address: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + if !value.to_boolean() { + context.vm.frame_mut().pc = address; + } + Ok(CompletionType::Normal) + } +} + impl Operation for JumpIfFalse { const NAME: &'static str = "JumpIfFalse"; const INSTRUCTION: &'static str = "INST - JumpIfFalse"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let address = context.vm.read::(); - if !context.vm.pop().to_boolean() { - context.vm.frame_mut().pc = address; - } - Ok(CompletionType::Normal) + let value = context.vm.read::().into(); + Self::operation(value, address, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::().into(); + Self::operation(value, address, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::(); + Self::operation(value, address, registers, context) } } @@ -71,19 +123,43 @@ impl Operation for JumpIfFalse { #[derive(Debug, Clone, Copy)] pub(crate) struct JumpIfNotUndefined; +impl JumpIfNotUndefined { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + address: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + if !value.is_undefined() { + context.vm.frame_mut().pc = address; + } + Ok(CompletionType::Normal) + } +} + impl Operation for JumpIfNotUndefined { const NAME: &'static str = "JumpIfNotUndefined"; const INSTRUCTION: &'static str = "INST - JumpIfNotUndefined"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let address = context.vm.read::(); - let value = context.vm.pop(); - if !value.is_undefined() { - context.vm.frame_mut().pc = address; - context.vm.push(value); - } - Ok(CompletionType::Normal) + let value = context.vm.read::().into(); + Self::operation(value, address, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::().into(); + Self::operation(value, address, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::(); + Self::operation(value, address, registers, context) } } @@ -94,20 +170,43 @@ impl Operation for JumpIfNotUndefined { #[derive(Debug, Clone, Copy)] pub(crate) struct JumpIfNullOrUndefined; +impl JumpIfNullOrUndefined { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + address: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + if value.is_null_or_undefined() { + context.vm.frame_mut().pc = address; + } + Ok(CompletionType::Normal) + } +} + impl Operation for JumpIfNullOrUndefined { const NAME: &'static str = "JumpIfNullOrUndefined"; const INSTRUCTION: &'static str = "INST - JumpIfNullOrUndefined"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let address = context.vm.read::(); - let value = context.vm.pop(); - if value.is_null_or_undefined() { - context.vm.frame_mut().pc = address; - } else { - context.vm.push(value); - } - Ok(CompletionType::Normal) + let value = context.vm.read::().into(); + Self::operation(value, address, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::().into(); + Self::operation(value, address, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::(); + Self::operation(value, address, registers, context) } } @@ -123,7 +222,7 @@ impl Operation for JumpTable { const INSTRUCTION: &'static str = "INST - JumpTable"; const COST: u8 = 5; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let default = context.vm.read::(); let count = context.vm.read::(); diff --git a/core/engine/src/vm/opcode/control_flow/return.rs b/core/engine/src/vm/opcode/control_flow/return.rs index 32a3a47bbf8..f380869d3f7 100644 --- a/core/engine/src/vm/opcode/control_flow/return.rs +++ b/core/engine/src/vm/opcode/control_flow/return.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsNativeError, JsResult, }; @@ -15,7 +15,7 @@ impl Operation for Return { const INSTRUCTION: &'static str = "INST - Return"; const COST: u8 = 4; - fn execute(_context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, _: &mut Context) -> JsResult { Ok(CompletionType::Return) } } @@ -32,7 +32,7 @@ impl Operation for CheckReturn { const INSTRUCTION: &'static str = "INST - CheckReturn"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let frame = context.vm.frame(); if !frame.construct() { return Ok(CompletionType::Normal); @@ -77,44 +77,6 @@ impl Operation for CheckReturn { } } -/// `GetAccumulator` implements the Opcode Operation for `Opcode::GetAccumulator` -/// -/// Operation: -/// - Gets the accumulator value, which is the implicit return value of a function. -#[derive(Debug, Clone, Copy)] -pub(crate) struct GetAccumulator; - -impl Operation for GetAccumulator { - const NAME: &'static str = "GetAccumulator"; - const INSTRUCTION: &'static str = "INST - GetAccumulator"; - const COST: u8 = 2; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.get_return_value(); - context.vm.push(value); - Ok(CompletionType::Normal) - } -} - -/// `SetAccumulatorFromStack` implements the Opcode Operation for `Opcode::SetAccumulatorFromStack` -/// -/// Operation: -/// - Sets the accumulator value, which is the implicit return value of a function. -#[derive(Debug, Clone, Copy)] -pub(crate) struct SetAccumulatorFromStack; - -impl Operation for SetAccumulatorFromStack { - const NAME: &'static str = "SetAccumulatorFromStack"; - const INSTRUCTION: &'static str = "INST - SetAccumulatorFromStack"; - const COST: u8 = 2; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - context.vm.set_return_value(value); - Ok(CompletionType::Normal) - } -} - /// `SetAccumulator` implements the Opcode Operation for `Opcode::SetAccumulator` /// /// Operation: @@ -124,13 +86,13 @@ pub(crate) struct SetAccumulator; impl SetAccumulator { #[allow(clippy::unnecessary_wraps)] - fn operation(register: u32, context: &mut Context) -> JsResult { - let value = context - .vm - .frame() - .register(register, &context.vm.stack) - .clone(); - context.vm.set_return_value(value); + fn operation( + register: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(register); + context.vm.set_return_value(value.clone()); Ok(CompletionType::Normal) } } @@ -140,19 +102,19 @@ impl Operation for SetAccumulator { const INSTRUCTION: &'static str = "INST - SetAccumulator"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let register = u32::from(context.vm.read::()); - Self::operation(register, context) + Self::operation(register, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let register = u32::from(context.vm.read::()); - Self::operation(register, context) + Self::operation(register, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let register = context.vm.read::(); - Self::operation(register, context) + Self::operation(register, registers, context) } } @@ -166,19 +128,9 @@ pub(crate) struct Move; impl Move { #[allow(clippy::unnecessary_wraps)] #[allow(clippy::needless_pass_by_value)] - fn operation( - dst: u32, - src: u32, - operand_types: u8, - context: &mut Context, - ) -> JsResult { - let rp = context.vm.frame().rp; - let value = context - .vm - .frame() - .read_value::<0>(operand_types, src, &context.vm); - - context.vm.stack[(rp + dst) as usize] = value; + fn operation(dst: u32, src: u32, registers: &mut Registers) -> JsResult { + let value = registers.get(src); + registers.set(dst, value.clone()); Ok(CompletionType::Normal) } } @@ -188,25 +140,22 @@ impl Operation for Move { const INSTRUCTION: &'static str = "INST - Move"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::().into(); let src = context.vm.read::().into(); - Self::operation(dst, src, operand_types, context) + Self::operation(dst, src, registers) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::().into(); let src = context.vm.read::().into(); - Self::operation(dst, src, operand_types, context) + Self::operation(dst, src, registers) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); let src = context.vm.read::(); - Self::operation(dst, src, operand_types, context) + Self::operation(dst, src, registers) } } @@ -217,11 +166,12 @@ pub(crate) struct PopIntoRegister; impl PopIntoRegister { #[allow(clippy::unnecessary_wraps)] #[allow(clippy::needless_pass_by_value)] - fn operation(dst: u32, context: &mut Context) -> JsResult { - let value = context.vm.pop(); - - let rp = context.vm.frame().rp; - context.vm.stack[(rp + dst) as usize] = value; + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + registers.set(dst, context.vm.pop()); Ok(CompletionType::Normal) } } @@ -231,19 +181,19 @@ impl Operation for PopIntoRegister { const INSTRUCTION: &'static str = "INST - PopIntoRegister"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); - Self::operation(dst, context) + Self::operation(dst, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); - Self::operation(dst, context) + Self::operation(dst, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); - Self::operation(dst, context) + Self::operation(dst, registers, context) } } @@ -254,10 +204,13 @@ pub(crate) struct PushFromRegister; impl PushFromRegister { #[allow(clippy::unnecessary_wraps)] #[allow(clippy::needless_pass_by_value)] - fn operation(dst: u32, context: &mut Context) -> JsResult { - let rp = context.vm.frame().rp; - let value = context.vm.stack[(rp + dst) as usize].clone(); - context.vm.push(value); + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(dst); + context.vm.push(value.clone()); Ok(CompletionType::Normal) } } @@ -267,19 +220,19 @@ impl Operation for PushFromRegister { const INSTRUCTION: &'static str = "INST - PushFromRegister"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); - Self::operation(dst, context) + Self::operation(dst, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); - Self::operation(dst, context) + Self::operation(dst, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); - Self::operation(dst, context) + Self::operation(dst, registers, context) } } @@ -292,9 +245,12 @@ pub(crate) struct SetRegisterFromAccumulator; impl SetRegisterFromAccumulator { #[allow(clippy::unnecessary_wraps)] - fn operation(register: u32, context: &mut Context) -> JsResult { - let rp = context.vm.frame().rp; - context.vm.stack[(rp + register) as usize] = context.vm.get_return_value(); + fn operation( + register: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + registers.set(register, context.vm.get_return_value()); Ok(CompletionType::Normal) } } @@ -304,18 +260,18 @@ impl Operation for SetRegisterFromAccumulator { const INSTRUCTION: &'static str = "INST - SetRegisterFromAccumulator"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let register = u32::from(context.vm.read::()); - Self::operation(register, context) + Self::operation(register, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let register = u32::from(context.vm.read::()); - Self::operation(register, context) + Self::operation(register, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let register = context.vm.read::(); - Self::operation(register, context) + Self::operation(register, registers, context) } } diff --git a/core/engine/src/vm/opcode/control_flow/throw.rs b/core/engine/src/vm/opcode/control_flow/throw.rs index 9261f29feb1..201fdc9f02d 100644 --- a/core/engine/src/vm/opcode/control_flow/throw.rs +++ b/core/engine/src/vm/opcode/control_flow/throw.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsError, JsNativeError, JsResult, }; @@ -10,13 +10,15 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct Throw; -impl Operation for Throw { - const NAME: &'static str = "Throw"; - const INSTRUCTION: &'static str = "INST - Throw"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let error = JsError::from_opaque(context.vm.pop()); +impl Throw { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let error = JsError::from_opaque(value.clone()); context.vm.pending_exception = Some(error); // Note: -1 because we increment after fetching the opcode. @@ -29,6 +31,27 @@ impl Operation for Throw { } } +impl Operation for Throw { + const NAME: &'static str = "Throw"; + const INSTRUCTION: &'static str = "INST - Throw"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} + /// `ReThrow` implements the Opcode Operation for `Opcode::ReThrow` /// /// Operation: @@ -41,7 +64,7 @@ impl Operation for ReThrow { const INSTRUCTION: &'static str = "INST - ReThrow"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { // Note: -1 because we increment after fetching the opcode. let pc = context.vm.frame().pc.saturating_sub(1); if context.vm.handle_exception_at(pc) { @@ -64,19 +87,19 @@ impl Operation for ReThrow { /// `Exception` implements the Opcode Operation for `Opcode::Exception` /// /// Operation: -/// - Get the thrown exception and push on the stack. +/// - Get the thrown exception and push it on the stack. #[derive(Debug, Clone, Copy)] pub(crate) struct Exception; -impl Operation for Exception { - const NAME: &'static str = "Exception"; - const INSTRUCTION: &'static str = "INST - Exception"; - const COST: u8 = 2; - - fn execute(context: &mut Context) -> JsResult { +impl Exception { + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { if let Some(error) = context.vm.pending_exception.take() { let error = error.to_opaque(context); - context.vm.push(error); + registers.set(dst, error); return Ok(CompletionType::Normal); } @@ -85,7 +108,28 @@ impl Operation for Exception { // This is done to run the finally code. // // This should be unreachable for regular functions. - ReThrow::execute(context) + ReThrow::execute(registers, context) + } +} + +impl Operation for Exception { + const NAME: &'static str = "Exception"; + const INSTRUCTION: &'static str = "INST - Exception"; + const COST: u8 = 2; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) } } @@ -96,21 +140,46 @@ impl Operation for Exception { #[derive(Debug, Clone, Copy)] pub(crate) struct MaybeException; +impl MaybeException { + #[allow(clippy::unnecessary_wraps)] + fn operation( + has_exception: u32, + exception: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + if let Some(error) = context.vm.pending_exception.take() { + let error = error.to_opaque(context); + registers.set(exception, error); + registers.set(has_exception, true.into()); + } else { + registers.set(has_exception, false.into()); + } + Ok(CompletionType::Normal) + } +} + impl Operation for MaybeException { const NAME: &'static str = "MaybeException"; const INSTRUCTION: &'static str = "INST - MaybeException"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - if let Some(error) = context.vm.pending_exception.take() { - let error = error.to_opaque(context); - context.vm.push(error); - context.vm.push(true); - return Ok(CompletionType::Normal); - } + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let has_exception = context.vm.read::().into(); + let exception = context.vm.read::().into(); + Self::operation(has_exception, exception, registers, context) + } - context.vm.push(false); - Ok(CompletionType::Normal) + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let has_exception = context.vm.read::().into(); + let exception = context.vm.read::().into(); + Self::operation(has_exception, exception, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let has_exception = context.vm.read::(); + let exception = context.vm.read::(); + Self::operation(has_exception, exception, registers, context) } } @@ -136,17 +205,17 @@ impl Operation for ThrowNewTypeError { const INSTRUCTION: &'static str = "INST - ThrowNewTypeError"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; Self::operation(context, index) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; Self::operation(context, index) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; Self::operation(context, index) } @@ -174,17 +243,17 @@ impl Operation for ThrowNewSyntaxError { const INSTRUCTION: &'static str = "INST - ThrowNewSyntaxError"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; Self::operation(context, index) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; Self::operation(context, index) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; Self::operation(context, index) } diff --git a/core/engine/src/vm/opcode/copy/mod.rs b/core/engine/src/vm/opcode/copy/mod.rs index a33e8d03551..00399fffa1a 100644 --- a/core/engine/src/vm/opcode/copy/mod.rs +++ b/core/engine/src/vm/opcode/copy/mod.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -12,30 +12,24 @@ pub(crate) struct CopyDataProperties; impl CopyDataProperties { fn operation( + object: u32, + source: u32, + keys: &[u32], + registers: &mut Registers, context: &mut Context, - excluded_key_count: usize, - excluded_key_count_computed: usize, ) -> JsResult { - let mut excluded_keys = Vec::with_capacity(excluded_key_count); - for _ in 0..excluded_key_count { - let key = context.vm.pop(); + let object = registers.get(object); + let source = registers.get(source); + let mut excluded_keys = Vec::with_capacity(keys.len()); + for key in keys { + let key = registers.get(*key); excluded_keys.push( key.to_property_key(context) .expect("key must be property key"), ); } - let value = context.vm.pop(); - let object = value.as_object().expect("not an object"); - let source = context.vm.pop(); - for _ in 0..excluded_key_count_computed { - let key = context.vm.pop(); - excluded_keys.push( - key.to_property_key(context) - .expect("key must be property key"), - ); - } - object.copy_data_properties(&source, excluded_keys, context)?; - context.vm.push(value); + let object = object.as_object().expect("not an object"); + object.copy_data_properties(source, excluded_keys, context)?; Ok(CompletionType::Normal) } } @@ -45,21 +39,36 @@ impl Operation for CopyDataProperties { const INSTRUCTION: &'static str = "INST - CopyDataProperties"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { - let excluded_key_count = context.vm.read::() as usize; - let excluded_key_count_computed = context.vm.read::() as usize; - Self::operation(context, excluded_key_count, excluded_key_count_computed) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let source = context.vm.read::().into(); + let key_count = context.vm.read::() as usize; + let mut keys = Vec::with_capacity(key_count); + for _ in 0..key_count { + keys.push(context.vm.read::().into()); + } + Self::operation(object, source, &keys, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let excluded_key_count = context.vm.read::() as usize; - let excluded_key_count_computed = context.vm.read::() as usize; - Self::operation(context, excluded_key_count, excluded_key_count_computed) + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let source = context.vm.read::().into(); + let key_count = context.vm.read::() as usize; + let mut keys = Vec::with_capacity(key_count); + for _ in 0..key_count { + keys.push(context.vm.read::().into()); + } + Self::operation(object, source, &keys, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let excluded_key_count = context.vm.read::() as usize; - let excluded_key_count_computed = context.vm.read::() as usize; - Self::operation(context, excluded_key_count, excluded_key_count_computed) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let source = context.vm.read::(); + let key_count = context.vm.read::() as usize; + let mut keys = Vec::with_capacity(key_count); + for _ in 0..key_count { + keys.push(context.vm.read::()); + } + Self::operation(object, source, &keys, registers, context) } } diff --git a/core/engine/src/vm/opcode/define/class/getter.rs b/core/engine/src/vm/opcode/define/class/getter.rs index 174576778c9..8babb0dfbbc 100644 --- a/core/engine/src/vm/opcode/define/class/getter.rs +++ b/core/engine/src/vm/opcode/define/class/getter.rs @@ -4,7 +4,7 @@ use crate::{ builtins::function::{set_function_name, OrdinaryFunction}, object::internal_methods::InternalMethodContext, property::PropertyDescriptor, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -16,9 +16,15 @@ use crate::{ pub(crate) struct DefineClassStaticGetterByName; impl DefineClassStaticGetterByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let function = context.vm.pop(); - let class = context.vm.pop(); + fn operation( + class: u32, + function: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let class = registers.get(class); let class = class.as_object().expect("class must be object"); let key = context .vm @@ -44,7 +50,7 @@ impl DefineClassStaticGetterByName { class.__define_own_property__( &key, PropertyDescriptor::builder() - .maybe_get(Some(function)) + .maybe_get(Some(function.clone())) .maybe_set(set) .enumerable(false) .configurable(true) @@ -60,19 +66,25 @@ impl Operation for DefineClassStaticGetterByName { const INSTRUCTION: &'static str = "INST - DefineClassStaticGetterByName"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let class = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } } @@ -84,9 +96,15 @@ impl Operation for DefineClassStaticGetterByName { pub(crate) struct DefineClassGetterByName; impl DefineClassGetterByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let function = context.vm.pop(); - let class_proto = context.vm.pop(); + fn operation( + class_proto: u32, + function: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let class_proto = registers.get(class_proto); let class_proto = class_proto.as_object().expect("class must be object"); let key = context .vm @@ -112,7 +130,7 @@ impl DefineClassGetterByName { class_proto.__define_own_property__( &key, PropertyDescriptor::builder() - .maybe_get(Some(function)) + .maybe_get(Some(function.clone())) .maybe_set(set) .enumerable(false) .configurable(true) @@ -128,19 +146,25 @@ impl Operation for DefineClassGetterByName { const INSTRUCTION: &'static str = "INST - DefineClassGetterByName"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let class_proto = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } } @@ -151,15 +175,17 @@ impl Operation for DefineClassGetterByName { #[derive(Debug, Clone, Copy)] pub(crate) struct DefineClassStaticGetterByValue; -impl Operation for DefineClassStaticGetterByValue { - const NAME: &'static str = "DefineClassStaticGetterByValue"; - const INSTRUCTION: &'static str = "INST - DefineClassStaticGetterByValue"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let function = context.vm.pop(); - let key = context.vm.pop(); - let class = context.vm.pop(); +impl DefineClassStaticGetterByValue { + fn operation( + function: u32, + key: u32, + class: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let key = registers.get(key); + let class = registers.get(class); let class = class.as_object().expect("class must be object"); let key = key .to_property_key(context) @@ -183,7 +209,7 @@ impl Operation for DefineClassStaticGetterByValue { class.define_property_or_throw( key, PropertyDescriptor::builder() - .maybe_get(Some(function)) + .maybe_get(Some(function.clone())) .maybe_set(set) .enumerable(false) .configurable(true) @@ -194,6 +220,33 @@ impl Operation for DefineClassStaticGetterByValue { } } +impl Operation for DefineClassStaticGetterByValue { + const NAME: &'static str = "DefineClassStaticGetterByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticGetterByValue"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class = context.vm.read::().into(); + Self::operation(function, key, class, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class = context.vm.read::().into(); + Self::operation(function, key, class, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let key = context.vm.read::(); + let class = context.vm.read::(); + Self::operation(function, key, class, registers, context) + } +} + /// `DefineClassGetterByValue` implements the Opcode Operation for `Opcode::DefineClassGetterByValue` /// /// Operation: @@ -201,15 +254,17 @@ impl Operation for DefineClassStaticGetterByValue { #[derive(Debug, Clone, Copy)] pub(crate) struct DefineClassGetterByValue; -impl Operation for DefineClassGetterByValue { - const NAME: &'static str = "DefineClassGetterByValue"; - const INSTRUCTION: &'static str = "INST - DefineClassGetterByValue"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let function = context.vm.pop(); - let key = context.vm.pop(); - let class_proto = context.vm.pop(); +impl DefineClassGetterByValue { + fn operation( + function: u32, + key: u32, + class_proto: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let key = registers.get(key); + let class_proto = registers.get(class_proto); let class_proto = class_proto.as_object().expect("class must be object"); let key = key .to_property_key(context) @@ -232,7 +287,7 @@ impl Operation for DefineClassGetterByValue { class_proto.__define_own_property__( &key, PropertyDescriptor::builder() - .maybe_get(Some(function)) + .maybe_get(Some(function.clone())) .maybe_set(set) .enumerable(false) .configurable(true) @@ -242,3 +297,30 @@ impl Operation for DefineClassGetterByValue { Ok(CompletionType::Normal) } } + +impl Operation for DefineClassGetterByValue { + const NAME: &'static str = "DefineClassGetterByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassGetterByValue"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); + Self::operation(function, key, class_proto, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); + Self::operation(function, key, class_proto, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let key = context.vm.read::(); + let class_proto = context.vm.read::(); + Self::operation(function, key, class_proto, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/define/class/method.rs b/core/engine/src/vm/opcode/define/class/method.rs index 43b25043363..c4c841bf7bc 100644 --- a/core/engine/src/vm/opcode/define/class/method.rs +++ b/core/engine/src/vm/opcode/define/class/method.rs @@ -2,7 +2,7 @@ use crate::{ builtins::function::{set_function_name, OrdinaryFunction}, object::internal_methods::InternalMethodContext, property::PropertyDescriptor, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -14,9 +14,15 @@ use crate::{ pub(crate) struct DefineClassStaticMethodByName; impl DefineClassStaticMethodByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let function = context.vm.pop(); - let class = context.vm.pop(); + fn operation( + class: u32, + function: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let class = registers.get(class); let class = class.as_object().expect("class must be object"); let key = context .vm @@ -38,7 +44,7 @@ impl DefineClassStaticMethodByName { class.__define_own_property__( &key, PropertyDescriptor::builder() - .value(function) + .value(function.clone()) .writable(true) .enumerable(false) .configurable(true) @@ -54,19 +60,25 @@ impl Operation for DefineClassStaticMethodByName { const INSTRUCTION: &'static str = "INST - DefineClassStaticMethodByName"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let class = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } } @@ -78,9 +90,15 @@ impl Operation for DefineClassStaticMethodByName { pub(crate) struct DefineClassMethodByName; impl DefineClassMethodByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let function = context.vm.pop(); - let class_proto = context.vm.pop(); + fn operation( + class_proto: u32, + function: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let class_proto = registers.get(class_proto); let class_proto = class_proto.as_object().expect("class must be object"); let key = context .vm @@ -102,7 +120,7 @@ impl DefineClassMethodByName { class_proto.__define_own_property__( &key, PropertyDescriptor::builder() - .value(function) + .value(function.clone()) .writable(true) .enumerable(false) .configurable(true) @@ -118,19 +136,25 @@ impl Operation for DefineClassMethodByName { const INSTRUCTION: &'static str = "INST - DefineClassMethodByName"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let class_proto = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } } @@ -141,15 +165,17 @@ impl Operation for DefineClassMethodByName { #[derive(Debug, Clone, Copy)] pub(crate) struct DefineClassStaticMethodByValue; -impl Operation for DefineClassStaticMethodByValue { - const NAME: &'static str = "DefineClassStaticMethodByValue"; - const INSTRUCTION: &'static str = "INST - DefineClassStaticMethodByValue"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let function = context.vm.pop(); - let key = context.vm.pop(); - let class = context.vm.pop(); +impl DefineClassStaticMethodByValue { + fn operation( + function: u32, + key: u32, + class: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let key = registers.get(key); + let class = registers.get(class); let class = class.as_object().expect("class must be object"); let key = key .to_property_key(context) @@ -168,7 +194,7 @@ impl Operation for DefineClassStaticMethodByValue { class.define_property_or_throw( key, PropertyDescriptor::builder() - .value(function) + .value(function.clone()) .writable(true) .enumerable(false) .configurable(true) @@ -179,6 +205,33 @@ impl Operation for DefineClassStaticMethodByValue { } } +impl Operation for DefineClassStaticMethodByValue { + const NAME: &'static str = "DefineClassStaticMethodByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticMethodByValue"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class = context.vm.read::().into(); + Self::operation(function, key, class, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class = context.vm.read::().into(); + Self::operation(function, key, class, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let key = context.vm.read::(); + let class = context.vm.read::(); + Self::operation(function, key, class, registers, context) + } +} + /// `DefineClassMethodByValue` implements the Opcode Operation for `Opcode::DefineClassMethodByValue` /// /// Operation: @@ -186,15 +239,17 @@ impl Operation for DefineClassStaticMethodByValue { #[derive(Debug, Clone, Copy)] pub(crate) struct DefineClassMethodByValue; -impl Operation for DefineClassMethodByValue { - const NAME: &'static str = "DefineClassMethodByValue"; - const INSTRUCTION: &'static str = "INST - DefineClassMethodByValue"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let function = context.vm.pop(); - let key = context.vm.pop(); - let class_proto = context.vm.pop(); +impl DefineClassMethodByValue { + fn operation( + function: u32, + key: u32, + class_proto: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let key = registers.get(key); + let class_proto = registers.get(class_proto); let class_proto = class_proto.as_object().expect("class must be object"); let key = key .to_property_key(context) @@ -213,7 +268,7 @@ impl Operation for DefineClassMethodByValue { class_proto.__define_own_property__( &key, PropertyDescriptor::builder() - .value(function) + .value(function.clone()) .writable(true) .enumerable(false) .configurable(true) @@ -223,3 +278,30 @@ impl Operation for DefineClassMethodByValue { Ok(CompletionType::Normal) } } + +impl Operation for DefineClassMethodByValue { + const NAME: &'static str = "DefineClassMethodByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassMethodByValue"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); + Self::operation(function, key, class_proto, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); + Self::operation(function, key, class_proto, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let key = context.vm.read::(); + let class_proto = context.vm.read::(); + Self::operation(function, key, class_proto, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/define/class/setter.rs b/core/engine/src/vm/opcode/define/class/setter.rs index 8d2272b2f43..d1b4a2ace9b 100644 --- a/core/engine/src/vm/opcode/define/class/setter.rs +++ b/core/engine/src/vm/opcode/define/class/setter.rs @@ -4,7 +4,7 @@ use crate::{ builtins::function::{set_function_name, OrdinaryFunction}, object::internal_methods::InternalMethodContext, property::PropertyDescriptor, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -16,9 +16,15 @@ use crate::{ pub(crate) struct DefineClassStaticSetterByName; impl DefineClassStaticSetterByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let function = context.vm.pop(); - let class = context.vm.pop(); + fn operation( + class: u32, + function: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let class = registers.get(class); let class = class.as_object().expect("class must be object"); let key = context .vm @@ -45,7 +51,7 @@ impl DefineClassStaticSetterByName { class.__define_own_property__( &key, PropertyDescriptor::builder() - .maybe_set(Some(function)) + .maybe_set(Some(function.clone())) .maybe_get(get) .enumerable(false) .configurable(true) @@ -61,19 +67,25 @@ impl Operation for DefineClassStaticSetterByName { const INSTRUCTION: &'static str = "INST - DefineClassStaticSetterByName"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let class = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } } @@ -85,9 +97,15 @@ impl Operation for DefineClassStaticSetterByName { pub(crate) struct DefineClassSetterByName; impl DefineClassSetterByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let function = context.vm.pop(); - let class_proto = context.vm.pop(); + fn operation( + class_proto: u32, + function: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let class_proto = registers.get(class_proto); let class_proto = class_proto.as_object().expect("class must be object"); let key = context .vm @@ -114,7 +132,7 @@ impl DefineClassSetterByName { class_proto.__define_own_property__( &key, PropertyDescriptor::builder() - .maybe_set(Some(function)) + .maybe_set(Some(function.clone())) .maybe_get(get) .enumerable(false) .configurable(true) @@ -131,19 +149,25 @@ impl Operation for DefineClassSetterByName { const INSTRUCTION: &'static str = "INST - DefineClassSetterByName"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let class_proto = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class_proto, function, index, registers, context) } } @@ -154,15 +178,17 @@ impl Operation for DefineClassSetterByName { #[derive(Debug, Clone, Copy)] pub(crate) struct DefineClassStaticSetterByValue; -impl Operation for DefineClassStaticSetterByValue { - const NAME: &'static str = "DefineClassStaticSetterByValue"; - const INSTRUCTION: &'static str = "INST - DefineClassStaticSetterByValue"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let function = context.vm.pop(); - let key = context.vm.pop(); - let class = context.vm.pop(); +impl DefineClassStaticSetterByValue { + fn operation( + function: u32, + key: u32, + class: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let key = registers.get(key); + let class = registers.get(class); let class = class.as_object().expect("class must be object"); let key = key .to_property_key(context) @@ -186,7 +212,7 @@ impl Operation for DefineClassStaticSetterByValue { class.define_property_or_throw( key, PropertyDescriptor::builder() - .maybe_set(Some(function)) + .maybe_set(Some(function.clone())) .maybe_get(get) .enumerable(false) .configurable(true) @@ -198,6 +224,33 @@ impl Operation for DefineClassStaticSetterByValue { } } +impl Operation for DefineClassStaticSetterByValue { + const NAME: &'static str = "DefineClassStaticSetterByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassStaticSetterByValue"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class = context.vm.read::().into(); + Self::operation(function, key, class, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class = context.vm.read::().into(); + Self::operation(function, key, class, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let key = context.vm.read::(); + let class = context.vm.read::(); + Self::operation(function, key, class, registers, context) + } +} + /// `DefineClassSetterByValue` implements the Opcode Operation for `Opcode::DefineClassSetterByValue` /// /// Operation: @@ -205,15 +258,17 @@ impl Operation for DefineClassStaticSetterByValue { #[derive(Debug, Clone, Copy)] pub(crate) struct DefineClassSetterByValue; -impl Operation for DefineClassSetterByValue { - const NAME: &'static str = "DefineClassSetterByValue"; - const INSTRUCTION: &'static str = "INST - DefineClassSetterByValue"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let function = context.vm.pop(); - let key = context.vm.pop(); - let class_proto = context.vm.pop(); +impl DefineClassSetterByValue { + fn operation( + function: u32, + key: u32, + class_proto: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let key = registers.get(key); + let class_proto = registers.get(class_proto); let class_proto = class_proto.as_object().expect("class must be object"); let key = key .to_property_key(context) @@ -237,7 +292,7 @@ impl Operation for DefineClassSetterByValue { class_proto.__define_own_property__( &key, PropertyDescriptor::builder() - .maybe_set(Some(function)) + .maybe_set(Some(function.clone())) .maybe_get(get) .enumerable(false) .configurable(true) @@ -248,3 +303,30 @@ impl Operation for DefineClassSetterByValue { Ok(CompletionType::Normal) } } + +impl Operation for DefineClassSetterByValue { + const NAME: &'static str = "DefineClassSetterByValue"; + const INSTRUCTION: &'static str = "INST - DefineClassSetterByValue"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); + Self::operation(function, key, class_proto, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let key = context.vm.read::().into(); + let class_proto = context.vm.read::().into(); + Self::operation(function, key, class_proto, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let key = context.vm.read::(); + let class_proto = context.vm.read::(); + Self::operation(function, key, class_proto, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/define/mod.rs b/core/engine/src/vm/opcode/define/mod.rs index ecc5cb4a7d0..9ec2f3109bc 100644 --- a/core/engine/src/vm/opcode/define/mod.rs +++ b/core/engine/src/vm/opcode/define/mod.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -18,7 +18,7 @@ pub(crate) struct DefVar; impl DefVar { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation(index: usize, context: &mut Context) -> JsResult { // TODO: spec specifies to return `empty` on empty vars, but we're trying to initialize. let binding_locator = context.vm.frame().code_block.bindings[index].clone(); @@ -36,19 +36,19 @@ impl Operation for DefVar { const INSTRUCTION: &'static str = "INST - DefVar"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(index, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(index, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(_: &mut Registers, context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(index, context) } } @@ -60,13 +60,18 @@ impl Operation for DefVar { pub(crate) struct DefInitVar; impl DefInitVar { - fn operation(context: &mut Context, index: usize) -> JsResult { - let value = context.vm.pop(); + fn operation( + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); let frame = context.vm.frame(); let strict = frame.code_block.strict(); let mut binding_locator = frame.code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; - context.set_binding(&binding_locator, value, strict)?; + context.set_binding(&binding_locator, value.clone(), strict)?; Ok(CompletionType::Normal) } @@ -77,19 +82,22 @@ impl Operation for DefInitVar { const INSTRUCTION: &'static str = "INST - DefInitVar"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } } @@ -102,13 +110,18 @@ pub(crate) struct PutLexicalValue; impl PutLexicalValue { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { - let value = context.vm.pop(); + fn operation( + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); let binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.vm.environments.put_lexical_value( binding_locator.scope(), binding_locator.binding_index(), - value, + value.clone(), ); Ok(CompletionType::Normal) @@ -120,18 +133,21 @@ impl Operation for PutLexicalValue { const INSTRUCTION: &'static str = "INST - PutLexicalValue"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } } diff --git a/core/engine/src/vm/opcode/define/own_property.rs b/core/engine/src/vm/opcode/define/own_property.rs index f297dcb877e..1d57bb3cc72 100644 --- a/core/engine/src/vm/opcode/define/own_property.rs +++ b/core/engine/src/vm/opcode/define/own_property.rs @@ -1,7 +1,7 @@ use crate::{ object::internal_methods::InternalMethodContext, property::PropertyDescriptor, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsNativeError, JsResult, }; @@ -13,19 +13,21 @@ use crate::{ pub(crate) struct DefineOwnPropertyByName; impl DefineOwnPropertyByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let value = context.vm.pop(); - let object = context.vm.pop(); - let object = if let Some(object) = object.as_object() { - object.clone() - } else { - object.to_object(context)? - }; + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context.vm.frame().code_block().constant_string(index); + let object = object.to_object(context)?; object.__define_own_property__( &name.into(), PropertyDescriptor::builder() - .value(value) + .value(value.clone()) .writable(true) .enumerable(true) .configurable(true) @@ -41,19 +43,25 @@ impl Operation for DefineOwnPropertyByName { const INSTRUCTION: &'static str = "INST - DefineOwnPropertyByName"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } @@ -64,25 +72,23 @@ impl Operation for DefineOwnPropertyByName { #[derive(Debug, Clone, Copy)] pub(crate) struct DefineOwnPropertyByValue; -impl Operation for DefineOwnPropertyByValue { - const NAME: &'static str = "DefineOwnPropertyByValue"; - const INSTRUCTION: &'static str = "INST - DefineOwnPropertyByValue"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let key = context.vm.pop(); - let object = context.vm.pop(); - let object = if let Some(object) = object.as_object() { - object.clone() - } else { - object.to_object(context)? - }; +impl DefineOwnPropertyByValue { + fn operation( + value: u32, + key: u32, + object: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let key = registers.get(key); + let object = registers.get(object); + let object = object.to_object(context)?; let key = key.to_property_key(context)?; let success = object.__define_own_property__( &key, PropertyDescriptor::builder() - .value(value) + .value(value.clone()) .writable(true) .enumerable(true) .configurable(true) @@ -97,3 +103,30 @@ impl Operation for DefineOwnPropertyByValue { Ok(CompletionType::Normal) } } + +impl Operation for DefineOwnPropertyByValue { + const NAME: &'static str = "DefineOwnPropertyByValue"; + const INSTRUCTION: &'static str = "INST - DefineOwnPropertyByValue"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let key = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(value, key, object, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let key = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(value, key, object, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let key = context.vm.read::(); + let object = context.vm.read::(); + Self::operation(value, key, object, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/delete/mod.rs b/core/engine/src/vm/opcode/delete/mod.rs index 6940c861d15..8370429be15 100644 --- a/core/engine/src/vm/opcode/delete/mod.rs +++ b/core/engine/src/vm/opcode/delete/mod.rs @@ -1,7 +1,7 @@ use crate::{ error::JsNativeError, object::internal_methods::InternalMethodContext, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -13,9 +13,14 @@ use crate::{ pub(crate) struct DeletePropertyByName; impl DeletePropertyByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let value = context.vm.pop(); - let object = value.to_object(context)?; + fn operation( + object_register: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object_register); + let object = object.to_object(context)?; let code_block = context.vm.frame().code_block(); let key = code_block.constant_string(index).into(); let strict = code_block.strict(); @@ -26,7 +31,7 @@ impl DeletePropertyByName { .with_message("Cannot delete property") .into()); } - context.vm.push(result); + registers.set(object_register, result.into()); Ok(CompletionType::Normal) } } @@ -36,19 +41,22 @@ impl Operation for DeletePropertyByName { const INSTRUCTION: &'static str = "INST - DeletePropertyByName"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, index, registers, context) } } @@ -59,16 +67,17 @@ impl Operation for DeletePropertyByName { #[derive(Debug, Clone, Copy)] pub(crate) struct DeletePropertyByValue; -impl Operation for DeletePropertyByValue { - const NAME: &'static str = "DeletePropertyByValue"; - const INSTRUCTION: &'static str = "INST - DeletePropertyByValue"; - const COST: u8 = 3; - - fn execute(context: &mut Context) -> JsResult { - let key_value = context.vm.pop(); - let value = context.vm.pop(); - let object = value.to_object(context)?; - let property_key = key_value.to_property_key(context)?; +impl DeletePropertyByValue { + fn operation( + object_register: u32, + key: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object_register); + let key = registers.get(key); + let object = object.to_object(context)?; + let property_key = key.to_property_key(context)?; let result = object.__delete__(&property_key, &mut InternalMethodContext::new(context))?; if !result && context.vm.frame().code_block().strict() { @@ -76,11 +85,35 @@ impl Operation for DeletePropertyByValue { .with_message("Cannot delete property") .into()); } - context.vm.push(result); + registers.set(object_register, result.into()); Ok(CompletionType::Normal) } } +impl Operation for DeletePropertyByValue { + const NAME: &'static str = "DeletePropertyByValue"; + const INSTRUCTION: &'static str = "INST - DeletePropertyByValue"; + const COST: u8 = 3; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let key = context.vm.read::().into(); + Self::operation(object, key, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let key = context.vm.read::().into(); + Self::operation(object, key, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let key = context.vm.read::(); + Self::operation(object, key, registers, context) + } +} + /// `DeleteName` implements the Opcode Operation for `Opcode::DeleteName` /// /// Operation: @@ -89,14 +122,16 @@ impl Operation for DeletePropertyByValue { pub(crate) struct DeleteName; impl DeleteName { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); - context.find_runtime_binding(&mut binding_locator)?; - let deleted = context.delete_binding(&binding_locator)?; - - context.vm.push(deleted); + registers.set(value, deleted.into()); Ok(CompletionType::Normal) } } @@ -106,19 +141,22 @@ impl Operation for DeleteName { const INSTRUCTION: &'static str = "INST - DeleteName"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } } @@ -134,7 +172,7 @@ impl Operation for DeleteSuperThrow { const INSTRUCTION: &'static str = "INST - DeleteSuperThrow"; const COST: u8 = 2; - fn execute(_: &mut Context) -> JsResult { + fn execute(_: &mut Registers, _: &mut Context) -> JsResult { Err(JsNativeError::reference() .with_message("cannot delete a property of `super`") .into()) diff --git a/core/engine/src/vm/opcode/dup/mod.rs b/core/engine/src/vm/opcode/dup/mod.rs deleted file mode 100644 index bf19e640aa6..00000000000 --- a/core/engine/src/vm/opcode/dup/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{ - vm::{opcode::Operation, CompletionType}, - Context, JsResult, -}; - -/// `Dup` implements the Opcode Operation for `Opcode::Dup` -/// -/// Operation: -/// - Push a copy of the top value on the stack. -#[derive(Debug, Clone, Copy)] -pub(crate) struct Dup; - -impl Operation for Dup { - const NAME: &'static str = "Dup"; - const INSTRUCTION: &'static str = "INST - Dup"; - const COST: u8 = 1; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - context.vm.push(value.clone()); - context.vm.push(value); - Ok(CompletionType::Normal) - } -} diff --git a/core/engine/src/vm/opcode/environment/mod.rs b/core/engine/src/vm/opcode/environment/mod.rs index 659f1400ac7..59a58836f3e 100644 --- a/core/engine/src/vm/opcode/environment/mod.rs +++ b/core/engine/src/vm/opcode/environment/mod.rs @@ -2,7 +2,7 @@ use crate::{ builtins::function::OrdinaryFunction, error::JsNativeError, object::internal_methods::InternalMethodContext, - vm::{opcode::Operation, CallFrameFlags, CompletionType}, + vm::{opcode::Operation, CallFrameFlags, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -13,17 +13,17 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct This; -impl Operation for This { - const NAME: &'static str = "This"; - const INSTRUCTION: &'static str = "INST - This"; - const COST: u8 = 1; - - fn execute(context: &mut Context) -> JsResult { +impl This { + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let frame = context.vm.frame_mut(); let this_index = frame.fp(); if frame.has_this_value_cached() { let this = context.vm.stack[this_index as usize].clone(); - context.vm.push(this); + registers.set(dst, this); return Ok(CompletionType::Normal); } @@ -34,11 +34,32 @@ impl Operation for This { .unwrap_or(context.realm().global_this().clone().into()); context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED; context.vm.stack[this_index as usize] = this.clone(); - context.vm.push(this); + registers.set(dst, this); Ok(CompletionType::Normal) } } +impl Operation for This { + const NAME: &'static str = "This"; + const INSTRUCTION: &'static str = "INST - This"; + const COST: u8 = 1; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) + } +} + /// `ThisForObjectEnvironmentName` implements the Opcode Operation for `Opcode::ThisForObjectEnvironmentName` /// /// Operation: @@ -47,12 +68,17 @@ impl Operation for This { pub(crate) struct ThisForObjectEnvironmentName; impl ThisForObjectEnvironmentName { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + dst: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let binding_locator = context.vm.frame().code_block.bindings[index].clone(); let this = context .this_from_object_environment_binding(&binding_locator)? .map_or(JsValue::undefined(), Into::into); - context.vm.push(this); + registers.set(dst, this); Ok(CompletionType::Normal) } } @@ -62,19 +88,22 @@ impl Operation for ThisForObjectEnvironmentName { const INSTRUCTION: &'static str = "INST - ThisForObjectEnvironmentName"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(dst, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(dst, index, registers, context) } } @@ -85,12 +114,12 @@ impl Operation for ThisForObjectEnvironmentName { #[derive(Debug, Clone, Copy)] pub(crate) struct Super; -impl Operation for Super { - const NAME: &'static str = "Super"; - const INSTRUCTION: &'static str = "INST - Super"; - const COST: u8 = 3; - - fn execute(context: &mut Context) -> JsResult { +impl Super { + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let home_object = { let env = context .vm @@ -117,11 +146,32 @@ impl Operation for Super { .flatten() .map_or_else(JsValue::null, JsValue::from); - context.vm.push(value); + registers.set(dst, value); Ok(CompletionType::Normal) } } +impl Operation for Super { + const NAME: &'static str = "Super"; + const INSTRUCTION: &'static str = "INST - Super"; + const COST: u8 = 3; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) + } +} + /// `SuperCallPrepare` implements the Opcode Operation for `Opcode::SuperCallPrepare` /// /// Operation: @@ -129,12 +179,13 @@ impl Operation for Super { #[derive(Debug, Clone, Copy)] pub(crate) struct SuperCallPrepare; -impl Operation for SuperCallPrepare { - const NAME: &'static str = "SuperCallPrepare"; - const INSTRUCTION: &'static str = "INST - SuperCallPrepare"; - const COST: u8 = 3; - - fn execute(context: &mut Context) -> JsResult { +impl SuperCallPrepare { + #[allow(clippy::unnecessary_wraps)] + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let this_env = context .vm .environments @@ -145,14 +196,35 @@ impl Operation for SuperCallPrepare { let super_constructor = active_function .__get_prototype_of__(&mut InternalMethodContext::new(context)) .expect("function object must have prototype"); - - context - .vm - .push(super_constructor.map_or_else(JsValue::null, JsValue::from)); + registers.set( + dst, + super_constructor.map_or_else(JsValue::null, JsValue::from), + ); Ok(CompletionType::Normal) } } +impl Operation for SuperCallPrepare { + const NAME: &'static str = "SuperCallPrepare"; + const INSTRUCTION: &'static str = "INST - SuperCallPrepare"; + const COST: u8 = 3; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) + } +} + /// `SuperCall` implements the Opcode Operation for `Opcode::SuperCall` /// /// Operation: @@ -161,7 +233,11 @@ impl Operation for SuperCallPrepare { pub(crate) struct SuperCall; impl SuperCall { - fn operation(context: &mut Context, argument_count: usize) -> JsResult { + fn operation( + registers: &mut Registers, + context: &mut Context, + argument_count: usize, + ) -> JsResult { let super_constructor_index = context.vm.stack.len() - argument_count - 1; let super_constructor = context.vm.stack[super_constructor_index].clone(); let Some(super_constructor) = super_constructor.as_constructor() else { @@ -185,9 +261,12 @@ impl SuperCall { context.vm.push(new_target); - super_constructor + if let Some(register_count) = super_constructor .__construct__(argument_count) - .resolve(context)?; + .resolve(context)? + { + registers.push_function(register_count); + } Ok(CompletionType::Normal) } } @@ -197,19 +276,19 @@ impl Operation for SuperCall { const INSTRUCTION: &'static str = "INST - SuperCall"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let value_count = context.vm.read::() as usize; - Self::operation(context, value_count) + Self::operation(registers, context, value_count) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let value_count = context.vm.read::() as usize; - Self::operation(context, value_count) + Self::operation(registers, context, value_count) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let value_count = context.vm.read::() as usize; - Self::operation(context, value_count) + Self::operation(registers, context, value_count) } } @@ -225,7 +304,7 @@ impl Operation for SuperCallSpread { const INSTRUCTION: &'static str = "INST - SuperCallSpread"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { // Get the arguments that are stored as an array object on the stack. let arguments_array = context.vm.pop(); let arguments_array_object = arguments_array @@ -264,9 +343,12 @@ impl Operation for SuperCallSpread { context.vm.push(new_target); - super_constructor + if let Some(register_count) = super_constructor .__construct__(arguments.len()) - .resolve(context)?; + .resolve(context)? + { + registers.push_function(register_count); + } Ok(CompletionType::Normal) } } @@ -283,8 +365,10 @@ impl Operation for SuperCallDerived { const INSTRUCTION: &'static str = "INST - SuperCallDerived"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let argument_count = context.vm.frame().argument_count as usize; + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let rp = context.vm.frame().rp; + let argument_count = context.vm.frame().argument_count; + let arguments_start_index = rp - argument_count; let this_env = context .vm @@ -309,17 +393,19 @@ impl Operation for SuperCallDerived { .into()); } - let arguments_start_index = context.vm.stack.len() - argument_count; - context - .vm - .stack - .insert(arguments_start_index, super_constructor.clone().into()); - + context.vm.push(super_constructor.clone()); + for i in 0..argument_count { + let value = context.vm.stack[(arguments_start_index + i) as usize].clone(); + context.vm.push(value); + } context.vm.push(new_target); - super_constructor - .__construct__(argument_count) - .resolve(context)?; + if let Some(register_count) = super_constructor + .__construct__(argument_count as usize) + .resolve(context)? + { + registers.push_function(register_count); + } Ok(CompletionType::Normal) } } @@ -331,22 +417,20 @@ impl Operation for SuperCallDerived { #[derive(Debug, Clone, Copy)] pub(crate) struct BindThisValue; -impl Operation for BindThisValue { - const NAME: &'static str = "BindThisValue"; - const INSTRUCTION: &'static str = "INST - BindThisValue"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { +impl BindThisValue { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { // Taken from `SuperCall : super Arguments` steps 7-12. // // - let result = context - .vm - .pop() + let result = registers + .get(value) .as_object() - .expect("construct result should be an object") - .clone(); + .expect("construct result should be an object"); // 7. Let thisER be GetThisEnvironment(). let this_env = context @@ -367,7 +451,28 @@ impl Operation for BindThisValue { result.initialize_instance_elements(&active_function, context)?; // 12. Return result. - context.vm.push(result); + registers.set(value, result.clone().into()); Ok(CompletionType::Normal) } } + +impl Operation for BindThisValue { + const NAME: &'static str = "BindThisValue"; + const INSTRUCTION: &'static str = "INST - BindThisValue"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/generator/mod.rs b/core/engine/src/vm/opcode/generator/mod.rs index 5439e25c1df..474e1c1cd7b 100644 --- a/core/engine/src/vm/opcode/generator/mod.rs +++ b/core/engine/src/vm/opcode/generator/mod.rs @@ -7,21 +7,18 @@ use crate::{ async_generator::{AsyncGenerator, AsyncGeneratorState}, generator::{GeneratorContext, GeneratorState}, }, - error::JsNativeError, js_string, object::PROTOTYPE, vm::{ call_frame::GeneratorResumeKind, opcode::{Operation, ReThrow}, - CallFrame, CompletionType, + CompletionType, Registers, }, Context, JsError, JsObject, JsResult, }; pub(crate) use yield_stm::*; -use super::SetAccumulatorFromStack; - /// `Generator` implements the Opcode Operation for `Opcode::Generator` /// /// Operation: @@ -34,15 +31,13 @@ impl Operation for Generator { const INSTRUCTION: &'static str = "INST - Generator"; const COST: u8 = 8; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let r#async = context.vm.read::() != 0; let active_function = context.vm.frame().function(&context.vm); let this_function_object = active_function.expect("active function should be set to the generator"); - let mut frame = GeneratorContext::from_current(context); - let proto = this_function_object .get(PROTOTYPE, context) .expect("generator must have a prototype property") @@ -79,24 +74,28 @@ impl Operation for Generator { }; if r#async { - let rp = frame - .call_frame - .as_ref() - .map_or(0, |frame| frame.rp as usize); - frame.stack[rp + CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX as usize] = - generator.clone().into(); + let generator_context = GeneratorContext::from_current( + context, + registers.clone_current_frame(), + Some(generator.clone()), + ); let mut gen = generator .downcast_mut::() .expect("must be object here"); - gen.context = Some(frame); + gen.context = Some(generator_context); } else { + let generator_context = + GeneratorContext::from_current(context, registers.clone_current_frame(), None); + let mut gen = generator .downcast_mut::() .expect("must be object here"); - gen.state = GeneratorState::SuspendedStart { context: frame }; + gen.state = GeneratorState::SuspendedStart { + context: generator_context, + }; } context.vm.set_return_value(generator.into()); @@ -116,12 +115,12 @@ impl Operation for AsyncGeneratorClose { const INSTRUCTION: &'static str = "INST - AsyncGeneratorClose"; const COST: u8 = 8; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { // Step 3.e-g in [AsyncGeneratorStart](https://tc39.es/ecma262/#sec-asyncgeneratorstart) let generator = context .vm .frame() - .async_generator_object(&context.vm.stack) + .async_generator_object(registers) .expect("There should be a object") .downcast::() .expect("must be async generator"); @@ -163,23 +162,48 @@ impl Operation for AsyncGeneratorClose { #[derive(Debug, Clone, Copy)] pub(crate) struct GeneratorNext; +impl GeneratorNext { + fn operation( + resume_kind: u32, + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let resume_kind = registers.get(resume_kind).to_generator_resume_kind(); + match resume_kind { + GeneratorResumeKind::Normal => Ok(CompletionType::Normal), + GeneratorResumeKind::Throw => Err(JsError::from_opaque(registers.get(value).clone())), + GeneratorResumeKind::Return => { + assert!(context.vm.pending_exception.is_none()); + let value = registers.get(value); + context.vm.set_return_value(value.clone()); + ReThrow::execute(registers, context) + } + } + } +} + impl Operation for GeneratorNext { const NAME: &'static str = "GeneratorNext"; const INSTRUCTION: &'static str = "INST - GeneratorNext"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let generator_resume_kind = context.vm.pop().to_generator_resume_kind(); - match generator_resume_kind { - GeneratorResumeKind::Normal => Ok(CompletionType::Normal), - GeneratorResumeKind::Throw => Err(JsError::from_opaque(context.vm.pop())), - GeneratorResumeKind::Return => { - assert!(context.vm.pending_exception.is_none()); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let resume_kind = context.vm.read::().into(); + let value = context.vm.read::().into(); + Self::operation(resume_kind, value, registers, context) + } - SetAccumulatorFromStack::execute(context)?; - ReThrow::execute(context) - } - } + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let resume_kind = context.vm.read::().into(); + let value = context.vm.read::().into(); + Self::operation(resume_kind, value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let resume_kind = context.vm.read::(); + let value = context.vm.read::(); + Self::operation(resume_kind, value, registers, context) } } @@ -190,23 +214,47 @@ impl Operation for GeneratorNext { #[derive(Debug, Clone, Copy)] pub(crate) struct JumpIfNotResumeKind; +impl JumpIfNotResumeKind { + #[allow(clippy::unnecessary_wraps)] + fn operation( + exit: u32, + expected: u8, + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let resume_kind = registers.get(value).to_generator_resume_kind(); + if resume_kind as u8 != expected { + context.vm.frame_mut().pc = exit; + } + Ok(CompletionType::Normal) + } +} + impl Operation for JumpIfNotResumeKind { const NAME: &'static str = "JumpIfNotResumeKind"; const INSTRUCTION: &'static str = "INST - JumpIfNotResumeKind"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let exit = context.vm.read::(); let resume_kind = context.vm.read::(); + let value = context.vm.read::().into(); + Self::operation(exit, resume_kind, value, registers, context) + } - let generator_resume_kind = context.vm.pop().to_generator_resume_kind(); - context.vm.push(generator_resume_kind); - - if generator_resume_kind as u8 != resume_kind { - context.vm.frame_mut().pc = exit; - } + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let exit = context.vm.read::(); + let resume_kind = context.vm.read::(); + let value = context.vm.read::().into(); + Self::operation(exit, resume_kind, value, registers, context) + } - Ok(CompletionType::Normal) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let exit = context.vm.read::(); + let resume_kind = context.vm.read::(); + let value = context.vm.read::(); + Self::operation(exit, resume_kind, value, registers, context) } } @@ -217,17 +265,18 @@ impl Operation for JumpIfNotResumeKind { #[derive(Debug, Clone, Copy)] pub(crate) struct GeneratorDelegateNext; -impl Operation for GeneratorDelegateNext { - const NAME: &'static str = "GeneratorDelegateNext"; - const INSTRUCTION: &'static str = "INST - GeneratorDelegateNext"; - const COST: u8 = 18; - - fn execute(context: &mut Context) -> JsResult { - let throw_method_undefined = context.vm.read::(); - let return_method_undefined = context.vm.read::(); - - let generator_resume_kind = context.vm.pop().to_generator_resume_kind(); - let received = context.vm.pop(); +impl GeneratorDelegateNext { + fn operation( + throw_method_undefined: u32, + return_method_undefined: u32, + value: u32, + resume_kind: u32, + is_return: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let resume_kind = registers.get(resume_kind).to_generator_resume_kind(); + let received = registers.get(value); // Preemptively popping removes the iterator from the iterator stack if any operation // throws, which avoids calling cleanup operations on the poisoned iterator. @@ -238,16 +287,15 @@ impl Operation for GeneratorDelegateNext { .pop() .expect("iterator stack should have at least an iterator"); - match generator_resume_kind { + match resume_kind { GeneratorResumeKind::Normal => { let result = iterator_record.next_method().call( &iterator_record.iterator().clone().into(), - &[received], + &[received.clone()], context, )?; - context.vm.push(false); - context.vm.push(result); - context.vm.push(GeneratorResumeKind::Normal); + registers.set(is_return, false.into()); + registers.set(value, result); } GeneratorResumeKind::Throw => { let throw = iterator_record @@ -256,17 +304,12 @@ impl Operation for GeneratorDelegateNext { if let Some(throw) = throw { let result = throw.call( &iterator_record.iterator().clone().into(), - &[received], + &[received.clone()], context, )?; - context.vm.push(false); - context.vm.push(result); - context.vm.push(GeneratorResumeKind::Normal); + registers.set(is_return, false.into()); + registers.set(value, result); } else { - let error = JsNativeError::typ() - .with_message("iterator does not have a throw method") - .to_opaque(context); - context.vm.push(error); context.vm.frame_mut().pc = throw_method_undefined; } } @@ -277,14 +320,12 @@ impl Operation for GeneratorDelegateNext { if let Some(r#return) = r#return { let result = r#return.call( &iterator_record.iterator().clone().into(), - &[received], + &[received.clone()], context, )?; - context.vm.push(true); - context.vm.push(result); - context.vm.push(GeneratorResumeKind::Normal); + registers.set(is_return, true.into()); + registers.set(value, result); } else { - context.vm.push(received); context.vm.frame_mut().pc = return_method_undefined; // The current iterator didn't have a cleanup `return` method, so we can @@ -300,6 +341,63 @@ impl Operation for GeneratorDelegateNext { } } +impl Operation for GeneratorDelegateNext { + const NAME: &'static str = "GeneratorDelegateNext"; + const INSTRUCTION: &'static str = "INST - GeneratorDelegateNext"; + const COST: u8 = 18; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let throw_method_undefined = context.vm.read::(); + let return_method_undefined = context.vm.read::(); + let value = context.vm.read::().into(); + let resume_kind = context.vm.read::().into(); + let is_return = context.vm.read::().into(); + Self::operation( + throw_method_undefined, + return_method_undefined, + value, + resume_kind, + is_return, + registers, + context, + ) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let throw_method_undefined = context.vm.read::(); + let return_method_undefined = context.vm.read::(); + let value = context.vm.read::().into(); + let resume_kind = context.vm.read::().into(); + let is_return = context.vm.read::().into(); + Self::operation( + throw_method_undefined, + return_method_undefined, + value, + resume_kind, + is_return, + registers, + context, + ) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let throw_method_undefined = context.vm.read::(); + let return_method_undefined = context.vm.read::(); + let value = context.vm.read::(); + let resume_kind = context.vm.read::(); + let is_return = context.vm.read::(); + Self::operation( + throw_method_undefined, + return_method_undefined, + value, + resume_kind, + is_return, + registers, + context, + ) + } +} + /// `GeneratorDelegateResume` implements the Opcode Operation for `Opcode::GeneratorDelegateResume` /// /// Operation: @@ -307,14 +405,19 @@ impl Operation for GeneratorDelegateNext { #[derive(Debug, Clone, Copy)] pub(crate) struct GeneratorDelegateResume; -impl Operation for GeneratorDelegateResume { - const NAME: &'static str = "GeneratorDelegateResume"; - const INSTRUCTION: &'static str = "INST - GeneratorDelegateResume"; - const COST: u8 = 7; - - fn execute(context: &mut Context) -> JsResult { - let return_gen = context.vm.read::(); - let exit = context.vm.read::(); +impl GeneratorDelegateResume { + fn operation( + return_gen: u32, + exit: u32, + value: u32, + resume_kind: u32, + is_return: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let resume_kind = registers.get(resume_kind).to_generator_resume_kind(); + let result = registers.get(value); + let is_return = registers.get(is_return).to_boolean(); let mut iterator = context .vm @@ -323,23 +426,16 @@ impl Operation for GeneratorDelegateResume { .pop() .expect("iterator stack should have at least an iterator"); - let generator_resume_kind = context.vm.pop().to_generator_resume_kind(); - - let result = context.vm.pop(); - let is_return = context.vm.pop().to_boolean(); - - if generator_resume_kind == GeneratorResumeKind::Throw { - return Err(JsError::from_opaque(result)); + if resume_kind == GeneratorResumeKind::Throw { + return Err(JsError::from_opaque(result.clone())); } - iterator.update_result(result, context)?; + iterator.update_result(result.clone(), context)?; if iterator.done() { - let value = iterator.value(context)?; - context.vm.push(value); - + let result = iterator.value(context)?; + registers.set(value, result); context.vm.frame_mut().pc = if is_return { return_gen } else { exit }; - return Ok(CompletionType::Normal); } @@ -348,3 +444,60 @@ impl Operation for GeneratorDelegateResume { Ok(CompletionType::Normal) } } + +impl Operation for GeneratorDelegateResume { + const NAME: &'static str = "GeneratorDelegateResume"; + const INSTRUCTION: &'static str = "INST - GeneratorDelegateResume"; + const COST: u8 = 7; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let r#return = context.vm.read::(); + let exit = context.vm.read::(); + let value = context.vm.read::().into(); + let resume_kind = context.vm.read::().into(); + let is_return = context.vm.read::().into(); + Self::operation( + r#return, + exit, + value, + resume_kind, + is_return, + registers, + context, + ) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let r#return = context.vm.read::(); + let exit = context.vm.read::(); + let value = context.vm.read::().into(); + let resume_kind = context.vm.read::().into(); + let is_return = context.vm.read::().into(); + Self::operation( + r#return, + exit, + value, + resume_kind, + is_return, + registers, + context, + ) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let r#return = context.vm.read::(); + let exit = context.vm.read::(); + let value = context.vm.read::(); + let resume_kind = context.vm.read::(); + let is_return = context.vm.read::(); + Self::operation( + r#return, + exit, + value, + resume_kind, + is_return, + registers, + context, + ) + } +} diff --git a/core/engine/src/vm/opcode/generator/yield_stm.rs b/core/engine/src/vm/opcode/generator/yield_stm.rs index d83b7418f36..631cf89d447 100644 --- a/core/engine/src/vm/opcode/generator/yield_stm.rs +++ b/core/engine/src/vm/opcode/generator/yield_stm.rs @@ -1,6 +1,6 @@ use crate::{ builtins::async_generator::{AsyncGenerator, AsyncGeneratorState}, - vm::{opcode::Operation, CompletionRecord, CompletionType, GeneratorResumeKind}, + vm::{opcode::Operation, CompletionRecord, CompletionType, GeneratorResumeKind, Registers}, Context, JsResult, JsValue, }; @@ -11,15 +11,37 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct GeneratorYield; +impl GeneratorYield { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + context.vm.set_return_value(value.clone()); + Ok(CompletionType::Yield) + } +} + impl Operation for GeneratorYield { const NAME: &'static str = "GeneratorYield"; const INSTRUCTION: &'static str = "INST - GeneratorYield"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - context.vm.set_return_value(value); - Ok(CompletionType::Yield) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } @@ -30,12 +52,13 @@ impl Operation for GeneratorYield { #[derive(Debug, Clone, Copy)] pub(crate) struct AsyncGeneratorYield; -impl Operation for AsyncGeneratorYield { - const NAME: &'static str = "AsyncGeneratorYield"; - const INSTRUCTION: &'static str = "INST - AsyncGeneratorYield"; - const COST: u8 = 8; - - fn execute(context: &mut Context) -> JsResult { +impl AsyncGeneratorYield { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { // AsyncGeneratorYield ( value ) // https://tc39.es/ecma262/#sec-asyncgeneratoryield @@ -46,15 +69,15 @@ impl Operation for AsyncGeneratorYield { let async_generator_object = context .vm .frame() - .async_generator_object(&context.vm.stack) + .async_generator_object(registers) .expect("`AsyncGeneratorYield` must only be called inside async generators"); let async_generator_object = async_generator_object .downcast::() .expect("must be async generator object"); // 5. Let completion be NormalCompletion(value). - let value = context.vm.pop(); - let completion = Ok(value); + let value = registers.get(value); + let completion = Ok(value.clone()); // TODO: 6. Assert: The execution context stack has at least two elements. // TODO: 7. Let previousContext be the second to top element of the execution context stack. @@ -106,3 +129,24 @@ impl Operation for AsyncGeneratorYield { Ok(CompletionType::Yield) } } + +impl Operation for AsyncGeneratorYield { + const NAME: &'static str = "AsyncGeneratorYield"; + const INSTRUCTION: &'static str = "INST - AsyncGeneratorYield"; + const COST: u8 = 8; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/get/argument.rs b/core/engine/src/vm/opcode/get/argument.rs index dcfd8cb6670..9fe9685bb6e 100644 --- a/core/engine/src/vm/opcode/get/argument.rs +++ b/core/engine/src/vm/opcode/get/argument.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -12,14 +12,19 @@ pub(crate) struct GetArgument; impl GetArgument { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + index: usize, + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let value = context .vm .frame() .argument(index, &context.vm) .cloned() .unwrap_or_default(); - context.vm.push(value); + registers.set(dst, value); Ok(CompletionType::Normal) } } @@ -29,18 +34,21 @@ impl Operation for GetArgument { const INSTRUCTION: &'static str = "INST - GetArgument"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; - Self::operation(context, index) + let dst = context.vm.read::().into(); + Self::operation(index, dst, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; - Self::operation(context, index) + let dst = context.vm.read::().into(); + Self::operation(index, dst, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; - Self::operation(context, index) + let dst = context.vm.read::(); + Self::operation(index, dst, registers, context) } } diff --git a/core/engine/src/vm/opcode/get/function.rs b/core/engine/src/vm/opcode/get/function.rs index cbd680151bf..58240a9eff0 100644 --- a/core/engine/src/vm/opcode/get/function.rs +++ b/core/engine/src/vm/opcode/get/function.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{code_block::create_function_object_fast, opcode::Operation, CompletionType}, + vm::{code_block::create_function_object_fast, opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -12,11 +12,15 @@ pub(crate) struct GetFunction; impl GetFunction { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, dst: u32, index: usize) -> JsResult { - let rp = context.vm.frame().rp; + fn operation( + dst: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let code = context.vm.frame().code_block().constant_function(index); let function = create_function_object_fast(code, context); - context.vm.stack[(rp + dst) as usize] = function.into(); + registers.set(dst, function.into()); Ok(CompletionType::Normal) } } @@ -26,21 +30,21 @@ impl Operation for GetFunction { const INSTRUCTION: &'static str = "INST - GetFunction"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); let index = context.vm.read::() as usize; - Self::operation(context, dst, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); let index = context.vm.read::() as usize; - Self::operation(context, dst, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, dst, index) + Self::operation(dst, index, registers, context) } } diff --git a/core/engine/src/vm/opcode/get/name.rs b/core/engine/src/vm/opcode/get/name.rs index 32671a457b8..6b60ad1136b 100644 --- a/core/engine/src/vm/opcode/get/name.rs +++ b/core/engine/src/vm/opcode/get/name.rs @@ -1,6 +1,6 @@ use crate::{ error::JsNativeError, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -12,15 +12,19 @@ use crate::{ pub(crate) struct GetName; impl GetName { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; - let value = context.get_binding(&binding_locator)?.ok_or_else(|| { + let result = context.get_binding(&binding_locator)?.ok_or_else(|| { let name = binding_locator.name().to_std_string_escaped(); JsNativeError::reference().with_message(format!("{name} is not defined")) })?; - - context.vm.push(value); + registers.set(value, result); Ok(CompletionType::Normal) } } @@ -30,19 +34,22 @@ impl Operation for GetName { const INSTRUCTION: &'static str = "INST - GetName"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } } @@ -54,7 +61,7 @@ impl Operation for GetName { pub(crate) struct GetLocator; impl GetLocator { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation(index: usize, context: &mut Context) -> JsResult { let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; @@ -69,19 +76,19 @@ impl Operation for GetLocator { const INSTRUCTION: &'static str = "INST - GetLocator"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(index, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(index, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(_: &mut Registers, context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(index, context) } } @@ -94,16 +101,21 @@ impl Operation for GetLocator { pub(crate) struct GetNameAndLocator; impl GetNameAndLocator { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; - let value = context.get_binding(&binding_locator)?.ok_or_else(|| { + let result = context.get_binding(&binding_locator)?.ok_or_else(|| { let name = binding_locator.name().to_std_string_escaped(); JsNativeError::reference().with_message(format!("{name} is not defined")) })?; context.vm.frame_mut().binding_stack.push(binding_locator); - context.vm.push(value); + registers.set(value, result); Ok(CompletionType::Normal) } } @@ -113,19 +125,22 @@ impl Operation for GetNameAndLocator { const INSTRUCTION: &'static str = "INST - GetNameAndLocator"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } } @@ -137,14 +152,19 @@ impl Operation for GetNameAndLocator { pub(crate) struct GetNameOrUndefined; impl GetNameOrUndefined { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); let is_global = binding_locator.is_global(); context.find_runtime_binding(&mut binding_locator)?; - let value = if let Some(value) = context.get_binding(&binding_locator)? { + let result = if let Some(value) = context.get_binding(&binding_locator)? { value } else if is_global { JsValue::undefined() @@ -155,7 +175,7 @@ impl GetNameOrUndefined { .into()); }; - context.vm.push(value); + registers.set(value, result); Ok(CompletionType::Normal) } } @@ -165,18 +185,21 @@ impl Operation for GetNameOrUndefined { const INSTRUCTION: &'static str = "INST - GetNameOrUndefined"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } } diff --git a/core/engine/src/vm/opcode/get/private.rs b/core/engine/src/vm/opcode/get/private.rs index cca43b0186c..d42cd7f24e9 100644 --- a/core/engine/src/vm/opcode/get/private.rs +++ b/core/engine/src/vm/opcode/get/private.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -11,19 +11,24 @@ use crate::{ pub(crate) struct GetPrivateField; impl GetPrivateField { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + dst: u32, + object: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let name = context.vm.frame().code_block().constant_string(index); - let value = context.vm.pop(); - let base_obj = value.to_object(context)?; - + let object = registers.get(object); + let object = object.to_object(context)?; let name = context .vm .environments .resolve_private_identifier(name) .expect("private name must be in environment"); - let result = base_obj.private_get(&name, context)?; - context.vm.push(result); + let result = object.private_get(&name, context)?; + registers.set(dst, result); Ok(CompletionType::Normal) } } @@ -33,18 +38,24 @@ impl Operation for GetPrivateField { const INSTRUCTION: &'static str = "INST - GetPrivateField"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let object = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, object, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let object = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, object, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + let object = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, object, index, registers, context) } } diff --git a/core/engine/src/vm/opcode/get/property.rs b/core/engine/src/vm/opcode/get/property.rs index 40586ec471f..a05f3d7273a 100644 --- a/core/engine/src/vm/opcode/get/property.rs +++ b/core/engine/src/vm/opcode/get/property.rs @@ -1,7 +1,7 @@ use crate::{ object::{internal_methods::InternalMethodContext, shape::slot::SlotAttributes}, property::PropertyKey, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -18,24 +18,12 @@ impl GetPropertyByName { receiver: u32, value: u32, index: usize, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let receiver = context - .vm - .frame() - .read_value::<0>(operand_types, receiver, &context.vm); - let value = context - .vm - .frame() - .read_value::<1>(operand_types, value, &context.vm); - - let rp = context.vm.frame().rp; - let object = if let Some(object) = value.as_object() { - object.clone() - } else { - value.clone().to_object(context)? - }; + let receiver = registers.get(receiver); + let object = registers.get(value); + let object = object.to_object(context)?; let ic = &context.vm.frame().code_block().ic[index]; let object_borrowed = object.borrow(); @@ -51,12 +39,12 @@ impl GetPropertyByName { drop(object_borrowed); if slot.attributes.has_get() && result.is_object() { result = result.as_object().expect("should contain getter").call( - &receiver, + receiver, &[], context, )?; } - context.vm.stack[(rp + dst) as usize] = result; + registers.set(dst, result); return Ok(CompletionType::Normal); } @@ -65,7 +53,7 @@ impl GetPropertyByName { let key: PropertyKey = ic.name.clone().into(); let context = &mut InternalMethodContext::new(context); - let result = object.__get__(&key, receiver, context)?; + let result = object.__get__(&key, receiver.clone(), context)?; // Cache the property. let slot = *context.slot(); @@ -76,7 +64,7 @@ impl GetPropertyByName { ic.set(shape, slot); } - context.vm.stack[(rp + dst) as usize] = result; + registers.set(dst, result); Ok(CompletionType::Normal) } } @@ -86,31 +74,28 @@ impl Operation for GetPropertyByName { const INSTRUCTION: &'static str = "INST - GetPropertyByName"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::().into(); let receiver = context.vm.read::().into(); let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(dst, receiver, value, index, operand_types, context) + Self::operation(dst, receiver, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::().into(); let receiver = context.vm.read::().into(); let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(dst, receiver, value, index, operand_types, context) + Self::operation(dst, receiver, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); let receiver = context.vm.read::(); let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(dst, receiver, value, index, operand_types, context) + Self::operation(dst, receiver, value, index, registers, context) } } @@ -121,21 +106,18 @@ impl Operation for GetPropertyByName { #[derive(Debug, Clone, Copy)] pub(crate) struct GetPropertyByValue; -impl Operation for GetPropertyByValue { - const NAME: &'static str = "GetPropertyByValue"; - const INSTRUCTION: &'static str = "INST - GetPropertyByValue"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let key = context.vm.pop(); - let receiver = context.vm.pop(); - let value = context.vm.pop(); - let object = if let Some(object) = value.as_object() { - object.clone() - } else { - value.to_object(context)? - }; - +impl GetPropertyByValue { + fn operation( + dst: u32, + key: u32, + receiver: u32, + object: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let key = registers.get(key); + let object = registers.get(object); + let object = object.to_object(context)?; let key = key.to_property_key(context)?; // Fast Path @@ -144,20 +126,56 @@ impl Operation for GetPropertyByValue { let object_borrowed = object.borrow(); if let Some(element) = object_borrowed.properties().get_dense_property(index.get()) { - context.vm.push(element); + registers.set(dst, element); return Ok(CompletionType::Normal); } } } + let receiver = registers.get(receiver); + // Slow path: - let result = object.__get__(&key, receiver, &mut InternalMethodContext::new(context))?; + let result = object.__get__( + &key, + receiver.clone(), + &mut InternalMethodContext::new(context), + )?; - context.vm.push(result); + registers.set(dst, result); Ok(CompletionType::Normal) } } +impl Operation for GetPropertyByValue { + const NAME: &'static str = "GetPropertyByValue"; + const INSTRUCTION: &'static str = "INST - GetPropertyByValue"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let key = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(dst, key, receiver, object, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let key = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(dst, key, receiver, object, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + let key = context.vm.read::(); + let receiver = context.vm.read::(); + let object = context.vm.read::(); + Self::operation(dst, key, receiver, object, registers, context) + } +} + /// `GetPropertyByValuePush` implements the Opcode Operation for `Opcode::GetPropertyByValuePush` /// /// Operation: @@ -165,41 +183,74 @@ impl Operation for GetPropertyByValue { #[derive(Debug, Clone, Copy)] pub(crate) struct GetPropertyByValuePush; -impl Operation for GetPropertyByValuePush { - const NAME: &'static str = "GetPropertyByValuePush"; - const INSTRUCTION: &'static str = "INST - GetPropertyByValuePush"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let key = context.vm.pop(); - let receiver = context.vm.pop(); - let value = context.vm.pop(); - let object = if let Some(object) = value.as_object() { - object.clone() - } else { - value.to_object(context)? - }; - - let key = key.to_property_key(context)?; +impl GetPropertyByValuePush { + fn operation( + dst: u32, + key: u32, + receiver: u32, + object: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let key_value = registers.get(key); + let object = registers.get(object); + let object = object.to_object(context)?; + let key_value = key_value.to_property_key(context)?; - // Fast path: + // Fast Path if object.is_array() { - if let PropertyKey::Index(index) = &key { + if let PropertyKey::Index(index) = &key_value { let object_borrowed = object.borrow(); if let Some(element) = object_borrowed.properties().get_dense_property(index.get()) { - context.vm.push(key); - context.vm.push(element); + registers.set(key, key_value.into()); + registers.set(dst, element); return Ok(CompletionType::Normal); } } } + let receiver = registers.get(receiver); + // Slow path: - let result = object.__get__(&key, receiver, &mut InternalMethodContext::new(context))?; + let result = object.__get__( + &key_value, + receiver.clone(), + &mut InternalMethodContext::new(context), + )?; - context.vm.push(key); - context.vm.push(result); + registers.set(key, key_value.into()); + registers.set(dst, result); Ok(CompletionType::Normal) } } + +impl Operation for GetPropertyByValuePush { + const NAME: &'static str = "GetPropertyByValuePush"; + const INSTRUCTION: &'static str = "INST - GetPropertyByValuePush"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let key = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(dst, key, receiver, object, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let key = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(dst, key, receiver, object, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + let key = context.vm.read::(); + let receiver = context.vm.read::(); + let object = context.vm.read::(); + Self::operation(dst, key, receiver, object, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/global.rs b/core/engine/src/vm/opcode/global.rs index 49666dc4373..7d923c04a82 100644 --- a/core/engine/src/vm/opcode/global.rs +++ b/core/engine/src/vm/opcode/global.rs @@ -1,6 +1,6 @@ use crate::{vm::CompletionType, Context, JsResult}; -use super::Operation; +use super::{Operation, Registers}; /// `HasRestrictedGlobalProperty` implements the Opcode Operation for `Opcode::HasRestrictedGlobalProperty` /// @@ -12,10 +12,15 @@ use super::Operation; pub(crate) struct HasRestrictedGlobalProperty; impl HasRestrictedGlobalProperty { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + dst: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let name = &context.vm.frame().code_block().constant_string(index); let value = context.has_restricted_global_property(name)?; - context.vm.push(value); + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -25,19 +30,22 @@ impl Operation for HasRestrictedGlobalProperty { const INSTRUCTION: &'static str = "INST - HasRestrictedGlobalProperty"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } } @@ -51,10 +59,15 @@ impl Operation for HasRestrictedGlobalProperty { pub(crate) struct CanDeclareGlobalFunction; impl CanDeclareGlobalFunction { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + dst: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let name = &context.vm.frame().code_block().constant_string(index); let value = context.can_declare_global_function(name)?; - context.vm.push(value); + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -64,19 +77,22 @@ impl Operation for CanDeclareGlobalFunction { const INSTRUCTION: &'static str = "INST - CanDeclareGlobalFunction"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } } @@ -90,10 +106,15 @@ impl Operation for CanDeclareGlobalFunction { pub(crate) struct CanDeclareGlobalVar; impl CanDeclareGlobalVar { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + dst: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let name = &context.vm.frame().code_block().constant_string(index); let value = context.can_declare_global_var(name)?; - context.vm.push(value); + registers.set(dst, value.into()); Ok(CompletionType::Normal) } } @@ -103,19 +124,22 @@ impl Operation for CanDeclareGlobalVar { const INSTRUCTION: &'static str = "INST - CanDeclareGlobalVar"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } } @@ -131,16 +155,18 @@ pub(crate) struct CreateGlobalFunctionBinding; impl CreateGlobalFunctionBinding { #[allow(clippy::unnecessary_wraps)] fn operation( - context: &mut Context, + function: u32, index: usize, configurable: bool, + registers: &mut Registers, + context: &mut Context, ) -> JsResult { + let value = registers.get(function); let name = context.vm.frame().code_block().constant_string(index); - let value = context.vm.pop(); let function = value .as_object() - .expect("valeu should be an function") + .expect("value must be an function") .clone(); context.create_global_function_binding(name, function, configurable)?; @@ -153,22 +179,25 @@ impl Operation for CreateGlobalFunctionBinding { const INSTRUCTION: &'static str = "INST - CreateGlobalFunctionBinding"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); let configurable = context.vm.read::() != 0; let index = context.vm.read::() as usize; - Self::operation(context, index, configurable) + Self::operation(function, index, configurable, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); let configurable = context.vm.read::() != 0; let index = context.vm.read::() as usize; - Self::operation(context, index, configurable) + Self::operation(function, index, configurable, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); let configurable = context.vm.read::() != 0; let index = context.vm.read::() as usize; - Self::operation(context, index, configurable) + Self::operation(function, index, configurable, registers, context) } } @@ -200,19 +229,19 @@ impl Operation for CreateGlobalVarBinding { const INSTRUCTION: &'static str = "INST - CreateGlobalVarBinding"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let configurable = context.vm.read::() != 0; let index = context.vm.read::() as usize; Self::operation(context, index, configurable) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(_: &mut Registers, context: &mut Context) -> JsResult { let configurable = context.vm.read::() != 0; let index = context.vm.read::() as usize; Self::operation(context, index, configurable) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(_: &mut Registers, context: &mut Context) -> JsResult { let configurable = context.vm.read::() != 0; let index = context.vm.read::() as usize; Self::operation(context, index, configurable) diff --git a/core/engine/src/vm/opcode/iteration/for_in.rs b/core/engine/src/vm/opcode/iteration/for_in.rs index 0baa43e8dd6..37befea2d8b 100644 --- a/core/engine/src/vm/opcode/iteration/for_in.rs +++ b/core/engine/src/vm/opcode/iteration/for_in.rs @@ -1,7 +1,7 @@ use crate::{ builtins::{iterable::IteratorRecord, object::for_in_iterator::ForInIterator}, js_string, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -12,14 +12,13 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct CreateForInIterator; -impl Operation for CreateForInIterator { - const NAME: &'static str = "CreateForInIterator"; - const INSTRUCTION: &'static str = "INST - CreateForInIterator"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let object = context.vm.pop(); - +impl CreateForInIterator { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(value); let object = object.to_object(context)?; let iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), context); let next_method = iterator @@ -35,3 +34,24 @@ impl Operation for CreateForInIterator { Ok(CompletionType::Normal) } } + +impl Operation for CreateForInIterator { + const NAME: &'static str = "CreateForInIterator"; + const INSTRUCTION: &'static str = "INST - CreateForInIterator"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/iteration/get.rs b/core/engine/src/vm/opcode/iteration/get.rs index fc9aadef814..c4c42db7a94 100644 --- a/core/engine/src/vm/opcode/iteration/get.rs +++ b/core/engine/src/vm/opcode/iteration/get.rs @@ -1,6 +1,6 @@ use crate::{ builtins::iterable::IteratorHint, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -11,16 +11,37 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct GetIterator; +impl GetIterator { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let iterator = value.get_iterator(IteratorHint::Sync, context)?; + context.vm.frame_mut().iterators.push(iterator); + Ok(CompletionType::Normal) + } +} + impl Operation for GetIterator { const NAME: &'static str = "GetIterator"; const INSTRUCTION: &'static str = "INST - GetIterator"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { - let object = context.vm.pop(); - let iterator = object.get_iterator(IteratorHint::Sync, context)?; - context.vm.frame_mut().iterators.push(iterator); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } @@ -31,15 +52,36 @@ impl Operation for GetIterator { #[derive(Debug, Clone, Copy)] pub(crate) struct GetAsyncIterator; +impl GetAsyncIterator { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let iterator = value.get_iterator(IteratorHint::Async, context)?; + context.vm.frame_mut().iterators.push(iterator); + Ok(CompletionType::Normal) + } +} + impl Operation for GetAsyncIterator { const NAME: &'static str = "GetAsyncIterator"; const INSTRUCTION: &'static str = "INST - GetAsyncIterator"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { - let object = context.vm.pop(); - let iterator = object.get_iterator(IteratorHint::Async, context)?; - context.vm.frame_mut().iterators.push(iterator); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } diff --git a/core/engine/src/vm/opcode/iteration/iterator.rs b/core/engine/src/vm/opcode/iteration/iterator.rs index 2f734cf2953..4125ccc7143 100644 --- a/core/engine/src/vm/opcode/iteration/iterator.rs +++ b/core/engine/src/vm/opcode/iteration/iterator.rs @@ -1,8 +1,8 @@ use crate::{ builtins::{iterable::create_iter_result_object, Array}, js_string, - vm::{opcode::Operation, CompletionType, GeneratorResumeKind}, - Context, JsResult, JsValue, + vm::{opcode::Operation, CompletionType, GeneratorResumeKind, Registers}, + Context, JsResult, }; /// `IteratorNext` implements the Opcode Operation for `Opcode::IteratorNext` @@ -17,7 +17,7 @@ impl Operation for IteratorNext { const INSTRUCTION: &'static str = "INST - IteratorNext"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let mut iterator = context .vm .frame_mut() @@ -33,36 +33,6 @@ impl Operation for IteratorNext { } } -/// `IteratorNextWithoutPop` implements the Opcode Operation for `Opcode::IteratorNextWithoutPop` -/// -/// Operation: -/// - Calls the `next` method of `iterator`, updating its record with the next value. -#[derive(Debug, Clone, Copy)] -pub(crate) struct IteratorNextWithoutPop; - -impl Operation for IteratorNextWithoutPop { - const NAME: &'static str = "IteratorNextWithoutPop"; - const INSTRUCTION: &'static str = "INST - IteratorNextWithoutPop"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let mut iterator = context - .vm - .frame_mut() - .iterators - .pop() - .expect("iterator stack should have at least an iterator"); - - let result = iterator.step(context); - - context.vm.frame_mut().iterators.push(iterator); - - result?; - - Ok(CompletionType::Normal) - } -} - /// `IteratorFinishAsyncNext` implements the Opcode Operation for `Opcode::IteratorFinishAsyncNext`. /// /// Operation: @@ -71,12 +41,13 @@ impl Operation for IteratorNextWithoutPop { #[derive(Debug, Clone, Copy)] pub(crate) struct IteratorFinishAsyncNext; -impl Operation for IteratorFinishAsyncNext { - const NAME: &'static str = "IteratorFinishAsyncNext"; - const INSTRUCTION: &'static str = "INST - IteratorFinishAsyncNext"; - const COST: u8 = 5; - - fn execute(context: &mut Context) -> JsResult { +impl IteratorFinishAsyncNext { + fn operation( + resume_kind: u32, + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let mut iterator = context .vm .frame_mut() @@ -84,25 +55,43 @@ impl Operation for IteratorFinishAsyncNext { .pop() .expect("iterator on the call frame must exist"); - let generator_resume_kind = context.vm.pop().to_generator_resume_kind(); - - if matches!(generator_resume_kind, GeneratorResumeKind::Throw) { - context.vm.push(generator_resume_kind); + let resume_kind = registers.get(resume_kind).to_generator_resume_kind(); + if matches!(resume_kind, GeneratorResumeKind::Throw) { // If after awaiting the `next` call the iterator returned an error, it can be considered // as poisoned, meaning we can remove it from the iterator stack to avoid calling // cleanup operations on it. return Ok(CompletionType::Normal); } - let next_result = context.vm.pop(); + let value = registers.get(value); + iterator.update_result(value.clone(), context)?; + context.vm.frame_mut().iterators.push(iterator); + Ok(CompletionType::Normal) + } +} - iterator.update_result(next_result, context)?; +impl Operation for IteratorFinishAsyncNext { + const NAME: &'static str = "IteratorFinishAsyncNext"; + const INSTRUCTION: &'static str = "INST - IteratorFinishAsyncNext"; + const COST: u8 = 5; - context.vm.frame_mut().iterators.push(iterator); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let resume_kind = context.vm.read::().into(); + let value = context.vm.read::().into(); + Self::operation(resume_kind, value, registers, context) + } - context.vm.push(generator_resume_kind); - Ok(CompletionType::Normal) + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let resume_kind = context.vm.read::().into(); + let value = context.vm.read::().into(); + Self::operation(resume_kind, value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let resume_kind = context.vm.read::(); + let value = context.vm.read::(); + Self::operation(resume_kind, value, registers, context) } } @@ -113,12 +102,13 @@ impl Operation for IteratorFinishAsyncNext { #[derive(Debug, Clone, Copy)] pub(crate) struct IteratorResult; -impl Operation for IteratorResult { - const NAME: &'static str = "IteratorResult"; - const INSTRUCTION: &'static str = "INST - IteratorResult"; - const COST: u8 = 3; - - fn execute(context: &mut Context) -> JsResult { +impl IteratorResult { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let last_result = context .vm .frame() @@ -128,10 +118,29 @@ impl Operation for IteratorResult { .last_result() .object() .clone(); + registers.set(value, last_result.into()); + Ok(CompletionType::Normal) + } +} - context.vm.push(last_result); +impl Operation for IteratorResult { + const NAME: &'static str = "IteratorResult"; + const INSTRUCTION: &'static str = "INST - IteratorResult"; + const COST: u8 = 3; - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } @@ -142,12 +151,12 @@ impl Operation for IteratorResult { #[derive(Debug, Clone, Copy)] pub(crate) struct IteratorValue; -impl Operation for IteratorValue { - const NAME: &'static str = "IteratorValue"; - const INSTRUCTION: &'static str = "INST - IteratorValue"; - const COST: u8 = 5; - - fn execute(context: &mut Context) -> JsResult { +impl IteratorValue { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let mut iterator = context .vm .frame_mut() @@ -155,8 +164,8 @@ impl Operation for IteratorValue { .pop() .expect("iterator on the call frame must exist"); - let value = iterator.value(context)?; - context.vm.push(value); + let iter_value = iterator.value(context)?; + registers.set(value, iter_value); context.vm.frame_mut().iterators.push(iterator); @@ -164,37 +173,24 @@ impl Operation for IteratorValue { } } -/// `IteratorValueWithoutPop` implements the Opcode Operation for `Opcode::IteratorValueWithoutPop` -/// -/// Operation: -/// - Gets the `value` property of the current iterator record. -#[derive(Debug, Clone, Copy)] -pub(crate) struct IteratorValueWithoutPop; - -impl Operation for IteratorValueWithoutPop { - const NAME: &'static str = "IteratorValueWithoutPop"; - const INSTRUCTION: &'static str = "INST - IteratorValueWithoutPop"; +impl Operation for IteratorValue { + const NAME: &'static str = "IteratorValue"; + const INSTRUCTION: &'static str = "INST - IteratorValue"; const COST: u8 = 5; - fn execute(context: &mut Context) -> JsResult { - let mut iterator = context - .vm - .frame_mut() - .iterators - .pop() - .expect("iterator on the call frame must exist"); - - let value = if iterator.done() { - Ok(JsValue::undefined()) - } else { - iterator.value(context) - }; - - context.vm.frame_mut().iterators.push(iterator); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } - context.vm.push(value?); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } - Ok(CompletionType::Normal) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } @@ -205,23 +201,43 @@ impl Operation for IteratorValueWithoutPop { #[derive(Debug, Clone, Copy)] pub(crate) struct IteratorDone; -impl Operation for IteratorDone { - const NAME: &'static str = "IteratorDone"; - const INSTRUCTION: &'static str = "INST - IteratorDone"; - const COST: u8 = 3; - - fn execute(context: &mut Context) -> JsResult { - let done = context +impl IteratorDone { + #[allow(clippy::unnecessary_wraps)] + fn operation( + done: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = context .vm .frame() .iterators .last() .expect("iterator on the call frame must exist") .done(); + registers.set(done, value.into()); + Ok(CompletionType::Normal) + } +} - context.vm.push(done); +impl Operation for IteratorDone { + const NAME: &'static str = "IteratorDone"; + const INSTRUCTION: &'static str = "INST - IteratorDone"; + const COST: u8 = 3; - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let done = context.vm.read::().into(); + Self::operation(done, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let done = context.vm.read::().into(); + Self::operation(done, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let done = context.vm.read::(); + Self::operation(done, registers, context) } } @@ -232,19 +248,20 @@ impl Operation for IteratorDone { #[derive(Debug, Clone, Copy)] pub(crate) struct IteratorReturn; -impl Operation for IteratorReturn { - const NAME: &'static str = "IteratorReturn"; - const INSTRUCTION: &'static str = "INST - IteratorReturn"; - const COST: u8 = 8; - - fn execute(context: &mut Context) -> JsResult { +impl IteratorReturn { + fn operation( + value: u32, + called: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let Some(record) = context.vm.frame_mut().iterators.pop() else { - context.vm.push(false); + registers.set(called, false.into()); return Ok(CompletionType::Normal); }; if record.done() { - context.vm.push(false); + registers.set(called, false.into()); return Ok(CompletionType::Normal); } @@ -252,23 +269,47 @@ impl Operation for IteratorReturn { .iterator() .get_method(js_string!("return"), context)? else { - context.vm.push(false); + registers.set(called, false.into()); return Ok(CompletionType::Normal); }; - let return_value = context.vm.get_return_value(); + let old_return_value = context.vm.get_return_value(); - let value = ret.call(&record.iterator().clone().into(), &[], context)?; + let return_value = ret.call(&record.iterator().clone().into(), &[], context)?; - context.vm.set_return_value(return_value); + context.vm.set_return_value(old_return_value); - context.vm.push(value); - context.vm.push(true); + registers.set(value, return_value); + registers.set(called, true.into()); Ok(CompletionType::Normal) } } +impl Operation for IteratorReturn { + const NAME: &'static str = "IteratorReturn"; + const INSTRUCTION: &'static str = "INST - IteratorReturn"; + const COST: u8 = 8; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let called = context.vm.read::().into(); + Self::operation(value, called, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let called = context.vm.read::().into(); + Self::operation(value, called, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let called = context.vm.read::(); + Self::operation(value, called, registers, context) + } +} + /// `IteratorToArray` implements the Opcode Operation for `Opcode::IteratorToArray` /// /// Operation: @@ -276,12 +317,12 @@ impl Operation for IteratorReturn { #[derive(Debug, Clone, Copy)] pub(crate) struct IteratorToArray; -impl Operation for IteratorToArray { - const NAME: &'static str = "IteratorToArray"; - const INSTRUCTION: &'static str = "INST - IteratorToArray"; - const COST: u8 = 8; - - fn execute(context: &mut Context) -> JsResult { +impl IteratorToArray { + fn operation( + array: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let mut iterator = context .vm .frame_mut() @@ -314,12 +355,30 @@ impl Operation for IteratorToArray { } context.vm.frame_mut().iterators.push(iterator); + let result = Array::create_array_from_list(values, context); + registers.set(array, result.into()); + Ok(CompletionType::Normal) + } +} - let array = Array::create_array_from_list(values, context); +impl Operation for IteratorToArray { + const NAME: &'static str = "IteratorToArray"; + const INSTRUCTION: &'static str = "INST - IteratorToArray"; + const COST: u8 = 8; - context.vm.push(array); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::().into(); + Self::operation(array, registers, context) + } - Ok(CompletionType::Normal) + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::().into(); + Self::operation(array, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::(); + Self::operation(array, registers, context) } } @@ -330,15 +389,37 @@ impl Operation for IteratorToArray { #[derive(Debug, Clone, Copy)] pub(crate) struct IteratorStackEmpty; +impl IteratorStackEmpty { + #[allow(clippy::unnecessary_wraps)] + fn operation( + empty: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let is_empty = context.vm.frame().iterators.is_empty(); + registers.set(empty, is_empty.into()); + Ok(CompletionType::Normal) + } +} + impl Operation for IteratorStackEmpty { const NAME: &'static str = "IteratorStackEmpty"; const INSTRUCTION: &'static str = "INST - IteratorStackEmpty"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let is_empty = context.vm.frame().iterators.is_empty(); - context.vm.push(is_empty); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let empty = context.vm.read::().into(); + Self::operation(empty, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let empty = context.vm.read::().into(); + Self::operation(empty, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let empty = context.vm.read::(); + Self::operation(empty, registers, context) } } @@ -349,19 +430,41 @@ impl Operation for IteratorStackEmpty { #[derive(Debug, Clone, Copy)] pub(crate) struct CreateIteratorResult; +impl CreateIteratorResult { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + done: bool, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let val = registers.get(value); + let result = create_iter_result_object(val.clone(), done, context); + registers.set(value, result); + Ok(CompletionType::Normal) + } +} + impl Operation for CreateIteratorResult { const NAME: &'static str = "CreateIteratorResult"; const INSTRUCTION: &'static str = "INST - CreateIteratorResult"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); let done = context.vm.read::() != 0; + Self::operation(value, done, registers, context) + } - let result = create_iter_result_object(value, done, context); - - context.vm.push(result); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let done = context.vm.read::() != 0; + Self::operation(value, done, registers, context) + } - Ok(CompletionType::Normal) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let done = context.vm.read::() != 0; + Self::operation(value, done, registers, context) } } diff --git a/core/engine/src/vm/opcode/iteration/loop_ops.rs b/core/engine/src/vm/opcode/iteration/loop_ops.rs index dc22333bef8..f34beab1dc5 100644 --- a/core/engine/src/vm/opcode/iteration/loop_ops.rs +++ b/core/engine/src/vm/opcode/iteration/loop_ops.rs @@ -1,6 +1,6 @@ use crate::JsNativeError; use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -16,7 +16,7 @@ impl Operation for IncrementLoopIteration { const INSTRUCTION: &'static str = "INST - IncrementLoopIteration"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let max = context.vm.runtime_limits.loop_iteration_limit(); let frame = context.vm.frame_mut(); let previous_iteration_count = frame.loop_iteration_count; diff --git a/core/engine/src/vm/opcode/locals/mod.rs b/core/engine/src/vm/opcode/locals/mod.rs index 20d4e626331..0af5596bbaa 100644 --- a/core/engine/src/vm/opcode/locals/mod.rs +++ b/core/engine/src/vm/opcode/locals/mod.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsNativeError, JsResult, }; @@ -13,12 +13,14 @@ pub(crate) struct PopIntoLocal; impl PopIntoLocal { #[allow(clippy::unnecessary_wraps)] #[allow(clippy::needless_pass_by_value)] - fn operation(dst: u32, context: &mut Context) -> JsResult { + fn operation( + src: u32, + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { context.vm.frame_mut().local_binings_initialized[dst as usize] = true; - let value = context.vm.pop(); - - let rp = context.vm.frame().rp; - context.vm.stack[(rp + dst) as usize] = value; + registers.set(dst, registers.get(src).clone()); Ok(CompletionType::Normal) } } @@ -28,19 +30,22 @@ impl Operation for PopIntoLocal { const INSTRUCTION: &'static str = "INST - PopIntoLocal"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { - let dst = u32::from(context.vm.read::()); - Self::operation(dst, context) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let src = context.vm.read::().into(); + let dst = context.vm.read::().into(); + Self::operation(src, dst, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let dst = u32::from(context.vm.read::()); - Self::operation(dst, context) + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let src = context.vm.read::().into(); + let dst = context.vm.read::().into(); + Self::operation(src, dst, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let src = context.vm.read::(); let dst = context.vm.read::(); - Self::operation(dst, context) + Self::operation(src, dst, registers, context) } } @@ -54,15 +59,18 @@ pub(crate) struct PushFromLocal; impl PushFromLocal { #[allow(clippy::unnecessary_wraps)] #[allow(clippy::needless_pass_by_value)] - fn operation(dst: u32, context: &mut Context) -> JsResult { - if !context.vm.frame().local_binings_initialized[dst as usize] { + fn operation( + src: u32, + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + if !context.vm.frame().local_binings_initialized[src as usize] { return Err(JsNativeError::reference() .with_message("access to uninitialized binding") .into()); } - let rp = context.vm.frame().rp; - let value = context.vm.stack[(rp + dst) as usize].clone(); - context.vm.push(value); + registers.set(dst, registers.get(src).clone()); Ok(CompletionType::Normal) } } @@ -72,18 +80,21 @@ impl Operation for PushFromLocal { const INSTRUCTION: &'static str = "INST - PushFromLocal"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { - let dst = u32::from(context.vm.read::()); - Self::operation(dst, context) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let src = context.vm.read::().into(); + let dst = context.vm.read::().into(); + Self::operation(src, dst, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let dst = u32::from(context.vm.read::()); - Self::operation(dst, context) + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let src = context.vm.read::().into(); + let dst = context.vm.read::().into(); + Self::operation(src, dst, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let src = context.vm.read::(); let dst = context.vm.read::(); - Self::operation(dst, context) + Self::operation(src, dst, registers, context) } } diff --git a/core/engine/src/vm/opcode/meta/mod.rs b/core/engine/src/vm/opcode/meta/mod.rs index c93fd5be4d9..7303b5f7839 100644 --- a/core/engine/src/vm/opcode/meta/mod.rs +++ b/core/engine/src/vm/opcode/meta/mod.rs @@ -2,7 +2,7 @@ use std::unreachable; use crate::{ module::ModuleKind, - vm::{opcode::Operation, ActiveRunnable, CompletionType}, + vm::{opcode::Operation, ActiveRunnable, CompletionType, Registers}, Context, JsObject, JsResult, JsValue, }; @@ -13,12 +13,13 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct NewTarget; -impl Operation for NewTarget { - const NAME: &'static str = "NewTarget"; - const INSTRUCTION: &'static str = "INST - NewTarget"; - const COST: u8 = 2; - - fn execute(context: &mut Context) -> JsResult { +impl NewTarget { + #[allow(clippy::unnecessary_wraps)] + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let new_target = if let Some(new_target) = context .vm .environments @@ -30,11 +31,32 @@ impl Operation for NewTarget { } else { JsValue::undefined() }; - context.vm.push(new_target); + registers.set(dst, new_target); Ok(CompletionType::Normal) } } +impl Operation for NewTarget { + const NAME: &'static str = "NewTarget"; + const INSTRUCTION: &'static str = "INST - NewTarget"; + const COST: u8 = 2; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) + } +} + /// `ImportMeta` implements the Opcode Operation for `Opcode::ImportMeta` /// /// Operation: @@ -42,12 +64,13 @@ impl Operation for NewTarget { #[derive(Debug, Clone, Copy)] pub(crate) struct ImportMeta; -impl Operation for ImportMeta { - const NAME: &'static str = "ImportMeta"; - const INSTRUCTION: &'static str = "INST - ImportMeta"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { +impl ImportMeta { + #[allow(clippy::unnecessary_wraps)] + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { // Meta Properties // // ImportMeta : import . meta @@ -90,8 +113,29 @@ impl Operation for ImportMeta { // b. Return importMeta. // f. Return importMeta. - context.vm.push(import_meta); + registers.set(dst, import_meta.into()); Ok(CompletionType::Normal) } } + +impl Operation for ImportMeta { + const NAME: &'static str = "ImportMeta"; + const INSTRUCTION: &'static str = "INST - ImportMeta"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/mod.rs b/core/engine/src/vm/opcode/mod.rs index 1ebc757d079..3c65fcc987e 100644 --- a/core/engine/src/vm/opcode/mod.rs +++ b/core/engine/src/vm/opcode/mod.rs @@ -1,7 +1,10 @@ use std::iter::FusedIterator; /// The opcodes of the vm. -use crate::{vm::CompletionType, Context, JsResult, JsValue}; +use crate::{ + vm::{CompletionType, Registers}, + Context, JsResult, JsValue, +}; // Operation modules mod arguments; @@ -13,7 +16,6 @@ mod control_flow; mod copy; mod define; mod delete; -mod dup; mod environment; mod generator; mod get; @@ -26,10 +28,8 @@ mod new; mod nop; mod pop; mod push; -mod require; mod rest_parameter; mod set; -mod swap; mod switch; mod templates; mod to; @@ -54,8 +54,6 @@ pub(crate) use define::*; #[doc(inline)] pub(crate) use delete::*; #[doc(inline)] -pub(crate) use dup::*; -#[doc(inline)] pub(crate) use environment::*; #[doc(inline)] pub(crate) use generator::*; @@ -82,14 +80,10 @@ pub(crate) use push::*; #[doc(inline)] pub(crate) use r#await::*; #[doc(inline)] -pub(crate) use require::*; -#[doc(inline)] pub(crate) use rest_parameter::*; #[doc(inline)] pub(crate) use set::*; #[doc(inline)] -pub(crate) use swap::*; -#[doc(inline)] pub(crate) use switch::*; #[doc(inline)] pub(crate) use templates::*; @@ -156,6 +150,16 @@ impl PartialEq for VaryingOperand { } impl VaryingOperand { + fn new(value: u32) -> Self { + if u8::try_from(value).is_ok() { + Self::u8(value as u8) + } else if u16::try_from(value).is_ok() { + Self::u16(value as u16) + } else { + Self::u32(value) + } + } + #[must_use] pub(crate) fn u8(value: u8) -> Self { Self { @@ -185,16 +189,6 @@ impl VaryingOperand { pub(crate) const fn kind(self) -> VaryingOperandKind { self.kind } - #[must_use] - pub(crate) fn to_string(self, operand_types: u8) -> String { - let type_ = (operand_types >> (N * 2)) & 0x0000_0011; - match type_ { - 0b0000_0000 => format!("reg{}", self.value), - 0b0000_0001 => format!("arg{}", self.value), - 0b0000_0010 => format!("{}", self.value), - _ => unreachable!("unknown operand kind"), - } - } } trait BytecodeConversion: Sized { @@ -371,6 +365,81 @@ impl BytecodeConversion for ThinVec { } } +impl BytecodeConversion for ThinVec<(u32, u32)> { + fn to_bytecode(&self, bytes: &mut Vec) { + let len = VaryingOperand::new(self.len() as u32); + match len.kind() { + VaryingOperandKind::U8 => u8::to_bytecode(&(self.len() as u8), bytes), + VaryingOperandKind::U16 => u16::to_bytecode(&(self.len() as u16), bytes), + VaryingOperandKind::U32 => u32::to_bytecode(&(self.len() as u32), bytes), + } + for item in self { + bytes.extend_from_slice(&item.0.to_ne_bytes()); + bytes.extend_from_slice(&item.1.to_ne_bytes()); + } + } + fn from_bytecode(bytes: &[u8], pc: &mut usize, varying_kind: VaryingOperandKind) -> Self { + let count = match varying_kind { + VaryingOperandKind::U8 => u8::from_bytecode(bytes, pc, varying_kind).into(), + VaryingOperandKind::U16 => u16::from_bytecode(bytes, pc, varying_kind).into(), + VaryingOperandKind::U32 => u32::from_bytecode(bytes, pc, varying_kind), + }; + let mut result = Self::with_capacity(count as usize); + for _ in 0..count { + let one = read::(bytes, *pc); + *pc += size_of::(); + let two = read::(bytes, *pc); + *pc += size_of::(); + result.push((one, two)); + } + result + } +} + +impl BytecodeConversion for ThinVec { + fn to_bytecode(&self, bytes: &mut Vec) { + if let Some(first) = self.first() { + match first.kind() { + VaryingOperandKind::U8 => u8::to_bytecode(&(self.len() as u8), bytes), + VaryingOperandKind::U16 => u16::to_bytecode(&(self.len() as u16), bytes), + VaryingOperandKind::U32 => u32::to_bytecode(&(self.len() as u32), bytes), + } + } else { + u8::to_bytecode(&0, bytes); + } + for item in self { + match item.kind() { + VaryingOperandKind::U8 => u8::to_bytecode(&(item.value() as u8), bytes), + VaryingOperandKind::U16 => u16::to_bytecode(&(item.value() as u16), bytes), + VaryingOperandKind::U32 => u32::to_bytecode(&item.value(), bytes), + } + } + } + fn from_bytecode(bytes: &[u8], pc: &mut usize, varying_kind: VaryingOperandKind) -> Self { + let count = match varying_kind { + VaryingOperandKind::U8 => u8::from_bytecode(bytes, pc, varying_kind).into(), + VaryingOperandKind::U16 => u16::from_bytecode(bytes, pc, varying_kind).into(), + VaryingOperandKind::U32 => u32::from_bytecode(bytes, pc, varying_kind), + }; + let mut result = Self::with_capacity(count as usize); + for _ in 0..count { + let item = match varying_kind { + VaryingOperandKind::U8 => { + VaryingOperand::u8(u8::from_bytecode(bytes, pc, varying_kind)) + } + VaryingOperandKind::U16 => { + VaryingOperand::u16(u16::from_bytecode(bytes, pc, varying_kind)) + } + VaryingOperandKind::U32 => { + VaryingOperand::u32(u32::from_bytecode(bytes, pc, varying_kind)) + } + }; + result.push(item); + } + result + } +} + /// Generate [`Opcode`]s and [`Instruction`]s enums. macro_rules! generate_opcodes { ( name $name:ident ) => { $name }; @@ -446,17 +515,18 @@ macro_rules! generate_opcodes { Self::INSTRUCTIONS[self as usize] } - const SPEND_FNS: [fn(&mut Context, &mut u32) -> JsResult; Self::MAX] = [ + const SPEND_FNS: [fn(&mut Registers, &mut Context, &mut u32) -> JsResult; Self::MAX] = [ $( $mapping)?)>::spend_budget_and_execute),*, ]; /// Spends the cost of this opcode into the provided budget and executes it. pub(super) fn spend_budget_and_execute( self, + registers: &mut Registers, context: &mut Context, budget: &mut u32 ) -> JsResult { - Self::SPEND_FNS[self as usize](context, budget) + Self::SPEND_FNS[self as usize](registers, context, budget) } const COSTS: [u8; Self::MAX] = [ @@ -468,14 +538,14 @@ macro_rules! generate_opcodes { Self::COSTS[self as usize] } - const EXECUTE_FNS: [fn(&mut Context) -> JsResult; Self::MAX * 3] = [ + const EXECUTE_FNS: [fn(&mut Registers, &mut Context) -> JsResult; Self::MAX * 3] = [ $( $mapping)?)>::execute),*, - $( $mapping)?)>::execute_with_u16_operands),*, - $( $mapping)?)>::execute_with_u32_operands),* + $( $mapping)?)>::execute_u16),*, + $( $mapping)?)>::execute_u32),* ]; - pub(super) fn execute(self, context: &mut Context) -> JsResult { - Self::EXECUTE_FNS[self as usize](context) + pub(super) fn execute(self, registers: &mut Registers, context: &mut Context) -> JsResult { + Self::EXECUTE_FNS[self as usize](registers, context) } } @@ -575,205 +645,167 @@ pub(crate) trait Operation { const COST: u8; /// Execute opcode with [`VaryingOperandKind::U8`] sized [`VaryingOperand`]s. - fn execute(context: &mut Context) -> JsResult; + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult; /// Execute opcode with [`VaryingOperandKind::U16`] sized [`VaryingOperand`]s. /// - /// By default if not implemented will call [`Reserved::execute_with_u16_operands()`] which panics. - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - Reserved::execute_with_u16_operands(context) + /// By default if not implemented will call [`Reserved::execute_u16()`] which panics. + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + Reserved::execute_u16(registers, context) } /// Execute opcode with [`VaryingOperandKind::U32`] sized [`VaryingOperand`]s. /// - /// By default if not implemented will call [`Reserved::execute_with_u32_operands()`] which panics. - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - Reserved::execute_with_u32_operands(context) + /// By default if not implemented will call [`Reserved::execute_u32()`] which panics. + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + Reserved::execute_u32(registers, context) } /// Spends the cost of this operation into `budget` and runs `execute`. fn spend_budget_and_execute( + registers: &mut Registers, context: &mut Context, budget: &mut u32, ) -> JsResult { *budget = budget.saturating_sub(u32::from(Self::COST)); - Self::execute(context) + Self::execute(registers, context) } } generate_opcodes! { /// Pop the top value from the stack. /// - /// Operands: - /// - /// Stack: value **=>** + /// - Stack: value **=>** Pop, - /// Push a copy of the top value on the stack. - /// - /// Operands: - /// - /// Stack: value **=>** value, value - Dup, - - /// Swap the top two values on the stack. - /// - /// Operands: - /// - /// Stack: second, first **=>** first, second - Swap, - - /// Rotates the top `n` values of the stack to the left by `1`. - /// - /// Equivalent to calling [`slice::rotate_left`] with argument `1` on the top `n` values of the - /// stack. - /// - /// Operands: n: `u8` - /// - /// Stack: v\[n\], v\[n-1\], ... , v\[1\], v\[0\] **=>** v\[n-1\], ... , v\[1\], v\[0\], v\[n\] - RotateLeft { n: u8 }, - - /// Rotates the top `n` values of the stack to the right by `1`. - /// - /// Equivalent to calling [`slice::rotate_right`] with argument `1` on the top `n` values of the - /// stack. - /// - /// Operands: n: `u8` - /// - /// Stack: v\[n\], v\[n-1\], ... , v\[1\], v\[0\] **=>** v\[0\], v\[n\], v\[n-1\], ... , v\[1\] - RotateRight { n: u8 }, - /// Push integer `0` on the stack. /// - /// Operands: - /// - /// Stack: **=>** `0` - PushZero, + /// - Registers: + /// - Output: dst + PushZero { dst: VaryingOperand }, /// Push integer `1` on the stack. /// - /// Operands: - /// - /// Stack: **=>** `1` - PushOne, + /// - Registers: + /// - Output: dst + PushOne { dst: VaryingOperand }, /// Push `i8` value on the stack. /// - /// Operands: value: `i8` - /// - /// Stack: **=>** value - PushInt8 { value: i8 }, + /// - Operands: + /// - value: `i8` + /// - Registers: + /// - Output: dst + PushInt8 { dst: VaryingOperand, value: i8 }, /// Push i16 value on the stack. /// - /// Operands: value: `i16` - /// - /// Stack: **=>** value - PushInt16 { value: i16 }, + /// - Operands: + /// - value: `i16` + /// - Registers: + /// - Output: dst + PushInt16 { dst: VaryingOperand, value: i16 }, /// Push i32 value on the stack. /// - /// Operands: value: `i32` - /// - /// Stack: **=>** value - PushInt32 { value: i32 }, + /// - Operands: + /// - value: `i32` + /// - Registers: + /// - Output: dst + PushInt32 { dst: VaryingOperand, value: i32 }, /// Push `f32` value on the stack. /// - /// Operands: value: `f32` - /// - /// Stack: **=>** value - PushFloat { value: f32 }, + /// - Operands: + /// - value: `f32` + /// - Registers: + /// - Output: dst + PushFloat { dst: VaryingOperand, value: f32 }, /// Push `f64` value on the stack. /// - /// Operands: value: `f64` - /// - /// Stack: **=>** value - PushDouble { value: f64 }, + /// - Operands: + /// - value: `f64` + /// - Registers: + /// - Output: dst + PushDouble { dst: VaryingOperand, value: f64 }, /// Push `NaN` integer on the stack. /// - /// Operands: - /// - /// Stack: **=>** `NaN` - PushNaN, + /// - Registers: + /// - Output: dst + PushNaN { dst: VaryingOperand }, /// Push `Infinity` value on the stack. /// - /// Operands: - /// - /// Stack: **=>** `Infinity` - PushPositiveInfinity, + /// - Registers: + /// - Output: dst + PushPositiveInfinity { dst: VaryingOperand }, /// Push `-Infinity` value on the stack. /// - /// Operands: - /// - /// Stack: **=>** `-Infinity` - PushNegativeInfinity, + /// - Registers: + /// - Output: dst + PushNegativeInfinity { dst: VaryingOperand }, /// Push `null` value on the stack. /// - /// Operands: - /// - /// Stack: **=>** `null` - PushNull, + /// - Registers: + /// - Output: dst + PushNull { dst: VaryingOperand }, /// Push `true` value on the stack. /// - /// Operands: - /// - /// Stack: **=>** `true` - PushTrue, + /// - Registers: + /// - Output: dst + PushTrue { dst: VaryingOperand }, /// Push `false` value on the stack. /// - /// Operands: - /// - /// Stack: **=>** `false` - PushFalse, + /// - Registers: + /// - Output: dst + PushFalse { dst: VaryingOperand }, /// Push `undefined` value on the stack. /// - /// Operands: - /// - /// Stack: **=>** `undefined` - PushUndefined, + /// - Registers: + /// - Output: dst + PushUndefined { dst: VaryingOperand }, /// Push literal value on the stack. /// /// Like strings and bigints. The index operand is used to index into the `literals` /// array to get the value. /// - /// Operands: index: `VaryingOperand` - /// - /// Stack: **=>** (`literals[index]`) - PushLiteral { index: VaryingOperand }, + /// - Operands: + /// - index: `VaryingOperand` + /// - Registers: + /// - Output: dst + PushLiteral { dst: VaryingOperand, index: VaryingOperand }, /// Push regexp value on the stack. /// - /// Operands: pattern_index: `VaryingOperand`, flags: `VaryingOperand` - /// - /// Stack: **=>** regexp - PushRegExp { pattern_index: VaryingOperand, flags_index: VaryingOperand }, + /// - Operands: + /// - pattern_index: `VaryingOperand` + /// - flags: `VaryingOperand` + /// - Registers: + /// - Output: dst + PushRegExp { dst: VaryingOperand, pattern_index: VaryingOperand, flags_index: VaryingOperand }, /// Push empty object `{}` value on the stack. /// - /// Operands: - /// - /// Stack: **=>** `{}` - PushEmptyObject, + /// - Registers: + /// - Output: dst + PushEmptyObject { dst: VaryingOperand }, /// Get the prototype of a superclass and push it on the stack. /// /// Additionally this sets the `[[prototype]]` of the class and the `DERIVED` flag. /// - /// Operands: - /// - /// Stack: class, superclass **=>** class, superclass.prototype + /// - Registers: + /// - Input: class, superclass + /// - Output: dst PushClassPrototype { - operand_types: u8, dst: VaryingOperand, class: VaryingOperand, superclass: VaryingOperand @@ -781,11 +813,10 @@ generate_opcodes! { /// Set the prototype of a class object. /// - /// Operands: - /// - /// Stack: class, prototype **=>** class.prototype + /// - Registers: + /// - Input: class, prototype + /// - Output: dst SetClassPrototype { - operand_types: u8, dst: VaryingOperand, prototype: VaryingOperand, class: VaryingOperand @@ -793,762 +824,864 @@ generate_opcodes! { /// Set home object internal slot of an object literal method. /// - /// Operands: - /// - /// Stack: home, function **=>** home, function - SetHomeObject, + /// - Registers: + /// - Input: function, home + SetHomeObject { + function: VaryingOperand, + home: VaryingOperand + }, /// Set the prototype of an object if the value is an object or null. /// - /// Operands: - /// - /// Stack: object, value **=>** - SetPrototype, + /// - Registers: + /// - Input: object, prototype + SetPrototype { + object: VaryingOperand, + prototype: VaryingOperand + }, /// Push an empty array value on the stack. /// - /// Operands: - /// - /// Stack: **=>** `[]` - PushNewArray, + /// - Registers: + /// - Output: dst + PushNewArray { dst: VaryingOperand }, /// Push a value to an array. /// - /// Operands: - /// - /// Stack: array, value **=>** array - PushValueToArray, + /// - Registers: + /// - Input: array, value + PushValueToArray { value: VaryingOperand, array: VaryingOperand }, /// Push an empty element/hole to an array. /// - /// Operands: - /// - /// Stack: array **=>** array - PushElisionToArray, + /// - Registers: + /// - Input: array + PushElisionToArray { array: VaryingOperand }, /// Push all iterator values to an array. /// - /// Operands: - /// - /// Stack: array, iterator, next_method **=>** array - PushIteratorToArray, + /// - Registers: + /// - Input: array + PushIteratorToArray { array: VaryingOperand }, /// Binary `+` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs + rhs) - Add { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Add { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `-` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs - rhs) - Sub { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Sub { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `/` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs / rhs) - Div { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Div { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `*` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs * rhs) - Mul { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Mul { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `%` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs % rhs) - Mod { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Mod { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `**` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs ** rhs) - Pow { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + Pow { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `>>` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs >> rhs) - ShiftRight { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers + /// - Input: lhs, rhs + /// - Output: dst + ShiftRight { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `<<` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** `(lhs << rhs)` - ShiftLeft { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + ShiftLeft { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `>>>` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs >>> rhs) - UnsignedShiftRight { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + UnsignedShiftRight { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary bitwise `|` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs | rhs) - BitOr { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + BitOr { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary bitwise `&` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs & rhs) - BitAnd { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + BitAnd { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary bitwise `^` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs ^ rhs) - BitXor { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + BitXor { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Unary bitwise `~` operator. /// - /// Operands: - /// - /// Stack: value **=>** ~value - BitNot { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + BitNot { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `in` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `in` rhs) - In { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + In { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `in` operator for private names. /// - /// Operands: index: `u32` - /// - /// Stack: rhs **=>** (private_name `in` rhs) - InPrivate { operand_types: u8, dst: VaryingOperand, index: VaryingOperand, rhs: VaryingOperand }, + /// - Operands: index: `u32` + /// - Registers: + /// - Input: rhs + /// - Output: dst + InPrivate { dst: VaryingOperand, index: VaryingOperand, rhs: VaryingOperand }, /// Binary `==` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `==` rhs) - Eq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + Eq { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `===` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `===` rhs) - StrictEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + StrictEq { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `!=` operator. - NotEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + NotEq { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `!==` operator. - StrictNotEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + StrictNotEq { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `>` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs > rhs) - GreaterThan { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + GreaterThan { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `>=` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs >= rhs) - GreaterThanOrEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + GreaterThanOrEq { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `<` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** `(lhs < rhs)` - LessThan { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + LessThan { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `<=` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** `(lhs <= rhs)` - LessThanOrEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + LessThanOrEq { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `instanceof` operator. /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs instanceof rhs) - InstanceOf { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, + /// - Registers: + /// - Input: lhs, rhs + /// - Output: dst + InstanceOf { dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary logical `&&` operator. /// /// This is a short-circuit operator, if the `lhs` value is `false`, then it jumps to `exit` address. /// - /// Operands: exit: `u32` - /// - /// Stack: lhs, rhs **=>** (lhs && rhs) - LogicalAnd { operand_types: u8, exit: u32, lhs: VaryingOperand }, + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: value + /// - Output: value + LogicalAnd { address: u32, value: VaryingOperand }, /// Binary logical `||` operator. /// /// This is a short-circuit operator, if the `lhs` value is `true`, then it jumps to `exit` address. /// - /// Operands: exit: `u32` - /// - /// Stack: lhs, rhs **=>** (lhs || rhs) - LogicalOr { operand_types: u8, exit: u32, lhs: VaryingOperand }, + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: value + /// - Output: value + LogicalOr { address: u32, value: VaryingOperand }, /// Binary `??` operator. /// /// This is a short-circuit operator, if the `lhs` value is **not** `null` or `undefined`, /// then it jumps to `exit` address. /// - /// Operands: exit: `u32` - /// - /// Stack: lhs, rhs **=>** (lhs ?? rhs) - Coalesce { operand_types: u8, exit: u32, lhs: VaryingOperand }, + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: value + /// - Output: value + Coalesce { address: u32, value: VaryingOperand }, /// Unary `typeof` operator. /// - /// Operands: - /// - /// Stack: value **=>** (`typeof` value) - TypeOf, - - /// Unary `void` operator. - /// - /// Operands: - /// - /// Stack: value **=>** `undefined` - Void, + /// - Registers: + /// - Input: value + /// - Output: value + TypeOf { value: VaryingOperand }, /// Unary logical `!` operator. /// - /// Operands: - /// - /// Stack: value **=>** (!value) - LogicalNot, + /// - Registers: + /// - Input: value + /// - Output: value + LogicalNot { value: VaryingOperand }, /// Unary `+` operator. /// - /// Operands: - /// - /// Stack: value **=>** (+value) - Pos, + /// - Registers: + /// - Input: value + /// - Output: value + Pos { value: VaryingOperand }, /// Unary `-` operator. /// - /// Operands: - /// - /// Stack: value **=>** (-value) - Neg, - - /// Convert value at register `src` to numeric and puts it in register `dst`. - ToNumeric { operand_types: u8, dst: VaryingOperand, src: VaryingOperand }, + /// - Registers: + /// - Input: value + /// - Output: value + Neg { value: VaryingOperand }, /// Unary `++` operator. /// - /// Operands: - /// - /// Stack: value **=>** (value + 1) - Inc { operand_types: u8, dst: VaryingOperand, src: VaryingOperand }, + /// - Registers: + /// - Input: src + /// - Output: dst + Inc { dst: VaryingOperand, src: VaryingOperand }, /// Unary `--` operator. /// - /// Operands: - /// - /// Stack: value **=>** (value - 1) - Dec { operand_types: u8, dst: VaryingOperand, src: VaryingOperand }, + /// - Registers: + /// - Input: src + /// - Output: dst + Dec { dst: VaryingOperand, src: VaryingOperand }, /// Declare `var` type variable. /// - /// Operands: index: `u32` - /// - /// Stack: **=>** - DefVar { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + DefVar { binding_index: VaryingOperand }, /// Declare and initialize `var` type variable. /// - /// Operands: index: `u32` - /// - /// Stack: value **=>** - DefInitVar { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + /// - Registers: + /// - Input: src + DefInitVar { src: VaryingOperand, binding_index: VaryingOperand }, /// Initialize a lexical binding. /// - /// Operands: index: `u32` - /// - /// Stack: value **=>** - PutLexicalValue { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + /// - Registers: + /// - Input: src + PutLexicalValue { src: VaryingOperand, binding_index: VaryingOperand }, /// Throws an error because the binding access is illegal. /// - /// Operands: index: `u32` - /// - /// Stack: **=>** + /// - Operands: + /// -index: `VaryingOperand` ThrowMutateImmutable { index: VaryingOperand }, /// Get i-th argument of the current frame. /// /// Returns `undefined` if `arguments.len()` < `index`. /// - /// Operands: index: `u32` - /// - /// Stack: **=>** value - GetArgument { index: VaryingOperand }, + /// - Operands: + /// - index: `VaryingOperand` + /// - Registers: + /// - Output: dst + GetArgument { index: VaryingOperand, dst: VaryingOperand }, /// Find a binding on the environment chain and push its value. /// - /// Operands: index: `u32` - /// - /// Stack: **=>** value - GetName { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + /// - Registers: + /// - Output: dst + GetName { dst: VaryingOperand, binding_index: VaryingOperand }, /// Find a binding on the environment and set the `current_binding` of the current frame. /// - /// Operands: index: `u32` - /// - /// Stack: **=>** - GetLocator { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + GetLocator { binding_index: VaryingOperand }, /// Find a binding on the environment chain and push its value to the stack and its /// `BindingLocator` to the `bindings_stack`. /// - /// Operands: index: `u32` - /// - /// Stack: **=>** value - GetNameAndLocator { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + /// - Registers: + /// - Output: dst + GetNameAndLocator { dst: VaryingOperand, binding_index: VaryingOperand }, /// Find a binding on the environment chain and push its value. If the binding does not exist push undefined. /// - /// Operands: index: `u32` - /// - /// Stack: **=>** value - GetNameOrUndefined { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + /// - Registers: + /// - Output: dst + GetNameOrUndefined { dst: VaryingOperand, binding_index: VaryingOperand }, /// Find a binding on the environment chain and assign its value. /// - /// Operands: index: `u32` - /// - /// Stack: value **=>** - SetName { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + /// - Registers: + /// - Input: src + SetName { src: VaryingOperand, binding_index: VaryingOperand }, /// Assigns a value to the binding pointed by the top of the `bindings_stack`. /// - /// Stack: value **=>** - SetNameByLocator, + /// - Registers: + /// - Input: src + SetNameByLocator { src: VaryingOperand }, /// Deletes a property of the global object. /// - /// Operands: index: `u32` - /// - /// Stack: **=>** deleted - DeleteName { index: VaryingOperand }, + /// - Operands: + /// - binding_index: `VaryingOperand` + /// - Registers: + /// - Output: dst + DeleteName { dst: VaryingOperand, binding_index: VaryingOperand }, /// Get a property by name from an object an push it on the stack. /// /// Like `object.name` /// - /// Operands: index: `u32` - /// - /// Stack: object, receiver **=>** value + /// - Operands: + /// - ic_index: `VaryingOperand` + /// - Registers: + /// - Input: receiver, value + /// - Output: dst GetPropertyByName { - operand_types: u8, dst: VaryingOperand, receiver: VaryingOperand, value: VaryingOperand, - index: VaryingOperand + ic_index: VaryingOperand }, /// Get a property by value from an object an push it on the stack. /// /// Like `object[key]` /// - /// Operands: - /// - /// Stack: object, receiver, key **=>** value - GetPropertyByValue, + /// - Registers: + /// - Input: object, receiver, key + /// - Output: dst + GetPropertyByValue { + dst: VaryingOperand, + key: VaryingOperand, + receiver: VaryingOperand, + object: VaryingOperand + }, /// Get a property by value from an object an push the key and value on the stack. /// /// Like `object[key]` /// - /// Operands: - /// - /// Stack: object, receiver, key **=>** key, value - GetPropertyByValuePush, + /// - Registers: + /// - Input: object, receiver, key + /// - Output: dst + GetPropertyByValuePush { + dst: VaryingOperand, + key: VaryingOperand, + receiver: VaryingOperand, + object: VaryingOperand + }, /// Sets a property by name of an object. /// /// Like `object.name = value` /// - /// Operands: index: `u32` - /// - /// Stack: object, receiver, value **=>** value - SetPropertyByName { index: VaryingOperand }, + /// - Operands: + /// - ic_index: `VaryingOperand` + /// - Registers: + /// - Input: object,receiver, value + SetPropertyByName { + value: VaryingOperand, + receiver: VaryingOperand, + object: VaryingOperand, + ic_index: VaryingOperand + }, /// Sets the name of a function object. /// /// This operation is corresponds to the `SetFunctionName` abstract operation in the [spec]. /// - /// The prefix operand is mapped as follows: - /// * 0 -> no prefix - /// * 1 -> "get " - /// * 2 -> "set " - /// - /// Operands: prefix: `u8` - /// - /// Stack: name, function **=>** function - /// /// [spec]: https://tc39.es/ecma262/#sec-setfunctionname - SetFunctionName { prefix: u8 }, + /// + /// - Operands: + /// - prefix + /// - 0: no prefix + /// - 1: "get " + /// - 2: "set " + /// - Registers: + /// - Input: function, name + SetFunctionName { function: VaryingOperand, name: VaryingOperand, prefix: u8 }, /// Defines a own property of an object by name. /// - /// Operands: index: `u32` - /// - /// Stack: object, value **=>** - DefineOwnPropertyByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + DefineOwnPropertyByName { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Defines a static class method by name. /// - /// Operands: index: `u32` - /// - /// Stack: class, function **=>** - DefineClassStaticMethodByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + DefineClassStaticMethodByName { + value: VaryingOperand, + object: VaryingOperand, + name_index: VaryingOperand + }, /// Defines a class method by name. /// - /// Operands: index: `u32` - /// - /// Stack: class_proto, function **=>** - DefineClassMethodByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + DefineClassMethodByName { + value: VaryingOperand, + object: VaryingOperand, + name_index: VaryingOperand + }, /// Sets a property by value of an object. /// /// Like `object[key] = value` /// - /// Operands: - /// - /// Stack: object, receiver, key, value **=>** value - SetPropertyByValue, + /// - Registers: + /// - Input: value, key, receiver, object + SetPropertyByValue { + value: VaryingOperand, + key: VaryingOperand, + receiver: VaryingOperand, + object: VaryingOperand + }, /// Defines a own property of an object by value. /// - /// Operands: - /// - /// Stack: object, key, value **=>** - DefineOwnPropertyByValue, + /// - Registers: + /// - Input: object, key, value + DefineOwnPropertyByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Defines a static class method by value. /// - /// Operands: - /// - /// Stack: class, key, function **=>** - DefineClassStaticMethodByValue, + /// - Registers: + /// - Input: object, key, value + DefineClassStaticMethodByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Defines a class method by value. /// - /// Operands: - /// - /// Stack: class_proto, key, function **=>** - DefineClassMethodByValue, + /// - Registers: + /// - Input: object, key, value + DefineClassMethodByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Sets a getter property by name of an object. /// /// Like `get name() value` /// - /// Operands: index: `u32` - /// - /// Stack: object, value **=>** - SetPropertyGetterByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + SetPropertyGetterByName { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Defines a static getter class method by name. /// /// Like `static get name() value` /// - /// Operands: index: `u32` - /// - /// Stack: class, binding_function **=>** - DefineClassStaticGetterByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + DefineClassStaticGetterByName { + value: VaryingOperand, + object: VaryingOperand, + name_index: VaryingOperand + }, /// Defines a getter class method by name. /// /// Like `get name() value` /// - /// Operands: index: `u32` - /// - /// Stack: class_proto, function **=>** class - DefineClassGetterByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + DefineClassGetterByName { + value: VaryingOperand, + object: VaryingOperand, + name_index: VaryingOperand + }, /// Sets a getter property by value of an object. /// /// Like `get [key]() value` /// - /// Operands: - /// - /// Stack: object, key, value **=>** - SetPropertyGetterByValue, + /// - Registers: + /// - Input: object, key, value + SetPropertyGetterByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Defines a static getter class method by value. /// /// Like `static get [key]() value` /// - /// Operands: - /// - /// Stack: class, key, function **=>** - DefineClassStaticGetterByValue, + /// - Registers: + /// - Input: object, key, value + DefineClassStaticGetterByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Defines a getter class method by value. /// /// Like `get [key]() value` /// - /// Operands: - /// - /// Stack: class_proto, key, function **=>** - DefineClassGetterByValue, + /// - Registers: + /// - Input: object, key, value + DefineClassGetterByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Sets a setter property by name of an object. /// /// Like `set name() value` /// - /// Operands: index: `u32` - /// - /// Stack: object, value **=>** - SetPropertySetterByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + SetPropertySetterByName { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Defines a static setter class method by name. /// /// Like `static set name() value` /// - /// Operands: index: `u32` - /// - /// Stack: class, function **=>** - DefineClassStaticSetterByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + DefineClassStaticSetterByName { + value: VaryingOperand, + object: VaryingOperand, + name_index: VaryingOperand + }, /// Defines a setter class method by name. /// /// Like `set name() value` /// - /// Operands: index: `u32` - /// - /// Stack: class_proto, function **=>** - DefineClassSetterByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + DefineClassSetterByName { + value: VaryingOperand, + object: VaryingOperand, + name_index: VaryingOperand + }, /// Sets a setter property by value of an object. /// /// Like `set [key]() value` /// - /// Operands: - /// - /// Stack: object, key, value **=>** - SetPropertySetterByValue, + /// - Registers: + /// - Input: object, key, value + SetPropertySetterByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Defines a static setter class method by value. /// /// Like `static set [key]() value` /// - /// Operands: - /// - /// Stack: class, key, function **=>** - DefineClassStaticSetterByValue, + /// - Registers: + /// - Input: object, key, value + DefineClassStaticSetterByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Defines a setter class method by value. /// /// Like `set [key]() value` /// - /// Operands: - /// - /// Stack: class_proto, key, function **=>** - DefineClassSetterByValue, + /// - Registers: + /// - Input: object, key, value + DefineClassSetterByValue { + value: VaryingOperand, + key: VaryingOperand, + object: VaryingOperand + }, /// Set the value of a private property of an object by it's name. /// /// Like `obj.#name = value` /// - /// Operands: index: `u32` - /// - /// Stack: object, value **=>** value - SetPrivateField { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + SetPrivateField { value: VaryingOperand, object: VaryingOperand, name_index: VaryingOperand }, /// Define a private property of a class constructor by it's name. /// /// Like `#name = value` /// - /// Operands: index: `u32` - /// - /// Stack: object, value **=>** - DefinePrivateField { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + DefinePrivateField { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Set a private method of a class constructor by it's name. /// /// Like `#name() {}` /// - /// Operands: index: `u32` - /// - /// Stack: object, value **=>** - SetPrivateMethod { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + SetPrivateMethod { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Set a private setter property of a class constructor by it's name. /// /// Like `set #name() {}` /// - /// Operands: index: `u32` - /// - /// Stack: object, value **=>** - SetPrivateSetter { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + SetPrivateSetter { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Set a private getter property of a class constructor by it's name. /// /// Like `get #name() {}` /// - /// Operands: index: `u32` - /// - /// Stack: object, value **=>** - SetPrivateGetter { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + SetPrivateGetter { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Get a private property by name from an object an push it on the stack. /// /// Like `object.#name` /// - /// Operands: index: `u32` - /// - /// Stack: object **=>** value - GetPrivateField { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object + /// - Output: dst + GetPrivateField { dst: VaryingOperand, object: VaryingOperand, name_index: VaryingOperand }, /// Push a field to a class. /// - /// Operands: is_anonymous_function: `bool` - /// - /// Stack: class, field_name, field_function **=>** - PushClassField { is_anonymous_function: bool }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - is_anonymous_function: `bool` + /// - Registers: + /// - Input: object, value + PushClassField { object: VaryingOperand, name_index: VaryingOperand, value: VaryingOperand, is_anonymous_function: bool }, /// Push a private field to the class. /// - /// Operands: index: `u32` - /// - /// Stack: class, field_function **=>** - PushClassFieldPrivate { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + PushClassFieldPrivate { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Push a private getter to the class. /// - /// Operands: index: `u32` - /// - /// Stack: class, getter **=>** - PushClassPrivateGetter { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + PushClassPrivateGetter { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Push a private setter to the class. /// - /// Operands: index: `u32` - /// - /// Stack: class, setter **=>** - PushClassPrivateSetter { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, value + PushClassPrivateSetter { object: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Push a private method to the class. /// - /// Operands: index: `u32` - /// - /// Stack: class, class_proto, method **=>** - PushClassPrivateMethod { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object, proto, value + PushClassPrivateMethod { object: VaryingOperand, proto: VaryingOperand, value: VaryingOperand, name_index: VaryingOperand }, /// Deletes a property by name of an object. /// /// Like `delete object.key` /// - /// Operands: index: `u32` - /// - /// Stack: object **=>** - DeletePropertyByName { index: VaryingOperand }, + /// - Operands: + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: object + DeletePropertyByName { object: VaryingOperand, name_index: VaryingOperand }, /// Deletes a property by value of an object. /// /// Like `delete object[key]` /// - /// Operands: - /// - /// Stack: object, key **=>** - DeletePropertyByValue, + /// - Registers: + /// - Input: object, key + DeletePropertyByValue { object: VaryingOperand, key: VaryingOperand }, /// Throws an error when trying to delete a property of `super` - /// - /// Operands: - /// - /// Stack: **=>** DeleteSuperThrow, /// Copy all properties of one object to another object. /// - /// Operands: excluded_key_count: `VaryingOperand`, excluded_key_count_computed: `VaryingOperand` - /// - /// Stack: excluded_key_computed_0 ... excluded_key_computed_n, source, value, excluded_key_0 ... excluded_key_n **=>** value - CopyDataProperties { excluded_key_count: VaryingOperand, excluded_key_count_computed: VaryingOperand }, + /// - Registers: + /// - Input: object, source, excluded_keys + CopyDataProperties { object: VaryingOperand, source: VaryingOperand, excluded_keys: ThinVec }, /// Call ToPropertyKey on the value on the stack. /// - /// Operands: - /// - /// Stack: value **=>** key - ToPropertyKey, + /// - Registers: + /// - Input: src + /// - Output: dst + ToPropertyKey { src: VaryingOperand, dst: VaryingOperand }, /// Unconditional jump to address. /// - /// Operands: address: `u32` - /// - /// Stack: **=>** + /// - Operands: + /// - address: `u32` Jump { address: u32 }, /// Conditional jump to address. /// /// If the value popped is [`truthy`][truthy] then jump to `address`. /// - /// Operands: address: `u32` - /// - /// Stack: cond **=>** + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Output: value /// /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy - JumpIfTrue { address: u32 }, + JumpIfTrue { address: u32, value: VaryingOperand }, /// Conditional jump to address. /// /// If the value popped is [`falsy`][falsy] then jump to `address`. /// - /// Operands: address: `u32` - /// - /// Stack: cond **=>** + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Output: value /// /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy - JumpIfFalse { address: u32 }, + JumpIfFalse { address: u32, value: VaryingOperand }, /// Conditional jump to address. /// /// If the value popped is not undefined jump to `address`. /// - /// Operands: address: `u32` - /// - /// Stack: value **=>** value - JumpIfNotUndefined { address: u32 }, + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Output: value + JumpIfNotUndefined { address: u32, value: VaryingOperand }, /// Conditional jump to address. /// /// If the value popped is undefined jump to `address`. /// - /// Operands: address: `u32` - /// - /// Stack: value **=>** value - JumpIfNullOrUndefined { address: u32 }, + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Output: value + JumpIfNullOrUndefined { address: u32, value: VaryingOperand }, /// Jump table that jumps depending on top value of the stack. /// @@ -1564,20 +1697,15 @@ generate_opcodes! { /// /// This sets pending exception and searches for an exception handler. /// - /// Operands: - /// - /// Stack: value **=>** - Throw, + /// - Registers: + /// - Input: src + Throw { src: VaryingOperand }, /// Rethrow thrown exception. /// /// This is also used to handle generator `return()` call, we throw an empty exception, by setting pending exception to [`None`], /// propagating it and calling finally code until there is no exception handler left, in that case we consume the empty exception and return /// from the generator. - /// - /// Operands: - /// - /// Stack: **=>** ReThrow, /// Get the thrown pending exception (if it's set) and push on the stack. @@ -1585,72 +1713,59 @@ generate_opcodes! { /// If there is no pending exception, which can happend if we are handling `return()` call on generator, /// then we rethrow the empty exception. See [`Opcode::ReThrow`]. /// - /// Operands: - /// - /// Stack: **=>** exception - Exception, + /// - Registers: + /// - Output: dst + Exception { dst: VaryingOperand }, /// Get the thrown pending exception if it's set and push `true`, otherwise push only `false`. /// - /// Operands: - /// - /// Stack: **=>** (`true`, exception) or `false` - MaybeException, + /// - Registers: + /// - Output: exception, has_exception + MaybeException { has_exception: VaryingOperand, exception: VaryingOperand }, /// Throw a new `TypeError` exception /// - /// Operands: message: u32 - /// - /// Stack: **=>** + /// - Operands: + /// - message: `VaryingOperand` ThrowNewTypeError { message: VaryingOperand }, /// Throw a new `SyntaxError` exception /// - /// Operands: message: u32 - /// - /// Stack: **=>** + /// - Operands: + /// - message: `VaryingOperand` ThrowNewSyntaxError { message: VaryingOperand }, - /// Pops value converts it to boolean and pushes it back. - /// - /// Operands: - /// - /// Stack: value **=>** (`ToBoolean(value)`) - ToBoolean, - /// Pushes `this` value /// - /// Operands: - /// - /// Stack: **=>** this - This, + /// - Registers: + /// - Output: dst + This { dst: VaryingOperand }, /// Pushes `this` value that is related to the object environment of the given binding /// - /// Operands: index: `VaryingOperand` - /// - /// Stack: **=>** value - ThisForObjectEnvironmentName { index: VaryingOperand }, + /// - Operands: + /// - index: `VaryingOperand` + /// - Registers: + /// - Output: dst + ThisForObjectEnvironmentName { dst: VaryingOperand, index: VaryingOperand }, /// Pushes the current `super` value to the stack. /// - /// Operands: - /// - /// Stack: **=>** super - Super, + /// - Registers: + /// - Output: dst + Super { dst: VaryingOperand }, /// Get the super constructor and the new target of the current environment. /// - /// Operands: - /// - /// Stack: **=>** super_constructor - SuperCallPrepare, + /// - Registers: + /// - Output: dst + SuperCallPrepare { dst: VaryingOperand }, /// Execute the `super()` method. /// - /// Operands: argument_count: `u32` - /// - /// Stack: super_constructor, argument_1, ... argument_n **=>** + /// - Operands: + /// - argument_count: `VaryingOperand` + /// - Stack: super_constructor, argument_1, ... argument_n **=>** SuperCall { argument_count: VaryingOperand }, /// Execute the `super()` method where the arguments contain spreads. @@ -1671,19 +1786,19 @@ generate_opcodes! { /// /// Performs steps 7-12 of [`SuperCall: super Arguments`][spec] /// - /// Operands: - /// - /// Stack: result **=>** result + /// - Registers: + /// - Input: value + /// - Output: value /// /// [spec]: https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation - BindThisValue, + BindThisValue { value: VaryingOperand }, /// Dynamically import a module. /// - /// Operands: - /// - /// Stack: specifier **=>** promise - ImportCall, + /// - Registers: + /// - Input: value + /// - Output: value + ImportCall { value: VaryingOperand }, /// Pop the two values of the stack, strict equal compares the two values, /// if true jumps to address, otherwise push the second pop'ed value. @@ -1691,41 +1806,40 @@ generate_opcodes! { /// Operands: address: `u32` /// /// Stack: value, cond **=>** cond (if `cond !== value`). - Case { address: u32 }, - - /// Pops the top of stack and jump to address. - /// - /// Operands: address: `u32` - /// - /// Stack: `value` **=>** - Default { address: u32 }, + /// - Operands: + /// - address: `u32` + /// - Registers: + /// - Input: value, condition + Case { address: u32, value: VaryingOperand, condition: VaryingOperand }, /// Get function from the pre-compiled inner functions. /// - /// Operands: index: `VaryingOperand` - /// - /// Stack: **=>** func + /// - Operands: + /// - index: `VaryingOperand` + /// - Registers: + /// - Output: dst GetFunction { dst: VaryingOperand, index: VaryingOperand }, /// Call a function named "eval". /// - /// Operands: argument_count: `VaryingOperand`, scope_index: `VaryingOperand` - /// - /// Stack: this, func, argument_1, ... argument_n **=>** result + /// - Operands: + /// - argument_count: `VaryingOperand` + /// - scope_index: `VaryingOperand` + /// - Stack: this, func, argument_1, ... argument_n **=>** result CallEval { argument_count: VaryingOperand, scope_index: VaryingOperand }, /// Call a function named "eval" where the arguments contain spreads. /// - /// Operands: - /// - /// Stack: this, func, arguments_array **=>** result - CallEvalSpread { index: VaryingOperand }, + /// - Operands: + /// - scope_index: `VaryingOperand` + /// - Stack: Stack: this, func, arguments_array **=>** result + CallEvalSpread { scope_index: VaryingOperand }, /// Call a function. /// - /// Operands: argument_count: `u32` - /// - /// Stack: this, func, argument_1, ... argument_n **=>** result + /// - Operands: + /// - argument_count: `VaryingOperand` + /// - Stack: this, func, argument_1, ... argument_n **=>** result Call { argument_count: VaryingOperand }, /// Call a function where the arguments contain spreads. @@ -1737,9 +1851,9 @@ generate_opcodes! { /// Call construct on a function. /// - /// Operands: argument_count: `u32` - /// - /// Stack: func, argument_1, ... argument_n **=>** result + /// - Operands: + /// - argument_count: `VaryingOperand` + /// - Stack: this, func, argument_1, ... argument_n **=>** result New { argument_count: VaryingOperand }, /// Call construct on a function where the arguments contain spreads. @@ -1750,381 +1864,315 @@ generate_opcodes! { NewSpread, /// Check return from a function. - /// - /// Operands: - /// - /// Stack: **=>** CheckReturn, /// Return from a function. - /// - /// Operands: - /// - /// Stack: **=>** Return, /// Close an async generator function. - /// - /// Operands: - /// - /// Stack: **=>** AsyncGeneratorClose, /// Creates the generator object and yields. /// - /// Operands: async: `u8` - /// - /// Stack: **=>** resume_kind + /// - Operands: + /// - async: `bool` + /// - Stack: **=>** resume_kind Generator { r#async: bool }, - /// Get return value of a function. - /// - /// Operands: - /// - /// Stack: **=>** value - GetAccumulator, - - /// Set return value of a function. - /// - /// Operands: - /// - /// Stack: value **=>** - SetAccumulatorFromStack, - /// Set return value of a function. /// - /// Operands: - /// - /// Stack: **=>** - SetAccumulator { register: VaryingOperand }, + /// - Registers: + /// - Input: src + SetAccumulator { src: VaryingOperand }, // Set return value of a function. /// - /// Operands: - /// - /// Stack: **=>** - SetRegisterFromAccumulator { register: VaryingOperand }, + /// - Registers: + /// - Output: dst + SetRegisterFromAccumulator { dst: VaryingOperand }, /// Move value of operand `src` to register `dst`. /// - /// Operands: - /// - /// Stack: **=>** - Move { operand_types: u8, dst: VaryingOperand, src: VaryingOperand }, + /// - Registers: + /// - Input: src + /// - Output: dst + Move { dst: VaryingOperand, src: VaryingOperand }, /// Pop value from the stack and push to register `dst` /// - /// Operands: - /// - /// Stack: value **=>** + /// - Registers: + /// - Output: dst PopIntoRegister { dst: VaryingOperand }, - /// Copy value at register `src` and push it into the stack. - /// - /// Operands: + /// Copy value at register `src` and push it on the stack. /// - /// Stack: **=>** value + /// - Registers: + /// - Input: src PushFromRegister { src: VaryingOperand }, /// Pop value from the stack and push to a local binding register `dst`. /// - /// Operands: - /// - /// Stack: value **=>** - PopIntoLocal { dst: VaryingOperand }, + /// - Registers: + /// - Input: src + /// - Output: dst + PopIntoLocal { src: VaryingOperand, dst: VaryingOperand }, /// Copy value at local binding register `src` and push it into the stack. /// - /// Operands: - /// - /// Stack: **=>** value - PushFromLocal { src: VaryingOperand }, + /// - Registers: + /// - Input: src + /// - Output: dst + PushFromLocal { src: VaryingOperand, dst: VaryingOperand }, /// Push a declarative environment. /// - /// Operands: index: `VaryingOperand` - /// - /// Stack: **=>** - PushScope { index: VaryingOperand }, + /// - Operands: + /// - scope_index: `VaryingOperand` + PushScope { scope_index: VaryingOperand }, /// Push an object environment. /// - /// Operands: - /// - /// Stack: object **=>** - PushObjectEnvironment, + /// - Registers: + /// - Input: src + PushObjectEnvironment { src: VaryingOperand }, /// Pop the current environment. - /// - /// Operands: - /// - /// Stack: **=>** PopEnvironment, - /// Increment loop itearation count. + /// Increment loop iteration count. /// /// Used for limiting the loop iteration. - /// - /// Operands: - /// - /// Stack: **=>** IncrementLoopIteration, /// Creates the ForInIterator of an object. /// - /// Stack: object **=>** - /// - /// Iterator Stack: `iterator` - CreateForInIterator, + /// - Registers: + /// - Input: src + CreateForInIterator { src: VaryingOperand }, /// Gets the iterator of an object. /// - /// Operands: - /// - /// Stack: object **=>** - /// - /// Iterator Stack: `iterator` - GetIterator, + /// - Registers: + /// - Input: src + /// - Iterator Stack: **=>** `iterator` + GetIterator { src: VaryingOperand }, /// Gets the async iterator of an object. /// - /// Operands: - /// - /// Stack: object **=>** - /// - /// Iterator Stack: `iterator` - GetAsyncIterator, + /// - Registers: + /// - Input: src + /// - Iterator Stack: **=>** `iterator` + GetAsyncIterator { src: VaryingOperand }, /// Calls the `next` method of `iterator`, updating its record with the next value. /// - /// Operands: - /// - /// Iterator Stack: `iterator` **=>** `iterator` + /// - Iterator Stack: `iterator` **=>** `iterator` IteratorNext, - /// Calls the `next` method of `iterator`, updating its record with the next value. - /// - /// Operands: - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorNextWithoutPop, - /// Returns `true` if the current iterator is done, or `false` otherwise /// - /// Stack: **=>** done - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorDone, + /// - Registers: + /// - Output: dst + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorDone { dst: VaryingOperand }, /// Finishes the call to `Opcode::IteratorNext` within a `for await` loop by setting the current /// result of the current iterator. /// - /// Operands: - /// - /// Stack: `next_result`, `resume_kind` **=>** `resume_kind` - /// - /// Iterator Stack: iterator **=>** iterator - IteratorFinishAsyncNext, + /// - Registers: + /// - Input: resume_kind, value + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorFinishAsyncNext { resume_kind: VaryingOperand, value: VaryingOperand }, /// Gets the `value` property of the current iterator record. /// - /// Stack: **=>** `value` - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorValue, - - /// Gets the `value` property of the current iterator record. - /// - /// Stack: **=>** `value` - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorValueWithoutPop, + /// - Registers: + /// - Output: dst + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorValue { dst: VaryingOperand }, /// Gets the last iteration result of the current iterator record. /// - /// Stack: **=>** `result` - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorResult, + /// - Registers: + /// - Output: dst + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorResult { dst: VaryingOperand }, /// Consume the iterator and construct and array with all the values. /// - /// Operands: - /// - /// Stack: **=>** array - /// - /// Iterator Stack: `iterator` **=>** `iterator` - IteratorToArray, + /// - Registers: + /// - Output: dst + /// - Iterator Stack: `iterator` **=>** `iterator` + IteratorToArray { dst: VaryingOperand }, /// Pushes `true` to the stack if the iterator stack is empty. /// - /// Stack: - /// - **=>** `is_empty` - /// - /// Iterator Stack: - /// - **=>** - IteratorStackEmpty, + /// - Registers: + /// - Output: dst + /// - Iterator Stack: **=>** + IteratorStackEmpty { dst: VaryingOperand }, /// Creates a new iterator result object. /// - /// Operands: - /// - done: bool (codified as u8 with `0` -> `false` and `!0` -> `true`) - /// - /// Stack: - /// - value **=>** - /// - CreateIteratorResult { done: bool }, + /// - Operands: + /// - done: `bool` (codified as u8 with `0` -> `false` and `!0` -> `true`) + /// - Registers: + /// - Input: value + /// - Output: value + CreateIteratorResult { value: VaryingOperand, done: bool }, /// Calls `return` on the current iterator and returns the result. /// - /// Stack: **=>** return_val (if return is a method), is_return_method - /// - /// Iterator Stack: `iterator` **=>** - IteratorReturn, + /// - Registers: + /// - Output: value, called + /// - Iterator Stack: `iterator` **=>** + IteratorReturn { value: VaryingOperand, called: VaryingOperand }, /// Concat multiple stack objects into a string. /// - /// Operands: value_count: `u32` - /// - /// Stack: `value_1`,...`value_n` **=>** `string` - ConcatToString { value_count: VaryingOperand }, - - /// Call RequireObjectCoercible on the stack value. - /// - /// Operands: - /// - /// Stack: value **=>** value - RequireObjectCoercible, + /// - Registers: + /// - Input: values + /// - Output: dst + ConcatToString { dst: VaryingOperand, values: ThinVec }, /// Require the stack value to be neither null nor undefined. /// - /// Operands: - /// - /// Stack: value **=>** value - ValueNotNullOrUndefined, + /// - Registers: + /// - Input: src + ValueNotNullOrUndefined { src: VaryingOperand }, /// Initialize the rest parameter value of a function from the remaining arguments. /// - /// Operands: - /// - /// Stack: `argument_1` .. `argument_n` **=>** `array` - RestParameterInit, + /// - Stack: `argument_1` .. `argument_n` **=>** + /// - Registers: + /// - Output: dst + RestParameterInit { dst: VaryingOperand }, /// Yields from the current generator execution. /// - /// Operands: - /// - /// Stack: value **=>** resume_kind, received - GeneratorYield, + /// - Registers: + /// - Input: src + /// - Output: resume_kind, received + GeneratorYield { src: VaryingOperand }, /// Resumes the current generator function. /// /// If the `resume_kind` is `Throw`, then the value is poped and thrown, otherwise if `Return` /// we pop the value, set it as the return value and throw and empty exception. See [`Opcode::ReThrow`]. /// - /// Operands: - /// - /// Stack: `resume_kind`, value **=>** value - GeneratorNext, + /// - Registers: + /// - Input: resume_kind, value + GeneratorNext { resume_kind: VaryingOperand, value: VaryingOperand }, /// Yields from the current async generator execution. /// - /// Operands: - /// - /// Stack: value **=>** received - AsyncGeneratorYield, + /// - Registers: + /// - Input: src + /// - Output: resume_kind, received + AsyncGeneratorYield { src: VaryingOperand }, /// Create a promise capacity for an async function, if not already set. - /// - /// Operands: - /// - /// Stack: **=>** CreatePromiseCapability, /// Resolves or rejects the promise capability of an async function. /// /// If the pending exception is set, reject and rethrow the exception, otherwise resolve. - /// - /// Operands: - /// - /// Stack: **=>** CompletePromiseCapability, /// Jumps to the specified address if the resume kind is not equal. /// - /// Operands: `exit`: `u32`, `resume_kind`: `u8`. - /// - /// Stack: `resume_kind` **=>** `resume_kind` - JumpIfNotResumeKind { exit: u32, resume_kind: GeneratorResumeKind }, + /// - Operands: + /// - address: `u32` + /// - resume_kind: `GeneratorResumeKind` + /// - Registers: + /// - Input: src + JumpIfNotResumeKind { address: u32, resume_kind: GeneratorResumeKind, src: VaryingOperand }, /// Delegates the current async generator function to another iterator. /// - /// Operands: throw_method_undefined: `u32`, return_method_undefined: `u32` - /// - /// Stack: received **=>** result - GeneratorDelegateNext { throw_method_undefined: u32, return_method_undefined: u32 }, + /// - Operands: + /// - throw_method_undefined: `u32`, + /// - return_method_undefined: `u32` + /// - Registers: + /// - Input: value, resume_kind + /// - Output: value, is_return + GeneratorDelegateNext { + throw_method_undefined: u32, + return_method_undefined: u32, + value: VaryingOperand, + resume_kind: VaryingOperand, + is_return: VaryingOperand + }, /// Resume the async generator with yield delegate logic after it awaits a value. /// - /// Operands: return: `u32`, exit: `u32` - /// - /// Stack: is_return, received **=>** value - GeneratorDelegateResume { r#return: u32, exit: u32 }, + /// - Operands: + /// - r#return: `u32`, + /// - exit: `u32` + /// - Registers: + /// - Input: value, resume_kind, is_return + /// - Output: value + GeneratorDelegateResume { + r#return: u32, + exit: u32, + value: VaryingOperand, + resume_kind: VaryingOperand, + is_return: VaryingOperand + }, /// Stops the current async function and schedules it to resume later. /// - /// Operands: - /// - /// Stack: promise **=>** received - Await, + /// - Registers: + /// - Input: src + /// - Output: resume_kind, received + Await { src: VaryingOperand }, /// Push the current new target to the stack. /// - /// Operands: - /// - /// Stack: **=>** `new.target` - NewTarget, + /// - Registers: + /// - Output: dst + NewTarget { dst: VaryingOperand }, /// Push the current `import.meta` to the stack. /// - /// Operands: - /// - /// Stack: **=>** `import.meta` - ImportMeta, + /// - Registers: + /// - Output: dst + ImportMeta { dst: VaryingOperand }, /// Pushes `true` to the stack if the top stack value is an object, or `false` otherwise. /// - /// Operands: - /// - /// Stack: value **=>** is_object - IsObject, + /// - Registers: + /// - Input: value + /// - Output: value + IsObject { value: VaryingOperand }, /// Lookup if a tagged template object is cached and skip the creation if it is. /// - /// Operands: exit: `u32`, site: `u64` - /// - /// Stack: **=>** template (if cached) - TemplateLookup { exit: u32, site: u64 }, + /// - Operands: + /// - address: `u32` + /// - site: `u64` + /// - Registers: + /// - Output: dst + TemplateLookup { address: u32, site: u64, dst: VaryingOperand }, /// Create a new tagged template object and cache it. /// - /// Operands: count: `VaryingOperand`, site: `u64` - /// - /// Stack: count * (cooked_value, raw_value) **=>** template - TemplateCreate { count: VaryingOperand, site: u64 }, + /// - Operands: + /// - site: `u64` + /// - Registers: + /// - Inputs: values + /// - Output: dst + TemplateCreate { site: u64, dst: VaryingOperand, values: ThinVec<(u32, u32)> }, /// Push a private environment. /// /// Operands: count: `u32`, count * name_index: `u32` /// - /// Stack: class **=>** class - PushPrivateEnvironment { operand_types: u8, class: VaryingOperand, name_indices: ThinVec }, + /// - Registers: + /// - Input: class, [name_indices] + PushPrivateEnvironment { class: VaryingOperand, name_indices: ThinVec }, /// Pop a private environment. - /// - /// Operands: - /// - /// Stack: **=>** PopPrivateEnvironment, /// Creates a mapped `arguments` object. @@ -2133,10 +2181,9 @@ generate_opcodes! { /// /// [spec]: https://tc39.es/ecma262/#sec-createunmappedargumentsobject /// - /// Operands: - /// - /// Stack: **=>** `arguments` - CreateMappedArgumentsObject, + /// - Registers: + /// - Output: dst + CreateMappedArgumentsObject { dst: VaryingOperand }, /// Creates an unmapped `arguments` object. /// @@ -2144,72 +2191,67 @@ generate_opcodes! { /// /// [spec]: https://tc39.es/ecma262/#sec-createmappedargumentsobject /// - /// Stack: **=>** `arguments` - CreateUnmappedArgumentsObject, + /// - Registers: + /// - Output: dst + CreateUnmappedArgumentsObject { dst: VaryingOperand }, /// Performs [`HasRestrictedGlobalProperty ( N )`][spec] /// - /// Operands: `index`: u32 - /// - /// Stack: **=>** + /// - Operands: + /// - index: `VaryingOperand` + /// - Registers: + /// - Output: dst /// /// [spec]: https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty - HasRestrictedGlobalProperty { index: VaryingOperand }, + HasRestrictedGlobalProperty { dst: VaryingOperand, index: VaryingOperand }, /// Performs [`CanDeclareGlobalFunction ( N )`][spec] /// - /// Operands: `index`: u32 - /// - /// Stack: **=>** + /// - Operands: + /// - index: `VaryingOperand` + /// - Registers: + /// - Output: dst /// /// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalfunction - CanDeclareGlobalFunction { index: VaryingOperand }, + CanDeclareGlobalFunction { dst: VaryingOperand, index: VaryingOperand }, /// Performs [`CanDeclareGlobalVar ( N )`][spec] /// - /// Operands: `index`: u32 - /// - /// Stack: **=>** + /// - Operands: + /// - index: `VaryingOperand` + /// - Registers: + /// - Output: dst /// /// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalvar - CanDeclareGlobalVar { index: VaryingOperand }, + CanDeclareGlobalVar { dst: VaryingOperand, index: VaryingOperand }, /// Performs [`CreateGlobalFunctionBinding ( N, V, D )`][spec] /// - /// Operands: configurable: `bool`, `index`: `VaryingOperand` - /// - /// Stack: `function` **=>** + /// - Operands: + /// - configurable: `bool` + /// - name_index: `VaryingOperand` + /// - Registers: + /// - Input: src /// /// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding - CreateGlobalFunctionBinding { configurable: bool, index: VaryingOperand }, + CreateGlobalFunctionBinding { src: VaryingOperand, configurable: bool, name_index: VaryingOperand }, /// Performs [`CreateGlobalVarBinding ( N, V, D )`][spec] /// - /// Operands: configurable: `bool`, `index`: `VaryingOperand` - /// - /// Stack: **=>** + /// - Operands: + /// - configurable: `bool` + /// - name_index: `VaryingOperand` /// /// [spec]: https://tc39.es/ecma262/#sec-createglobalvarbinding - CreateGlobalVarBinding { configurable: bool, index: VaryingOperand }, - - /// No-operation instruction, does nothing. - /// - /// Operands: - /// - /// Stack: **=>** - Nop, + CreateGlobalVarBinding { configurable: bool, name_index: VaryingOperand }, /// Opcode prefix modifier, makes all [`VaryingOperand`]s of an instruction [`u16`] sized. /// - /// Operands: opcode (operands if any). - /// /// Stack: The stack changes based on the opcode that is being prefixed. U16Operands, /// Opcode prefix modifier, [`Opcode`] prefix operand modifier, makes all [`VaryingOperand`]s of an instruction [`u32`] sized. /// - /// Operands: opcode (operands if any). - /// /// Stack: The stack changes based on the opcode that is being prefixed. U32Operands, @@ -2307,6 +2349,34 @@ generate_opcodes! { Reserved46 => Reserved, /// Reserved [`Opcode`]. Reserved47 => Reserved, + /// Reserved [`Opcode`]. + Reserved48 => Reserved, + /// Reserved [`Opcode`]. + Reserved49 => Reserved, + /// Reserved [`Opcode`]. + Reserved50 => Reserved, + /// Reserved [`Opcode`]. + Reserved51 => Reserved, + /// Reserved [`Opcode`]. + Reserved52 => Reserved, + /// Reserved [`Opcode`]. + Reserved53 => Reserved, + /// Reserved [`Opcode`]. + Reserved54 => Reserved, + /// Reserved [`Opcode`]. + Reserved55 => Reserved, + /// Reserved [`Opcode`]. + Reserved56 => Reserved, + /// Reserved [`Opcode`]. + Reserved57 => Reserved, + /// Reserved [`Opcode`]. + Reserved58 => Reserved, + /// Reserved [`Opcode`]. + Reserved59 => Reserved, + /// Reserved [`Opcode`]. + Reserved60 => Reserved, + /// Reserved [`Opcode`]. + Reserved61 => Reserved, } /// Specific opcodes for bindings. diff --git a/core/engine/src/vm/opcode/modifier.rs b/core/engine/src/vm/opcode/modifier.rs index deb33909310..365a3c8f4c5 100644 --- a/core/engine/src/vm/opcode/modifier.rs +++ b/core/engine/src/vm/opcode/modifier.rs @@ -1,6 +1,6 @@ use crate::{vm::CompletionType, Context, JsResult}; -use super::{Opcode, Operation}; +use super::{Opcode, Operation, Registers}; /// `U16Operands` implements the Opcode Operation for `Opcode::U16Operands` /// @@ -14,20 +14,21 @@ impl Operation for U16Operands { const INSTRUCTION: &'static str = "INST - U16Operands"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let opcode = context.vm.read::() as usize; - Opcode::EXECUTE_FNS[Opcode::MAX + opcode](context) + Opcode::EXECUTE_FNS[Opcode::MAX + opcode](registers, context) } fn spend_budget_and_execute( + registers: &mut Registers, context: &mut Context, budget: &mut u32, ) -> JsResult { let opcode: Opcode = context.vm.read::().into(); *budget = budget.saturating_sub(u32::from(opcode.cost()) + u32::from(Self::COST)); - Opcode::EXECUTE_FNS[Opcode::MAX + opcode as usize](context) + Opcode::EXECUTE_FNS[Opcode::MAX + opcode as usize](registers, context) } } @@ -43,19 +44,20 @@ impl Operation for U32Operands { const INSTRUCTION: &'static str = "INST - U32Operands"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let opcode = context.vm.read::() as usize; - Opcode::EXECUTE_FNS[Opcode::MAX * 2 + opcode](context) + Opcode::EXECUTE_FNS[Opcode::MAX * 2 + opcode](registers, context) } fn spend_budget_and_execute( + registers: &mut Registers, context: &mut Context, budget: &mut u32, ) -> JsResult { let opcode: Opcode = context.vm.read::().into(); *budget = budget.saturating_sub(u32::from(opcode.cost()) + u32::from(Self::COST)); - Opcode::EXECUTE_FNS[Opcode::MAX * 2 + opcode as usize](context) + Opcode::EXECUTE_FNS[Opcode::MAX * 2 + opcode as usize](registers, context) } } diff --git a/core/engine/src/vm/opcode/new/mod.rs b/core/engine/src/vm/opcode/new/mod.rs index 81a48ea173d..e0e10036e21 100644 --- a/core/engine/src/vm/opcode/new/mod.rs +++ b/core/engine/src/vm/opcode/new/mod.rs @@ -1,6 +1,6 @@ use crate::{ error::JsNativeError, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -12,7 +12,11 @@ use crate::{ pub(crate) struct New; impl New { - fn operation(context: &mut Context, argument_count: usize) -> JsResult { + fn operation( + registers: &mut Registers, + context: &mut Context, + argument_count: usize, + ) -> JsResult { let at = context.vm.stack.len() - argument_count; let func = &context.vm.stack[at - 1]; @@ -23,7 +27,9 @@ impl New { context.vm.push(cons.clone()); // Push new.target - cons.__construct__(argument_count).resolve(context)?; + if let Some(register_count) = cons.__construct__(argument_count).resolve(context)? { + registers.push_function(register_count); + } Ok(CompletionType::Normal) } } @@ -33,19 +39,19 @@ impl Operation for New { const INSTRUCTION: &'static str = "INST - New"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::() as usize; - Self::operation(context, argument_count) + Self::operation(registers, context, argument_count) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::() as usize; - Self::operation(context, argument_count) + Self::operation(registers, context, argument_count) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let argument_count = context.vm.read::() as usize; - Self::operation(context, argument_count) + Self::operation(registers, context, argument_count) } } @@ -61,7 +67,7 @@ impl Operation for NewSpread { const INSTRUCTION: &'static str = "INST - NewSpread"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { // Get the arguments that are stored as an array object on the stack. let arguments_array = context.vm.pop(); let arguments_array_object = arguments_array @@ -85,7 +91,9 @@ impl Operation for NewSpread { context.vm.push_values(&arguments); context.vm.push(cons.clone()); // Push new.target - cons.__construct__(argument_count).resolve(context)?; + if let Some(register_count) = cons.__construct__(argument_count).resolve(context)? { + registers.push_function(register_count); + } Ok(CompletionType::Normal) } } diff --git a/core/engine/src/vm/opcode/nop/mod.rs b/core/engine/src/vm/opcode/nop/mod.rs index d5571a9b7f7..d82c7c80787 100644 --- a/core/engine/src/vm/opcode/nop/mod.rs +++ b/core/engine/src/vm/opcode/nop/mod.rs @@ -1,25 +1,8 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; -/// `Nop` implements the Opcode Operation for `Opcode::Nop` -/// -/// Operation: -/// - No-operation instruction, does nothing -#[derive(Debug, Clone, Copy)] -pub(crate) struct Nop; - -impl Operation for Nop { - const NAME: &'static str = "Nop"; - const INSTRUCTION: &'static str = "INST - Nop"; - const COST: u8 = 1; - - fn execute(_: &mut Context) -> JsResult { - Ok(CompletionType::Normal) - } -} - /// `Reserved` implements the Opcode Operation for `Opcode::Reserved` /// /// Operation: @@ -32,15 +15,15 @@ impl Operation for Reserved { const INSTRUCTION: &'static str = "INST - Reserved"; const COST: u8 = 0; - fn execute(_: &mut Context) -> JsResult { + fn execute(_: &mut Registers, _: &mut Context) -> JsResult { unreachable!("Reserved opcodes are unreachable!") } - fn execute_with_u16_operands(_: &mut Context) -> JsResult { + fn execute_u16(_: &mut Registers, _: &mut Context) -> JsResult { unreachable!("Reserved.U16 opcodes are unreachable!") } - fn execute_with_u32_operands(_: &mut Context) -> JsResult { + fn execute_u32(_: &mut Registers, _: &mut Context) -> JsResult { unreachable!("Reserved.U32 opcodes are unreachable!") } } diff --git a/core/engine/src/vm/opcode/pop/mod.rs b/core/engine/src/vm/opcode/pop/mod.rs index 35cc64ae66d..4ad7f1f8a52 100644 --- a/core/engine/src/vm/opcode/pop/mod.rs +++ b/core/engine/src/vm/opcode/pop/mod.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -15,7 +15,7 @@ impl Operation for Pop { const INSTRUCTION: &'static str = "INST - Pop"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let _val = context.vm.pop(); Ok(CompletionType::Normal) } @@ -33,7 +33,7 @@ impl Operation for PopEnvironment { const INSTRUCTION: &'static str = "INST - PopEnvironment"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { context.vm.environments.pop(); Ok(CompletionType::Normal) } diff --git a/core/engine/src/vm/opcode/push/array.rs b/core/engine/src/vm/opcode/push/array.rs index d5650c46503..9454a40e846 100644 --- a/core/engine/src/vm/opcode/push/array.rs +++ b/core/engine/src/vm/opcode/push/array.rs @@ -1,7 +1,7 @@ use crate::{ builtins::Array, string::StaticJsStrings, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -12,19 +12,41 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct PushNewArray; +impl PushNewArray { + #[allow(clippy::unnecessary_wraps)] + fn operation( + array: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = context + .intrinsics() + .templates() + .array() + .create(Array, Vec::from([JsValue::new(0)])); + registers.set(array, value.into()); + Ok(CompletionType::Normal) + } +} + impl Operation for PushNewArray { const NAME: &'static str = "PushNewArray"; const INSTRUCTION: &'static str = "INST - PushNewArray"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let array = context - .intrinsics() - .templates() - .array() - .create(Array, vec![JsValue::new(0)]); - context.vm.push(array); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::().into(); + Self::operation(array, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::().into(); + Self::operation(array, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::(); + Self::operation(array, registers, context) } } @@ -35,25 +57,50 @@ impl Operation for PushNewArray { #[derive(Debug, Clone, Copy)] pub(crate) struct PushValueToArray; -impl Operation for PushValueToArray { - const NAME: &'static str = "PushValueToArray"; - const INSTRUCTION: &'static str = "INST - PushValueToArray"; - const COST: u8 = 3; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let array = context.vm.pop(); +impl PushValueToArray { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + array: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let array = registers.get(array); let o = array.as_object().expect("should be an object"); let len = o .length_of_array_like(context) .expect("should have 'length' property"); - o.create_data_property_or_throw(len, value, context) + o.create_data_property_or_throw(len, value.clone(), context) .expect("should be able to create new data property"); - context.vm.push(array); Ok(CompletionType::Normal) } } +impl Operation for PushValueToArray { + const NAME: &'static str = "PushValueToArray"; + const INSTRUCTION: &'static str = "INST - PushValueToArray"; + const COST: u8 = 3; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let array = context.vm.read::().into(); + Self::operation(value, array, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let array = context.vm.read::().into(); + Self::operation(value, array, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let array = context.vm.read::(); + Self::operation(value, array, registers, context) + } +} + /// `PushEllisionToArray` implements the Opcode Operation for `Opcode::PushEllisionToArray` /// /// Operation: @@ -61,22 +108,40 @@ impl Operation for PushValueToArray { #[derive(Debug, Clone, Copy)] pub(crate) struct PushElisionToArray; +impl PushElisionToArray { + fn operation( + array: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let array = registers.get(array); + let o = array.as_object().expect("should always be an object"); + let len = o + .length_of_array_like(context) + .expect("arrays should always have a 'length' property"); + o.set(StaticJsStrings::LENGTH, len + 1, true, context)?; + Ok(CompletionType::Normal) + } +} + impl Operation for PushElisionToArray { const NAME: &'static str = "PushElisionToArray"; const INSTRUCTION: &'static str = "INST - PushElisionToArray"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let array = context.vm.pop(); - let o = array.as_object().expect("should always be an object"); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::().into(); + Self::operation(array, registers, context) + } - let len = o - .length_of_array_like(context) - .expect("arrays should always have a 'length' property"); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::().into(); + Self::operation(array, registers, context) + } - o.set(StaticJsStrings::LENGTH, len + 1, true, context)?; - context.vm.push(array); - Ok(CompletionType::Normal) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::(); + Self::operation(array, registers, context) } } @@ -87,25 +152,43 @@ impl Operation for PushElisionToArray { #[derive(Debug, Clone, Copy)] pub(crate) struct PushIteratorToArray; -impl Operation for PushIteratorToArray { - const NAME: &'static str = "PushIteratorToArray"; - const INSTRUCTION: &'static str = "INST - PushIteratorToArray"; - const COST: u8 = 8; - - fn execute(context: &mut Context) -> JsResult { +impl PushIteratorToArray { + fn operation( + array: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let array = registers.get(array); let mut iterator = context .vm .frame_mut() .iterators .pop() .expect("iterator stack should have at least an iterator"); - let array = context.vm.pop(); - while let Some(next) = iterator.step_value(context)? { - Array::push(&array, &[next], context)?; + Array::push(array, &[next], context)?; } - - context.vm.push(array); Ok(CompletionType::Normal) } } + +impl Operation for PushIteratorToArray { + const NAME: &'static str = "PushIteratorToArray"; + const INSTRUCTION: &'static str = "INST - PushIteratorToArray"; + const COST: u8 = 8; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::().into(); + Self::operation(array, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::().into(); + Self::operation(array, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let array = context.vm.read::(); + Self::operation(array, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/push/class/field.rs b/core/engine/src/vm/opcode/push/class/field.rs index 3614e41700e..cd6ffd9daa4 100644 --- a/core/engine/src/vm/opcode/push/class/field.rs +++ b/core/engine/src/vm/opcode/push/class/field.rs @@ -1,7 +1,7 @@ use crate::{ builtins::function::OrdinaryFunction, object::JsFunction, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -12,38 +12,38 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct PushClassField; -impl Operation for PushClassField { - const NAME: &'static str = "PushClassField"; - const INSTRUCTION: &'static str = "INST - PushClassField"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let is_annonymus_function = context.vm.read::() != 0; - let field_function_value = context.vm.pop(); - let field_name_value = context.vm.pop(); - let class_value = context.vm.pop(); +impl PushClassField { + fn operation( + class: u32, + name: u32, + function: u32, + is_anonyms_function: bool, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let class = registers.get(class); + let name = registers.get(name); + let function = registers.get(function); - let field_name_key = field_name_value.to_property_key(context)?; - let field_function_object = field_function_value + let name = name.to_property_key(context)?; + let function = function .as_object() .expect("field value must be function object"); - let class_object = class_value - .as_object() - .expect("class must be function object"); + let class = class.as_object().expect("class must be function object"); - field_function_object + function .downcast_mut::() .expect("field value must be function object") - .set_home_object(class_object.clone()); + .set_home_object(class.clone()); - class_object + class .downcast_mut::() .expect("class must be function object") .push_field( - field_name_key.clone(), - JsFunction::from_object_unchecked(field_function_object.clone()), - if is_annonymus_function { - Some(field_name_key) + name.clone(), + JsFunction::from_object_unchecked(function.clone()), + if is_anonyms_function { + Some(name) } else { None }, @@ -52,6 +52,57 @@ impl Operation for PushClassField { } } +impl Operation for PushClassField { + const NAME: &'static str = "PushClassField"; + const INSTRUCTION: &'static str = "INST - PushClassField"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let class = context.vm.read::().into(); + let name = context.vm.read::().into(); + let function = context.vm.read::().into(); + let is_anonyms_function = context.vm.read::() != 0; + Self::operation( + class, + name, + function, + is_anonyms_function, + registers, + context, + ) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let class = context.vm.read::().into(); + let name = context.vm.read::().into(); + let function = context.vm.read::().into(); + let is_anonyms_function = context.vm.read::() != 0; + Self::operation( + class, + name, + function, + is_anonyms_function, + registers, + context, + ) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let class = context.vm.read::(); + let name = context.vm.read::(); + let function = context.vm.read::(); + let is_anonyms_function = context.vm.read::() != 0; + Self::operation( + class, + name, + function, + is_anonyms_function, + registers, + context, + ) + } +} + /// `PushClassFieldPrivate` implements the Opcode Operation for `Opcode::PushClassFieldPrivate` /// /// Operation: @@ -61,29 +112,33 @@ pub(crate) struct PushClassFieldPrivate; impl PushClassFieldPrivate { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + class: u32, + function: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let class = registers.get(class); + let function = registers.get(function); let name = context.vm.frame().code_block().constant_string(index); - let field_function_value = context.vm.pop(); - let class_value = context.vm.pop(); - let field_function_object = field_function_value + let function = function .as_object() .expect("field value must be function object"); - let class_object = class_value - .as_object() - .expect("class must be function object"); + let class = class.as_object().expect("class must be function object"); - field_function_object + function .downcast_mut::() .expect("field value must be function object") - .set_home_object(class_object.clone()); + .set_home_object(class.clone()); - class_object + class .downcast_mut::() .expect("class must be function object") .push_field_private( - class_object.private_name(name), - JsFunction::from_object_unchecked(field_function_object.clone()), + class.private_name(name), + JsFunction::from_object_unchecked(function.clone()), ); Ok(CompletionType::Normal) } @@ -94,18 +149,24 @@ impl Operation for PushClassFieldPrivate { const INSTRUCTION: &'static str = "INST - PushClassFieldPrivate"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let class = context.vm.read::().into(); + let function = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let class = context.vm.read::().into(); + let function = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let class = context.vm.read::(); + let function = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(class, function, index, registers, context) } } diff --git a/core/engine/src/vm/opcode/push/class/mod.rs b/core/engine/src/vm/opcode/push/class/mod.rs index f7492cdb366..8362ac09333 100644 --- a/core/engine/src/vm/opcode/push/class/mod.rs +++ b/core/engine/src/vm/opcode/push/class/mod.rs @@ -1,7 +1,7 @@ use crate::{ error::JsNativeError, object::PROTOTYPE, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -23,18 +23,11 @@ impl PushClassPrototype { dst: u32, class: u32, superclass: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - let class = context - .vm - .frame() - .read_value::<0>(operand_types, class, &context.vm); - let superclass = context - .vm - .frame() - .read_value::<1>(operand_types, superclass, &context.vm); + let class = registers.get(class); + let superclass = registers.get(superclass); // // Taken from `15.7.14 Runtime Semantics: ClassDefinitionEvaluation`: // @@ -77,7 +70,7 @@ impl PushClassPrototype { class_object.set_prototype(Some(constructor_parent)); } - context.vm.stack[(rp + dst) as usize] = proto_parent; + registers.set(dst, proto_parent); Ok(CompletionType::Normal) } } @@ -87,27 +80,24 @@ impl Operation for PushClassPrototype { const INSTRUCTION: &'static str = "INST - PushClassPrototype"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); let class = u32::from(context.vm.read::()); let superclass = u32::from(context.vm.read::()); - Self::operation(dst, class, superclass, operand_types, context) + Self::operation(dst, class, superclass, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); let class = u32::from(context.vm.read::()); let superclass = u32::from(context.vm.read::()); - Self::operation(dst, class, superclass, operand_types, context) + Self::operation(dst, class, superclass, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); let class = context.vm.read::(); let superclass = context.vm.read::(); - Self::operation(dst, class, superclass, operand_types, context) + Self::operation(dst, class, superclass, registers, context) } } diff --git a/core/engine/src/vm/opcode/push/class/private.rs b/core/engine/src/vm/opcode/push/class/private.rs index c016e4e3e88..b33dd4454c7 100644 --- a/core/engine/src/vm/opcode/push/class/private.rs +++ b/core/engine/src/vm/opcode/push/class/private.rs @@ -3,7 +3,7 @@ use crate::{ js_str, js_string, object::{internal_methods::InternalMethodContext, PrivateElement}, property::PropertyDescriptor, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -16,16 +16,24 @@ pub(crate) struct PushClassPrivateMethod; impl PushClassPrivateMethod { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + object: u32, + prototype: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let prototype = registers.get(prototype); + let value = registers.get(value); let name = context.vm.frame().code_block().constant_string(index); - let method = context.vm.pop(); - let method_object = method.as_callable().expect("method must be callable"); - let class_proto = context.vm.pop(); - let class_proto_object = class_proto + + let value = value.as_callable().expect("method must be callable"); + let prototype = prototype .as_object() - .expect("class_proto must be function object"); - let class = context.vm.pop(); - let class_object = class.as_object().expect("class must be function object"); + .expect("class_prototype must be function object"); + let object = object.as_object().expect("class must be function object"); let name_string = js_string!(js_str!("#"), &name); let desc = PropertyDescriptor::builder() @@ -34,24 +42,24 @@ impl PushClassPrivateMethod { .enumerable(false) .configurable(true) .build(); - method_object + value .__define_own_property__( &js_string!("name").into(), desc, &mut InternalMethodContext::new(context), ) .expect("failed to set name property on private method"); - method_object + value .downcast_mut::() .expect("method must be function object") - .set_home_object(class_proto_object.clone()); + .set_home_object(prototype.clone()); - class_object + object .downcast_mut::() .expect("class must be function object") .push_private_method( - class_object.private_name(name), - PrivateElement::Method(method_object.clone()), + object.private_name(name), + PrivateElement::Method(value.clone()), ); Ok(CompletionType::Normal) @@ -63,19 +71,28 @@ impl Operation for PushClassPrivateMethod { const INSTRUCTION: &'static str = "INST - PushClassPrivateMethod"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let prototype = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, prototype, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let prototype = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, prototype, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let prototype = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, prototype, value, index, registers, context) } } @@ -88,20 +105,27 @@ pub(crate) struct PushClassPrivateGetter; impl PushClassPrivateGetter { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context.vm.frame().code_block().constant_string(index); - let getter = context.vm.pop(); - let getter_object = getter.as_callable().expect("getter must be callable"); - let class = context.vm.pop(); - let class_object = class.as_object().expect("class must be function object"); - class_object + let value = value.as_callable().expect("getter must be callable"); + let object = object.as_object().expect("class must be function object"); + + object .downcast_mut::() .expect("class must be function object") .push_private_method( - class_object.private_name(name), + object.private_name(name), PrivateElement::Accessor { - getter: Some(getter_object.clone()), + getter: Some(value.clone()), setter: None, }, ); @@ -115,19 +139,25 @@ impl Operation for PushClassPrivateGetter { const INSTRUCTION: &'static str = "INST - PushClassPrivateGetter"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } @@ -140,21 +170,28 @@ pub(crate) struct PushClassPrivateSetter; impl PushClassPrivateSetter { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context.vm.frame().code_block().constant_string(index); - let setter = context.vm.pop(); - let setter_object = setter.as_callable().expect("getter must be callable"); - let class = context.vm.pop(); - let class_object = class.as_object().expect("class must be function object"); - class_object + let value = value.as_callable().expect("getter must be callable"); + let object = object.as_object().expect("class must be function object"); + + object .downcast_mut::() .expect("class must be function object") .push_private_method( - class_object.private_name(name), + object.private_name(name), PrivateElement::Accessor { getter: None, - setter: Some(setter_object.clone()), + setter: Some(value.clone()), }, ); @@ -167,18 +204,24 @@ impl Operation for PushClassPrivateSetter { const INSTRUCTION: &'static str = "INST - PushClassPrivateSetter"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } diff --git a/core/engine/src/vm/opcode/push/environment.rs b/core/engine/src/vm/opcode/push/environment.rs index 3df84da8d97..e2c130b6d39 100644 --- a/core/engine/src/vm/opcode/push/environment.rs +++ b/core/engine/src/vm/opcode/push/environment.rs @@ -1,7 +1,7 @@ use crate::{ builtins::function::OrdinaryFunction, environments::PrivateEnvironment, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsString, }; use boa_gc::Gc; @@ -15,7 +15,7 @@ pub(crate) struct PushScope; impl PushScope { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation(index: usize, context: &mut Context) -> JsResult { let scope = context.vm.frame().code_block().constant_scope(index); context .vm @@ -30,19 +30,19 @@ impl Operation for PushScope { const INSTRUCTION: &'static str = "INST - PushScope"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(index, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(index, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(index, context) } } @@ -53,17 +53,37 @@ impl Operation for PushScope { #[derive(Debug, Clone, Copy)] pub(crate) struct PushObjectEnvironment; +impl PushObjectEnvironment { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(value); + let object = object.to_object(context)?; + context.vm.environments.push_object(object); + Ok(CompletionType::Normal) + } +} + impl Operation for PushObjectEnvironment { const NAME: &'static str = "PushObjectEnvironment"; const INSTRUCTION: &'static str = "INST - PushObjectEnvironment"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let object = context.vm.pop(); - let object = object.to_object(context)?; + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } - context.vm.environments.push_object(object); - Ok(CompletionType::Normal) + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } @@ -79,14 +99,11 @@ impl PushPrivateEnvironment { fn operation( class: u32, names: Vec, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let class_value = context - .vm - .frame() - .read_value::<0>(operand_types, class, &context.vm); - let class = class_value.as_object().expect("should be a object"); + let class = registers.get(class); + let class = class.as_object().expect("should be a object"); let ptr: *const _ = class.as_ref(); let environment = Gc::new(PrivateEnvironment::new(ptr.cast::<()>() as usize, names)); @@ -106,8 +123,7 @@ impl Operation for PushPrivateEnvironment { const INSTRUCTION: &'static str = "INST - PushPrivateEnvironment"; const COST: u8 = 5; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let class = u32::from(context.vm.read::()); let count = context.vm.read::(); let mut names = Vec::with_capacity(count as usize); @@ -120,11 +136,10 @@ impl Operation for PushPrivateEnvironment { .constant_string(index as usize); names.push(name); } - Self::operation(class, names, operand_types, context) + Self::operation(class, names, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let class = u32::from(context.vm.read::()); let count = context.vm.read::(); let mut names = Vec::with_capacity(count as usize); @@ -137,11 +152,10 @@ impl Operation for PushPrivateEnvironment { .constant_string(index as usize); names.push(name); } - Self::operation(class, names, operand_types, context) + Self::operation(class, names, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let class = context.vm.read::(); let count = context.vm.read::(); let mut names = Vec::with_capacity(count as usize); @@ -154,7 +168,7 @@ impl Operation for PushPrivateEnvironment { .constant_string(index as usize); names.push(name); } - Self::operation(class, names, operand_types, context) + Self::operation(class, names, registers, context) } } @@ -170,7 +184,7 @@ impl Operation for PopPrivateEnvironment { const INSTRUCTION: &'static str = "INST - PopPrivateEnvironment"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { context.vm.environments.pop_private(); Ok(CompletionType::Normal) } diff --git a/core/engine/src/vm/opcode/push/literal.rs b/core/engine/src/vm/opcode/push/literal.rs index e7b66c25bdd..42ae0267a43 100644 --- a/core/engine/src/vm/opcode/push/literal.rs +++ b/core/engine/src/vm/opcode/push/literal.rs @@ -1,6 +1,6 @@ use crate::{ object::JsRegExp, - vm::{opcode::Operation, CompletionType, Constant}, + vm::{opcode::Operation, CompletionType, Constant, Registers}, Context, JsResult, JsValue, }; @@ -13,14 +13,19 @@ pub(crate) struct PushLiteral; impl PushLiteral { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + dst: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let constant = &context.vm.frame().code_block().constants[index]; let value: JsValue = match constant { Constant::BigInt(v) => v.clone().into(), Constant::String(v) => v.clone().into(), _ => unreachable!("constant should be a string or bigint"), }; - context.vm.push(value); + registers.set(dst, value); Ok(CompletionType::Normal) } } @@ -30,19 +35,22 @@ impl Operation for PushLiteral { const INSTRUCTION: &'static str = "INST - PushLiteral"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, index, registers, context) } } @@ -55,16 +63,17 @@ pub(crate) struct PushRegExp; impl PushRegExp { fn operation( - context: &mut Context, + dst: u32, pattern_index: usize, flags_index: usize, + registers: &mut Registers, + context: &mut Context, ) -> JsResult { let code_block = context.vm.frame().code_block(); let pattern = code_block.constant_string(pattern_index); let flags = code_block.constant_string(flags_index); - let regexp = JsRegExp::new(pattern, flags, context)?; - context.vm.push(regexp); + registers.set(dst, regexp.into()); Ok(CompletionType::Normal) } } @@ -74,21 +83,24 @@ impl Operation for PushRegExp { const INSTRUCTION: &'static str = "INST - PushRegExp"; const COST: u8 = 5; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let pattern_index = context.vm.read::() as usize; let flags_index = context.vm.read::() as usize; - Self::operation(context, pattern_index, flags_index) + Self::operation(dst, pattern_index, flags_index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let pattern_index = context.vm.read::() as usize; let flags_index = context.vm.read::() as usize; - Self::operation(context, pattern_index, flags_index) + Self::operation(dst, pattern_index, flags_index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let pattern_index = context.vm.read::() as usize; let flags_index = context.vm.read::() as usize; - Self::operation(context, pattern_index, flags_index) + Self::operation(dst, pattern_index, flags_index, registers, context) } } diff --git a/core/engine/src/vm/opcode/push/mod.rs b/core/engine/src/vm/opcode/push/mod.rs index 5a351dbf931..a2b64b7ddf9 100644 --- a/core/engine/src/vm/opcode/push/mod.rs +++ b/core/engine/src/vm/opcode/push/mod.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -26,14 +26,31 @@ macro_rules! implement_push_generics { #[derive(Debug, Clone, Copy)] pub(crate) struct $name; + impl $name { + fn operation(dst: u32, registers: &mut Registers, _: &mut Context) -> JsResult { + registers.set(dst, $push_value.into()); + Ok(CompletionType::Normal) + } + } + impl Operation for $name { const NAME: &'static str = stringify!($name); const INSTRUCTION: &'static str = stringify!("INST - " + $name); const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - context.vm.push($push_value); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) } } }; diff --git a/core/engine/src/vm/opcode/push/numbers.rs b/core/engine/src/vm/opcode/push/numbers.rs index 59c2af00333..f56e14ea883 100644 --- a/core/engine/src/vm/opcode/push/numbers.rs +++ b/core/engine/src/vm/opcode/push/numbers.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -12,15 +12,34 @@ macro_rules! implement_push_numbers_with_conversion { #[derive(Debug, Clone, Copy)] pub(crate) struct $name; + impl $name { + fn operation(dst: u32, value: $num_type, registers: &mut Registers) -> JsResult { + registers.set(dst, i32::from(value).into()); + Ok(CompletionType::Normal) + } + } + impl Operation for $name { const NAME: &'static str = stringify!($name); const INSTRUCTION: &'static str = stringify!("INST - " + $name); const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let value = context.vm.read::<$num_type>(); - context.vm.push(i32::from(value)); - Ok(CompletionType::Normal) + Self::operation(dst, value, registers) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let value = context.vm.read::<$num_type>(); + Self::operation(dst, value, registers) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let value = context.vm.read::<$num_type>(); + Self::operation(dst, value, registers) } } }; @@ -35,15 +54,34 @@ macro_rules! implement_push_numbers_no_conversion { #[derive(Debug, Clone, Copy)] pub(crate) struct $name; + impl $name { + fn operation(dst: u32, value: $num_type, registers: &mut Registers) -> JsResult { + registers.set(dst, value.into()); + Ok(CompletionType::Normal) + } + } + impl Operation for $name { const NAME: &'static str = stringify!($name); const INSTRUCTION: &'static str = stringify!("INST - " + $name); const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); let value = context.vm.read::<$num_type>(); - context.vm.push(value); - Ok(CompletionType::Normal) + Self::operation(dst, value, registers) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let value = context.vm.read::<$num_type>(); + Self::operation(dst, value, registers) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + let value = context.vm.read::<$num_type>(); + Self::operation(dst, value, registers) } } }; diff --git a/core/engine/src/vm/opcode/push/object.rs b/core/engine/src/vm/opcode/push/object.rs index 26297b79f25..7be4d4c5758 100644 --- a/core/engine/src/vm/opcode/push/object.rs +++ b/core/engine/src/vm/opcode/push/object.rs @@ -1,6 +1,6 @@ use crate::{ builtins::OrdinaryObject, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -11,18 +11,40 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct PushEmptyObject; -impl Operation for PushEmptyObject { - const NAME: &'static str = "PushEmptyObject"; - const INSTRUCTION: &'static str = "INST - PushEmptyObject"; - const COST: u8 = 1; - - fn execute(context: &mut Context) -> JsResult { +impl PushEmptyObject { + #[allow(clippy::unnecessary_wraps)] + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let o = context .intrinsics() .templates() .ordinary_object() .create(OrdinaryObject, Vec::default()); - context.vm.push(o); + registers.set(dst, o.into()); Ok(CompletionType::Normal) } } + +impl Operation for PushEmptyObject { + const NAME: &'static str = "PushEmptyObject"; + const INSTRUCTION: &'static str = "INST - PushEmptyObject"; + const COST: u8 = 1; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/require/mod.rs b/core/engine/src/vm/opcode/require/mod.rs deleted file mode 100644 index f0453f2ba29..00000000000 --- a/core/engine/src/vm/opcode/require/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{ - vm::{opcode::Operation, CompletionType}, - Context, JsResult, -}; - -/// `RequireObjectCoercible` implements the Opcode Operation for `Opcode::RequireObjectCoercible` -/// -/// Operation: -/// - Call `RequireObjectCoercible` on the stack value. -#[derive(Debug, Clone, Copy)] -pub(crate) struct RequireObjectCoercible; - -impl Operation for RequireObjectCoercible { - const NAME: &'static str = "RequireObjectCoercible"; - const INSTRUCTION: &'static str = "INST - RequireObjectCoercible"; - const COST: u8 = 1; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let value = value.require_object_coercible()?; - context.vm.push(value.clone()); - Ok(CompletionType::Normal) - } -} diff --git a/core/engine/src/vm/opcode/rest_parameter/mod.rs b/core/engine/src/vm/opcode/rest_parameter/mod.rs index 947fd0fcb49..232c57989df 100644 --- a/core/engine/src/vm/opcode/rest_parameter/mod.rs +++ b/core/engine/src/vm/opcode/rest_parameter/mod.rs @@ -1,6 +1,6 @@ use crate::{ builtins::Array, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -11,40 +11,58 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct RestParameterInit; -impl Operation for RestParameterInit { - const NAME: &'static str = "RestParameterInit"; - const INSTRUCTION: &'static str = "INST - RestParameterInit"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { +impl RestParameterInit { + #[allow(clippy::unnecessary_wraps)] + fn operation( + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let frame = context.vm.frame(); let argument_count = frame.argument_count; let param_count = frame.code_block().parameter_length; - let register_count = frame.code_block().register_count; - - if argument_count >= param_count { + let array = if argument_count >= param_count { let rest_count = argument_count - param_count + 1; let len = context.vm.stack.len() as u32; - let start = (len - rest_count - register_count) as usize; - let end = (len - register_count) as usize; + let start = (len - rest_count) as usize; + let end = len as usize; let args = &context.vm.stack[start..end]; let array = Array::create_array_from_list(args.iter().cloned(), context); context.vm.stack.drain(start..end); - //context.vm.stack.splice(start..end, [array.clone().into()]); context.vm.frame_mut().rp -= (start..end).len() as u32; context.vm.frame_mut().argument_count -= (start..end).len() as u32; - context.vm.push(array); + array } else { - let array = - Array::array_create(0, None, context).expect("could not create an empty array"); - context.vm.push(array); - } + Array::array_create(0, None, context).expect("could not create an empty array") + }; + registers.set(dst, array.into()); Ok(CompletionType::Normal) } } + +impl Operation for RestParameterInit { + const NAME: &'static str = "RestParameterInit"; + const INSTRUCTION: &'static str = "INST - RestParameterInit"; + const COST: u8 = 6; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::().into(); + Self::operation(dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/set/class_prototype.rs b/core/engine/src/vm/opcode/set/class_prototype.rs index 1384826d224..bb92e7873da 100644 --- a/core/engine/src/vm/opcode/set/class_prototype.rs +++ b/core/engine/src/vm/opcode/set/class_prototype.rs @@ -2,7 +2,7 @@ use crate::{ builtins::{function::OrdinaryFunction, OrdinaryObject}, object::{internal_methods::InternalMethodContext, JsObject, CONSTRUCTOR, PROTOTYPE}, property::PropertyDescriptorBuilder, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, JsValue, }; @@ -19,16 +19,11 @@ impl SetClassPrototype { dst: u32, prototype: u32, class: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - let prototype_value = - context - .vm - .frame() - .read_value::<0>(operand_types, prototype, &context.vm); - let prototype = match &prototype_value { + let prototype = registers.get(prototype); + let prototype = match prototype { JsValue::Object(proto) => Some(proto.clone()), JsValue::Null => None, JsValue::Undefined => Some(context.intrinsics().constructors().object().prototype()), @@ -41,10 +36,7 @@ impl SetClassPrototype { prototype, OrdinaryObject, ); - let class = context - .vm - .frame() - .read_value::<1>(operand_types, class, &context.vm); + let class = registers.get(class); { let class_object = class.as_object().expect("class must be object"); @@ -69,7 +61,7 @@ impl SetClassPrototype { .__define_own_property__( &CONSTRUCTOR.into(), PropertyDescriptorBuilder::new() - .value(class) + .value(class.clone()) .writable(true) .enumerable(false) .configurable(true) @@ -78,7 +70,7 @@ impl SetClassPrototype { ) .expect("cannot fail per spec"); - context.vm.stack[(rp + dst) as usize] = proto.into(); + registers.set(dst, proto.into()); Ok(CompletionType::Normal) } } @@ -88,27 +80,24 @@ impl Operation for SetClassPrototype { const INSTRUCTION: &'static str = "INST - SetClassPrototype"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); let prototype = u32::from(context.vm.read::()); let class = u32::from(context.vm.read::()); - Self::operation(dst, prototype, class, operand_types, context) + Self::operation(dst, prototype, class, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = u32::from(context.vm.read::()); let prototype = u32::from(context.vm.read::()); let class = u32::from(context.vm.read::()); - Self::operation(dst, prototype, class, operand_types, context) + Self::operation(dst, prototype, class, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); let prototype = context.vm.read::(); let class = context.vm.read::(); - Self::operation(dst, prototype, class, operand_types, context) + Self::operation(dst, prototype, class, registers, context) } } diff --git a/core/engine/src/vm/opcode/set/home_object.rs b/core/engine/src/vm/opcode/set/home_object.rs index bdbf68e9939..70636cef4bf 100644 --- a/core/engine/src/vm/opcode/set/home_object.rs +++ b/core/engine/src/vm/opcode/set/home_object.rs @@ -1,6 +1,6 @@ use crate::{ builtins::function::OrdinaryFunction, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -11,14 +11,16 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct SetHomeObject; -impl Operation for SetHomeObject { - const NAME: &'static str = "SetHomeObject"; - const INSTRUCTION: &'static str = "INST - SetHomeObject"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let function = context.vm.pop(); - let home = context.vm.pop(); +impl SetHomeObject { + #[allow(clippy::unnecessary_wraps)] + fn operation( + function: u32, + home: u32, + registers: &mut Registers, + _: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let home = registers.get(home); function .as_object() @@ -27,8 +29,30 @@ impl Operation for SetHomeObject { .expect("must be function object") .set_home_object(home.as_object().expect("must be object").clone()); - context.vm.push(home); - context.vm.push(function); Ok(CompletionType::Normal) } } + +impl Operation for SetHomeObject { + const NAME: &'static str = "SetHomeObject"; + const INSTRUCTION: &'static str = "INST - SetHomeObject"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let home = context.vm.read::().into(); + Self::operation(function, home, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let home = context.vm.read::().into(); + Self::operation(function, home, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let home = context.vm.read::(); + Self::operation(function, home, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/set/name.rs b/core/engine/src/vm/opcode/set/name.rs index 2d2fa2ec377..05e8e7b795e 100644 --- a/core/engine/src/vm/opcode/set/name.rs +++ b/core/engine/src/vm/opcode/set/name.rs @@ -2,7 +2,7 @@ use boa_ast::scope::{BindingLocator, BindingLocatorScope}; use crate::{ environments::Environment, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsNativeError, JsResult, }; @@ -31,17 +31,17 @@ impl Operation for ThrowMutateImmutable { const INSTRUCTION: &'static str = "INST - ThrowMutateImmutable"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::(); Self::operation(context, index as usize) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::() as usize; Self::operation(context, index) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(_: &mut Registers, context: &mut Context) -> JsResult { let index = context.vm.read::(); Self::operation(context, index as usize) } @@ -55,17 +55,22 @@ impl Operation for ThrowMutateImmutable { pub(crate) struct SetName; impl SetName { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); let code_block = context.vm.frame().code_block(); let mut binding_locator = code_block.bindings[index].clone(); let strict = code_block.strict(); - let value = context.vm.pop(); context.find_runtime_binding(&mut binding_locator)?; verify_initialized(&binding_locator, context)?; - context.set_binding(&binding_locator, value, strict)?; + context.set_binding(&binding_locator, value.clone(), strict)?; Ok(CompletionType::Normal) } @@ -76,19 +81,22 @@ impl Operation for SetName { const INSTRUCTION: &'static str = "INST - SetName"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(value, index, registers, context) } } @@ -99,28 +107,49 @@ impl Operation for SetName { #[derive(Debug, Clone, Copy)] pub(crate) struct SetNameByLocator; -impl Operation for SetNameByLocator { - const NAME: &'static str = "SetNameByLocator"; - const INSTRUCTION: &'static str = "INST - SetNameByLocator"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { +impl SetNameByLocator { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let frame = context.vm.frame_mut(); let strict = frame.code_block.strict(); let binding_locator = frame .binding_stack .pop() .expect("locator should have been popped before"); - let value = context.vm.pop(); + let value = registers.get(value); verify_initialized(&binding_locator, context)?; - context.set_binding(&binding_locator, value, strict)?; + context.set_binding(&binding_locator, value.clone(), strict)?; Ok(CompletionType::Normal) } } +impl Operation for SetNameByLocator { + const NAME: &'static str = "SetNameByLocator"; + const INSTRUCTION: &'static str = "INST - SetNameByLocator"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} + /// Checks that the binding pointed by `locator` exists and is initialized. fn verify_initialized(locator: &BindingLocator, context: &mut Context) -> JsResult<()> { if !context.is_initialized_binding(locator)? { diff --git a/core/engine/src/vm/opcode/set/private.rs b/core/engine/src/vm/opcode/set/private.rs index dd0c8631f16..3cc305c2237 100644 --- a/core/engine/src/vm/opcode/set/private.rs +++ b/core/engine/src/vm/opcode/set/private.rs @@ -2,7 +2,7 @@ use crate::{ js_str, js_string, object::PrivateElement, property::PropertyDescriptor, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -14,12 +14,17 @@ use crate::{ pub(crate) struct SetPrivateField; impl SetPrivateField { - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + value: u32, + object: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { let name = context.vm.frame().code_block().constant_string(index); - let value = context.vm.pop(); - let object = context.vm.pop(); + let value = registers.get(value); + let object = registers.get(object); let base_obj = object.to_object(context)?; - let name = context .vm .environments @@ -27,7 +32,6 @@ impl SetPrivateField { .expect("private name must be in environment"); base_obj.private_set(&name, value.clone(), context)?; - context.vm.push(value); Ok(CompletionType::Normal) } } @@ -37,19 +41,25 @@ impl Operation for SetPrivateField { const INSTRUCTION: &'static str = "INST - SetPrivateField"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let object = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, object, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let object = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, object, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let object = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, object, index, registers, context) } } @@ -62,17 +72,25 @@ pub(crate) struct DefinePrivateField; impl DefinePrivateField { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context.vm.frame().code_block().constant_string(index); - let value = context.vm.pop(); - let object = context.vm.pop(); + let object = object .as_object() .expect("class prototype must be an object"); - object - .borrow_mut() - .append_private_element(object.private_name(name), PrivateElement::Field(value)); + object.borrow_mut().append_private_element( + object.private_name(name), + PrivateElement::Field(value.clone()), + ); Ok(CompletionType::Normal) } @@ -83,19 +101,25 @@ impl Operation for DefinePrivateField { const INSTRUCTION: &'static str = "INST - DefinePrivateField"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } @@ -108,10 +132,21 @@ pub(crate) struct SetPrivateMethod; impl SetPrivateMethod { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context.vm.frame().code_block().constant_string(index); - let value = context.vm.pop(); + let value = value.as_callable().expect("method must be callable"); + let object = object + .as_object() + .expect("class prototype must be an object"); let name_string = js_string!(js_str!("#"), &name); let desc = PropertyDescriptor::builder() @@ -124,11 +159,6 @@ impl SetPrivateMethod { .__define_own_property__(&js_string!("name").into(), desc, &mut context.into()) .expect("failed to set name property on private method"); - let object = context.vm.pop(); - let object = object - .as_object() - .expect("class prototype must be an object"); - object.borrow_mut().append_private_element( object.private_name(name), PrivateElement::Method(value.clone()), @@ -143,19 +173,25 @@ impl Operation for SetPrivateMethod { const INSTRUCTION: &'static str = "INST - SetPrivateMethod"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } @@ -168,11 +204,18 @@ pub(crate) struct SetPrivateSetter; impl SetPrivateSetter { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context.vm.frame().code_block().constant_string(index); - let value = context.vm.pop(); + let value = value.as_callable().expect("setter must be callable"); - let object = context.vm.pop(); let object = object .as_object() .expect("class prototype must be an object"); @@ -194,19 +237,25 @@ impl Operation for SetPrivateSetter { const INSTRUCTION: &'static str = "INST - SetPrivateSetter"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } @@ -219,11 +268,18 @@ pub(crate) struct SetPrivateGetter; impl SetPrivateGetter { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context.vm.frame().code_block().constant_string(index); - let value = context.vm.pop(); + let value = value.as_callable().expect("getter must be callable"); - let object = context.vm.pop(); let object = object .as_object() .expect("class prototype must be an object"); @@ -245,18 +301,24 @@ impl Operation for SetPrivateGetter { const INSTRUCTION: &'static str = "INST - SetPrivateGetter"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } diff --git a/core/engine/src/vm/opcode/set/property.rs b/core/engine/src/vm/opcode/set/property.rs index 07256681061..f61eb329bbd 100644 --- a/core/engine/src/vm/opcode/set/property.rs +++ b/core/engine/src/vm/opcode/set/property.rs @@ -4,7 +4,7 @@ use crate::{ builtins::function::set_function_name, object::{internal_methods::InternalMethodContext, shape::slot::SlotAttributes}, property::{PropertyDescriptor, PropertyKey}, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsNativeError, JsResult, JsValue, }; @@ -16,15 +16,18 @@ use crate::{ pub(crate) struct SetPropertyByName; impl SetPropertyByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let value = context.vm.pop(); - let receiver = context.vm.pop(); - let object = context.vm.pop(); - let object = if let Some(object) = object.as_object() { - object.clone() - } else { - object.to_object(context)? - }; + fn operation( + value: u32, + receiver: u32, + object: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let receiver = registers.get(receiver); + let object = registers.get(object); + let object = object.to_object(context)?; let ic = &context.vm.frame().code_block().ic[index]; @@ -45,7 +48,7 @@ impl SetPropertyByName { drop(object_borrowed); if slot.attributes.has_set() && result.is_object() { result.as_object().expect("should contain getter").call( - &receiver, + receiver, &[value.clone()], context, )?; @@ -60,7 +63,6 @@ impl SetPropertyByName { let mut object_borrowed = object.borrow_mut(); object_borrowed.properties_mut().storage[slot_index] = value.clone(); } - context.vm.push(value); return Ok(CompletionType::Normal); } drop(object_borrowed); @@ -68,7 +70,7 @@ impl SetPropertyByName { let name: PropertyKey = ic.name.clone().into(); let context = &mut InternalMethodContext::new(context); - let succeeded = object.__set__(name.clone(), value.clone(), receiver, context)?; + let succeeded = object.__set__(name.clone(), value.clone(), receiver.clone(), context)?; if !succeeded && context.vm.frame().code_block.strict() { return Err(JsNativeError::typ() .with_message(format!("cannot set non-writable property: {name}")) @@ -83,7 +85,7 @@ impl SetPropertyByName { let shape = object_borrowed.shape(); ic.set(shape, slot); } - context.vm.stack.push(value); + Ok(CompletionType::Normal) } } @@ -93,19 +95,28 @@ impl Operation for SetPropertyByName { const INSTRUCTION: &'static str = "INST - SetPropertyByName"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let object = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(value, receiver, object, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let object = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(value, receiver, object, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let receiver = context.vm.read::(); + let object = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(value, receiver, object, index, registers, context) } } @@ -116,21 +127,20 @@ impl Operation for SetPropertyByName { #[derive(Debug, Clone, Copy)] pub(crate) struct SetPropertyByValue; -impl Operation for SetPropertyByValue { - const NAME: &'static str = "SetPropertyByValue"; - const INSTRUCTION: &'static str = "INST - SetPropertyByValue"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let key = context.vm.pop(); - let receiver = context.vm.pop(); - let object = context.vm.pop(); - let object = if let Some(object) = object.as_object() { - object.clone() - } else { - object.to_object(context)? - }; +impl SetPropertyByValue { + fn operation( + value: u32, + key: u32, + receiver: u32, + object: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let key = registers.get(key); + let receiver = registers.get(receiver); + let object = registers.get(object); + let object = object.to_object(context)?; let key = key.to_property_key(context)?; @@ -147,9 +157,8 @@ impl Operation for SetPropertyByValue { if object_borrowed .properties_mut() - .set_dense_property(index.get(), &value) + .set_dense_property(index.get(), value) { - context.vm.push(value); return Ok(CompletionType::Normal); } } @@ -157,18 +166,52 @@ impl Operation for SetPropertyByValue { } // Slow path: - let succeeded = - object.__set__(key.clone(), value.clone(), receiver, &mut context.into())?; + let succeeded = object.__set__( + key.clone(), + value.clone(), + receiver.clone(), + &mut context.into(), + )?; if !succeeded && context.vm.frame().code_block.strict() { return Err(JsNativeError::typ() .with_message(format!("cannot set non-writable property: {key}")) .into()); } - context.vm.stack.push(value); + Ok(CompletionType::Normal) } } +impl Operation for SetPropertyByValue { + const NAME: &'static str = "SetPropertyByValue"; + const INSTRUCTION: &'static str = "INST - SetPropertyByValue"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let key = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(value, key, receiver, object, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let key = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(value, key, receiver, object, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let key = context.vm.read::(); + let receiver = context.vm.read::(); + let object = context.vm.read::(); + Self::operation(value, key, receiver, object, registers, context) + } +} + /// `SetPropertyGetterByName` implements the Opcode Operation for `Opcode::SetPropertyGetterByName` /// /// Operation: @@ -177,16 +220,23 @@ impl Operation for SetPropertyByValue { pub(crate) struct SetPropertyGetterByName; impl SetPropertyGetterByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let value = context.vm.pop(); - let object = context.vm.pop(); - let object = object.to_object(context)?; + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context .vm .frame() .code_block() .constant_string(index) .into(); + + let object = object.to_object(context)?; let set = object .__get_own_property__(&name, &mut InternalMethodContext::new(context))? .as_ref() @@ -195,7 +245,7 @@ impl SetPropertyGetterByName { object.__define_own_property__( &name, PropertyDescriptor::builder() - .maybe_get(Some(value)) + .maybe_get(Some(value.clone())) .maybe_set(set) .enumerable(true) .configurable(true) @@ -211,19 +261,25 @@ impl Operation for SetPropertyGetterByName { const INSTRUCTION: &'static str = "INST - SetPropertyGetterByName"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } @@ -234,15 +290,17 @@ impl Operation for SetPropertyGetterByName { #[derive(Debug, Clone, Copy)] pub(crate) struct SetPropertyGetterByValue; -impl Operation for SetPropertyGetterByValue { - const NAME: &'static str = "SetPropertyGetterByValue"; - const INSTRUCTION: &'static str = "INST - SetPropertyGetterByValue"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let key = context.vm.pop(); - let object = context.vm.pop(); +impl SetPropertyGetterByValue { + fn operation( + value: u32, + key: u32, + object: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let key = registers.get(key); + let object = registers.get(object); let object = object.to_object(context)?; let name = key.to_property_key(context)?; @@ -254,7 +312,7 @@ impl Operation for SetPropertyGetterByValue { object.__define_own_property__( &name, PropertyDescriptor::builder() - .maybe_get(Some(value)) + .maybe_get(Some(value.clone())) .maybe_set(set) .enumerable(true) .configurable(true) @@ -265,6 +323,33 @@ impl Operation for SetPropertyGetterByValue { } } +impl Operation for SetPropertyGetterByValue { + const NAME: &'static str = "SetPropertyGetterByValue"; + const INSTRUCTION: &'static str = "INST - SetPropertyGetterByValue"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let key = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(value, key, object, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let key = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(value, key, object, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let key = context.vm.read::(); + let object = context.vm.read::(); + Self::operation(value, key, object, registers, context) + } +} + /// `SetPropertySetterByName` implements the Opcode Operation for `Opcode::SetPropertySetterByName` /// /// Operation: @@ -273,10 +358,15 @@ impl Operation for SetPropertyGetterByValue { pub(crate) struct SetPropertySetterByName; impl SetPropertySetterByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let value = context.vm.pop(); - let object = context.vm.pop(); - let object = object.to_object(context)?; + fn operation( + object: u32, + value: u32, + index: usize, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let name = context .vm .frame() @@ -284,6 +374,8 @@ impl SetPropertySetterByName { .constant_string(index) .into(); + let object = object.to_object(context)?; + let get = object .__get_own_property__(&name, &mut InternalMethodContext::new(context))? .as_ref() @@ -292,7 +384,7 @@ impl SetPropertySetterByName { object.__define_own_property__( &name, PropertyDescriptor::builder() - .maybe_set(Some(value)) + .maybe_set(Some(value.clone())) .maybe_get(get) .enumerable(true) .configurable(true) @@ -308,19 +400,25 @@ impl Operation for SetPropertySetterByName { const INSTRUCTION: &'static str = "INST - SetPropertySetterByName"; const COST: u8 = 4; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(object, value, index, registers, context) } } @@ -331,15 +429,18 @@ impl Operation for SetPropertySetterByName { #[derive(Debug, Clone, Copy)] pub(crate) struct SetPropertySetterByValue; -impl Operation for SetPropertySetterByValue { - const NAME: &'static str = "SetPropertySetterByValue"; - const INSTRUCTION: &'static str = "INST - SetPropertySetterByValue"; - const COST: u8 = 4; +impl SetPropertySetterByValue { + fn operation( + value: u32, + key: u32, + object: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let key = registers.get(key); + let object = registers.get(object); - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let key = context.vm.pop(); - let object = context.vm.pop(); let object = object.to_object(context)?; let name = key.to_property_key(context)?; @@ -351,7 +452,7 @@ impl Operation for SetPropertySetterByValue { object.__define_own_property__( &name, PropertyDescriptor::builder() - .maybe_set(Some(value)) + .maybe_set(Some(value.clone())) .maybe_get(get) .enumerable(true) .configurable(true) @@ -362,6 +463,33 @@ impl Operation for SetPropertySetterByValue { } } +impl Operation for SetPropertySetterByValue { + const NAME: &'static str = "SetPropertySetterByValue"; + const INSTRUCTION: &'static str = "INST - SetPropertySetterByValue"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let key = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(value, key, object, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let key = context.vm.read::().into(); + let object = context.vm.read::().into(); + Self::operation(value, key, object, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let key = context.vm.read::(); + let object = context.vm.read::(); + Self::operation(value, key, object, registers, context) + } +} + /// `SetFunctionName` implements the Opcode Operation for `Opcode::SetFunctionName` /// /// Operation: @@ -369,17 +497,19 @@ impl Operation for SetPropertySetterByValue { #[derive(Debug, Clone, Copy)] pub(crate) struct SetFunctionName; -impl Operation for SetFunctionName { - const NAME: &'static str = "SetFunctionName"; - const INSTRUCTION: &'static str = "INST - SetFunctionName"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let prefix = context.vm.read::(); - let function = context.vm.pop(); - let name = context.vm.pop(); - - let name = match name { +impl SetFunctionName { + #[allow(clippy::unnecessary_wraps)] + fn operation( + function: u32, + name: u32, + prefix: u8, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let function = registers.get(function); + let name = registers.get(name); + + let name = match name.clone() { JsValue::String(name) => name.into(), JsValue::Symbol(name) => name.into(), _ => unreachable!(), @@ -398,7 +528,33 @@ impl Operation for SetFunctionName { context, ); - context.vm.stack.push(function); Ok(CompletionType::Normal) } } + +impl Operation for SetFunctionName { + const NAME: &'static str = "SetFunctionName"; + const INSTRUCTION: &'static str = "INST - SetFunctionName"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let name = context.vm.read::().into(); + let prefix = context.vm.read::(); + Self::operation(function, name, prefix, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::().into(); + let name = context.vm.read::().into(); + let prefix = context.vm.read::(); + Self::operation(function, name, prefix, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let function = context.vm.read::(); + let name = context.vm.read::(); + let prefix = context.vm.read::(); + Self::operation(function, name, prefix, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/set/prototype.rs b/core/engine/src/vm/opcode/set/prototype.rs index 2469de1c884..c4a115de8b5 100644 --- a/core/engine/src/vm/opcode/set/prototype.rs +++ b/core/engine/src/vm/opcode/set/prototype.rs @@ -1,6 +1,6 @@ use crate::{ object::internal_methods::InternalMethodContext, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -11,14 +11,16 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct SetPrototype; -impl Operation for SetPrototype { - const NAME: &'static str = "SetPrototype"; - const INSTRUCTION: &'static str = "INST - SetPrototype"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let object = context.vm.pop(); +impl SetPrototype { + #[allow(clippy::unnecessary_wraps)] + fn operation( + object: u32, + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let object = registers.get(object); + let value = registers.get(value); let prototype = if let Some(prototype) = value.as_object() { Some(prototype.clone()) @@ -36,3 +38,27 @@ impl Operation for SetPrototype { Ok(CompletionType::Normal) } } + +impl Operation for SetPrototype { + const NAME: &'static str = "SetPrototype"; + const INSTRUCTION: &'static str = "INST - SetPrototype"; + const COST: u8 = 4; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); + Self::operation(object, value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::().into(); + let value = context.vm.read::().into(); + Self::operation(object, value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let object = context.vm.read::(); + let value = context.vm.read::(); + Self::operation(object, value, registers, context) + } +} diff --git a/core/engine/src/vm/opcode/swap/mod.rs b/core/engine/src/vm/opcode/swap/mod.rs deleted file mode 100644 index 27336bd8387..00000000000 --- a/core/engine/src/vm/opcode/swap/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::{ - vm::{opcode::Operation, CompletionType}, - Context, JsResult, -}; - -/// `Swap` implements the Opcode Operation for `Opcode::Swap` -/// -/// Operation: -/// - Swap the top two values on the stack. -#[derive(Debug, Clone, Copy)] -pub(crate) struct Swap; - -impl Operation for Swap { - const NAME: &'static str = "Swap"; - const INSTRUCTION: &'static str = "INST - Swap"; - const COST: u8 = 1; - - fn execute(context: &mut Context) -> JsResult { - let len = context.vm.stack.len(); - assert!(len > 1); - context.vm.stack.swap(len - 1, len - 2); - Ok(CompletionType::Normal) - } -} - -/// `RotateLeft` implements the Opcode Operation for `Opcode::RotateLeft` -/// -/// Operation: -/// - Rotates the n top values to the left. -#[derive(Debug, Clone, Copy)] -pub(crate) struct RotateLeft; - -impl Operation for RotateLeft { - const NAME: &'static str = "RotateLeft"; - const INSTRUCTION: &'static str = "INST - RotateLeft"; - const COST: u8 = 2; - - fn execute(context: &mut Context) -> JsResult { - let n = context.vm.read::() as usize; - let len = context.vm.stack.len(); - context.vm.stack[(len - n)..].rotate_left(1); - Ok(CompletionType::Normal) - } -} - -/// `RotateRight` implements the Opcode Operation for `Opcode::RotateRight` -/// -/// Operation: -/// - Rotates the n top values to the right. -#[derive(Debug, Clone, Copy)] -pub(crate) struct RotateRight; - -impl Operation for RotateRight { - const NAME: &'static str = "RotateRight"; - const INSTRUCTION: &'static str = "INST - RotateRight"; - const COST: u8 = 2; - - fn execute(context: &mut Context) -> JsResult { - let n = context.vm.read::() as usize; - let len = context.vm.stack.len(); - context.vm.stack[(len - n)..].rotate_right(1); - Ok(CompletionType::Normal) - } -} diff --git a/core/engine/src/vm/opcode/switch/mod.rs b/core/engine/src/vm/opcode/switch/mod.rs index 31e185825d6..12aa45b048a 100644 --- a/core/engine/src/vm/opcode/switch/mod.rs +++ b/core/engine/src/vm/opcode/switch/mod.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -11,41 +11,47 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct Case; +impl Case { + #[allow(clippy::unnecessary_wraps)] + fn operation( + address: u32, + value: u32, + condition: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let condition = registers.get(condition); + if value.strict_equals(condition) { + context.vm.frame_mut().pc = address; + } + Ok(CompletionType::Normal) + } +} + impl Operation for Case { const NAME: &'static str = "Case"; const INSTRUCTION: &'static str = "INST - Case"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let address = context.vm.read::(); - let cond = context.vm.pop(); - let value = context.vm.pop(); - - if value.strict_equals(&cond) { - context.vm.frame_mut().pc = address; - } else { - context.vm.push(value); - } - Ok(CompletionType::Normal) + let value = context.vm.read::().into(); + let condition = context.vm.read::().into(); + Self::operation(address, value, condition, registers, context) } -} - -/// `Default` implements the Opcode Operation for `Opcode::Default` -/// -/// Operation: -/// - Pops the top of stack and jump to address. -#[derive(Debug, Clone, Copy)] -pub(crate) struct Default; -impl Operation for Default { - const NAME: &'static str = "Default"; - const INSTRUCTION: &'static str = "INST - Default"; - const COST: u8 = 2; + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::().into(); + let condition = context.vm.read::().into(); + Self::operation(address, value, condition, registers, context) + } - fn execute(context: &mut Context) -> JsResult { - let exit = context.vm.read::(); - let _val = context.vm.pop(); - context.vm.frame_mut().pc = exit; - Ok(CompletionType::Normal) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let address = context.vm.read::(); + let value = context.vm.read::(); + let condition = context.vm.read::(); + Self::operation(address, value, condition, registers, context) } } diff --git a/core/engine/src/vm/opcode/templates/mod.rs b/core/engine/src/vm/opcode/templates/mod.rs index 7b8f7393501..b920e633259 100644 --- a/core/engine/src/vm/opcode/templates/mod.rs +++ b/core/engine/src/vm/opcode/templates/mod.rs @@ -3,7 +3,7 @@ use crate::{ js_string, object::IntegrityLevel, property::PropertyDescriptor, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -14,21 +14,48 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct TemplateLookup; +impl TemplateLookup { + #[allow(clippy::unnecessary_wraps)] + fn operation( + jump: u32, + site: u64, + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + if let Some(template) = context.realm().lookup_template(site) { + registers.set(dst, template.into()); + context.vm.frame_mut().pc = jump; + } + + Ok(CompletionType::Normal) + } +} + impl Operation for TemplateLookup { const NAME: &'static str = "TemplateLookup"; const INSTRUCTION: &'static str = "INST - TemplateLookup"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let jump = context.vm.read::(); let site = context.vm.read::(); + let dst = context.vm.read::().into(); + Self::operation(jump, site, dst, registers, context) + } - if let Some(template) = context.realm().lookup_template(site) { - context.vm.push(template); - context.vm.frame_mut().pc = jump; - } + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let jump = context.vm.read::(); + let site = context.vm.read::(); + let dst = context.vm.read::().into(); + Self::operation(jump, site, dst, registers, context) + } - Ok(CompletionType::Normal) + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let jump = context.vm.read::(); + let site = context.vm.read::(); + let dst = context.vm.read::(); + Self::operation(jump, site, dst, registers, context) } } @@ -41,20 +68,25 @@ pub(crate) struct TemplateCreate; impl TemplateCreate { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, count: u32, site: u64) -> JsResult { - let template = - Array::array_create(count.into(), None, context).expect("cannot fail per spec"); - let raw_obj = - Array::array_create(count.into(), None, context).expect("cannot fail per spec"); - - for index in (0..count).rev() { - let raw_value = context.vm.pop(); - let cooked_value = context.vm.pop(); + fn operation( + site: u64, + dst: u32, + count: u64, + values: &[(u32, u32)], + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let template = Array::array_create(count, None, context).expect("cannot fail per spec"); + let raw_obj = Array::array_create(count, None, context).expect("cannot fail per spec"); + + for (index, (cooked, raw)) in values.iter().enumerate() { + let raw_value = registers.get(*raw); + let cooked_value = registers.get(*cooked); template .define_property_or_throw( index, PropertyDescriptor::builder() - .value(cooked_value) + .value(cooked_value.clone()) .writable(false) .enumerable(true) .configurable(false), @@ -65,7 +97,7 @@ impl TemplateCreate { .define_property_or_throw( index, PropertyDescriptor::builder() - .value(raw_value) + .value(raw_value.clone()) .writable(false) .enumerable(true) .configurable(false), @@ -94,7 +126,7 @@ impl TemplateCreate { context.realm().push_template(site, template.clone()); - context.vm.push(template); + registers.set(dst, template.into()); Ok(CompletionType::Normal) } } @@ -104,21 +136,42 @@ impl Operation for TemplateCreate { const INSTRUCTION: &'static str = "INST - TemplateCreate"; const COST: u8 = 6; - fn execute(context: &mut Context) -> JsResult { - let count = u32::from(context.vm.read::()); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let site = context.vm.read::(); - Self::operation(context, count, site) + let dst = context.vm.read::().into(); + let count = context.vm.read::().into(); + let mut values = Vec::with_capacity(count as usize); + for _ in 0..count { + let cooked = context.vm.read::().into(); + let raw = context.vm.read::().into(); + values.push((cooked, raw)); + } + Self::operation(site, dst, count, &values, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let count = u32::from(context.vm.read::()); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let site = context.vm.read::(); - Self::operation(context, count, site) + let dst = context.vm.read::().into(); + let count = context.vm.read::().into(); + let mut values = Vec::with_capacity(count as usize); + for _ in 0..count { + let cooked = context.vm.read::().into(); + let raw = context.vm.read::().into(); + values.push((cooked, raw)); + } + Self::operation(site, dst, count, &values, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let count = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let site = context.vm.read::(); - Self::operation(context, count, site) + let dst = context.vm.read::(); + let count = context.vm.read::().into(); + let mut values = Vec::with_capacity(count as usize); + for _ in 0..count { + let cooked = context.vm.read::(); + let raw = context.vm.read::(); + values.push((cooked, raw)); + } + Self::operation(site, dst, count, &values, registers, context) } } diff --git a/core/engine/src/vm/opcode/to/mod.rs b/core/engine/src/vm/opcode/to/mod.rs index 99a56f46a26..7e99a365757 100644 --- a/core/engine/src/vm/opcode/to/mod.rs +++ b/core/engine/src/vm/opcode/to/mod.rs @@ -1,43 +1,49 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; -/// `ToBoolean` implements the Opcode Operation for `Opcode::ToBoolean` +/// `ToPropertyKey` implements the Opcode Operation for `Opcode::ToPropertyKey` /// /// Operation: -/// - Pops value converts it to boolean and pushes it back. +/// - Call `ToPropertyKey` on the value on the stack. #[derive(Debug, Clone, Copy)] -pub(crate) struct ToBoolean; - -impl Operation for ToBoolean { - const NAME: &'static str = "ToBoolean"; - const INSTRUCTION: &'static str = "INST - ToBoolean"; - const COST: u8 = 1; +pub(crate) struct ToPropertyKey; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - context.vm.push(value.to_boolean()); +impl ToPropertyKey { + fn operation( + value: u32, + dst: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + let value = registers.get(value); + let key = value.to_property_key(context)?; + registers.set(dst, key.into()); Ok(CompletionType::Normal) } } -/// `ToPropertyKey` implements the Opcode Operation for `Opcode::ToPropertyKey` -/// -/// Operation: -/// - Call `ToPropertyKey` on the value on the stack. -#[derive(Debug, Clone, Copy)] -pub(crate) struct ToPropertyKey; - impl Operation for ToPropertyKey { const NAME: &'static str = "ToPropertyKey"; const INSTRUCTION: &'static str = "INST - ToPropertyKey"; const COST: u8 = 2; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let key = value.to_property_key(context)?; - context.vm.push(key); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let dst = context.vm.read::().into(); + Self::operation(value, dst, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + let dst = context.vm.read::().into(); + Self::operation(value, dst, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + let dst = context.vm.read::(); + Self::operation(value, dst, registers, context) } } diff --git a/core/engine/src/vm/opcode/unary_ops/decrement.rs b/core/engine/src/vm/opcode/unary_ops/decrement.rs index 6a5f7adf874..8aed83467c7 100644 --- a/core/engine/src/vm/opcode/unary_ops/decrement.rs +++ b/core/engine/src/vm/opcode/unary_ops/decrement.rs @@ -1,6 +1,6 @@ use crate::{ - value::JsValue, - vm::{opcode::Operation, CompletionType}, + value::{JsValue, Numeric}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsBigInt, JsResult, }; @@ -16,22 +16,25 @@ impl Dec { fn operation( src: u32, dst: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - let value = context - .vm - .frame() - .read_value::<0>(operand_types, src, &context.vm); - let value = match value { - JsValue::Integer(number) if number > i32::MIN => JsValue::from(number - 1), - JsValue::Rational(value) => JsValue::from(value - 1f64), - JsValue::BigInt(bigint) => JsBigInt::sub(&bigint, &JsBigInt::one()).into(), - _ => unreachable!("there is always a call to ToNumeric before Inc"), - }; + let value = registers.get(src); - context.vm.stack[(rp + dst) as usize] = value; + let (numeric, value) = match value { + JsValue::Integer(number) if *number > i32::MIN => { + (JsValue::from(*number), JsValue::from(number - 1)) + } + _ => match value.to_numeric(context)? { + Numeric::Number(number) => (JsValue::from(number), JsValue::from(number - 1f64)), + Numeric::BigInt(bigint) => ( + JsValue::from(bigint.clone()), + JsValue::from(JsBigInt::sub(&bigint, &JsBigInt::one())), + ), + }, + }; + registers.set(src, numeric); + registers.set(dst, value); Ok(CompletionType::Normal) } } @@ -41,22 +44,19 @@ impl Operation for Dec { const INSTRUCTION: &'static str = "INST - Dec"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst: u32 = context.vm.read::().into(); let src: u32 = context.vm.read::().into(); - Self::operation(src, dst, operand_types, context) + Self::operation(src, dst, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst: u32 = context.vm.read::().into(); let src: u32 = context.vm.read::().into(); - Self::operation(src, dst, operand_types, context) + Self::operation(src, dst, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst: u32 = context.vm.read::(); let src: u32 = context.vm.read::(); - Self::operation(src, dst, operand_types, context) + Self::operation(src, dst, registers, context) } } diff --git a/core/engine/src/vm/opcode/unary_ops/increment.rs b/core/engine/src/vm/opcode/unary_ops/increment.rs index f72bd7cc487..944574e00d8 100644 --- a/core/engine/src/vm/opcode/unary_ops/increment.rs +++ b/core/engine/src/vm/opcode/unary_ops/increment.rs @@ -1,56 +1,9 @@ use crate::{ - value::JsValue, - vm::{opcode::Operation, CompletionType}, + value::{JsValue, Numeric}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsBigInt, JsResult, }; -/// `ToNumeric` implements the Opcode Operation for `Opcode::ToNumeric` -#[derive(Debug, Clone, Copy)] -pub(crate) struct ToNumeric; - -impl ToNumeric { - fn operation( - src: u32, - dst: u32, - operand_types: u8, - context: &mut Context, - ) -> JsResult { - let rp = context.vm.frame().rp; - let value = context - .vm - .frame() - .read_value::<0>(operand_types, src, &context.vm) - .to_numeric(context)?; - context.vm.stack[(rp + dst) as usize] = value.into(); - Ok(CompletionType::Normal) - } -} - -impl Operation for ToNumeric { - const NAME: &'static str = "ToNumeric"; - const INSTRUCTION: &'static str = "INST - ToNumeric"; - const COST: u8 = 3; - - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let dst = context.vm.read::().into(); - let src = context.vm.read::().into(); - Self::operation(src, dst, operand_types, context) - } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let dst = context.vm.read::().into(); - let src = context.vm.read::().into(); - Self::operation(src, dst, operand_types, context) - } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); - let dst = context.vm.read::(); - let src = context.vm.read::(); - Self::operation(src, dst, operand_types, context) - } -} - /// `Inc` implements the Opcode Operation for `Opcode::Inc` /// /// Operation: @@ -63,22 +16,25 @@ impl Inc { fn operation( src: u32, dst: u32, - operand_types: u8, + registers: &mut Registers, context: &mut Context, ) -> JsResult { - let rp = context.vm.frame().rp; - let value = context - .vm - .frame() - .read_value::<0>(operand_types, src, &context.vm); - let value = match value { - JsValue::Integer(number) if number < i32::MAX => JsValue::from(number + 1), - JsValue::Rational(value) => JsValue::from(value + 1f64), - JsValue::BigInt(bigint) => JsBigInt::add(&bigint, &JsBigInt::one()).into(), - _ => unreachable!("there is always a call to ToNumeric before Inc"), - }; + let value = registers.get(src); - context.vm.stack[(rp + dst) as usize] = value; + let (numeric, value) = match value { + JsValue::Integer(number) if *number < i32::MAX => { + (JsValue::from(*number), JsValue::from(number + 1)) + } + _ => match value.to_numeric(context)? { + Numeric::Number(number) => (JsValue::from(number), JsValue::from(number + 1f64)), + Numeric::BigInt(bigint) => ( + JsValue::from(bigint.clone()), + JsValue::from(JsBigInt::add(&bigint, &JsBigInt::one())), + ), + }, + }; + registers.set(src, numeric); + registers.set(dst, value); Ok(CompletionType::Normal) } } @@ -88,22 +44,21 @@ impl Operation for Inc { const INSTRUCTION: &'static str = "INST - Inc"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::().into(); let src = context.vm.read::().into(); - Self::operation(src, dst, operand_types, context) + Self::operation(src, dst, registers, context) } - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::().into(); let src = context.vm.read::().into(); - Self::operation(src, dst, operand_types, context) + Self::operation(src, dst, registers, context) } - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let operand_types = context.vm.read::(); + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { let dst = context.vm.read::(); let src = context.vm.read::(); - Self::operation(src, dst, operand_types, context) + Self::operation(src, dst, registers, context) } } diff --git a/core/engine/src/vm/opcode/unary_ops/logical.rs b/core/engine/src/vm/opcode/unary_ops/logical.rs index e2b7d44491d..a187b6307c9 100644 --- a/core/engine/src/vm/opcode/unary_ops/logical.rs +++ b/core/engine/src/vm/opcode/unary_ops/logical.rs @@ -1,5 +1,5 @@ use crate::{ - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -10,14 +10,35 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct LogicalNot; +impl LogicalNot { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + registers: &mut Registers, + _: &mut Context, + ) -> JsResult { + registers.set(value, (!registers.get(value).to_boolean()).into()); + Ok(CompletionType::Normal) + } +} + impl Operation for LogicalNot { const NAME: &'static str = "LogicalNot"; const INSTRUCTION: &'static str = "INST - LogicalNot"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - context.vm.push(!value.to_boolean()); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } diff --git a/core/engine/src/vm/opcode/unary_ops/mod.rs b/core/engine/src/vm/opcode/unary_ops/mod.rs index d9dc848b5a0..07d7868d26d 100644 --- a/core/engine/src/vm/opcode/unary_ops/mod.rs +++ b/core/engine/src/vm/opcode/unary_ops/mod.rs @@ -1,7 +1,7 @@ use crate::{ builtins::Number, value::Numeric, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsBigInt, JsResult, }; use std::ops::Neg as StdNeg; @@ -9,12 +9,10 @@ use std::ops::Neg as StdNeg; pub(crate) mod decrement; pub(crate) mod increment; pub(crate) mod logical; -pub(crate) mod void; pub(crate) use decrement::*; pub(crate) use increment::*; pub(crate) use logical::*; -pub(crate) use void::*; /// `TypeOf` implements the Opcode Operation for `Opcode::TypeOf` /// @@ -23,15 +21,36 @@ pub(crate) use void::*; #[derive(Debug, Clone, Copy)] pub(crate) struct TypeOf; +impl TypeOf { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + registers: &mut Registers, + _: &mut Context, + ) -> JsResult { + registers.set(value, registers.get(value).js_type_of().into()); + Ok(CompletionType::Normal) + } +} + impl Operation for TypeOf { const NAME: &'static str = "TypeOf"; const INSTRUCTION: &'static str = "INST - TypeOf"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - context.vm.push(value.js_type_of()); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } @@ -42,16 +61,35 @@ impl Operation for TypeOf { #[derive(Debug, Clone, Copy)] pub(crate) struct Pos; +impl Pos { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + registers.set(value, registers.get(value).to_number(context)?.into()); + Ok(CompletionType::Normal) + } +} + impl Operation for Pos { const NAME: &'static str = "Pos"; const INSTRUCTION: &'static str = "INST - Pos"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - let value = value.to_number(context)?; - context.vm.push(value); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } @@ -62,18 +100,38 @@ impl Operation for Pos { #[derive(Debug, Clone, Copy)] pub(crate) struct Neg; +impl Neg { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + match registers.get(value).to_numeric(context)? { + Numeric::Number(number) => registers.set(value, number.neg().into()), + Numeric::BigInt(bigint) => registers.set(value, JsBigInt::neg(&bigint).into()), + } + Ok(CompletionType::Normal) + } +} + impl Operation for Neg { const NAME: &'static str = "Neg"; const INSTRUCTION: &'static str = "INST - Neg"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - match value.to_numeric(context)? { - Numeric::Number(number) => context.vm.push(number.neg()), - Numeric::BigInt(bigint) => context.vm.push(JsBigInt::neg(&bigint)), - } - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } @@ -84,17 +142,37 @@ impl Operation for Neg { #[derive(Debug, Clone, Copy)] pub(crate) struct BitNot; +impl BitNot { + fn operation( + value: u32, + registers: &mut Registers, + context: &mut Context, + ) -> JsResult { + match registers.get(value).to_numeric(context)? { + Numeric::Number(number) => registers.set(value, Number::not(number).into()), + Numeric::BigInt(bigint) => registers.set(value, JsBigInt::not(&bigint).into()), + } + Ok(CompletionType::Normal) + } +} + impl Operation for BitNot { const NAME: &'static str = "BitNot"; const INSTRUCTION: &'static str = "INST - BitNot"; const COST: u8 = 3; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - match value.to_numeric(context)? { - Numeric::Number(number) => context.vm.push(Number::not(number)), - Numeric::BigInt(bigint) => context.vm.push(JsBigInt::not(&bigint)), - } - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } } diff --git a/core/engine/src/vm/opcode/unary_ops/void.rs b/core/engine/src/vm/opcode/unary_ops/void.rs deleted file mode 100644 index 8d33d9e6886..00000000000 --- a/core/engine/src/vm/opcode/unary_ops/void.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::{ - vm::{opcode::Operation, CompletionType}, - Context, JsResult, JsValue, -}; - -/// `Void` implements the Opcode Operation for `Opcode::Void` -/// -/// Operation: -/// - Unary `void` operator. -#[derive(Debug, Clone, Copy)] -pub(crate) struct Void; - -impl Operation for Void { - const NAME: &'static str = "Void"; - const INSTRUCTION: &'static str = "INST - Void"; - const COST: u8 = 1; - - fn execute(context: &mut Context) -> JsResult { - let _old = context.vm.pop(); - context.vm.push(JsValue::undefined()); - Ok(CompletionType::Normal) - } -} diff --git a/core/engine/src/vm/opcode/value/mod.rs b/core/engine/src/vm/opcode/value/mod.rs index 2fe6969ae89..f367e432a7a 100644 --- a/core/engine/src/vm/opcode/value/mod.rs +++ b/core/engine/src/vm/opcode/value/mod.rs @@ -1,6 +1,6 @@ use crate::{ error::JsNativeError, - vm::{opcode::Operation, CompletionType}, + vm::{opcode::Operation, CompletionType, Registers}, Context, JsResult, }; @@ -11,13 +11,13 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct ValueNotNullOrUndefined; -impl Operation for ValueNotNullOrUndefined { - const NAME: &'static str = "ValueNotNullOrUndefined"; - const INSTRUCTION: &'static str = "INST - ValueNotNullOrUndefined"; - const COST: u8 = 2; - - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); +impl ValueNotNullOrUndefined { + fn operation( + value: u32, + registers: &mut Registers, + _: &mut Context, + ) -> JsResult { + let value = registers.get(value); if value.is_null() { return Err(JsNativeError::typ() .with_message("Cannot destructure 'null' value") @@ -28,11 +28,31 @@ impl Operation for ValueNotNullOrUndefined { .with_message("Cannot destructure 'undefined' value") .into()); } - context.vm.push(value); Ok(CompletionType::Normal) } } +impl Operation for ValueNotNullOrUndefined { + const NAME: &'static str = "ValueNotNullOrUndefined"; + const INSTRUCTION: &'static str = "INST - ValueNotNullOrUndefined"; + const COST: u8 = 2; + + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) + } +} + /// `IsObject` implements the Opcode Operation for `Opcode::IsObject` /// /// Operation: @@ -40,14 +60,36 @@ impl Operation for ValueNotNullOrUndefined { #[derive(Debug, Clone, Copy)] pub(crate) struct IsObject; +impl IsObject { + #[allow(clippy::unnecessary_wraps)] + fn operation( + value: u32, + registers: &mut Registers, + _: &mut Context, + ) -> JsResult { + let is_object = registers.get(value).is_object(); + registers.set(value, is_object.into()); + Ok(CompletionType::Normal) + } +} + impl Operation for IsObject { const NAME: &'static str = "IsObject"; const INSTRUCTION: &'static str = "INST - IsObject"; const COST: u8 = 1; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - context.vm.push(value.is_object()); - Ok(CompletionType::Normal) + fn execute(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u16(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::().into(); + Self::operation(value, registers, context) + } + + fn execute_u32(registers: &mut Registers, context: &mut Context) -> JsResult { + let value = context.vm.read::(); + Self::operation(value, registers, context) } }