Skip to content

Commit

Permalink
feat(parser): Add support for class static initialization block
Browse files Browse the repository at this point in the history
  • Loading branch information
prantlf committed May 7, 2022
1 parent 2ea3869 commit 1510e36
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 5 deletions.
14 changes: 11 additions & 3 deletions src/estree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export type Node =
| ReturnStatement
| SequenceExpression
| SpreadElement
| StaticBlock
| Super
| SwitchCase
| SwitchStatement
Expand Down Expand Up @@ -274,6 +275,10 @@ interface MethodDefinitionBase extends _Node {
decorators?: Decorator[];
}

export interface BlockStatementBase extends _Node {
body: Statement[];
}

export interface ArrayExpression extends _Node {
type: 'ArrayExpression';
elements: (Expression | SpreadElement | null)[];
Expand Down Expand Up @@ -321,9 +326,8 @@ export interface BinaryExpression extends _Node {
right: Expression;
}

export interface BlockStatement extends _Node {
export interface BlockStatement extends BlockStatementBase {
type: 'BlockStatement';
body: Statement[];
}

export interface BreakStatement extends _Node {
Expand Down Expand Up @@ -354,9 +358,13 @@ export interface CatchClause extends _Node {
body: BlockStatement;
}

export interface StaticBlock extends BlockStatementBase {
type: 'StaticBlock';
}

export interface ClassBody extends _Node {
type: 'ClassBody';
body: (ClassElement | PropertyDefinition)[];
body: (ClassElement | PropertyDefinition | StaticBlock)[];
}

export interface PropertyDefinition extends _Node {
Expand Down
41 changes: 39 additions & 2 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1770,6 +1770,41 @@ export function parseCatchBlock(
});
}

/**
* Parses class static initialization block
*
* @see [Link](https://github.com/tc39/proposal-class-static-block)
*
* @param parser Parser object
* @param context Context masks
* @param scope Scope instance
* @param start Start pos of node
* @param line
* @param column
*/
export function parseStaticBlock(
parser: ParserState,
context: Context,
scope: ScopeState | undefined,
start: number,
line: number,
column: number
): ESTree.StaticBlock {
// StaticBlock ::
// '{' StatementList '}'

if (scope) scope = addChildScope(scope, ScopeKind.Block);

const ctorContext = Context.InClass | Context.SuperCall;
context = ((context | ctorContext) ^ ctorContext) | Context.SuperProperty;
const { body } = parseBlock(parser, context, scope, {}, start, line, column);

return finishNode(parser, context, start, line, column, {
type: 'StaticBlock',
body
});
}

/**
* Parses do while statement
*
Expand Down Expand Up @@ -8163,7 +8198,7 @@ export function parseClassBody(
context = (context | Context.DisallowIn) ^ Context.DisallowIn;
parser.flags = (parser.flags | Flags.HasConstructor) ^ Flags.HasConstructor;

const body: (ESTree.MethodDefinition | ESTree.PropertyDefinition)[] = [];
const body: (ESTree.MethodDefinition | ESTree.PropertyDefinition | ESTree.StaticBlock)[] = [];
let decorators: ESTree.Decorator[];

while (parser.token !== Token.RightBrace) {
Expand Down Expand Up @@ -8231,7 +8266,7 @@ function parseClassElementList(
start: number,
line: number,
column: number
): ESTree.MethodDefinition | ESTree.PropertyDefinition {
): ESTree.MethodDefinition | ESTree.PropertyDefinition | ESTree.StaticBlock {
let kind: PropertyKind = isStatic ? PropertyKind.Static : PropertyKind.None;
let key: ESTree.Expression | ESTree.PrivateIdentifier | null = null;

Expand Down Expand Up @@ -8302,6 +8337,8 @@ function parseClassElementList(
key = parsePrivateIdentifier(parser, context | Context.InClass, tokenPos, linePos, colPos);
} else if (context & Context.OptionsNext && (parser.token & Token.IsClassField) === Token.IsClassField) {
kind |= PropertyKind.ClassField;
} else if (context & Context.OptionsNext && isStatic && token === Token.LeftBrace) {
return parseStaticBlock(parser, context, scope, tokenPos, linePos, colPos);
} else if (token === Token.EscapedFutureReserved) {
key = parseIdentifier(parser, context, 0);
if (parser.token !== Token.LeftParen)
Expand Down
149 changes: 149 additions & 0 deletions test/parser/next/static-block.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { Context } from '../../../src/common';
import { pass, fail } from '../../test-utils';

describe('Next - Class static initialization block', () => {
fail('Next - Class static initialization block (fail)', [
['class A { static {} }', Context.OptionsWebCompat],
['class A { static {} }', Context.None],
['class A { static { super() } }', Context.OptionsNext],
['class A {}; class B extends A { static { super() } }', Context.OptionsNext],
['class A { static async {} }', Context.OptionsNext],
['class A { async static {} }', Context.OptionsNext]
]);

pass('Next - Class static initialization block (pass)', [
[
`class A { static {} }`,
Context.OptionsNext,
{
body: [
{
body: {
body: [
{
body: [],
type: 'StaticBlock'
}
],
type: 'ClassBody'
},
decorators: [],
id: {
name: 'A',
type: 'Identifier'
},
superClass: null,
type: 'ClassDeclaration'
}
],
sourceType: 'script',
type: 'Program'
}
],
[
`class A { static { this.a } }`,
Context.OptionsNext,
{
body: [
{
body: {
body: [
{
body: [
{
expression: {
computed: false,
object: {
type: 'ThisExpression'
},
property: {
name: 'a',
type: 'Identifier'
},
type: 'MemberExpression'
},
type: 'ExpressionStatement'
}
],
type: 'StaticBlock'
}
],
type: 'ClassBody'
},
decorators: [],
id: {
name: 'A',
type: 'Identifier'
},
superClass: null,
type: 'ClassDeclaration'
}
],
sourceType: 'script',
type: 'Program'
}
],
[
`class A {}; class B extends A { static { super.a } }`,
Context.OptionsNext,
{
body: [
{
body: {
body: [],
type: 'ClassBody'
},
decorators: [],
id: {
name: 'A',
type: 'Identifier'
},
superClass: null,
type: 'ClassDeclaration'
},
{
type: 'EmptyStatement'
},
{
body: {
body: [
{
body: [
{
expression: {
computed: false,
object: {
type: 'Super'
},
property: {
name: 'a',
type: 'Identifier'
},
type: 'MemberExpression'
},
type: 'ExpressionStatement'
}
],
type: 'StaticBlock'
}
],
type: 'ClassBody'
},
decorators: [],
id: {
name: 'B',
type: 'Identifier'
},
superClass: {
name: 'A',
type: 'Identifier'
},
type: 'ClassDeclaration'
}
],
sourceType: 'script',
type: 'Program'
}
]
]);
});

0 comments on commit 1510e36

Please sign in to comment.