From b4644a5b87dd3a6630ee09597c5c056de4295519 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Mon, 30 Oct 2023 19:42:47 +0100 Subject: [PATCH] Move `FunctionKind` to `CodeBlock` (#3440) * Move `FunctionKind` flag to CodeBlock * Add `IS_ARROW` flag to codeblock * Remove GetAsyncArrowFunction and GetFunctionAsync opcodes * Remove GetGeneratorAsync opcode --- boa_engine/src/builtins/function/mod.rs | 142 +++++------------- boa_engine/src/bytecompiler/declarations.rs | 19 +-- boa_engine/src/bytecompiler/expression/mod.rs | 10 +- boa_engine/src/bytecompiler/function.rs | 17 ++- boa_engine/src/bytecompiler/jump_control.rs | 2 +- boa_engine/src/bytecompiler/mod.rs | 57 ++----- boa_engine/src/bytecompiler/statement/mod.rs | 2 +- boa_engine/src/bytecompiler/statement/try.rs | 8 +- boa_engine/src/bytecompiler/utils.rs | 4 +- boa_engine/src/module/source.rs | 8 +- boa_engine/src/object/mod.rs | 2 +- boa_engine/src/vm/code_block.rs | 134 ++++++++--------- boa_engine/src/vm/flowgraph/mod.rs | 12 +- boa_engine/src/vm/opcode/get/function.rs | 87 +---------- boa_engine/src/vm/opcode/get/generator.rs | 40 +---- boa_engine/src/vm/opcode/mod.rs | 27 +--- 16 files changed, 160 insertions(+), 411 deletions(-) diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 0d9575c677..5940e365ff 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -39,7 +39,7 @@ use boa_gc::{self, custom_trace, Finalize, Gc, Trace}; use boa_interner::Sym; use boa_parser::{Parser, Source}; use boa_profiler::Profiler; -use std::{fmt, io::Read}; +use std::io::Read; use thin_vec::ThinVec; pub(crate) mod arguments; @@ -143,68 +143,6 @@ unsafe impl Trace for ClassFieldDefinition { }} } -#[derive(Finalize)] -pub(crate) enum FunctionKind { - /// A bytecode function. - Ordinary { - /// The `[[Fields]]` internal slot. - fields: ThinVec, - - /// The `[[PrivateMethods]]` internal slot. - private_methods: ThinVec<(PrivateName, PrivateElement)>, - }, - - /// A bytecode async function. - Async, - - /// A bytecode generator function. - Generator, - - /// A bytecode async generator function. - AsyncGenerator, -} - -impl fmt::Debug for FunctionKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Ordinary { .. } => f - .debug_struct("FunctionKind::Ordinary") - .finish_non_exhaustive(), - Self::Async { .. } => f - .debug_struct("FunctionKind::Async") - .finish_non_exhaustive(), - Self::Generator { .. } => f - .debug_struct("FunctionKind::Generator") - .finish_non_exhaustive(), - Self::AsyncGenerator { .. } => f - .debug_struct("FunctionKind::AsyncGenerator") - .finish_non_exhaustive(), - } - } -} - -unsafe impl Trace for FunctionKind { - custom_trace! {this, { - match this { - Self::Ordinary { - fields, - private_methods, - .. - } => { - for elem in fields { - mark(elem); - } - for (_, elem) in private_methods { - mark(elem); - } - } - Self::Async - | Self::Generator - | Self::AsyncGenerator => {} - } - }} -} - /// Boa representation of a JavaScript Function Object. /// /// `FunctionBody` is specific to this interpreter, it will either be Rust code or JavaScript code @@ -228,11 +166,31 @@ pub struct OrdinaryFunction { /// The [`Realm`] the function is defined in. pub(crate) realm: Realm, - /// The kind of ordinary function. - pub(crate) kind: FunctionKind, + /// The `[[Fields]]` internal slot. + fields: ThinVec, + + /// The `[[PrivateMethods]]` internal slot. + private_methods: ThinVec<(PrivateName, PrivateElement)>, } impl OrdinaryFunction { + pub(crate) fn new( + code: Gc, + environments: EnvironmentStack, + script_or_module: Option, + realm: Realm, + ) -> Self { + Self { + code, + environments, + home_object: None, + script_or_module, + realm, + fields: ThinVec::default(), + private_methods: ThinVec::default(), + } + } + /// Returns the codeblock of the function. #[must_use] pub fn codeblock(&self) -> &CodeBlock { @@ -266,47 +224,27 @@ impl OrdinaryFunction { /// Returns the values of the `[[Fields]]` internal slot. pub(crate) fn get_fields(&self) -> &[ClassFieldDefinition] { - if let FunctionKind::Ordinary { fields, .. } = &self.kind { - fields - } else { - &[] - } + &self.fields } /// Pushes a value to the `[[Fields]]` internal slot if present. pub(crate) fn push_field(&mut self, key: PropertyKey, value: JsFunction) { - if let FunctionKind::Ordinary { fields, .. } = &mut self.kind { - fields.push(ClassFieldDefinition::Public(key, value)); - } + self.fields.push(ClassFieldDefinition::Public(key, value)); } /// Pushes a private value to the `[[Fields]]` internal slot if present. pub(crate) fn push_field_private(&mut self, name: PrivateName, value: JsFunction) { - if let FunctionKind::Ordinary { fields, .. } = &mut self.kind { - fields.push(ClassFieldDefinition::Private(name, value)); - } + self.fields.push(ClassFieldDefinition::Private(name, value)); } /// Returns the values of the `[[PrivateMethods]]` internal slot. pub(crate) fn get_private_methods(&self) -> &[(PrivateName, PrivateElement)] { - if let FunctionKind::Ordinary { - private_methods, .. - } = &self.kind - { - private_methods - } else { - &[] - } + &self.private_methods } /// Pushes a private method to the `[[PrivateMethods]]` internal slot if present. pub(crate) fn push_private_method(&mut self, name: PrivateName, method: PrivateElement) { - if let FunctionKind::Ordinary { - private_methods, .. - } = &mut self.kind - { - private_methods.push((name, method)); - } + self.private_methods.push((name, method)); } /// Gets the `Realm` from where this function originates. @@ -315,14 +253,9 @@ impl OrdinaryFunction { &self.realm } - /// Gets a reference to the [`FunctionKind`] of the `Function`. - pub(crate) const fn kind(&self) -> &FunctionKind { - &self.kind - } - /// Check if function is [`FunctionKind::Ordinary`]. - pub(crate) const fn is_ordinary(&self) -> bool { - matches!(self.kind(), FunctionKind::Ordinary { .. }) + pub(crate) fn is_ordinary(&self) -> bool { + self.code.is_ordinary() } } @@ -633,9 +566,9 @@ impl BuiltInFunctionObject { let environments = context.vm.environments.pop_to_global(); let function_object = if generator { - crate::vm::create_generator_function_object(code, r#async, Some(prototype), context) + crate::vm::create_generator_function_object(code, Some(prototype), context) } else { - crate::vm::create_function_object(code, r#async, prototype, context) + crate::vm::create_function_object(code, prototype, context) }; context.vm.environments.extend(environments); @@ -655,12 +588,8 @@ impl BuiltInFunctionObject { ); let environments = context.vm.environments.pop_to_global(); - let function_object = crate::vm::create_generator_function_object( - code, - r#async, - Some(prototype), - context, - ); + let function_object = + crate::vm::create_generator_function_object(code, Some(prototype), context); context.vm.environments.extend(environments); Ok(function_object) @@ -677,8 +606,7 @@ impl BuiltInFunctionObject { ); let environments = context.vm.environments.pop_to_global(); - let function_object = - crate::vm::create_function_object(code, r#async, prototype, context); + let function_object = crate::vm::create_function_object(code, prototype, context); context.vm.environments.extend(environments); Ok(function_object) diff --git a/boa_engine/src/bytecompiler/declarations.rs b/boa_engine/src/bytecompiler/declarations.rs index 332d7e313e..0afd0ac2a5 100644 --- a/boa_engine/src/bytecompiler/declarations.rs +++ b/boa_engine/src/bytecompiler/declarations.rs @@ -286,9 +286,9 @@ impl ByteCompiler<'_, '_> { // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv. let function = if generator { - create_generator_function_object(code, r#async, None, self.context) + create_generator_function_object(code, None, self.context) } else { - create_function_object_fast(code, r#async, false, false, self.context) + create_function_object_fast(code, false, self.context) }; // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). @@ -737,9 +737,9 @@ impl ByteCompiler<'_, '_> { // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. let function = if generator { - create_generator_function_object(code, r#async, None, self.context) + create_generator_function_object(code, None, self.context) } else { - create_function_object_fast(code, r#async, false, false, self.context) + create_function_object_fast(code, false, self.context) }; // i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true). @@ -750,15 +750,8 @@ impl ByteCompiler<'_, '_> { else { // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. let index = self.push_function_to_constants(code); - if r#async && generator { - self.emit_with_varying_operand(Opcode::GetGeneratorAsync, index); - } else if generator { + if generator { self.emit_with_varying_operand(Opcode::GetGenerator, index); - } else if r#async { - self.emit( - Opcode::GetFunctionAsync, - &[Operand::Varying(index), Operand::Bool(false)], - ); } else { self.emit( Opcode::GetFunction, @@ -1033,7 +1026,7 @@ impl ByteCompiler<'_, '_> { } if generator { - self.emit(Opcode::Generator, &[Operand::Bool(self.in_async())]); + self.emit(Opcode::Generator, &[Operand::Bool(self.is_async())]); self.emit_opcode(Opcode::Pop); } diff --git a/boa_engine/src/bytecompiler/expression/mod.rs b/boa_engine/src/bytecompiler/expression/mod.rs index 1808a5900c..51c5c3b1f8 100644 --- a/boa_engine/src/bytecompiler/expression/mod.rs +++ b/boa_engine/src/bytecompiler/expression/mod.rs @@ -175,7 +175,7 @@ impl ByteCompiler<'_, '_> { // stack: value if r#yield.delegate() { - if self.in_async() { + if self.is_async() { self.emit_opcode(Opcode::GetAsyncIterator); } else { self.emit_opcode(Opcode::GetIterator); @@ -192,14 +192,14 @@ impl ByteCompiler<'_, '_> { let (throw_method_undefined, return_method_undefined) = self.emit_opcode_with_two_operands(Opcode::GeneratorDelegateNext); - if self.in_async() { + if self.is_async() { self.emit_opcode(Opcode::Pop); self.emit_opcode(Opcode::Await); } let (return_gen, exit) = self.emit_opcode_with_two_operands(Opcode::GeneratorDelegateResume); - if self.in_async() { + if self.is_async() { self.emit_opcode(Opcode::IteratorValue); self.async_generator_yield(); } else { @@ -210,7 +210,7 @@ impl ByteCompiler<'_, '_> { self.patch_jump(return_gen); self.patch_jump(return_method_undefined); - if self.in_async() { + if self.is_async() { self.emit_opcode(Opcode::Await); self.emit_opcode(Opcode::Pop); } @@ -219,7 +219,7 @@ impl ByteCompiler<'_, '_> { self.r#return(true); self.patch_jump(throw_method_undefined); - self.iterator_close(self.in_async()); + self.iterator_close(self.is_async()); self.emit_opcode(Opcode::Throw); self.patch_jump(exit); diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index e955df6262..d0d7f7c9e8 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -99,8 +99,15 @@ impl FunctionCompiler { context, ); compiler.length = length; - compiler.in_async = self.r#async; - compiler.in_generator = self.generator; + compiler + .code_block_flags + .set(CodeBlockFlags::IS_ASYNC, self.r#async); + compiler + .code_block_flags + .set(CodeBlockFlags::IS_GENERATOR, self.generator); + compiler + .code_block_flags + .set(CodeBlockFlags::IS_ARROW, self.arrow); if self.arrow { compiler.this_mode = ThisMode::Lexical; @@ -125,7 +132,7 @@ impl FunctionCompiler { // `FunctionDeclarationInstantiation` (so they are propagated). // // See: 15.6.2 Runtime Semantics: EvaluateAsyncGeneratorBody: https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncgeneratorbody - if compiler.in_async() && !compiler.in_generator() { + if compiler.is_async() && !compiler.is_generator() { // 1. Let promiseCapability be ! NewPromiseCapability(%Promise%). // // Note: If the promise capability is already set, then we do nothing. @@ -154,10 +161,10 @@ impl FunctionCompiler { // - 27.6.3.2 AsyncGeneratorStart ( generator, generatorBody ): // // Note: We do handle exceptions thrown by generator body in `AsyncGeneratorStart`. - if compiler.in_generator() { + if compiler.is_generator() { assert!(compiler.async_handler.is_none()); - if compiler.in_async() { + if compiler.is_async() { // Patched in `ByteCompiler::finish()`. compiler.async_handler = Some(compiler.push_handler()); } diff --git a/boa_engine/src/bytecompiler/jump_control.rs b/boa_engine/src/bytecompiler/jump_control.rs index 40845ea0c5..7ba5dec8a6 100644 --- a/boa_engine/src/bytecompiler/jump_control.rs +++ b/boa_engine/src/bytecompiler/jump_control.rs @@ -129,7 +129,7 @@ impl JumpRecord { compiler.emit_opcode(Opcode::SetReturnValue); } - match (compiler.in_async(), compiler.in_generator()) { + match (compiler.is_async(), compiler.is_generator()) { // Taken from: // - 27.6.3.2 AsyncGeneratorStart ( generator, generatorBody ): https://tc39.es/ecma262/#sec-asyncgeneratorstart // diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 71a63509f8..e187655e92 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -261,14 +261,12 @@ pub struct ByteCompiler<'ctx, 'host> { current_open_environments_count: u32, current_stack_value_count: u32, - code_block_flags: CodeBlockFlags, + pub(crate) code_block_flags: CodeBlockFlags, handlers: ThinVec, literals_map: FxHashMap, names_map: FxHashMap, bindings_map: FxHashMap, jump_info: Vec, - pub(crate) in_async: bool, - in_generator: bool, /// Used to handle exception throws that escape the async function types. /// @@ -320,8 +318,6 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { names_map: FxHashMap::default(), bindings_map: FxHashMap::default(), jump_info: Vec::new(), - in_async: false, - in_generator: false, async_handler: None, json_parse, variable_environment, @@ -337,16 +333,16 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { self.code_block_flags.contains(CodeBlockFlags::STRICT) } - pub(crate) const fn in_async(&self) -> bool { - self.in_async + pub(crate) const fn is_async(&self) -> bool { + self.code_block_flags.contains(CodeBlockFlags::IS_ASYNC) } - pub(crate) const fn in_generator(&self) -> bool { - self.in_generator + pub(crate) const fn is_generator(&self) -> bool { + self.code_block_flags.contains(CodeBlockFlags::IS_GENERATOR) } - pub(crate) const fn in_async_generator(&self) -> bool { - self.in_async() && self.in_generator() + pub(crate) const fn is_async_generator(&self) -> bool { + self.is_async() && self.is_generator() } pub(crate) fn interner(&self) -> &Interner { @@ -1257,25 +1253,12 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { use_expr: bool, ) { let name = function.name; - let (generator, r#async, arrow) = ( - function.kind.is_generator(), - function.kind.is_async(), - function.kind.is_arrow(), - ); + let (generator, arrow) = (function.kind.is_generator(), function.kind.is_arrow()); let index = self.function(function); - if r#async && generator { - self.emit_with_varying_operand(Opcode::GetGeneratorAsync, index); - } else if generator { + if generator { self.emit_with_varying_operand(Opcode::GetGenerator, index); - } else if r#async && arrow { - self.emit(Opcode::GetAsyncArrowFunction, &[Operand::Varying(index)]); - } else if r#async { - self.emit( - Opcode::GetFunctionAsync, - &[Operand::Varying(index), Operand::Bool(false)], - ); } else if arrow { self.emit(Opcode::GetArrowFunction, &[Operand::Varying(index)]); } else { @@ -1342,17 +1325,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { let index = self.push_function_to_constants(code); - if r#async && generator { - self.emit_with_varying_operand(Opcode::GetGeneratorAsync, index); - } else if generator { + if generator { self.emit_with_varying_operand(Opcode::GetGenerator, index); - } else if r#async && arrow { - self.emit(Opcode::GetAsyncArrowFunction, &[Operand::Varying(index)]); - } else if r#async { - self.emit( - Opcode::GetFunctionAsync, - &[Operand::Varying(index), Operand::Bool(true)], - ); } else if arrow { self.emit(Opcode::GetArrowFunction, &[Operand::Varying(index)]); } else { @@ -1405,17 +1379,8 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { let index = self.push_function_to_constants(code); - if r#async && generator { - self.emit_with_varying_operand(Opcode::GetGeneratorAsync, index); - } else if generator { + if generator { self.emit_with_varying_operand(Opcode::GetGenerator, index); - } else if r#async && arrow { - self.emit(Opcode::GetAsyncArrowFunction, &[Operand::Varying(index)]); - } else if r#async { - self.emit( - Opcode::GetFunctionAsync, - &[Operand::Varying(index), Operand::Bool(true)], - ); } else if arrow { self.emit(Opcode::GetArrowFunction, &[Operand::Varying(index)]); } else { diff --git a/boa_engine/src/bytecompiler/statement/mod.rs b/boa_engine/src/bytecompiler/statement/mod.rs index 81f7b3d45a..91c332ea6a 100644 --- a/boa_engine/src/bytecompiler/statement/mod.rs +++ b/boa_engine/src/bytecompiler/statement/mod.rs @@ -65,7 +65,7 @@ impl ByteCompiler<'_, '_> { Statement::Return(ret) => { if let Some(expr) = ret.target() { self.compile_expr(expr, true); - if self.in_async_generator() { + if self.is_async_generator() { self.emit_opcode(Opcode::Await); self.emit_opcode(Opcode::GeneratorNext); } diff --git a/boa_engine/src/bytecompiler/statement/try.rs b/boa_engine/src/bytecompiler/statement/try.rs index 65486a1e43..9a70cbb848 100644 --- a/boa_engine/src/bytecompiler/statement/try.rs +++ b/boa_engine/src/bytecompiler/statement/try.rs @@ -38,7 +38,7 @@ impl ByteCompiler<'_, '_> { // 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.in_generator() || has_catch) { + let catch_handler = if has_finally && (self.is_generator() || has_catch) { self.current_stack_value_count += 2; Some(self.push_handler()) } else { @@ -50,7 +50,7 @@ impl ByteCompiler<'_, '_> { self.compile_catch_stmt(catch, has_finally, use_expr); } else { // Note: implicit !has_catch - if self.in_generator() && has_finally { + if self.is_generator() && has_finally { // Is this a generator `return()` empty exception? // // This is false because when the `Exception` opcode is executed, @@ -77,7 +77,7 @@ impl ByteCompiler<'_, '_> { } // Note: implicit has_finally - if !has_catch && self.in_generator() { + if !has_catch && self.is_generator() { // Is this a generator `return()` empty exception? self.emit_opcode(Opcode::PushTrue); } @@ -153,7 +153,7 @@ impl ByteCompiler<'_, '_> { if has_catch { self.emit_opcode(Opcode::ReThrow); - } else if self.in_generator() { + } else if self.is_generator() { let is_generator_exit = self.jump_if_true(); self.emit_opcode(Opcode::Throw); self.patch_jump(is_generator_exit); diff --git a/boa_engine/src/bytecompiler/utils.rs b/boa_engine/src/bytecompiler/utils.rs index 6ba4335fe2..8319fb31a4 100644 --- a/boa_engine/src/bytecompiler/utils.rs +++ b/boa_engine/src/bytecompiler/utils.rs @@ -50,7 +50,7 @@ impl ByteCompiler<'_, '_> { let start = self.next_opcode_location(); self.emit_opcode(Opcode::IteratorStackEmpty); let empty = self.jump_if_true(); - self.iterator_close(self.in_async_generator()); + self.iterator_close(self.is_async_generator()); self.emit(Opcode::Jump, &[Operand::U32(start)]); self.patch_jump(empty); } @@ -65,7 +65,7 @@ impl ByteCompiler<'_, '_> { /// [yield]: https://tc39.es/ecma262/#sec-yield pub(super) fn r#yield(&mut self) { // 1. Let generatorKind be GetGeneratorKind(). - if self.in_async() { + if self.is_async() { // 2. If generatorKind is async, return ? AsyncGeneratorYield(? Await(value)). self.emit_opcode(Opcode::Await); self.emit_opcode(Opcode::GeneratorNext); diff --git a/boa_engine/src/module/source.rs b/boa_engine/src/module/source.rs index 4365f63aeb..331fff3924 100644 --- a/boa_engine/src/module/source.rs +++ b/boa_engine/src/module/source.rs @@ -29,7 +29,7 @@ use crate::{ realm::Realm, vm::{ create_function_object_fast, create_generator_function_object, ActiveRunnable, CallFrame, - CallFrameFlags, CodeBlock, CompletionRecord, Opcode, + CallFrameFlags, CodeBlock, CodeBlockFlags, CompletionRecord, Opcode, }, Context, JsArgs, JsError, JsNativeError, JsObject, JsResult, JsString, JsValue, NativeFunction, }; @@ -1413,7 +1413,7 @@ impl SourceTextModule { let mut compiler = ByteCompiler::new(Sym::MAIN, true, false, env.clone(), env.clone(), context); - compiler.in_async = true; + compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC; compiler.async_handler = Some(compiler.push_handler()); let mut imports = Vec::new(); @@ -1660,9 +1660,9 @@ impl SourceTextModule { let code = codeblock.constant_function(index as usize); let function = if kind.is_generator() { - create_generator_function_object(code, kind.is_async(), None, context) + create_generator_function_object(code, None, context) } else { - create_function_object_fast(code, kind.is_async(), false, false, context) + create_function_object_fast(code, false, context) }; context.vm.environments.put_lexical_value( diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 50fa5d0ed1..802f1e2d48 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -244,7 +244,7 @@ unsafe impl Trace for Object { } /// A Private Name. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Trace, Finalize)] pub struct PrivateName { /// The `[[Description]]` internal slot of the private name. description: JsString, diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index 0b76074714..d0e2767613 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -3,7 +3,7 @@ //! This module is for the `CodeBlock` which implements a function representation in the VM use crate::{ - builtins::function::{FunctionKind, OrdinaryFunction, ThisMode}, + builtins::function::{OrdinaryFunction, ThisMode}, environments::{BindingLocator, CompileTimeEnvironment}, object::{JsObject, ObjectData, PROTOTYPE}, property::PropertyDescriptor, @@ -48,7 +48,7 @@ unsafe impl Readable for f64 {} bitflags! { /// Flags for [`CodeBlock`]. #[derive(Clone, Copy, Debug, Finalize)] - pub(crate) struct CodeBlockFlags: u8 { + pub(crate) struct CodeBlockFlags: u16 { /// Is this function in strict mode. const STRICT = 0b0000_0001; @@ -72,9 +72,13 @@ bitflags! { /// `[[ConstructorKind]]` const IS_DERIVED_CONSTRUCTOR = 0b0100_0000; + const IS_ASYNC = 0b1000_0000; + const IS_GENERATOR = 0b0001_0000_0000; + const IS_ARROW = 0b0010_0000_0000; + /// Trace instruction execution to `stdout`. #[cfg(feature = "trace")] - const TRACEABLE = 0b1000_0000; + const TRACEABLE = 0b1000_0000_0000_0000; } } @@ -251,6 +255,26 @@ impl CodeBlock { .contains(CodeBlockFlags::IS_DERIVED_CONSTRUCTOR) } + /// Returns true if this function an async function. + pub(crate) fn is_async(&self) -> bool { + self.flags.get().contains(CodeBlockFlags::IS_ASYNC) + } + + /// Returns true if this function an generator function. + pub(crate) fn is_generator(&self) -> bool { + self.flags.get().contains(CodeBlockFlags::IS_GENERATOR) + } + + /// Returns true if this function an async function. + pub(crate) fn is_ordinary(&self) -> bool { + !self.is_async() && !self.is_generator() + } + + /// Returns true if this function an arrow function. + pub(crate) fn is_arrow(&self) -> bool { + self.flags.get().contains(CodeBlockFlags::IS_ARROW) + } + /// Find exception [`Handler`] in the code block given the current program counter (`pc`). #[inline] pub(crate) fn find_handler(&self, pc: u32) -> Option<(usize, &Handler)> { @@ -431,8 +455,7 @@ impl CodeBlock { Instruction::TemplateCreate { count, site } => { format!("{}, {site}", count.value()) } - Instruction::GetFunction { index, method } - | Instruction::GetFunctionAsync { index, method } => { + Instruction::GetFunction { index, method } => { let index = index.value() as usize; format!( "{index:04}: '{}' (length: {}), method: {method}", @@ -440,10 +463,7 @@ impl CodeBlock { self.constant_function(index).length ) } - Instruction::GetArrowFunction { index } - | Instruction::GetAsyncArrowFunction { index } - | Instruction::GetGenerator { index } - | Instruction::GetGeneratorAsync { index } => { + Instruction::GetArrowFunction { index } | Instruction::GetGenerator { index } => { let index = index.value() as usize; format!( "{index:04}: '{}' (length: {})", @@ -692,7 +712,10 @@ impl CodeBlock { | Instruction::Reserved53 | Instruction::Reserved54 | Instruction::Reserved55 - | Instruction::Reserved56 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved56 + | Instruction::Reserved57 + | Instruction::Reserved58 + | Instruction::Reserved59 => unreachable!("Reserved opcodes are unrechable"), } } } @@ -821,7 +844,6 @@ impl ToInternedString for CodeBlock { /// This is slower than direct object template construction that is done in [`create_function_object_fast`]. pub(crate) fn create_function_object( code: Gc, - r#async: bool, prototype: JsObject, context: &mut Context<'_>, ) -> JsObject { @@ -832,34 +854,19 @@ pub(crate) fn create_function_object( let script_or_module = context.get_active_script_or_module(); - let function = if r#async { - OrdinaryFunction { - code, - environments: context.vm.environments.clone(), - home_object: None, - script_or_module, - kind: FunctionKind::Async, - realm: context.realm().clone(), - } - } else { - OrdinaryFunction { - code, - environments: context.vm.environments.clone(), - home_object: None, - script_or_module, - kind: FunctionKind::Ordinary { - fields: ThinVec::new(), - private_methods: ThinVec::new(), - }, - realm: context.realm().clone(), - } - }; + let is_async = code.is_async(); + let function = OrdinaryFunction::new( + code, + context.vm.environments.clone(), + script_or_module, + context.realm().clone(), + ); - let data = ObjectData::ordinary_function(function, !r#async); + let data = ObjectData::ordinary_function(function, !is_async); let templates = context.intrinsics().templates(); - let (mut template, storage, constructor_prototype) = if r#async { + let (mut template, storage, constructor_prototype) = if is_async { ( templates.function_without_proto().clone(), vec![length, name], @@ -896,8 +903,6 @@ pub(crate) fn create_function_object( /// with all the properties and prototype set. pub(crate) fn create_function_object_fast( code: Gc, - r#async: bool, - arrow: bool, method: bool, context: &mut Context<'_>, ) -> JsObject { @@ -908,33 +913,24 @@ pub(crate) fn create_function_object_fast( let script_or_module = context.get_active_script_or_module(); - let kind = if r#async { - FunctionKind::Async - } else { - FunctionKind::Ordinary { - fields: ThinVec::new(), - private_methods: ThinVec::new(), - } - }; - - let function = OrdinaryFunction { + let is_async = code.is_async(); + let is_arrow = code.is_arrow(); + let function = OrdinaryFunction::new( code, - environments: context.vm.environments.clone(), + context.vm.environments.clone(), script_or_module, - home_object: None, - kind, - realm: context.realm().clone(), - }; + context.realm().clone(), + ); - let data = ObjectData::ordinary_function(function, !method && !arrow && !r#async); + let data = ObjectData::ordinary_function(function, !method && !is_arrow && !is_async); - if r#async { + if is_async { context .intrinsics() .templates() .async_function() .create(data, vec![length, name]) - } else if arrow || method { + } else if is_arrow || method { context .intrinsics() .templates() @@ -962,13 +958,13 @@ pub(crate) fn create_function_object_fast( /// Creates a new generator function object. pub(crate) fn create_generator_function_object( code: Gc, - r#async: bool, prototype: Option, context: &mut Context<'_>, ) -> JsObject { + let is_async = code.is_async(); let function_prototype = if let Some(prototype) = prototype { prototype - } else if r#async { + } else if is_async { context .intrinsics() .constructors() @@ -998,7 +994,7 @@ pub(crate) fn create_generator_function_object( let prototype = JsObject::from_proto_and_data_with_shared_shape( context.root_shape(), - if r#async { + if is_async { context.intrinsics().objects().async_generator() } else { context.intrinsics().objects().generator() @@ -1008,29 +1004,25 @@ pub(crate) fn create_generator_function_object( let script_or_module = context.get_active_script_or_module(); - let constructor = if r#async { - let function = OrdinaryFunction { + let constructor = if is_async { + let function = OrdinaryFunction::new( code, - environments: context.vm.environments.clone(), - home_object: None, + context.vm.environments.clone(), script_or_module, - kind: FunctionKind::AsyncGenerator, - realm: context.realm().clone(), - }; + context.realm().clone(), + ); JsObject::from_proto_and_data_with_shared_shape( context.root_shape(), function_prototype, ObjectData::async_generator_function(function), ) } else { - let function = OrdinaryFunction { + let function = OrdinaryFunction::new( code, - environments: context.vm.environments.clone(), - home_object: None, + context.vm.environments.clone(), script_or_module, - kind: FunctionKind::Generator, - realm: context.realm().clone(), - }; + context.realm().clone(), + ); JsObject::from_proto_and_data_with_shared_shape( context.root_shape(), function_prototype, diff --git a/boa_engine/src/vm/flowgraph/mod.rs b/boa_engine/src/vm/flowgraph/mod.rs index f81ca59353..e732dce3b3 100644 --- a/boa_engine/src/vm/flowgraph/mod.rs +++ b/boa_engine/src/vm/flowgraph/mod.rs @@ -242,14 +242,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::GetArrowFunction { .. } - | Instruction::GetAsyncArrowFunction { .. } - | Instruction::GetFunction { .. } - | Instruction::GetFunctionAsync { .. } => { + Instruction::GetArrowFunction { .. } | Instruction::GetFunction { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Instruction::GetGenerator { .. } | Instruction::GetGeneratorAsync { .. } => { + Instruction::GetGenerator { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } @@ -522,7 +519,10 @@ impl CodeBlock { | Instruction::Reserved53 | Instruction::Reserved54 | Instruction::Reserved55 - | Instruction::Reserved56 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved56 + | Instruction::Reserved57 + | Instruction::Reserved58 + | Instruction::Reserved59 => unreachable!("Reserved opcodes are unrechable"), } } diff --git a/boa_engine/src/vm/opcode/get/function.rs b/boa_engine/src/vm/opcode/get/function.rs index 2c4b5208dd..498d8f45a8 100644 --- a/boa_engine/src/vm/opcode/get/function.rs +++ b/boa_engine/src/vm/opcode/get/function.rs @@ -14,7 +14,7 @@ impl GetArrowFunction { #[allow(clippy::unnecessary_wraps)] fn operation(context: &mut Context<'_>, index: usize) -> JsResult { let code = context.vm.frame().code_block().constant_function(index); - let function = create_function_object_fast(code, false, true, false, context); + let function = create_function_object_fast(code, false, context); context.vm.push(function); Ok(CompletionType::Normal) } @@ -41,44 +41,6 @@ impl Operation for GetArrowFunction { } } -/// `GetAsyncArrowFunction` implements the Opcode Operation for `Opcode::GetAsyncArrowFunction` -/// -/// Operation: -/// - Get async arrow function from the pre-compiled inner functions. -#[derive(Debug, Clone, Copy)] -pub(crate) struct GetAsyncArrowFunction; - -impl GetAsyncArrowFunction { - #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context<'_>, index: usize) -> JsResult { - let code = context.vm.frame().code_block().constant_function(index); - let function = create_function_object_fast(code, true, true, false, context); - context.vm.push(function); - Ok(CompletionType::Normal) - } -} - -impl Operation for GetAsyncArrowFunction { - const NAME: &'static str = "GetAsyncArrowFunction"; - const INSTRUCTION: &'static str = "INST - GetAsyncArrowFunction"; - const COST: u8 = 3; - - fn execute(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - Self::operation(context, index) - } - - fn execute_with_u16_operands(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - Self::operation(context, index) - } - - fn execute_with_u32_operands(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - Self::operation(context, index) - } -} - /// `GetFunction` implements the Opcode Operation for `Opcode::GetFunction` /// /// Operation: @@ -94,7 +56,7 @@ impl GetFunction { method: bool, ) -> JsResult { let code = context.vm.frame().code_block().constant_function(index); - let function = create_function_object_fast(code, false, false, method, context); + let function = create_function_object_fast(code, method, context); context.vm.push(function); Ok(CompletionType::Normal) } @@ -123,48 +85,3 @@ impl Operation for GetFunction { Self::operation(context, index, method) } } - -/// `GetFunctionAsync` implements the Opcode Operation for `Opcode::GetFunctionAsync` -/// -/// Operation: -/// - Get async function from the pre-compiled inner functions. -#[derive(Debug, Clone, Copy)] -pub(crate) struct GetFunctionAsync; - -impl GetFunctionAsync { - #[allow(clippy::unnecessary_wraps)] - fn operation( - context: &mut Context<'_>, - index: usize, - method: bool, - ) -> JsResult { - let code = context.vm.frame().code_block().constant_function(index); - let function = create_function_object_fast(code, true, false, method, context); - context.vm.push(function); - Ok(CompletionType::Normal) - } -} - -impl Operation for GetFunctionAsync { - const NAME: &'static str = "GetFunctionAsync"; - const INSTRUCTION: &'static str = "INST - GetFunctionAsync"; - const COST: u8 = 3; - - fn execute(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - let method = context.vm.read::() != 0; - Self::operation(context, index, method) - } - - fn execute_with_u16_operands(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - let method = context.vm.read::() != 0; - Self::operation(context, index, method) - } - - fn execute_with_u32_operands(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - let method = context.vm.read::() != 0; - Self::operation(context, index, method) - } -} diff --git a/boa_engine/src/vm/opcode/get/generator.rs b/boa_engine/src/vm/opcode/get/generator.rs index 1ecc72d3f7..54ef24492b 100644 --- a/boa_engine/src/vm/opcode/get/generator.rs +++ b/boa_engine/src/vm/opcode/get/generator.rs @@ -14,7 +14,7 @@ impl GetGenerator { #[allow(clippy::unnecessary_wraps)] fn operation(context: &mut Context<'_>, index: usize) -> JsResult { let code = context.vm.frame().code_block().constant_function(index); - let function = create_generator_function_object(code, false, None, context); + let function = create_generator_function_object(code, None, context); context.vm.push(function); Ok(CompletionType::Normal) } @@ -40,41 +40,3 @@ impl Operation for GetGenerator { Self::operation(context, index) } } - -/// `GetGeneratorAsync` implements the Opcode Operation for `Opcode::GetGeneratorAsync` -/// -/// Operation: -/// - Get async generator function from the pre-compiled inner functions. -#[derive(Debug, Clone, Copy)] -pub(crate) struct GetGeneratorAsync; - -impl GetGeneratorAsync { - #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context<'_>, index: usize) -> JsResult { - let code = context.vm.frame().code_block().constant_function(index); - let function = create_generator_function_object(code, true, None, context); - context.vm.push(function); - Ok(CompletionType::Normal) - } -} - -impl Operation for GetGeneratorAsync { - const NAME: &'static str = "GetGeneratorAsync"; - const INSTRUCTION: &'static str = "INST - GetGeneratorAsync"; - const COST: u8 = 3; - - fn execute(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - Self::operation(context, index) - } - - fn execute_with_u16_operands(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - Self::operation(context, index) - } - - fn execute_with_u32_operands(context: &mut Context<'_>) -> JsResult { - let index = context.vm.read::() as usize; - Self::operation(context, index) - } -} diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index 12c567553e..97a2612798 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -1676,13 +1676,6 @@ generate_opcodes! { /// Stack: **=>** func GetArrowFunction { index: VaryingOperand }, - /// Get async arrow function from the pre-compiled inner functions. - /// - /// Operands: index: `VaryingOperand` - /// - /// Stack: **=>** func - GetAsyncArrowFunction { index: VaryingOperand }, - /// Get function from the pre-compiled inner functions. /// /// Operands: index: `VaryingOperand`, is_method: `u8` @@ -1690,13 +1683,6 @@ generate_opcodes! { /// Stack: **=>** func GetFunction { index: VaryingOperand, method: bool }, - /// Get async function from the pre-compiled inner functions. - /// - /// Operands: index: `VaryingOperand`, method: `u8` - /// - /// Stack: **=>** func - GetFunctionAsync { index: VaryingOperand, method: bool }, - /// Get generator function from the pre-compiled inner functions. /// /// Operands: index: `VaryingOperand`, @@ -1704,13 +1690,6 @@ generate_opcodes! { /// Stack: **=>** func GetGenerator { index: VaryingOperand }, - /// Get async generator function from the pre-compiled inner functions. - /// - /// Operands: index: `VaryingOperand`, - /// - /// Stack: **=>** func - GetGeneratorAsync { index: VaryingOperand }, - /// Call a function named "eval". /// /// Operands: argument_count: `VaryingOperand` @@ -2221,6 +2200,12 @@ generate_opcodes! { Reserved55 => Reserved, /// Reserved [`Opcode`]. Reserved56 => Reserved, + /// Reserved [`Opcode`]. + Reserved57 => Reserved, + /// Reserved [`Opcode`]. + Reserved58 => Reserved, + /// Reserved [`Opcode`]. + Reserved59 => Reserved, } /// Specific opcodes for bindings.