Browse Source

Unify generator and ordinary function creation (#3441)

* Unify generator and ordinary function creation

* Remove GetGenerator opcode

* Remove GetArrowFunction opcode
pull/3432/head
Haled Odat 1 year ago committed by GitHub
parent
commit
2c12bae7b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 50
      boa_engine/src/builtins/function/mod.rs
  2. 25
      boa_engine/src/bytecompiler/class.rs
  3. 26
      boa_engine/src/bytecompiler/declarations.rs
  4. 14
      boa_engine/src/bytecompiler/function.rs
  5. 40
      boa_engine/src/bytecompiler/mod.rs
  6. 32
      boa_engine/src/context/intrinsics.rs
  7. 18
      boa_engine/src/module/source.rs
  8. 39
      boa_engine/src/object/mod.rs
  9. 174
      boa_engine/src/vm/code_block.rs
  10. 10
      boa_engine/src/vm/flowgraph/mod.rs
  11. 3
      boa_engine/src/vm/mod.rs
  12. 55
      boa_engine/src/vm/opcode/get/function.rs
  13. 42
      boa_engine/src/vm/opcode/get/generator.rs
  14. 2
      boa_engine/src/vm/opcode/get/mod.rs
  15. 22
      boa_engine/src/vm/opcode/mod.rs

50
boa_engine/src/builtins/function/mod.rs

@ -409,7 +409,7 @@ impl BuiltInFunctionObject {
// 22. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
let prototype = get_prototype_from_constructor(&new_target, default, context)?;
if let Some((body_arg, args)) = args.split_last() {
let (parameters, body) = if let Some((body_arg, args)) = args.split_last() {
let parameters = if args.is_empty() {
FormalParameterList::default()
} else {
@ -551,55 +551,18 @@ impl BuiltInFunctionObject {
.into());
}
let code = FunctionCompiler::new()
.name(Sym::ANONYMOUS)
.generator(generator)
.r#async(r#async)
.compile(
&parameters,
&body,
context.realm().environment().compile_env(),
context.realm().environment().compile_env(),
context,
);
let environments = context.vm.environments.pop_to_global();
let function_object = if generator {
crate::vm::create_generator_function_object(code, Some(prototype), context)
(parameters, body)
} else {
crate::vm::create_function_object(code, prototype, context)
(FormalParameterList::default(), FunctionBody::default())
};
context.vm.environments.extend(environments);
Ok(function_object)
} else if generator {
let code = FunctionCompiler::new()
.name(Sym::ANONYMOUS)
.generator(true)
.r#async(r#async)
.compile(
&FormalParameterList::default(),
&FunctionBody::default(),
context.realm().environment().compile_env(),
context.realm().environment().compile_env(),
context,
);
let environments = context.vm.environments.pop_to_global();
let function_object =
crate::vm::create_generator_function_object(code, Some(prototype), context);
context.vm.environments.extend(environments);
Ok(function_object)
} else {
let code = FunctionCompiler::new()
.generator(generator)
.r#async(r#async)
.name(Sym::ANONYMOUS)
.compile(
&FormalParameterList::default(),
&FunctionBody::default(),
&parameters,
&body,
context.realm().environment().compile_env(),
context.realm().environment().compile_env(),
context,
@ -611,7 +574,6 @@ impl BuiltInFunctionObject {
Ok(function_object)
}
}
/// `Function.prototype.apply ( thisArg, argArray )`
///

25
boa_engine/src/bytecompiler/class.rs

@ -88,10 +88,7 @@ impl ByteCompiler<'_, '_> {
let code = Gc::new(compiler.finish());
let index = self.push_function_to_constants(code);
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(false)],
);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_opcode(Opcode::Dup);
if let Some(node) = class.super_ref() {
@ -301,10 +298,7 @@ impl ByteCompiler<'_, '_> {
let code = Gc::new(field_compiler.finish());
let index = self.push_function_to_constants(code);
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(false)],
);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_opcode(Opcode::PushClassField);
}
ClassElement::PrivateFieldDefinition(name, field) => {
@ -330,10 +324,7 @@ impl ByteCompiler<'_, '_> {
let code = Gc::new(field_compiler.finish());
let index = self.push_function_to_constants(code);
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(false)],
);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_with_varying_operand(Opcode::PushClassFieldPrivate, name_index);
}
ClassElement::StaticFieldDefinition(name, field) => {
@ -554,10 +545,7 @@ impl ByteCompiler<'_, '_> {
StaticElement::StaticBlock(code) => {
self.emit_opcode(Opcode::Dup);
let index = self.push_function_to_constants(code);
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(false)],
);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_opcode(Opcode::SetHomeObject);
self.emit_with_varying_operand(Opcode::Call, 0);
self.emit_opcode(Opcode::Pop);
@ -566,10 +554,7 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Dup);
self.emit_opcode(Opcode::Dup);
let index = self.push_function_to_constants(code);
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(false)],
);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_opcode(Opcode::SetHomeObject);
self.emit_with_varying_operand(Opcode::Call, 0);
if let Some(name_index) = name_index {

26
boa_engine/src/bytecompiler/declarations.rs

@ -3,10 +3,7 @@ use std::rc::Rc;
use crate::{
bytecompiler::{ByteCompiler, FunctionCompiler, FunctionSpec, NodeKind},
environments::CompileTimeEnvironment,
vm::{
create_function_object_fast, create_generator_function_object, BindingOpcode,
CodeBlockFlags, Opcode,
},
vm::{create_function_object_fast, BindingOpcode, CodeBlockFlags, Opcode},
JsNativeError, JsResult,
};
use boa_ast::{
@ -285,11 +282,7 @@ impl ByteCompiler<'_, '_> {
let _ = self.push_function_to_constants(code.clone());
// b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv.
let function = if generator {
create_generator_function_object(code, None, self.context)
} else {
create_function_object_fast(code, false, self.context)
};
let function = create_function_object_fast(code, self.context);
// c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false).
self.context
@ -736,11 +729,7 @@ impl ByteCompiler<'_, '_> {
let _ = self.push_function_to_constants(code.clone());
// b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv.
let function = if generator {
create_generator_function_object(code, None, self.context)
} else {
create_function_object_fast(code, false, self.context)
};
let function = create_function_object_fast(code, self.context);
// i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true).
self.context
@ -750,14 +739,7 @@ impl ByteCompiler<'_, '_> {
else {
// b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv.
let index = self.push_function_to_constants(code);
if generator {
self.emit_with_varying_operand(Opcode::GetGenerator, index);
} else {
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(false)],
);
}
self.emit_with_varying_operand(Opcode::GetFunction, index);
// i. Let bindingExists be ! varEnv.HasBinding(fn).
let binding_exists = var_env.has_binding(name);

14
boa_engine/src/bytecompiler/function.rs

@ -20,6 +20,7 @@ pub(crate) struct FunctionCompiler {
r#async: bool,
strict: bool,
arrow: bool,
method: bool,
binding_identifier: Option<Sym>,
}
@ -32,6 +33,7 @@ impl FunctionCompiler {
r#async: false,
strict: false,
arrow: false,
method: false,
binding_identifier: None,
}
}
@ -53,6 +55,11 @@ impl FunctionCompiler {
self.arrow = arrow;
self
}
/// Indicate if the function is a method function.
pub(crate) const fn method(mut self, method: bool) -> Self {
self.method = method;
self
}
/// Indicate if the function is a generator function.
pub(crate) const fn generator(mut self, generator: bool) -> Self {
self.generator = generator;
@ -105,9 +112,10 @@ impl FunctionCompiler {
compiler
.code_block_flags
.set(CodeBlockFlags::IS_GENERATOR, self.generator);
compiler
.code_block_flags
.set(CodeBlockFlags::IS_ARROW, self.arrow);
compiler.code_block_flags.set(
CodeBlockFlags::HAS_PROTOTYPE_PROPERTY,
!self.arrow && !self.method,
);
if self.arrow {
compiler.this_mode = ThisMode::Lexical;

40
boa_engine/src/bytecompiler/mod.rs

@ -299,6 +299,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
) -> ByteCompiler<'ctx, 'host> {
let mut code_block_flags = CodeBlockFlags::empty();
code_block_flags.set(CodeBlockFlags::STRICT, strict);
code_block_flags |= CodeBlockFlags::HAS_PROTOTYPE_PROPERTY;
Self {
function_name: name,
length: 0,
@ -1253,20 +1254,9 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
use_expr: bool,
) {
let name = function.name;
let (generator, arrow) = (function.kind.is_generator(), function.kind.is_arrow());
let index = self.function(function);
if generator {
self.emit_with_varying_operand(Opcode::GetGenerator, index);
} else if arrow {
self.emit(Opcode::GetArrowFunction, &[Operand::Varying(index)]);
} else {
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(false)],
);
}
self.emit_with_varying_operand(Opcode::GetFunction, index);
match node_kind {
NodeKind::Declaration => {
@ -1314,6 +1304,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.r#async(r#async)
.strict(self.strict())
.arrow(arrow)
.method(true)
.binding_identifier(binding_identifier)
.compile(
parameters,
@ -1324,17 +1315,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
);
let index = self.push_function_to_constants(code);
if generator {
self.emit_with_varying_operand(Opcode::GetGenerator, index);
} else if arrow {
self.emit(Opcode::GetArrowFunction, &[Operand::Varying(index)]);
} else {
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(true)],
);
}
self.emit_with_varying_operand(Opcode::GetFunction, index);
}
/// Compile a class method AST Node into bytecode.
@ -1368,6 +1349,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
.r#async(r#async)
.strict(true)
.arrow(arrow)
.method(true)
.binding_identifier(binding_identifier)
.compile(
parameters,
@ -1378,17 +1360,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
);
let index = self.push_function_to_constants(code);
if generator {
self.emit_with_varying_operand(Opcode::GetGenerator, index);
} else if arrow {
self.emit(Opcode::GetArrowFunction, &[Operand::Varying(index)]);
} else {
self.emit(
Opcode::GetFunction,
&[Operand::Varying(index), Operand::Bool(true)],
);
}
self.emit_with_varying_operand(Opcode::GetFunction, index);
}
fn call(&mut self, callable: Callable<'_>, use_expr: bool) {

32
boa_engine/src/context/intrinsics.rs

@ -1348,6 +1348,8 @@ pub(crate) struct ObjectTemplates {
function: ObjectTemplate,
async_function: ObjectTemplate,
generator_function: ObjectTemplate,
async_generator_function: ObjectTemplate,
function_without_proto: ObjectTemplate,
function_with_prototype_without_proto: ObjectTemplate,
@ -1410,12 +1412,16 @@ impl ObjectTemplates {
PROTOTYPE.into(),
Attribute::WRITABLE | Attribute::PERMANENT | Attribute::NON_ENUMERABLE,
);
let mut generator_function = function_with_prototype.clone();
let mut async_generator_function = function_with_prototype.clone();
let function_with_prototype_without_proto = function_with_prototype.clone();
function.set_prototype(constructors.function().prototype());
function_with_prototype.set_prototype(constructors.function().prototype());
async_function.set_prototype(constructors.async_function().prototype());
generator_function.set_prototype(constructors.generator_function().prototype());
async_generator_function.set_prototype(constructors.async_generator_function().prototype());
let mut function_prototype = ordinary_object.clone();
function_prototype.property(
@ -1504,6 +1510,8 @@ impl ObjectTemplates {
function_prototype,
function,
async_function,
generator_function,
async_generator_function,
function_without_proto,
function_with_prototype_without_proto,
namespace,
@ -1675,6 +1683,30 @@ impl ObjectTemplates {
&self.async_function
}
/// Cached function object property template.
///
/// Transitions:
///
/// 1. `"length"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)
/// 2. `"name"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)
/// 3. `"prototype"`: (`WRITABLE`, `PERMANENT`, `NON_ENUMERABLE`)
/// 4. `__proto__`: `GeneratorFunction.prototype`
pub(crate) const fn generator_function(&self) -> &ObjectTemplate {
&self.generator_function
}
/// Cached function object property template.
///
/// Transitions:
///
/// 1. `"length"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)
/// 2. `"name"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)
/// 3. `"prototype"`: (`WRITABLE`, `PERMANENT`, `NON_ENUMERABLE`)
/// 4. `__proto__`: `AsyncGeneratorFunction.prototype`
pub(crate) const fn async_generator_function(&self) -> &ObjectTemplate {
&self.async_generator_function
}
/// Cached function object without `__proto__` template.
///
/// Transitions:

18
boa_engine/src/module/source.rs

@ -28,8 +28,8 @@ use crate::{
object::{FunctionObjectBuilder, JsPromise, RecursionLimiter},
realm::Realm,
vm::{
create_function_object_fast, create_generator_function_object, ActiveRunnable, CallFrame,
CallFrameFlags, CodeBlock, CodeBlockFlags, CompletionRecord, Opcode,
create_function_object_fast, ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock,
CodeBlockFlags, CompletionRecord, Opcode,
},
Context, JsArgs, JsError, JsNativeError, JsObject, JsResult, JsString, JsValue, NativeFunction,
};
@ -1583,11 +1583,7 @@ impl SourceTextModule {
// are correctly resolved to the outer functions instead of as global bindings.
let functions = functions
.into_iter()
.map(|(spec, locator)| {
let kind = spec.kind;
(compiler.function(spec), locator, kind)
})
.map(|(spec, locator)| (compiler.function(spec), locator))
.collect::<Vec<_>>();
compiler.compile_module_item_list(self.inner.code.source.items());
@ -1656,14 +1652,10 @@ impl SourceTextModule {
}
// deferred initialization of function exports
for (index, locator, kind) in functions {
for (index, locator) in functions {
let code = codeblock.constant_function(index as usize);
let function = if kind.is_generator() {
create_generator_function_object(code, None, context)
} else {
create_function_object_fast(code, false, context)
};
let function = create_function_object_fast(code, context);
context.vm.environments.put_lexical_value(
locator.environment_index(),

39
boa_engine/src/object/mod.rs

@ -304,9 +304,6 @@ pub enum ObjectKind {
/// The `AsyncGenerator` object kind.
AsyncGenerator(AsyncGenerator),
/// The `AsyncGeneratorFunction` object kind.
AsyncGeneratorFunction(OrdinaryFunction),
/// The `Array` object kind.
Array,
@ -352,9 +349,6 @@ pub enum ObjectKind {
/// The `Generator` object kind.
Generator(Generator),
/// The `GeneratorFunction` object kind.
GeneratorFunction(OrdinaryFunction),
/// A native rust function.
NativeFunction(NativeFunctionObject),
@ -498,7 +492,7 @@ unsafe impl Trace for ObjectKind {
Self::RegExpStringIterator(i) => mark(i),
Self::DataView(v) => mark(v),
Self::ForInIterator(i) => mark(i),
Self::OrdinaryFunction(f) | Self::GeneratorFunction(f) | Self::AsyncGeneratorFunction(f) => mark(f),
Self::OrdinaryFunction(f) => mark(f),
Self::BoundFunction(f) => mark(f),
Self::Generator(g) => mark(g),
Self::NativeFunction(f) => mark(f),
@ -582,15 +576,6 @@ impl ObjectData {
}
}
/// Create the `AsyncGeneratorFunction` object data
#[must_use]
pub fn async_generator_function(function: OrdinaryFunction) -> Self {
Self {
internal_methods: &FUNCTION_INTERNAL_METHODS,
kind: ObjectKind::GeneratorFunction(function),
}
}
/// Create the `Array` object data and reference its exclusive internal methods
#[must_use]
pub fn array() -> Self {
@ -760,15 +745,6 @@ impl ObjectData {
}
}
/// Create the `GeneratorFunction` object data
#[must_use]
pub fn generator_function(function: OrdinaryFunction) -> Self {
Self {
internal_methods: &FUNCTION_INTERNAL_METHODS,
kind: ObjectKind::GeneratorFunction(function),
}
}
/// Create the `Set` object data
#[must_use]
pub fn set(set: OrderedSet) -> Self {
@ -1116,7 +1092,6 @@ impl Debug for ObjectKind {
f.write_str(match self {
Self::AsyncFromSyncIterator(_) => "AsyncFromSyncIterator",
Self::AsyncGenerator(_) => "AsyncGenerator",
Self::AsyncGeneratorFunction(_) => "AsyncGeneratorFunction",
Self::Array => "Array",
Self::ArrayIterator(_) => "ArrayIterator",
Self::ArrayBuffer(_) => "ArrayBuffer",
@ -1125,7 +1100,6 @@ impl Debug for ObjectKind {
Self::OrdinaryFunction(_) => "Function",
Self::BoundFunction(_) => "BoundFunction",
Self::Generator(_) => "Generator",
Self::GeneratorFunction(_) => "GeneratorFunction",
Self::NativeFunction { .. } => "NativeFunction",
Self::RegExp(_) => "RegExp",
Self::RegExpStringIterator(_) => "RegExpStringIterator",
@ -1515,10 +1489,7 @@ impl Object {
#[inline]
#[must_use]
pub const fn is_ordinary_function(&self) -> bool {
matches!(
self.kind,
ObjectKind::OrdinaryFunction(_) | ObjectKind::GeneratorFunction(_)
)
matches!(self.kind, ObjectKind::OrdinaryFunction(_))
}
/// Gets the function data if the object is a `Function`.
@ -1526,8 +1497,7 @@ impl Object {
#[must_use]
pub const fn as_function(&self) -> Option<&OrdinaryFunction> {
match self.kind {
ObjectKind::OrdinaryFunction(ref function)
| ObjectKind::GeneratorFunction(ref function) => Some(function),
ObjectKind::OrdinaryFunction(ref function) => Some(function),
_ => None,
}
}
@ -1536,8 +1506,7 @@ impl Object {
#[inline]
pub fn as_function_mut(&mut self) -> Option<&mut OrdinaryFunction> {
match self.kind {
ObjectKind::OrdinaryFunction(ref mut function)
| ObjectKind::GeneratorFunction(ref mut function) => Some(function),
ObjectKind::OrdinaryFunction(ref mut function) => Some(function),
_ => None,
}
}

174
boa_engine/src/vm/code_block.rs

@ -5,9 +5,7 @@
use crate::{
builtins::function::{OrdinaryFunction, ThisMode},
environments::{BindingLocator, CompileTimeEnvironment},
object::{JsObject, ObjectData, PROTOTYPE},
property::PropertyDescriptor,
string::utf16,
object::{JsObject, ObjectData},
Context, JsBigInt, JsString, JsValue,
};
use bitflags::bitflags;
@ -74,7 +72,9 @@ bitflags! {
const IS_ASYNC = 0b1000_0000;
const IS_GENERATOR = 0b0001_0000_0000;
const IS_ARROW = 0b0010_0000_0000;
/// Arrow and method functions don't have `"prototype"` property.
const HAS_PROTOTYPE_PROPERTY = 0b0010_0000_0000;
/// Trace instruction execution to `stdout`.
#[cfg(feature = "trace")]
@ -269,9 +269,11 @@ impl CodeBlock {
!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)
/// Returns true if this function has the `"prototype"` property when function object is created.
pub(crate) fn has_prototype_property(&self) -> bool {
self.flags
.get()
.contains(CodeBlockFlags::HAS_PROTOTYPE_PROPERTY)
}
/// Find exception [`Handler`] in the code block given the current program counter (`pc`).
@ -454,15 +456,7 @@ impl CodeBlock {
Instruction::TemplateCreate { count, site } => {
format!("{}, {site}", count.value())
}
Instruction::GetFunction { index, method } => {
let index = index.value() as usize;
format!(
"{index:04}: '{}' (length: {}), method: {method}",
self.constant_function(index).name().to_std_string_escaped(),
self.constant_function(index).length
)
}
Instruction::GetArrowFunction { index } | Instruction::GetGenerator { index } => {
Instruction::GetFunction { index } => {
let index = index.value() as usize;
format!(
"{index:04}: '{}' (length: {})",
@ -714,7 +708,9 @@ impl CodeBlock {
| Instruction::Reserved56
| Instruction::Reserved57
| Instruction::Reserved58
| Instruction::Reserved59 => unreachable!("Reserved opcodes are unrechable"),
| Instruction::Reserved59
| Instruction::Reserved60
| Instruction::Reserved61 => unreachable!("Reserved opcodes are unrechable"),
}
}
}
@ -854,6 +850,7 @@ pub(crate) fn create_function_object(
let script_or_module = context.get_active_script_or_module();
let is_async = code.is_async();
let is_generator = code.is_generator();
let function = OrdinaryFunction::new(
code,
context.vm.environments.clone(),
@ -861,11 +858,27 @@ pub(crate) fn create_function_object(
context.realm().clone(),
);
let data = ObjectData::ordinary_function(function, !is_async);
let data = ObjectData::ordinary_function(function, !is_async && !is_generator);
let templates = context.intrinsics().templates();
let (mut template, storage, constructor_prototype) = if is_async {
let (mut template, storage, constructor_prototype) = if is_generator {
let prototype = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
if is_async {
context.intrinsics().objects().async_generator()
} else {
context.intrinsics().objects().generator()
},
ObjectData::ordinary(),
);
(
templates.function_with_prototype_without_proto().clone(),
vec![length, name, prototype.into()],
None,
)
} else if is_async {
(
templates.function_without_proto().clone(),
vec![length, name],
@ -902,7 +915,6 @@ pub(crate) fn create_function_object(
/// with all the properties and prototype set.
pub(crate) fn create_function_object_fast(
code: Gc<CodeBlock>,
method: bool,
context: &mut Context<'_>,
) -> JsObject {
let _timer = Profiler::global().start_event("create_function_object_fast", "vm");
@ -913,7 +925,8 @@ pub(crate) fn create_function_object_fast(
let script_or_module = context.get_active_script_or_module();
let is_async = code.is_async();
let is_arrow = code.is_arrow();
let is_generator = code.is_generator();
let has_prototype_property = code.has_prototype_property();
let function = OrdinaryFunction::new(
code,
context.vm.environments.clone(),
@ -921,15 +934,35 @@ pub(crate) fn create_function_object_fast(
context.realm().clone(),
);
let data = ObjectData::ordinary_function(function, !method && !is_arrow && !is_async);
let data = ObjectData::ordinary_function(
function,
has_prototype_property && !is_async && !is_generator,
);
if is_generator {
let prototype = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
if is_async {
context.intrinsics().objects().async_generator()
} else {
context.intrinsics().objects().generator()
},
ObjectData::ordinary(),
);
let template = if is_async {
context.intrinsics().templates().async_generator_function()
} else {
context.intrinsics().templates().generator_function()
};
template.create(data, vec![length, name, prototype.into()])
} else if is_async {
context
.intrinsics()
.templates()
.async_function()
.create(data, vec![length, name])
} else if is_arrow || method {
} else if !has_prototype_property {
context
.intrinsics()
.templates()
@ -953,98 +986,3 @@ pub(crate) fn create_function_object_fast(
constructor
}
}
/// Creates a new generator function object.
pub(crate) fn create_generator_function_object(
code: Gc<CodeBlock>,
prototype: Option<JsObject>,
context: &mut Context<'_>,
) -> JsObject {
let is_async = code.is_async();
let function_prototype = if let Some(prototype) = prototype {
prototype
} else if is_async {
context
.intrinsics()
.constructors()
.async_generator_function()
.prototype()
} else {
context
.intrinsics()
.constructors()
.generator_function()
.prototype()
};
let name_property = PropertyDescriptor::builder()
.value(code.name().clone())
.writable(false)
.enumerable(false)
.configurable(true)
.build();
let length_property = PropertyDescriptor::builder()
.value(code.length)
.writable(false)
.enumerable(false)
.configurable(true)
.build();
let prototype = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
if is_async {
context.intrinsics().objects().async_generator()
} else {
context.intrinsics().objects().generator()
},
ObjectData::ordinary(),
);
let script_or_module = context.get_active_script_or_module();
let constructor = if is_async {
let function = OrdinaryFunction::new(
code,
context.vm.environments.clone(),
script_or_module,
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::new(
code,
context.vm.environments.clone(),
script_or_module,
context.realm().clone(),
);
JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
function_prototype,
ObjectData::generator_function(function),
)
};
let prototype_property = PropertyDescriptor::builder()
.value(prototype)
.writable(true)
.enumerable(false)
.configurable(false)
.build();
constructor
.define_property_or_throw(PROTOTYPE, prototype_property, context)
.expect("failed to define the prototype property of the generator function");
constructor
.define_property_or_throw(utf16!("name"), name_property, context)
.expect("failed to define the name property of the generator function");
constructor
.define_property_or_throw(utf16!("length"), length_property, context)
.expect("failed to define the length property of the generator function");
constructor
}

10
boa_engine/src/vm/flowgraph/mod.rs

@ -242,11 +242,7 @@ 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::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::GetFunction { .. } => {
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
}
@ -522,7 +518,9 @@ impl CodeBlock {
| Instruction::Reserved56
| Instruction::Reserved57
| Instruction::Reserved58
| Instruction::Reserved59 => unreachable!("Reserved opcodes are unrechable"),
| Instruction::Reserved59
| Instruction::Reserved60
| Instruction::Reserved61 => unreachable!("Reserved opcodes are unrechable"),
}
}

3
boa_engine/src/vm/mod.rs

@ -40,8 +40,7 @@ pub use {
pub(crate) use {
call_frame::CallFrameFlags,
code_block::{
create_function_object, create_function_object_fast, create_generator_function_object,
CodeBlockFlags, Constant, Handler,
create_function_object, create_function_object_fast, CodeBlockFlags, Constant, Handler,
},
completion_record::CompletionRecord,
opcode::BindingOpcode,

55
boa_engine/src/vm/opcode/get/function.rs

@ -3,44 +3,6 @@ use crate::{
Context, JsResult,
};
/// `GetArrowFunction` implements the Opcode Operation for `Opcode::GetArrowFunction`
///
/// Operation:
/// - Get arrow function from the pre-compiled inner functions.
#[derive(Debug, Clone, Copy)]
pub(crate) struct GetArrowFunction;
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, context);
context.vm.push(function);
Ok(CompletionType::Normal)
}
}
impl Operation for GetArrowFunction {
const NAME: &'static str = "GetArrowFunction";
const INSTRUCTION: &'static str = "INST - GetArrowFunction";
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:
@ -50,13 +12,9 @@ pub(crate) struct GetFunction;
impl GetFunction {
#[allow(clippy::unnecessary_wraps)]
fn operation(
context: &mut Context<'_>,
index: usize,
method: bool,
) -> JsResult<CompletionType> {
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, method, context);
let function = create_function_object_fast(code, context);
context.vm.push(function);
Ok(CompletionType::Normal)
}
@ -69,19 +27,16 @@ impl Operation for GetFunction {
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)
Self::operation(context, index)
}
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)
Self::operation(context, index)
}
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)
Self::operation(context, index)
}
}

42
boa_engine/src/vm/opcode/get/generator.rs

@ -1,42 +0,0 @@
use crate::{
vm::{code_block::create_generator_function_object, opcode::Operation, CompletionType},
Context, JsResult,
};
/// `GetGenerator` implements the Opcode Operation for `Opcode::GetGenerator`
///
/// Operation:
/// - Get generator function from the pre-compiled inner functions.
#[derive(Debug, Clone, Copy)]
pub(crate) struct GetGenerator;
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, None, context);
context.vm.push(function);
Ok(CompletionType::Normal)
}
}
impl Operation for GetGenerator {
const NAME: &'static str = "GetGenerator";
const INSTRUCTION: &'static str = "INST - GetGenerator";
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)
}
}

2
boa_engine/src/vm/opcode/get/mod.rs

@ -1,13 +1,11 @@
pub(crate) mod argument;
pub(crate) mod function;
pub(crate) mod generator;
pub(crate) mod name;
pub(crate) mod private;
pub(crate) mod property;
pub(crate) use argument::*;
pub(crate) use function::*;
pub(crate) use generator::*;
pub(crate) use name::*;
pub(crate) use private::*;
pub(crate) use property::*;

22
boa_engine/src/vm/opcode/mod.rs

@ -1669,26 +1669,12 @@ generate_opcodes! {
/// Stack: `value` **=>**
Default { address: u32 },
/// Get arrow function from the pre-compiled inner functions.
///
/// Operands: index: `u32`
///
/// Stack: **=>** func
GetArrowFunction { index: VaryingOperand },
/// Get function from the pre-compiled inner functions.
///
/// Operands: index: `VaryingOperand`, is_method: `u8`
///
/// Stack: **=>** func
GetFunction { index: VaryingOperand, method: bool },
/// Get generator function from the pre-compiled inner functions.
///
/// Operands: index: `VaryingOperand`,
/// Operands: index: `VaryingOperand`
///
/// Stack: **=>** func
GetGenerator { index: VaryingOperand },
GetFunction { index: VaryingOperand },
/// Call a function named "eval".
///
@ -2206,6 +2192,10 @@ generate_opcodes! {
Reserved58 => Reserved,
/// Reserved [`Opcode`].
Reserved59 => Reserved,
/// Reserved [`Opcode`].
Reserved60 => Reserved,
/// Reserved [`Opcode`].
Reserved61 => Reserved,
}
/// Specific opcodes for bindings.

Loading…
Cancel
Save