Skip to content

Commit

Permalink
Refactor Context::run() method
Browse files Browse the repository at this point in the history
- Remove async generator close from run
- Remove promise capability from run
- Remove promise capability check from call_internal
- Remove generator creation from call_internal
- Move promise capability creation to separate opcode
  • Loading branch information
HalidOdat committed Jul 30, 2023
1 parent 2b01ef1 commit 9a8398a
Show file tree
Hide file tree
Showing 15 changed files with 517 additions and 374 deletions.
16 changes: 10 additions & 6 deletions boa_engine/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,7 @@ impl BuiltInFunctionObject {
let code = FunctionCompiler::new()
.name(Sym::ANONYMOUS)
.generator(true)
.r#async(r#async)
.compile(
&FormalParameterList::default(),
&FunctionBody::default(),
Expand All @@ -793,12 +794,15 @@ impl BuiltInFunctionObject {

Ok(function_object)
} else {
let code = FunctionCompiler::new().name(Sym::ANONYMOUS).compile(
&FormalParameterList::default(),
&FunctionBody::default(),
context.realm().environment().compile_env(),
context,
);
let code = FunctionCompiler::new()
.r#async(r#async)
.name(Sym::ANONYMOUS)
.compile(
&FormalParameterList::default(),
&FunctionBody::default(),
context.realm().environment().compile_env(),
context,
);

let environments = context.vm.environments.pop_to_global();
let function_object =
Expand Down
7 changes: 3 additions & 4 deletions boa_engine/src/bytecompiler/declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,11 +1010,10 @@ impl ByteCompiler<'_, '_> {
if !formals.has_rest_parameter() {
self.emit_opcode(Opcode::RestParameterPop);
}

if generator {
self.emit_opcode(Opcode::PushUndefined);
// Don't need to use `AsyncGeneratorYield` since
// we just want to stop the execution of the generator.
self.emit_opcode(Opcode::GeneratorYield);
self.emit_opcode(Opcode::Generator);
self.emit_u8(self.in_async().into());
self.emit_opcode(Opcode::Pop);
}

Expand Down
39 changes: 38 additions & 1 deletion boa_engine/src/bytecompiler/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
builtins::function::ThisMode,
bytecompiler::ByteCompiler,
environments::CompileTimeEnvironment,
vm::{CodeBlock, CodeBlockFlags},
vm::{CodeBlock, CodeBlockFlags, Opcode},
Context,
};
use boa_ast::function::{FormalParameterList, FunctionBody};
Expand Down Expand Up @@ -120,6 +120,31 @@ impl FunctionCompiler {
// Function environment
compiler.push_compile_environment(true);

// Taken from:
// - 15.9.3 Runtime Semantics: EvaluateAsyncConciseBody: <https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncconcisebody>
// - 15.8.4 Runtime Semantics: EvaluateAsyncFunctionBody: <https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncfunctionbody>
//
// Note: In `EvaluateAsyncGeneratorBody` unlike the async non-generator functions we don't handle exceptions thrown by
// `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() {
// 1. Let promiseCapability be ! NewPromiseCapability(%Promise%).
//
// Note: If the promise capability is already set, then we do nothing.
// This is a deviation from the spec, but it allows to set the promise capability by
// ExecuteAsyncModule ( module ): <https://tc39.es/ecma262/#sec-execute-async-module>
compiler.emit_opcode(Opcode::CreatePromiseCapability);

// 2. Let declResult be Completion(FunctionDeclarationInstantiation(functionObject, argumentsList)).
//
// Note: We push an exception handler so we catch exceptions that are thrown by the
// `FunctionDeclarationInstantiation` abstract function.
//
// Patched in `ByteCompiler::finish()`.
compiler.async_handler = Some(compiler.push_handler());
}

let (env_label, additional_env) = compiler.function_declaration_instantiation(
body,
parameters,
Expand All @@ -128,6 +153,18 @@ impl FunctionCompiler {
self.generator,
);

// Taken from:
// - 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() {
assert!(compiler.async_handler.is_none());
if compiler.in_async() {
// Patched in `ByteCompiler::finish()`.
compiler.async_handler = Some(compiler.push_handler());
}
}

compiler.compile_statement_list(body.statements(), false, false);

if let Some(env_labels) = env_label {
Expand Down
18 changes: 17 additions & 1 deletion boa_engine/src/bytecompiler/jump_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,23 @@ impl JumpRecord {
match self.kind {
JumpRecordKind::Break => compiler.patch_jump(self.label),
JumpRecordKind::Continue => compiler.patch_jump_with_target(self.label, start_address),
JumpRecordKind::Return => compiler.emit_opcode(Opcode::Return),
JumpRecordKind::Return => {
match (compiler.in_async(), compiler.in_generator()) {
// Taken from:
// - 27.6.3.2 AsyncGeneratorStart ( generator, generatorBody ): https://tc39.es/ecma262/#sec-asyncgeneratorstart
//
// Note: If we are returning we have to close the async generator function.
(true, true) => compiler.emit_opcode(Opcode::AsyncGeneratorClose),

// Taken from:
// - 27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ): <https://tc39.es/ecma262/#sec-asyncblockstart>
//
// Note: If there is promise capability resolve or reject it based on pending exception.
(true, false) => compiler.emit_opcode(Opcode::CompletePromiseCapability),
(_, _) => {}
}
compiler.emit_opcode(Opcode::Return);
}
}
}
}
Expand Down
13 changes: 11 additions & 2 deletions boa_engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,13 @@ pub struct ByteCompiler<'ctx, 'host> {
names_map: FxHashMap<Identifier, u32>,
bindings_map: FxHashMap<BindingLocator, u32>,
jump_info: Vec<JumpControlInfo>,
in_async: bool,
pub(crate) in_async: bool,
in_generator: bool,

/// Used to handle exception throws that escape the async function types.
///
/// Async functions and async generator functions, need to be closed and resolved.
pub(crate) async_handler: Option<u32>,
json_parse: bool,

// TODO: remove when we separate scripts from the context
Expand Down Expand Up @@ -305,6 +310,7 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
jump_info: Vec::new(),
in_async: false,
in_generator: false,
async_handler: None,
json_parse,
current_environment,
context,
Expand Down Expand Up @@ -1410,7 +1416,10 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
#[allow(clippy::missing_const_for_fn)]
pub fn finish(mut self) -> CodeBlock {
// Push return at the end of the function compilation.
self.emit_opcode(Opcode::Return);
if let Some(async_handler) = self.async_handler {
self.patch_handler(async_handler);
}
self.r#return();

let name = self
.context
Expand Down
12 changes: 12 additions & 0 deletions boa_engine/src/environments/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ impl EnvironmentStack {
self.stack[0] = Environment::Declarative(global);
}

/// Gets the current global environment.
pub(crate) fn global(&self) -> Gc<DeclarativeEnvironment> {
let env = self.stack[0].clone();

match env {
Environment::Declarative(ref env) => env.clone(),
Environment::Object(_) => {
unreachable!("first environment should be the global environment")
}
}
}

/// Extends the length of the next outer function environment to the number of compiled bindings.
///
/// This is only useful when compiled bindings are added after the initial compilation (eval).
Expand Down
4 changes: 4 additions & 0 deletions boa_engine/src/module/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,10 @@ impl SourceTextModule {

let mut compiler =
ByteCompiler::new(Sym::MAIN, true, false, module_compile_env.clone(), context);

compiler.in_async = true;
compiler.async_handler = Some(compiler.push_handler());

let mut imports = Vec::new();

let (codeblock, functions) = {
Expand Down
Loading

0 comments on commit 9a8398a

Please sign in to comment.