Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

188 lines
6.3 KiB

use std::rc::Rc;
use crate::{
builtins::function::ThisMode,
bytecompiler::ByteCompiler,
environments::CompileTimeEnvironment,
js_string,
vm::{CodeBlock, CodeBlockFlags, Opcode},
JsString,
};
use boa_ast::function::{FormalParameterList, FunctionBody};
use boa_gc::Gc;
use boa_interner::Interner;
/// `FunctionCompiler` is used to compile AST functions to bytecode.
#[derive(Debug, Clone)]
#[allow(clippy::struct_excessive_bools)]
pub(crate) struct FunctionCompiler {
name: JsString,
generator: bool,
r#async: bool,
strict: bool,
arrow: bool,
method: bool,
binding_identifier: Option<JsString>,
}
impl FunctionCompiler {
/// Create a new `FunctionCompiler`.
pub(crate) fn new() -> Self {
Self {
name: js_string!(),
generator: false,
r#async: false,
strict: false,
arrow: false,
method: false,
binding_identifier: None,
}
}
/// Set the name of the function.
pub(crate) fn name<N>(mut self, name: N) -> Self
where
N: Into<Option<JsString>>,
{
let name = name.into();
if let Some(name) = name {
self.name = name;
}
self
}
/// Indicate if the function is an arrow function.
pub(crate) const fn arrow(mut self, arrow: bool) -> Self {
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;
self
}
/// Indicate if the function is an async function.
pub(crate) const fn r#async(mut self, r#async: bool) -> Self {
self.r#async = r#async;
self
}
/// Indicate if the function is in a strict context.
pub(crate) const fn strict(mut self, strict: bool) -> Self {
self.strict = strict;
self
}
/// Indicate if the function has a binding identifier.
pub(crate) fn binding_identifier(mut self, binding_identifier: Option<JsString>) -> Self {
self.binding_identifier = binding_identifier;
self
}
/// Compile a function statement list and it's parameters into bytecode.
pub(crate) fn compile(
mut self,
parameters: &FormalParameterList,
body: &FunctionBody,
variable_environment: Rc<CompileTimeEnvironment>,
lexical_environment: Rc<CompileTimeEnvironment>,
interner: &mut Interner,
) -> Gc<CodeBlock> {
self.strict = self.strict || body.strict();
let length = parameters.length();
let mut compiler = ByteCompiler::new(
self.name,
self.strict,
false,
variable_environment,
lexical_environment,
interner,
);
compiler.length = length;
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::HAS_PROTOTYPE_PROPERTY,
!self.arrow && !self.method && !self.r#async && !self.generator,
);
if self.arrow {
compiler.this_mode = ThisMode::Lexical;
}
if let Some(binding_identifier) = self.binding_identifier {
compiler.code_block_flags |= CodeBlockFlags::HAS_BINDING_IDENTIFIER;
let _ = compiler.push_compile_environment(false);
compiler
.lexical_environment
.create_immutable_binding(binding_identifier, self.strict);
}
// Function environment
let _ = 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.is_async() && !compiler.is_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());
}
compiler.function_declaration_instantiation(
body,
parameters,
self.arrow,
self.strict,
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.is_generator() {
assert!(compiler.async_handler.is_none());
if compiler.is_async() {
// Patched in `ByteCompiler::finish()`.
compiler.async_handler = Some(compiler.push_handler());
}
}
compiler.compile_statement_list(body.statements(), false, false);
compiler.params = parameters.clone();
Gc::new(compiler.finish())
}
}