use std::collections::hash_map::Entry;
use std::collections::HashMap;
use hermes_ast::context::{GCLock, NodeRc};
use hermes_ast::node::{builder, Node, NodeField};
use hermes_ast::visitor::{Path, TransformResult, VisitorMut};
use hermes_ast::SemaId;
use crate::ids::{DeclId, FunctionInfoId};
use crate::sem_context::{
Atom, Binding, ConstructorKind, CustomDirectives, DeclKind, FuncIsArrow,
};
use super::expressions::replacement_of;
use super::{SemanticResolver, DEBUG_INFO_SETTING_ALL};
const TYPED: bool = false;
pub(super) struct ClassContext {
pub(super) has_constructor: bool,
class_node: NodeRc,
}
#[derive(Clone, Copy, Default)]
struct PrivateAccessorInfo {
is_accessor: bool,
is_static: bool,
is_getter: bool,
is_setter: bool,
is_overloaded_method: bool,
original_name_decl: Option<DeclId>,
}
fn insert_if_vacant(
map: &mut HashMap<Atom, PrivateAccessorInfo>,
key: Atom,
value: PrivateAccessorInfo,
) -> bool {
match map.entry(key) {
Entry::Occupied(_) => false,
Entry::Vacant(e) => {
e.insert(value);
true
}
}
}
#[must_use = "every enter_class must be paired with exit_class"]
pub(super) struct ClassState;
fn class_like_super_class<'gc>(node: &'gc Node<'gc>) -> Option<&'gc Node<'gc>> {
match node {
Node::ClassExpression(n) => n.super_class,
Node::ClassDeclaration(n) => n.super_class,
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
}
}
fn class_like_id<'gc>(node: &'gc Node<'gc>) -> Option<&'gc Node<'gc>> {
let id = match node {
Node::ClassExpression(n) => n.id,
Node::ClassDeclaration(n) => n.id,
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
};
match id {
Some(n) if matches!(n, Node::Identifier(_)) => Some(n),
_ => None,
}
}
fn class_like_body<'gc>(node: &'gc Node<'gc>) -> &'gc Node<'gc> {
let body = match node {
Node::ClassExpression(n) => n.body,
Node::ClassDeclaration(n) => n.body,
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
};
assert!(
matches!(body, Node::ClassBody(_)),
"a ClassLikeNode's body is not a ClassBody"
);
body
}
fn class_like_has_decorators(node: &Node) -> bool {
let decorators = match node {
Node::ClassExpression(n) => n.decorators,
Node::ClassDeclaration(n) => n.decorators,
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
};
!decorators.is_empty()
}
fn class_like_implicit_ctor(node: &Node) -> Option<SemaId> {
match node {
Node::ClassExpression(n) => n.implicit_ctor_function_info.get(),
Node::ClassDeclaration(n) => n.implicit_ctor_function_info.get(),
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
}
}
fn set_class_like_implicit_ctor(node: &Node, info: FunctionInfoId) {
let id = Some(info.sema_id());
match node {
Node::ClassExpression(n) => n.implicit_ctor_function_info.set(id),
Node::ClassDeclaration(n) => n.implicit_ctor_function_info.set(id),
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
}
}
fn class_like_instance_elements_init(node: &Node) -> Option<SemaId> {
match node {
Node::ClassExpression(n) => {
n.instance_elements_init_function_info.get()
}
Node::ClassDeclaration(n) => {
n.instance_elements_init_function_info.get()
}
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
}
}
fn set_class_like_instance_elements_init(node: &Node, info: FunctionInfoId) {
let id = Some(info.sema_id());
match node {
Node::ClassExpression(n) => {
n.instance_elements_init_function_info.set(id)
}
Node::ClassDeclaration(n) => {
n.instance_elements_init_function_info.set(id)
}
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
}
}
fn class_like_static_elements_init(node: &Node) -> Option<SemaId> {
match node {
Node::ClassExpression(n) => n.static_elements_init_function_info.get(),
Node::ClassDeclaration(n) => n.static_elements_init_function_info.get(),
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
}
}
fn set_class_like_static_elements_init(node: &Node, info: FunctionInfoId) {
let id = Some(info.sema_id());
match node {
Node::ClassExpression(n) => {
n.static_elements_init_function_info.set(id)
}
Node::ClassDeclaration(n) => {
n.static_elements_init_function_info.set(id)
}
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
}
}
fn build_class_replacement<'gc>(
gc: &'gc GCLock,
node: &'gc Node<'gc>,
super_class: Option<&'gc Node<'gc>>,
body: Option<&'gc Node<'gc>>,
) -> TransformResult<&'gc Node<'gc>> {
match node {
Node::ClassDeclaration(n) => {
let mut b = builder::ClassDeclaration::from_node(n);
if let Some(v) = super_class {
b.super_class(Some(v));
}
if let Some(v) = body {
b.body(v);
}
b.build(gc)
}
Node::ClassExpression(n) => {
let mut b = builder::ClassExpression::from_node(n);
if let Some(v) = super_class {
b.super_class(Some(v));
}
if let Some(v) = body {
b.body(v);
}
b.build(gc)
}
_ => panic!("invalid ClassLikeNode: {}", node.node_type_str()),
}
}
impl<'bt, 'sc, 'sm, 'ad> SemanticResolver<'bt, 'sc, 'sm, 'ad> {
fn enter_class<'gc>(
&mut self,
gc: &'gc GCLock,
class_node: &'gc Node<'gc>,
) -> ClassState {
self.class_stack.push(ClassContext {
has_constructor: false,
class_node: NodeRc::from_node(gc, class_node),
});
ClassState
}
fn exit_class(&mut self, _state: ClassState) {
self.class_stack.pop().expect("no active class context");
}
fn cur_class_context(&self) -> &ClassContext {
self.class_stack.last().expect("no active class context")
}
pub(super) fn cur_class_context_mut(&mut self) -> &mut ClassContext {
self.class_stack.last_mut().expect("no active class context")
}
pub(super) fn cur_class_is_derived(&self, gc: &GCLock) -> bool {
let class_node_rc = self.cur_class_context().class_node.clone();
class_like_super_class(class_node_rc.node(gc)).is_some()
}
fn cur_class_node_rc(&self) -> NodeRc {
self.cur_class_context().class_node.clone()
}
fn create_implicit_constructor_function_info(&mut self, gc: &GCLock) {
if self.cur_class_context().has_constructor {
return;
}
let class_node_rc = self.cur_class_node_rc();
let class_node = class_node_rc.node(gc);
debug_assert!(class_like_implicit_ctor(class_node).is_none());
let cons_kind = if self.cur_class_is_derived(gc) {
ConstructorKind::Derived
} else {
ConstructorKind::Base
};
let parent = self.cur_function_info();
let implicit_ctor = self.sem_ctx.new_function(
FuncIsArrow::No,
cons_kind,
Some(parent),
self.cur_scope,
true,
CustomDirectives::default(),
);
let lex_scope = self.sem_ctx.new_scope(implicit_ctor, self.cur_scope);
if DEBUG_INFO_SETTING_ALL {
let ptr = self.binding_table.current_scope();
self.sem_ctx.scope_mut(lex_scope).binding_table_scope = ptr;
}
let idx = self.sem_ctx.function(implicit_ctor).get_scopes().len() as u32
- 1;
self.sem_ctx.function_mut(implicit_ctor).function_body_scope_idx = idx;
set_class_like_implicit_ctor(class_node, implicit_ctor);
}
fn get_or_create_instance_elements_init_function_info(
&mut self,
gc: &GCLock,
) -> FunctionInfoId {
let class_node_rc = self.cur_class_node_rc();
let class_node = class_node_rc.node(gc);
if class_like_instance_elements_init(class_node).is_none() {
let field_init_func = self.new_elements_init_function_info();
set_class_like_instance_elements_init(class_node, field_init_func);
}
FunctionInfoId::from_sema_id(
class_like_instance_elements_init(class_node)
.expect("just set, or already present"),
)
}
fn get_or_create_static_elements_init_function_info(
&mut self,
gc: &GCLock,
) -> FunctionInfoId {
let class_node_rc = self.cur_class_node_rc();
let class_node = class_node_rc.node(gc);
if class_like_static_elements_init(class_node).is_none() {
let static_field_init_func = self.new_elements_init_function_info();
set_class_like_static_elements_init(
class_node,
static_field_init_func,
);
}
FunctionInfoId::from_sema_id(
class_like_static_elements_init(class_node)
.expect("just set, or already present"),
)
}
fn new_elements_init_function_info(&mut self) -> FunctionInfoId {
let parent = self.cur_function_info();
let field_init_func = self.sem_ctx.new_function(
FuncIsArrow::No,
ConstructorKind::None,
Some(parent),
self.cur_scope,
true,
CustomDirectives::default(),
);
let lex_scope = self.sem_ctx.new_scope(field_init_func, self.cur_scope);
if DEBUG_INFO_SETTING_ALL {
let ptr = self.binding_table.current_scope();
self.sem_ctx.scope_mut(lex_scope).binding_table_scope = ptr;
}
let idx = self.sem_ctx.function(field_init_func).get_scopes().len()
as u32
- 1;
self.sem_ctx
.function_mut(field_init_func)
.function_body_scope_idx = idx;
field_init_func
}
fn create_static_block_function_info(
&mut self,
node: &Node,
) -> FunctionInfoId {
let parent = self.cur_function_info();
let static_block_func = self.sem_ctx.new_function(
FuncIsArrow::No,
ConstructorKind::None,
Some(parent),
self.cur_scope,
true,
CustomDirectives::default(),
);
self.sem_ctx.function_mut(static_block_func).is_static_block = true;
let block = node
.as_static_block()
.expect("create_static_block_function_info: not a StaticBlock");
block.function_info.set(Some(static_block_func.sema_id()));
static_block_func
}
pub(super) fn visit_class_declaration<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) -> TransformResult<&'gc Node<'gc>> {
if TYPED {
panic!(
"sema: typed-mode class declarations need the typed-dialect \
track (cpp:892-901)"
);
}
self.visit_class_as_expr(gc, node)
}
pub(super) fn visit_class_expression<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) -> TransformResult<&'gc Node<'gc>> {
self.visit_class_as_expr(gc, node)
}
fn visit_class_as_expr<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) -> TransformResult<&'gc Node<'gc>> {
if self.compile() && class_like_has_decorators(node) {
self.sm
.error_range(node.range(), "decorators are not supported");
}
let strict_func = self.cur_function_info();
let saved_strict = self.sem_ctx.function(strict_func).strict;
self.sem_ctx.function_mut(strict_func).strict = true;
let class_state = self.enter_class(gc, node);
let scope_state = self.enter_scope(Some(node), false);
if let Some(ident_node) = class_like_id(node) {
if self.validate_declaration_name(
gc,
DeclKind::ClassExprName,
ident_node,
) {
let ident = ident_node
.as_identifier()
.expect("class_like_id only yields Identifiers");
let name = ident.name.get();
let cur_scope = self.cur_scope.expect("just entered a scope");
let decl = self.sem_ctx.new_decl_in_scope_default(
name,
DeclKind::ClassExprName,
cur_scope,
);
self.sem_ctx.set_expression_decl(
ident_node.node_id(),
ident,
Some(decl),
);
self.binding_table.try_emplace(
name,
Binding::new(
decl,
Some(NodeRc::from_node(gc, ident_node)),
),
);
}
}
let super_repl = match class_like_super_class(node) {
Some(super_class) => replacement_of(self.call(
gc,
super_class,
Some(Path::new(node, NodeField::super_class)),
)),
None => None,
};
self.collect_declared_private_identifiers(gc, node);
let body_repl = replacement_of(self.call(
gc,
class_like_body(node),
Some(Path::new(node, NodeField::body)),
));
if self.recursion_depth != 0 {
self.create_implicit_constructor_function_info(gc);
}
let result = build_class_replacement(gc, node, super_repl, body_repl);
self.exit_scope(scope_state);
self.exit_class(class_state);
self.sem_ctx.function_mut(strict_func).strict = saved_strict;
result
}
fn collect_declared_private_identifiers<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) {
let mut private_declarations: HashMap<Atom, PrivateAccessorInfo> =
HashMap::new();
const DEFAULT_DUP_ERR_MSG: &str =
"Duplicate private identifier declaration.";
let body = class_like_body(node)
.as_class_body()
.expect("class_like_body checked the kind");
for elm in body.body.iter() {
if let Node::ClassPrivateProperty(prop) = elm {
let id_node = prop.key;
let id = id_node
.as_identifier()
.expect("a ClassPrivateProperty key is an Identifier");
if !insert_if_vacant(
&mut private_declarations,
id.name.get(),
PrivateAccessorInfo::default(),
) {
self.sm.error_range(id_node.range(), DEFAULT_DUP_ERR_MSG);
} else {
self.declare_private_name(
gc,
id_node,
DeclKind::PrivateField,
false,
);
}
continue;
}
if let Node::MethodDefinition(method) = elm {
let Node::PrivateName(private_name) = method.key else {
continue;
};
let id_node = private_name.id;
let id = id_node
.as_identifier()
.expect("a PrivateName's id is an Identifier");
let name = id.name.get();
let meth_kind = method.kind.get();
if meth_kind == self.kw().ident_method {
let is_overload = if TYPED {
panic!(
"sema: @Hermes.overload private methods need \
the typed-dialect track (cpp:2227-2230)"
)
} else {
false
};
let inserted = insert_if_vacant(
&mut private_declarations,
name,
PrivateAccessorInfo::default(),
);
if !inserted {
let existing = private_declarations[&name];
if !is_overload || !existing.is_overloaded_method {
self.sm.error_range(
id_node.range(),
DEFAULT_DUP_ERR_MSG,
);
}
self.resolve_private_name(gc, id_node);
} else {
private_declarations
.get_mut(&name)
.expect("just inserted")
.is_overloaded_method = is_overload;
self.declare_private_name(
gc,
id_node,
DeclKind::PrivateMethod,
method.r#static.get(),
);
}
continue;
}
debug_assert!(
meth_kind == self.kw().ident_set
|| meth_kind == self.kw().ident_get,
"unrecognized method kind."
);
let is_setter = meth_kind == self.kw().ident_set;
let cur_info = PrivateAccessorInfo {
is_accessor: true,
is_static: method.r#static.get(),
is_getter: !is_setter,
is_setter,
is_overloaded_method: false,
original_name_decl: None,
};
if insert_if_vacant(&mut private_declarations, name, cur_info) {
let decl = self.declare_private_name(
gc,
id_node,
if is_setter {
DeclKind::PrivateSetter
} else {
DeclKind::PrivateGetter
},
method.r#static.get(),
);
private_declarations
.get_mut(&name)
.expect("just inserted")
.original_name_decl = Some(decl);
continue;
}
let existing_info = private_declarations[&name];
if !existing_info.is_accessor
|| (cur_info.is_setter && existing_info.is_setter)
|| (cur_info.is_getter && existing_info.is_getter)
{
self.sm.error_range(id_node.range(), DEFAULT_DUP_ERR_MSG);
continue;
}
if cur_info.is_static != existing_info.is_static {
self.sm.error_range(
id_node.range(),
"static and non-static private accessor with the \
same name",
);
continue;
}
let original_name_decl = existing_info
.original_name_decl
.expect("an accessor entry always records its decl");
{
let existing_info = private_declarations
.get_mut(&name)
.expect("looked up just above");
existing_info.is_getter = true;
existing_info.is_setter = true;
}
self.sem_ctx.decl_mut(original_name_decl).kind =
DeclKind::PrivateGetterSetter;
self.sem_ctx.set_both_decl(
id_node.node_id(),
id,
Some(original_name_decl),
);
}
}
}
pub(super) fn visit_private_name<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) -> TransformResult<&'gc Node<'gc>> {
let private_name = node
.as_private_name()
.expect("visit_private_name: not a PrivateName");
let identifier_node = private_name.id;
let decl = self.resolve_private_name(gc, identifier_node);
if decl.is_none() {
let name = identifier_node
.as_identifier()
.expect("a PrivateName's id is an Identifier")
.name
.get();
let name = String::from_utf8_lossy(gc.bytes(name)).into_owned();
self.sm.error_range(
identifier_node.range(),
format!(
"the private name \"#{name}\" was not declared in any \
enclosing class"
),
);
}
node.visit_children_mut(gc, self)
}
pub(super) fn visit_class_private_property<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) -> TransformResult<&'gc Node<'gc>> {
let prop = node.as_class_private_property().expect(
"visit_class_private_property: not a ClassPrivateProperty",
);
if self.compile() && !prop.decorators.is_empty() {
self.sm
.error_range(node.range(), "decorators are not supported");
}
let mut value_repl = None;
if let Some(value) = prop.value {
let saved_can_ref_super = self.can_reference_super;
self.can_reference_super = true;
let saved_forbid_await = self.forbid_await_expression;
self.forbid_await_expression = true;
let saved_forbid_arguments =
self.forbid_special_arguments_reference;
self.forbid_special_arguments_reference = true;
let sem_info = if prop.r#static.get() {
self.get_or_create_static_elements_init_function_info(gc)
} else {
self.get_or_create_instance_elements_init_function_info(gc)
};
let func_state = self.enter_function_with_info(sem_info);
self.declare_arguments();
let old_scope = self.cur_scope;
self.cur_scope = Some(
self.sem_ctx
.function(self.cur_function_info())
.get_function_body_scope(),
);
value_repl = replacement_of(self.call(
gc,
value,
Some(Path::new(node, NodeField::value)),
));
self.cur_scope = old_scope;
self.exit_function(func_state);
self.forbid_special_arguments_reference = saved_forbid_arguments;
self.forbid_await_expression = saved_forbid_await;
self.can_reference_super = saved_can_ref_super;
} else if !TYPED {
if prop.r#static.get() {
self.get_or_create_static_elements_init_function_info(gc);
} else {
self.get_or_create_instance_elements_init_function_info(gc);
}
}
build_class_private_property(gc, prop, value_repl)
}
pub(super) fn visit_static_block<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) -> TransformResult<&'gc Node<'gc>> {
self.get_or_create_static_elements_init_function_info(gc);
let static_block_info = self.create_static_block_function_info(node);
let func_state =
self.enter_function_static_block(gc, node, static_block_info);
let scope_state =
self.enter_scope(Some(node), true);
self.process_collected_declarations(gc, node);
if DEBUG_INFO_SETTING_ALL {
let ptr = self.binding_table.current_scope();
self.sem_ctx
.function_mut(static_block_info)
.binding_table_scope = ptr;
}
let saved_forbid_await = self.forbid_await_expression;
self.forbid_await_expression = true;
let saved_forbid_await_ident = self.forbid_await_as_identifier;
self.forbid_await_as_identifier = true;
let saved_forbid_arguments_ident = self.forbid_arguments_as_identifier;
self.forbid_arguments_as_identifier = true;
let saved_can_ref_super = self.can_reference_super;
self.can_reference_super = true;
let result = node.visit_children_mut(gc, self);
self.can_reference_super = saved_can_ref_super;
self.forbid_arguments_as_identifier = saved_forbid_arguments_ident;
self.forbid_await_as_identifier = saved_forbid_await_ident;
self.forbid_await_expression = saved_forbid_await;
self.exit_scope(scope_state);
self.exit_function(func_state);
result
}
pub(super) fn visit_class_property<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) -> TransformResult<&'gc Node<'gc>> {
let prop = node
.as_class_property()
.expect("visit_class_property: not a ClassProperty");
if self.compile() && !prop.decorators.is_empty() {
self.sm
.error_range(node.range(), "decorators are not supported");
}
let mut key_repl = None;
if prop.computed.get() {
let saved_can_ref_super = self.can_reference_super;
self.can_reference_super = false;
key_repl = replacement_of(self.call(
gc,
prop.key,
Some(Path::new(node, NodeField::key)),
));
self.can_reference_super = saved_can_ref_super;
if self.recursion_depth == 0 {
return build_class_property(gc, prop, key_repl, None);
}
}
let mut value_repl = None;
if let Some(value) = prop.value {
let saved_can_ref_super = self.can_reference_super;
self.can_reference_super = true;
let saved_forbid_await = self.forbid_await_expression;
self.forbid_await_expression = true;
let saved_forbid_arguments =
self.forbid_special_arguments_reference;
self.forbid_special_arguments_reference = true;
let sem_info = if prop.r#static.get() {
self.get_or_create_static_elements_init_function_info(gc)
} else {
self.get_or_create_instance_elements_init_function_info(gc)
};
let func_state = self.enter_function_with_info(sem_info);
self.declare_arguments();
let old_scope = self.cur_scope;
self.cur_scope = Some(
self.sem_ctx
.function(self.cur_function_info())
.get_function_body_scope(),
);
value_repl = replacement_of(self.call(
gc,
value,
Some(Path::new(node, NodeField::value)),
));
self.cur_scope = old_scope;
self.exit_function(func_state);
self.forbid_special_arguments_reference = saved_forbid_arguments;
self.forbid_await_expression = saved_forbid_await;
self.can_reference_super = saved_can_ref_super;
} else if !TYPED {
if prop.r#static.get() {
self.get_or_create_static_elements_init_function_info(gc);
} else {
self.get_or_create_instance_elements_init_function_info(gc);
}
}
build_class_property(gc, prop, key_repl, value_repl)
}
pub(super) fn visit_method_definition<'gc>(
&mut self,
gc: &'gc GCLock,
node: &'gc Node<'gc>,
) -> TransformResult<&'gc Node<'gc>> {
let method = node
.as_method_definition()
.expect("visit_method_definition: not a MethodDefinition");
if self.compile() && !TYPED && !method.decorators.is_empty() {
self.sm
.error_range(node.range(), "decorators are not supported");
}
let mut key_repl = None;
if method.computed.get() {
key_repl = replacement_of(self.call(
gc,
method.key,
Some(Path::new(node, NodeField::key)),
));
}
if self.recursion_depth == 0 {
return build_method_definition(gc, method, key_repl, None);
}
if matches!(method.key, Node::PrivateName(_)) && !method.r#static.get()
{
self.get_or_create_instance_elements_init_function_info(gc);
}
let value_repl = replacement_of(self.call(
gc,
method.value,
Some(Path::new(node, NodeField::value)),
));
build_method_definition(gc, method, key_repl, value_repl)
}
pub(super) fn visit_super<'gc>(
&mut self,
path: Option<Path<'gc>>,
) -> TransformResult<&'gc Node<'gc>> {
if let Some(path) = path {
if matches!(
path.parent,
Node::MemberExpression(_) | Node::OptionalMemberExpression(_)
) && !self.can_reference_super
{
self.sm.error_range(
path.parent.range(),
"super not allowed here",
);
}
}
TransformResult::Unchanged
}
}
fn build_class_property<'gc>(
gc: &'gc GCLock,
prop: &'gc hermes_ast::node::ClassProperty<'gc>,
key: Option<&'gc Node<'gc>>,
value: Option<&'gc Node<'gc>>,
) -> TransformResult<&'gc Node<'gc>> {
let mut b = builder::ClassProperty::from_node(prop);
if let Some(v) = key {
b.key(v);
}
if let Some(v) = value {
b.value(Some(v));
}
b.build(gc)
}
fn build_class_private_property<'gc>(
gc: &'gc GCLock,
prop: &'gc hermes_ast::node::ClassPrivateProperty<'gc>,
value: Option<&'gc Node<'gc>>,
) -> TransformResult<&'gc Node<'gc>> {
let mut b = builder::ClassPrivateProperty::from_node(prop);
if let Some(v) = value {
b.value(Some(v));
}
b.build(gc)
}
fn build_method_definition<'gc>(
gc: &'gc GCLock,
method: &'gc hermes_ast::node::MethodDefinition<'gc>,
key: Option<&'gc Node<'gc>>,
value: Option<&'gc Node<'gc>>,
) -> TransformResult<&'gc Node<'gc>> {
let mut b = builder::MethodDefinition::from_node(method);
if let Some(v) = key {
b.key(v);
}
if let Some(v) = value {
b.value(v);
}
b.build(gc)
}