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.
 
 

594 lines
29 KiB

use super::{ByteCompiler, Literal, Operand, ToJsString};
use crate::{
js_string,
vm::{BindingOpcode, CodeBlock, CodeBlockFlags, Opcode},
};
use boa_ast::{
expression::Identifier,
function::{Class, ClassElement, FormalParameterList},
property::{MethodDefinition, PropertyName},
};
use boa_gc::Gc;
use boa_interner::Sym;
// Static class elements that are initialized at a later time in the class creation.
enum StaticElement {
// A static class block with it's function code.
StaticBlock(Gc<CodeBlock>),
// A static class field with it's function code and optional name index.
StaticField((Gc<CodeBlock>, Option<u32>)),
}
impl ByteCompiler<'_> {
/// This function compiles a class declaration or expression.
///
/// The compilation of a class declaration and expression is mostly equal.
/// A class declaration binds the resulting class object to it's identifier.
/// A class expression leaves the resulting class object on the stack for following operations.
pub(crate) fn compile_class(&mut self, class: &Class, expression: bool) {
// 11.2.2 Strict Mode Code - <https://tc39.es/ecma262/#sec-strict-mode-code>
// - All parts of a ClassDeclaration or a ClassExpression are strict mode code.
let strict = self.strict();
self.code_block_flags |= CodeBlockFlags::STRICT;
let class_name = class
.name()
.map_or(Sym::EMPTY_STRING, Identifier::sym)
.to_js_string(self.interner());
let old_lex_env = if class.has_binding_identifier() {
let old_lex_env = self.lexical_environment.clone();
let env_index = self.push_compile_environment(false);
self.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index);
self.lexical_environment
.create_immutable_binding(class_name.clone(), true);
Some(old_lex_env)
} else {
None
};
let mut compiler = ByteCompiler::new(
class_name.clone(),
true,
self.json_parse,
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.interner,
);
compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;
// Function environment
let _ = compiler.push_compile_environment(true);
if let Some(expr) = class.constructor() {
compiler.length = expr.parameters().length();
compiler.params = expr.parameters().clone();
compiler.function_declaration_instantiation(
expr.body(),
expr.parameters(),
false,
true,
false,
);
compiler.compile_statement_list(expr.body().statements(), false, false);
compiler.emit_opcode(Opcode::PushUndefined);
} else if class.super_ref().is_some() {
compiler.emit_opcode(Opcode::SuperCallDerived);
compiler.emit_opcode(Opcode::BindThisValue);
} else {
compiler.emit_opcode(Opcode::PushUndefined);
}
compiler.emit_opcode(Opcode::SetReturnValue);
// 17. If ClassHeritageopt is present, set F.[[ConstructorKind]] to derived.
compiler.code_block_flags.set(
CodeBlockFlags::IS_DERIVED_CONSTRUCTOR,
class.super_ref().is_some(),
);
let code = Gc::new(compiler.finish());
let index = self.push_function_to_constants(code);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_opcode(Opcode::Dup);
if let Some(node) = class.super_ref() {
self.compile_expr(node, true);
self.emit_opcode(Opcode::PushClassPrototype);
} else {
self.emit_opcode(Opcode::PushUndefined);
}
self.emit_opcode(Opcode::SetClassPrototype);
self.emit_opcode(Opcode::Swap);
let count_label = self.emit_opcode_with_operand(Opcode::PushPrivateEnvironment);
let mut count = 0;
for element in class.elements() {
match element {
ClassElement::PrivateMethodDefinition(name, _)
| ClassElement::PrivateStaticMethodDefinition(name, _)
| ClassElement::PrivateFieldDefinition(name, _)
| ClassElement::PrivateStaticFieldDefinition(name, _) => {
count += 1;
let index = self.get_or_insert_private_name(*name);
self.emit_u32(index);
}
_ => {}
}
}
self.patch_jump_with_target(count_label, count);
let mut static_elements = Vec::new();
let mut static_field_name_count = 0;
if old_lex_env.is_some() {
self.emit_opcode(Opcode::Dup);
self.emit_binding(BindingOpcode::InitLexical, class_name.clone());
}
// TODO: set function name for getter and setters
for element in class.elements() {
match element {
ClassElement::StaticMethodDefinition(name, method_definition) => {
self.emit_opcode(Opcode::Dup);
match method_definition {
MethodDefinition::Get(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticGetterByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticGetterByValue);
}
},
MethodDefinition::Set(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticSetterByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticSetterByValue);
}
},
MethodDefinition::Ordinary(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticMethodByValue);
}
},
MethodDefinition::Async(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticMethodByValue);
}
},
MethodDefinition::Generator(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticMethodByValue);
}
},
MethodDefinition::AsyncGenerator(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassStaticMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassStaticMethodByValue);
}
},
}
}
ClassElement::PrivateStaticMethodDefinition(name, method_definition) => {
self.emit_opcode(Opcode::Dup);
match method_definition {
MethodDefinition::Get(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateGetter, index);
}
MethodDefinition::Set(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateSetter, index);
}
MethodDefinition::Ordinary(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateMethod, index);
}
MethodDefinition::Async(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateMethod, index);
}
MethodDefinition::Generator(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateMethod, index);
}
MethodDefinition::AsyncGenerator(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::SetPrivateMethod, index);
}
}
}
ClassElement::FieldDefinition(name, field) => {
self.emit_opcode(Opcode::Dup);
match name {
PropertyName::Literal(name) => {
self.emit_push_literal(Literal::String(
self.interner().resolve_expect(*name).into_common(false),
));
}
PropertyName::Computed(name) => {
self.compile_expr(name, true);
}
}
let mut field_compiler = ByteCompiler::new(
js_string!(),
true,
self.json_parse,
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.interner,
);
// Function environment
let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field {
field_compiler.compile_expr(node, true);
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
field_compiler.emit_opcode(Opcode::SetReturnValue);
field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;
let code = Gc::new(field_compiler.finish());
let index = self.push_function_to_constants(code);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_opcode(Opcode::PushClassField);
}
ClassElement::PrivateFieldDefinition(name, field) => {
self.emit_opcode(Opcode::Dup);
let name_index = self.get_or_insert_private_name(*name);
let mut field_compiler = ByteCompiler::new(
class_name.clone(),
true,
self.json_parse,
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.interner,
);
let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field {
field_compiler.compile_expr(node, true);
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
field_compiler.emit_opcode(Opcode::SetReturnValue);
field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;
let code = Gc::new(field_compiler.finish());
let index = self.push_function_to_constants(code);
self.emit_with_varying_operand(Opcode::GetFunction, index);
self.emit_with_varying_operand(Opcode::PushClassFieldPrivate, name_index);
}
ClassElement::StaticFieldDefinition(name, field) => {
let name_index = match name {
PropertyName::Literal(name) => {
Some(self.get_or_insert_name((*name).into()))
}
PropertyName::Computed(name) => {
self.compile_expr(name, true);
self.emit(
Opcode::RotateRight,
&[Operand::U8(3 + static_field_name_count)],
);
static_field_name_count += 1;
None
}
};
let mut field_compiler = ByteCompiler::new(
class_name.clone(),
true,
self.json_parse,
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.interner,
);
let _ = field_compiler.push_compile_environment(true);
if let Some(node) = field {
field_compiler.compile_expr(node, true);
} else {
field_compiler.emit_opcode(Opcode::PushUndefined);
}
field_compiler.emit_opcode(Opcode::SetReturnValue);
field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;
let code = field_compiler.finish();
let code = Gc::new(code);
static_elements.push(StaticElement::StaticField((code, name_index)));
}
ClassElement::PrivateStaticFieldDefinition(name, field) => {
self.emit_opcode(Opcode::Dup);
if let Some(node) = field {
self.compile_expr(node, true);
} else {
self.emit_opcode(Opcode::PushUndefined);
}
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::DefinePrivateField, index);
}
ClassElement::StaticBlock(body) => {
let mut compiler = ByteCompiler::new(
Sym::EMPTY_STRING.to_js_string(self.interner()),
true,
false,
self.variable_environment.clone(),
self.lexical_environment.clone(),
self.interner,
);
let _ = compiler.push_compile_environment(true);
compiler.function_declaration_instantiation(
body,
&FormalParameterList::default(),
false,
true,
false,
);
compiler.compile_statement_list(body.statements(), false, false);
let code = Gc::new(compiler.finish());
static_elements.push(StaticElement::StaticBlock(code));
}
ClassElement::PrivateMethodDefinition(name, method_definition) => {
self.emit_opcode(Opcode::Dup);
match method_definition {
MethodDefinition::Get(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateGetter, index);
}
MethodDefinition::Set(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateSetter, index);
}
MethodDefinition::Ordinary(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index);
}
MethodDefinition::Async(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index);
}
MethodDefinition::Generator(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index);
}
MethodDefinition::AsyncGenerator(expr) => {
self.method(expr.into());
let index = self.get_or_insert_private_name(*name);
self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index);
}
}
}
ClassElement::MethodDefinition(name, method_definition) => {
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::Dup);
match method_definition {
MethodDefinition::Get(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassGetterByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassGetterByValue);
}
},
MethodDefinition::Set(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassSetterByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassSetterByValue);
}
},
MethodDefinition::Ordinary(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassMethodByValue);
}
},
MethodDefinition::Async(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassMethodByValue);
}
},
MethodDefinition::Generator(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassMethodByValue);
}
},
MethodDefinition::AsyncGenerator(expr) => match name {
PropertyName::Literal(name) => {
self.method(expr.into());
let index = self.get_or_insert_name((*name).into());
self.emit_with_varying_operand(
Opcode::DefineClassMethodByName,
index,
);
}
PropertyName::Computed(name_node) => {
self.compile_expr(name_node, true);
self.emit_opcode(Opcode::ToPropertyKey);
self.method(expr.into());
self.emit_opcode(Opcode::DefineClassMethodByValue);
}
},
}
self.emit_opcode(Opcode::Swap);
}
}
}
for element in static_elements {
match element {
StaticElement::StaticBlock(code) => {
self.emit_opcode(Opcode::Dup);
let index = self.push_function_to_constants(code);
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);
}
StaticElement::StaticField((code, name_index)) => {
self.emit_opcode(Opcode::Dup);
self.emit_opcode(Opcode::Dup);
let index = self.push_function_to_constants(code);
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 {
self.emit_with_varying_operand(Opcode::DefineOwnPropertyByName, name_index);
} else {
self.emit(Opcode::RotateLeft, &[Operand::U8(5)]);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::DefineOwnPropertyByValue);
}
}
}
}
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::Pop);
if let Some(old_lex_env) = old_lex_env {
self.pop_compile_environment();
self.lexical_environment = old_lex_env;
self.emit_opcode(Opcode::PopEnvironment);
}
self.emit_opcode(Opcode::PopPrivateEnvironment);
if !expression {
self.emit_binding(BindingOpcode::InitVar, class_name);
}
// NOTE: Reset strict mode to before class declaration/expression evalutation.
self.code_block_flags.set(CodeBlockFlags::STRICT, strict);
}
}