feat(semantic): improve check super implementation, reduce access nodes (#1827)

This improves the performance at which we can check that super is in a non-class.
This commit is contained in:
Dunqing 2023-12-27 22:36:28 +08:00 committed by GitHub
parent 736803173b
commit b9bdf361e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 49 deletions

View file

@ -836,74 +836,79 @@ fn check_super<'a>(sup: &Super, node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) {
_ => None,
};
let Some(class_id) = ctx.class_table_builder.current_class_id else {
for scope_id in ctx.scope.ancestors(ctx.current_scope_id) {
let flags = ctx.scope.get_flags(scope_id);
if flags.is_function()
&& matches!(
ctx.nodes.parent_kind(ctx.scope.get_node_id(scope_id)),
Some(AstKind::ObjectProperty(_))
)
{
if let Some(super_call_span) = super_call_span {
ctx.error(UnexpectedSuperCall(super_call_span));
}
return;
};
}
// ModuleBody : ModuleItemList
// * It is a Syntax Error if ModuleItemList Contains super.
// ScriptBody : StatementList
// * It is a Syntax Error if StatementList Contains super
return super_call_span.map_or_else(
|| ctx.error(UnexpectedSuperReference(sup.span)),
|super_call_span| ctx.error(UnexpectedSuperCall(super_call_span)),
);
};
// skip(1) is the self `Super`
// skip(2) is the parent `CallExpression` or `NewExpression`
for node_id in ctx.nodes.ancestors(node.id()).skip(2) {
match ctx.nodes.kind(node_id) {
AstKind::Class(class) => {
// ClassTail : ClassHeritageopt { ClassBody }
// It is a Syntax Error if ClassHeritage is not present and the following algorithm returns true:
// 1. Let constructor be ConstructorMethod of ClassBody.
// 2. If constructor is empty, return false.
// 3. Return HasDirectSuper of constructor.
if class.super_class.is_none() {
return ctx.error(SuperWithoutDerivedClass(sup.span, class.span));
}
break;
}
AstKind::MethodDefinition(def) => {
// ClassElement : MethodDefinition
// It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true.
if let Some(super_call_span) = super_call_span {
if def.kind == MethodDefinitionKind::Constructor {
// pass through and let AstKind::Class check ClassHeritage
} else {
return ctx.error(UnexpectedSuperCall(super_call_span));
// It is a Syntax Error if SuperCall in nested set/get function
if ctx.scope.get_flags(node.scope_id()).is_set_or_get_accessor() {
return ctx.error(UnexpectedSuperCall(super_call_span));
}
// check ClassHeritage
if let AstKind::Class(class) =
ctx.nodes.kind(ctx.class_table_builder.classes.get_node_id(class_id))
{
// ClassTail : ClassHeritageopt { ClassBody }
// It is a Syntax Error if ClassHeritage is not present and the following algorithm returns true:
// 1. Let constructor be ConstructorMethod of ClassBody.
// 2. If constructor is empty, return false.
// 3. Return HasDirectSuper of constructor.
if class.super_class.is_none() {
return ctx.error(SuperWithoutDerivedClass(sup.span, class.span));
}
}
break;
}
} else {
// super references are allowed in method
break;
return ctx.error(UnexpectedSuperCall(super_call_span));
}
// super references are allowed in method
break;
}
// FieldDefinition : ClassElementName Initializer opt
// * It is a Syntax Error if Initializer is present and Initializer Contains SuperCall is true.
// PropertyDefinition : MethodDefinition
// * It is a Syntax Error if HasDirectSuper of MethodDefinition is true.
AstKind::PropertyDefinition(_) => {
AstKind::PropertyDefinition(_)
// ClassStaticBlockBody : ClassStaticBlockStatementList
// * It is a Syntax Error if ClassStaticBlockStatementList Contains SuperCall is true.
| AstKind::StaticBlock(_) => {
if let Some(super_call_span) = super_call_span {
return ctx.error(UnexpectedSuperCall(super_call_span));
}
break;
}
AstKind::ObjectProperty(prop) => {
if prop.value.is_function() {
match super_call_span {
Some(super_call_span) if super_call_span.start > prop.key.span().end => {
return ctx.error(UnexpectedSuperCall(super_call_span));
}
_ => {
break;
}
}
}
}
// ClassStaticBlockBody : ClassStaticBlockStatementList
// * It is a Syntax Error if ClassStaticBlockStatementList Contains SuperCall is true.
AstKind::StaticBlock(_) => {
if let Some(super_call_span) = super_call_span {
return ctx.error(UnexpectedSuperCall(super_call_span));
}
}
// ModuleBody : ModuleItemList
// * It is a Syntax Error if ModuleItemList Contains super.
// ScriptBody : StatementList
// * It is a Syntax Error if StatementList Contains super
AstKind::Program(_) => {
return super_call_span.map_or_else(
|| ctx.error(UnexpectedSuperReference(sup.span)),
|super_call_span| ctx.error(UnexpectedSuperCall(super_call_span)),
);
}
_ => {}
}
}

View file

@ -86,6 +86,10 @@ impl ClassTable {
self.private_identifiers[class_id].iter()
}
pub fn get_node_id(&self, class_id: ClassId) -> AstNodeId {
self.declarations[class_id]
}
pub fn get_property_id(&self, class_id: ClassId, name: &Atom) -> Option<PropertyId> {
self.properties[class_id].iter_enumerated().find_map(|(property_id, property)| {
if property.name == *name {

View file

@ -112,8 +112,8 @@ impl ScopeTree {
&self.bindings[scope_id]
}
pub fn get_node_id(&self, scope_id: ScopeId) -> Option<&AstNodeId> {
self.node_ids.get(&scope_id)
pub fn get_node_id(&self, scope_id: ScopeId) -> AstNodeId {
self.node_ids[&scope_id]
}
pub fn iter_bindings(&self) -> impl Iterator<Item = (ScopeId, SymbolId, Atom)> + '_ {

View file

@ -44,6 +44,10 @@ impl ScopeFlags {
self.contains(Self::Function)
}
pub fn is_constructor(&self) -> bool {
self.contains(Self::Constructor)
}
pub fn is_class_static_block(&self) -> bool {
self.contains(Self::ClassStaticBlock)
}
@ -55,4 +59,8 @@ impl ScopeFlags {
pub fn is_set_accessor(&self) -> bool {
self.contains(Self::SetAccessor)
}
pub fn is_set_or_get_accessor(&self) -> bool {
self.intersects(Self::SetAccessor | Self::GetAccessor)
}
}