Browse Source

Move `FunctionKind` to `CodeBlock` (#3440)

* Move `FunctionKind` flag to CodeBlock

* Add `IS_ARROW` flag to codeblock

* Remove GetAsyncArrowFunction and GetFunctionAsync opcodes

* Remove GetGeneratorAsync opcode
pull/3441/head
Haled Odat 1 year ago committed by GitHub
parent
commit
b4644a5b87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 142
      boa_engine/src/builtins/function/mod.rs
  2. 19
      boa_engine/src/bytecompiler/declarations.rs
  3. 10
      boa_engine/src/bytecompiler/expression/mod.rs
  4. 17
      boa_engine/src/bytecompiler/function.rs
  5. 2
      boa_engine/src/bytecompiler/jump_control.rs
  6. 57
      boa_engine/src/bytecompiler/mod.rs
  7. 2
      boa_engine/src/bytecompiler/statement/mod.rs
  8. 8
      boa_engine/src/bytecompiler/statement/try.rs
  9. 4
      boa_engine/src/bytecompiler/utils.rs
  10. 8
      boa_engine/src/module/source.rs
  11. 2
      boa_engine/src/object/mod.rs
  12. 134
      boa_engine/src/vm/code_block.rs
  13. 12
      boa_engine/src/vm/flowgraph/mod.rs
  14. 87
      boa_engine/src/vm/opcode/get/function.rs
  15. 40
      boa_engine/src/vm/opcode/get/generator.rs
  16. 27
      boa_engine/src/vm/opcode/mod.rs

142
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<ClassFieldDefinition>,
/// 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<ClassFieldDefinition>,
/// The `[[PrivateMethods]]` internal slot.
private_methods: ThinVec<(PrivateName, PrivateElement)>,
}
impl OrdinaryFunction {
pub(crate) fn new(
code: Gc<CodeBlock>,
environments: EnvironmentStack,
script_or_module: Option<ActiveRunnable>,
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)

19
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);
}

10
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);

17
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 ): <https://tc39.es/ecma262/#sec-asyncgeneratorstart>
//
// 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());
}

2
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
//

57
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<Handler>,
literals_map: FxHashMap<Literal, u32>,
names_map: FxHashMap<Identifier, u32>,
bindings_map: FxHashMap<BindingLocator, u32>,
jump_info: Vec<JumpControlInfo>,
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 {

2
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);
}

8
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);

4
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);

8
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(

2
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,

134
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<CodeBlock>,
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<CodeBlock>,
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<CodeBlock>,
r#async: bool,
prototype: Option<JsObject>,
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,

12
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"),
}
}

87
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<CompletionType> {
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<CompletionType> {
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<CompletionType> {
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index)
}
fn execute_with_u16_operands(context: &mut Context<'_>) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context<'_>) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index)
}
}
/// `GetFunction` implements the Opcode Operation for `Opcode::GetFunction`
///
/// Operation:
@ -94,7 +56,7 @@ impl GetFunction {
method: bool,
) -> JsResult<CompletionType> {
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<CompletionType> {
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<CompletionType> {
let index = context.vm.read::<u8>() as usize;
let method = context.vm.read::<u8>() != 0;
Self::operation(context, index, method)
}
fn execute_with_u16_operands(context: &mut Context<'_>) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
let method = context.vm.read::<u8>() != 0;
Self::operation(context, index, method)
}
fn execute_with_u32_operands(context: &mut Context<'_>) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
let method = context.vm.read::<u8>() != 0;
Self::operation(context, index, method)
}
}

40
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<CompletionType> {
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<CompletionType> {
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<CompletionType> {
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index)
}
fn execute_with_u16_operands(context: &mut Context<'_>) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context<'_>) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index)
}
}

27
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.

Loading…
Cancel
Save