Skip to content

Commit 29ccbb2

Browse files
authored
Support accessor keyword in class properties (#353)
1 parent 12c3ba3 commit 29ccbb2

File tree

13 files changed

+255
-74
lines changed

13 files changed

+255
-74
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Runtime.CompilerServices;
2+
using Esprima.Utils;
3+
4+
namespace Esprima.Ast;
5+
6+
public sealed class AccessorProperty : ClassProperty
7+
{
8+
private readonly NodeList<Decorator> _decorators;
9+
10+
public AccessorProperty(
11+
Expression key,
12+
bool computed,
13+
Expression? value,
14+
bool isStatic,
15+
in NodeList<Decorator> decorators)
16+
: base(Nodes.AccessorProperty, PropertyKind.Property, key, computed)
17+
{
18+
Value = value;
19+
Static = isStatic;
20+
_decorators = decorators;
21+
}
22+
23+
public new Expression? Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
24+
protected override Expression? GetValue() => Value;
25+
26+
public bool Static { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
27+
public ref readonly NodeList<Decorator> Decorators { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref _decorators; }
28+
29+
internal override Node? NextChildNode(ref ChildNodes.Enumerator enumerator) => enumerator.MoveNextNullableAt2(Decorators, Key, Value);
30+
31+
protected internal override object? Accept(AstVisitor visitor) => visitor.VisitAccessorProperty(this);
32+
33+
public AccessorProperty UpdateWith(Expression key, Expression? value, in NodeList<Decorator> decorators)
34+
{
35+
if (key == Key && value == Value && NodeList.AreSame(decorators, Decorators))
36+
{
37+
return this;
38+
}
39+
40+
return new AccessorProperty(key, Computed, value, Static, decorators);
41+
}
42+
}

src/Esprima/Ast/Nodes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public enum Nodes
7676
ExportAllDeclaration,
7777
ExportDefaultDeclaration,
7878
ClassExpression,
79+
AccessorProperty,
7980

8081
Extension = int.MaxValue
8182
};

src/Esprima/Character.Generated.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public static partial class Character
55
// see test case CanGenerateMasks
66
private static ReadOnlySpan<byte> _characterData => new byte[]
77
{
8-
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 6, 0, 0, 0,
8+
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0,
99
0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
1010
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 6, 0, 0, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
1111
6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

src/Esprima/JavascriptParser.cs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public void ReleaseLargeBuffers()
6868
public bool InIteration;
6969
public bool InSwitch;
7070
public bool InClassConstructor;
71+
public bool InClassBody;
7172
public bool Strict;
7273
public bool AllowIdentifierEscape;
7374

@@ -758,6 +759,8 @@ private protected virtual Expression ParsePrimaryExpression()
758759
expr = Finalize(node, new Literal(token.RegexValue!.Pattern, token.RegexValue.Flags, token.Value, raw));
759760
break;
760761
case "#":
762+
if (!_context.InClassBody)
763+
ThrowUnexpectedToken(_lookahead);
761764
NextToken();
762765
token = NextToken();
763766
expr = Finalize(node, new PrivateIdentifier((string) token.Value!));
@@ -958,6 +961,7 @@ private FunctionExpression ParsePropertyMethodFunction(bool isGenerator)
958961
var previousAllowYield = _context.AllowYield;
959962
_context.AllowYield = true;
960963
var parameters = ParseFormalParameters();
964+
_context.AllowYield = !isGenerator;
961965
var method = ParsePropertyMethod(ref parameters, out var hasStrictDirective);
962966
_context.AllowYield = previousAllowYield;
963967

@@ -3881,6 +3885,13 @@ private Statement ParseStatement()
38813885
case TokenType.Punctuator:
38823886
switch ((string?) _lookahead.Value)
38833887
{
3888+
case "#!":
3889+
ThrowUnexpectedToken(_lookahead);
3890+
statement = null;
3891+
break;
3892+
case "#":
3893+
statement = MatchAsyncFunction() ? ParseFunctionDeclaration() : ParseLabelledStatement();
3894+
break;
38843895
case "{":
38853896
statement = ParseBlock();
38863897
break;
@@ -4635,6 +4646,7 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
46354646
var isAsync = false;
46364647
var isGenerator = false;
46374648
var isPrivate = false;
4649+
var isAccessor = false;
46384650

46394651
var decorators = ParseDecorators();
46404652

@@ -4654,7 +4666,10 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
46544666
if (Match("#"))
46554667
{
46564668
isPrivate = true;
4669+
token = _lookahead;
46574670
NextToken();
4671+
if (token.End != _lookahead.Start)
4672+
ThrowUnexpectedToken(_lookahead);
46584673
token = _lookahead;
46594674
}
46604675
key = ParseObjectPropertyKey(isPrivate);
@@ -4675,11 +4690,16 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
46754690
computed = Match("[");
46764691
if (Match("*"))
46774692
{
4693+
isGenerator = true;
46784694
NextToken();
4695+
computed = Match("[");
46794696
if (Match("#"))
46804697
{
46814698
isPrivate = true;
4699+
token = _lookahead;
46824700
NextToken();
4701+
if (token.End != _lookahead.Start)
4702+
ThrowUnexpectedToken(_lookahead);
46834703
token = _lookahead;
46844704
}
46854705
}
@@ -4688,11 +4708,14 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
46884708
if (Match("#"))
46894709
{
46904710
isPrivate = true;
4711+
token = _lookahead;
46914712
NextToken();
4713+
if (token.End != _lookahead.Start)
4714+
ThrowUnexpectedToken(_lookahead);
46924715
token = _lookahead;
46934716
}
4694-
key = ParseObjectPropertyKey();
46954717
}
4718+
key = ParseObjectPropertyKey();
46964719
}
46974720
else if (Match("{"))
46984721
{
@@ -4714,7 +4737,10 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
47144737
if (Match("#"))
47154738
{
47164739
isPrivate = true;
4740+
token = _lookahead;
47174741
NextToken();
4742+
if (token.End != _lookahead.Start)
4743+
ThrowUnexpectedToken(_lookahead);
47184744
}
47194745

47204746
token = _lookahead;
@@ -4728,6 +4754,21 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
47284754
}
47294755
}
47304756

4757+
if (object.Equals(token.Value, "accessor") && (_lookahead.Type == TokenType.Identifier || object.Equals(_lookahead.Value, "#")))
4758+
{
4759+
isAccessor = true;
4760+
if (Match("#"))
4761+
{
4762+
isPrivate = true;
4763+
token = _lookahead;
4764+
NextToken();
4765+
if (token.End != _lookahead.Start)
4766+
ThrowUnexpectedToken(_lookahead);
4767+
token = _lookahead;
4768+
}
4769+
key = ParseObjectPropertyKey(isPrivate);
4770+
}
4771+
47314772
var lookaheadPropertyKey = QualifiedPropertyName(_lookahead);
47324773
if (token.Type == TokenType.Identifier)
47334774
{
@@ -4737,7 +4778,10 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
47374778
if (Match("#"))
47384779
{
47394780
isPrivate = true;
4781+
token = _lookahead;
47404782
NextToken();
4783+
if (token.End != _lookahead.Start)
4784+
ThrowUnexpectedToken(_lookahead);
47414785
token = _lookahead;
47424786
}
47434787
computed = Match("[");
@@ -4751,7 +4795,10 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
47514795
if (Match("#"))
47524796
{
47534797
isPrivate = true;
4798+
token = _lookahead;
47544799
NextToken();
4800+
if (token.End != _lookahead.Start)
4801+
ThrowUnexpectedToken(_lookahead);
47554802
token = _lookahead;
47564803
}
47574804
computed = Match("[");
@@ -4776,7 +4823,10 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
47764823
if (Match("#"))
47774824
{
47784825
isPrivate = true;
4826+
token = _lookahead;
47794827
NextToken();
4828+
if (token.End != _lookahead.Start)
4829+
ThrowUnexpectedToken(_lookahead);
47804830
token = _lookahead;
47814831
}
47824832
computed = Match("[");
@@ -4813,7 +4863,7 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
48134863

48144864
if (!computed)
48154865
{
4816-
if (isStatic && IsPropertyKey(key!, "prototype"))
4866+
if (isStatic && !isPrivate && IsPropertyKey(key!, "prototype"))
48174867
{
48184868
ThrowUnexpectedToken(token, Messages.StaticPrototype);
48194869
}
@@ -4838,6 +4888,12 @@ private ClassElement ParseClassElement(ref bool hasConstructor)
48384888
}
48394889
}
48404890

4891+
if (isAccessor)
4892+
{
4893+
ConsumeSemicolon();
4894+
return Finalize(node, new AccessorProperty(key!, computed, value!, isStatic, NodeList.From(ref decorators)));
4895+
}
4896+
48414897
if (kind == PropertyKind.Property)
48424898
{
48434899
ConsumeSemicolon();
@@ -4873,7 +4929,10 @@ private ArrayList<ClassElement> ParseClassElementList()
48734929
private ClassBody ParseClassBody()
48744930
{
48754931
var node = CreateNode();
4932+
var previousInClassBody = _context.InClassBody;
4933+
_context.InClassBody = true;
48764934
var elementList = ParseClassElementList();
4935+
_context.InClassBody = previousInClassBody;
48774936

48784937
return Finalize(node, new ClassBody(NodeList.From(ref elementList)));
48794938
}

src/Esprima/Scanner.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,14 @@ private Token ScanPunctuator()
874874

875875
break;
876876

877+
case '#':
878+
++_index;
879+
if (_source.Length >= _index + 1 && _source[_index] == '!')
880+
{
881+
_index += 1;
882+
str = "#!";
883+
}
884+
break;
877885
case ')':
878886
case ';':
879887
case ',':

src/Esprima/Utils/AstToJavascriptConverter.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,45 @@ binaryExpression.Right is UnaryExpression rightUnaryExpression &&
13951395
return propertyDefinition;
13961396
}
13971397

1398+
protected internal override object? VisitAccessorProperty(AccessorProperty accessorProperty)
1399+
{
1400+
if (accessorProperty.Decorators.Count > 0)
1401+
{
1402+
_writeContext.SetNodeProperty(nameof(accessorProperty.Decorators), static node => ref node.As<AccessorProperty>().Decorators);
1403+
VisitAuxiliaryNodeList(accessorProperty.Decorators, separator: string.Empty);
1404+
1405+
_writeContext.ClearNodeProperty();
1406+
}
1407+
1408+
if (accessorProperty.Static)
1409+
{
1410+
_writeContext.SetNodeProperty(nameof(accessorProperty.Static), static node => node.As<AccessorProperty>().Static);
1411+
Writer.WriteKeyword("static", TokenFlags.SurroundingSpaceRecommended, ref _writeContext);
1412+
}
1413+
else
1414+
{
1415+
Writer.SpaceRecommendedAfterLastToken();
1416+
}
1417+
1418+
Writer.WriteKeyword("accessor", TokenFlags.SurroundingSpaceRecommended, ref _writeContext);
1419+
1420+
_writeContext.SetNodeProperty(nameof(accessorProperty.Key), static node => node.As<AccessorProperty>().Key);
1421+
VisitPropertyKey(accessorProperty.Key, accessorProperty.Computed, leadingBracketFlags: TokenFlags.LeadingSpaceRecommended);
1422+
1423+
if (accessorProperty.Value is not null)
1424+
{
1425+
_writeContext.ClearNodeProperty();
1426+
Writer.WritePunctuator("=", TokenFlags.InBetween | TokenFlags.SurroundingSpaceRecommended, ref _writeContext);
1427+
1428+
_writeContext.SetNodeProperty(nameof(accessorProperty.Value), static node => node.As<AccessorProperty>().Value);
1429+
VisitRootExpression(accessorProperty.Value, RootExpressionFlags(needsBrackets: ExpressionNeedsBracketsInList(accessorProperty.Value)));
1430+
}
1431+
1432+
Writer.WritePunctuator(";", TokenFlags.Trailing | TokenFlags.TrailingSpaceRecommended, ref _writeContext);
1433+
1434+
return accessorProperty;
1435+
}
1436+
13981437
protected internal override object? VisitRestElement(RestElement restElement)
13991438
{
14001439
_writeContext.SetNodeProperty(nameof(restElement.Argument), static node => node.As<RestElement>().Argument);

src/Esprima/Utils/AstToJsonConverter.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,24 @@ public ImportCompat() : base(Nodes.Import) { }
974974
return propertyDefinition;
975975
}
976976

977+
protected internal override object? VisitAccessorProperty(AccessorProperty accessorProperty)
978+
{
979+
using (StartNodeObject(accessorProperty))
980+
{
981+
Member("key", accessorProperty.Key);
982+
Member("computed", accessorProperty.Computed);
983+
Member("value", accessorProperty.Value);
984+
Member("kind", accessorProperty.Kind);
985+
Member("static", accessorProperty.Static);
986+
if (accessorProperty.Decorators.Count > 0)
987+
{
988+
Member("decorators", accessorProperty.Decorators);
989+
}
990+
}
991+
992+
return accessorProperty;
993+
}
994+
977995
protected internal override object? VisitRestElement(RestElement restElement)
978996
{
979997
using (StartNodeObject(restElement))

src/Esprima/Utils/AstVisitor.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,24 @@ private static Exception UnsupportedNodeType(Type nodeType, [CallerMemberName] s
602602
return propertyDefinition;
603603
}
604604

605+
protected internal virtual object? VisitAccessorProperty(AccessorProperty accessorProperty)
606+
{
607+
ref readonly var decorators = ref accessorProperty.Decorators;
608+
for (var i = 0; i < decorators.Count; i++)
609+
{
610+
Visit(decorators[i]);
611+
}
612+
613+
Visit(accessorProperty.Key);
614+
615+
if (accessorProperty.Value is not null)
616+
{
617+
Visit(accessorProperty.Value);
618+
}
619+
620+
return accessorProperty;
621+
}
622+
605623
protected internal virtual object? VisitRestElement(RestElement restElement)
606624
{
607625
Visit(restElement.Argument);

src/Esprima/Utils/AstVisitorEventSource.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ public class AstVisitorEventSource : AstVisitor
112112
public event EventHandler<Property>? VisitedProperty;
113113
public event EventHandler<PropertyDefinition>? VisitingPropertyDefinition;
114114
public event EventHandler<PropertyDefinition>? VisitedPropertyDefinition;
115+
public event EventHandler<AccessorProperty>? VisitingAccessorProperty;
116+
public event EventHandler<AccessorProperty>? VisitedAccessorProperty;
115117
public event EventHandler<RestElement>? VisitingRestElement;
116118
public event EventHandler<RestElement>? VisitedRestElement;
117119
public event EventHandler<ReturnStatement>? VisitingReturnStatement;
@@ -569,6 +571,14 @@ public class AstVisitorEventSource : AstVisitor
569571
return result;
570572
}
571573

574+
protected internal override object? VisitAccessorProperty(AccessorProperty accessorProperty)
575+
{
576+
VisitingAccessorProperty?.Invoke(this, accessorProperty);
577+
var result = base.VisitAccessorProperty(accessorProperty);
578+
VisitedAccessorProperty?.Invoke(this, accessorProperty);
579+
return result;
580+
}
581+
572582
protected internal override object? VisitRestElement(RestElement restElement)
573583
{
574584
VisitingRestElement?.Invoke(this, restElement);

0 commit comments

Comments
 (0)