Skip to content

support es2022 class properties & private properties #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Esprima/Ast/Nodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ public enum Nodes
MemberExpression,
NewExpression,
ObjectExpression,
PrivateIdentifier,
Program,
Property,
PropertyDefinition,
RestElement,
ReturnStatement,
SequenceExpression,
Expand Down
21 changes: 21 additions & 0 deletions src/Esprima/Ast/PrivateIdentifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Esprima.Utils;

namespace Esprima.Ast
{
public sealed class PrivateIdentifier : Expression
{
public readonly string? Name;

public PrivateIdentifier(string? name) : base(Nodes.PrivateIdentifier)
{
Name = name;
}

public override NodeCollection ChildNodes => NodeCollection.Empty;

protected internal override void Accept(AstVisitor visitor)
{
visitor.VisitPrivateIdentifier(this);
}
}
}
27 changes: 27 additions & 0 deletions src/Esprima/Ast/PropertyDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Esprima.Utils;

namespace Esprima.Ast
{
public sealed class PropertyDefinition : ClassProperty
{
public readonly bool Static;

public PropertyDefinition(
Expression key,
bool computed,
Expression value,
bool isStatic)
: base(Nodes.PropertyDefinition)
{
Static = isStatic;
Key = key;
Computed = computed;
Value = value;
}

protected internal override void Accept(AstVisitor visitor)
{
visitor.VisitPropertyDefinition(this);
}
}
}
3 changes: 2 additions & 1 deletion src/Esprima/Ast/PropertyKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public enum PropertyKind
Set = 4,
Init = 8,
Constructor = 16,
Method = 32
Method = 32,
Property = 64
};
}
125 changes: 103 additions & 22 deletions src/Esprima/JavascriptParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,10 @@ private Expression ParsePrimaryExpression()
{
expr = ParseClassExpression();
}
else if (MatchKeyword("new"))
{
expr = ParseNewExpression();
}
else if (MatchImportCall())
{
expr = ParseImportCall();
Expand Down Expand Up @@ -834,7 +838,7 @@ private FunctionExpression ParsePropertyMethodAsyncFunction(bool isGenerator)
return Finalize(node, new FunctionExpression(null, NodeList.From(ref parameters.Parameters), method, isGenerator, hasStrictDirective, true));
}

private Expression ParseObjectPropertyKey()
private Expression ParseObjectPropertyKey(Boolean isPrivate = false)
{
var node = CreateNode();
var token = NextToken();
Expand Down Expand Up @@ -866,7 +870,7 @@ private Expression ParseObjectPropertyKey()
case TokenType.BooleanLiteral:
case TokenType.NullLiteral:
case TokenType.Keyword:
key = Finalize(node, new Identifier((string?) token.Value));
key = isPrivate ? Finalize(node, new PrivateIdentifier((string?) token.Value)) : Finalize(node, new Identifier((string?) token.Value));
break;

case TokenType.Punctuator:
Expand Down Expand Up @@ -964,7 +968,7 @@ private Property ParseObjectProperty(Token hasProto)
kind = PropertyKind.Init;
computed = Match("[");
key = ParseObjectPropertyKey();
value = ParseGeneratorMethod();
value = ParseGeneratorMethod(isAsync);
method = true;
}
else
Expand Down Expand Up @@ -1420,6 +1424,29 @@ private Identifier ParseIdentifierName()
return Finalize(node, new Identifier((string?) token.Value));
}

private Expression ParseIdentifierOrPrivateIdentifierName()
{
var isPrivateField = false;

var node = CreateNode();

var token = NextToken();

if (Equals(token.Value, "#"))
{
token = NextToken();
token.Value = '#' + (string?)token.Value;
isPrivateField = true;
}

if (!IsIdentifierName(token))
{
return ThrowUnexpectedToken<Identifier>(token);
}

return isPrivateField ? Finalize(node, new PrivateIdentifier((string?) token.Value)) : Finalize(node, new Identifier((string?) token.Value));
}

private Expression ParseNewExpression()
{
var node = CreateNode();
Expand Down Expand Up @@ -1672,7 +1699,7 @@ private Expression ParseLeftHandSideExpressionAllowCall()
Expect(".");
}

var property = ParseIdentifierName();
var property = ParseIdentifierOrPrivateIdentifierName();
expr = Finalize(StartNode(startToken), new StaticMemberExpression(expr, property, optional));
}
else
Expand Down Expand Up @@ -4164,7 +4191,7 @@ private static bool QualifiedPropertyName(Token token)
TokenType.NullLiteral => true,
TokenType.NumericLiteral => true,
TokenType.Keyword => true,
TokenType.Punctuator => Equals(token.Value, "["),
TokenType.Punctuator => Equals(token.Value, "[") || Equals(token.Value, "#"),
_ => false
};
}
Expand Down Expand Up @@ -4212,7 +4239,7 @@ private FunctionExpression ParseSetterMethod()
return Finalize(node, new FunctionExpression(null, NodeList.From(ref formalParameters.Parameters), method, isGenerator, hasStrictDirective, false));
}

private FunctionExpression ParseGeneratorMethod()
private FunctionExpression ParseGeneratorMethod(bool isAsync = false)
{
var node = CreateNode();

Expand All @@ -4224,7 +4251,7 @@ private FunctionExpression ParseGeneratorMethod()
var method = ParsePropertyMethod(parameters, out var hasStrictDirective);
_context.AllowYield = previousAllowYield;

return Finalize(node, new FunctionExpression(null, NodeList.From(ref parameters.Parameters), method, true, hasStrictDirective, false));
return Finalize(node, new FunctionExpression(null, NodeList.From(ref parameters.Parameters), method, true, hasStrictDirective, isAsync));
}

// https://tc39.github.io/ecma262/#sec-generator-function-definitions
Expand Down Expand Up @@ -4321,12 +4348,13 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)

var kind = PropertyKind.None;
Expression? key = null;
FunctionExpression? value = null;
Expression? value = null;
var computed = false;
var method = false;
var isStatic = false;
var isAsync = false;
var isGenerator = false;
var isGenerator = false;
var isPrivate = false;

if (Match("*"))
{
Expand All @@ -4336,10 +4364,17 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
else
{
computed = Match("[");
key = ParseObjectPropertyKey();
if (Match("#"))
{
isPrivate = true;
NextToken();
token = _lookahead;
}
key = ParseObjectPropertyKey(isPrivate);
var id = key switch
{
Identifier identifier => identifier.Name,
PrivateIdentifier privateIdentifier => privateIdentifier.Name,
Literal literal => literal.StringValue, // "constructor"
_ => null
};
Expand All @@ -4352,9 +4387,21 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
if (Match("*"))
{
NextToken();
if (Match("#"))
{
isPrivate = true;
NextToken();
token = _lookahead;
}
}
else
{
if (Match("#"))
{
isPrivate = true;
NextToken();
token = _lookahead;
}
key = ParseObjectPropertyKey();
}
}
Expand All @@ -4368,11 +4415,17 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
if (isGenerator)
{
NextToken();
}

if (Match("#"))
{
isPrivate = true;
NextToken();
}

token = _lookahead;
computed = Match("[");
key = ParseObjectPropertyKey();
key = ParseObjectPropertyKey(isPrivate);
if (token.Type == TokenType.Identifier && (string?) token.Value == "constructor")
{
TolerateUnexpectedToken(token, Messages.ConstructorIsAsync);
Expand All @@ -4386,26 +4439,49 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
{
if (lookaheadPropertyKey && (string?) token.Value == "get")
{
kind = PropertyKind.Get;
kind = PropertyKind.Get;
if (Match("#"))
{
isPrivate = true;
NextToken();
token = _lookahead;
}
computed = Match("[");
key = ParseObjectPropertyKey();
key = ParseObjectPropertyKey(isPrivate);
_context.AllowYield = false;
value = ParseGetterMethod();
}
else if (lookaheadPropertyKey && (string?) token.Value == "set")
{
kind = PropertyKind.Set;
if (Match("#"))
{
isPrivate = true;
NextToken();
token = _lookahead;
}
computed = Match("[");
key = ParseObjectPropertyKey();
value = ParseSetterMethod();
key = ParseObjectPropertyKey(isPrivate);
value = ParseSetterMethod();
}
else if (!Match("("))
{
kind = PropertyKind.Property;
computed = false;

if (Match("="))
{
NextToken();
value = IsolateCoverGrammar(this.parseAssignmentExpression);
}
}
}
else if (token.Type == TokenType.Punctuator && (string?) token.Value == "*" && lookaheadPropertyKey)
{
kind = PropertyKind.Init;
computed = Match("[");
key = ParseObjectPropertyKey();
value = ParseGeneratorMethod();
key = ParseObjectPropertyKey(isPrivate);
value = ParseGeneratorMethod(isAsync);
method = true;
}

Expand Down Expand Up @@ -4441,7 +4517,7 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)

if (!isStatic && IsPropertyKey(key!, "constructor"))
{
if (kind != PropertyKind.Method || !method || value!.Generator)
if (kind != PropertyKind.Method || !method || ((FunctionExpression)value!).Generator)
{
ThrowUnexpectedToken(token, Messages.ConstructorSpecialMethod);
}
Expand All @@ -4457,10 +4533,15 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)

kind = PropertyKind.Constructor;
}
}


return Finalize(node, new MethodDefinition(key!, computed, value!, kind, isStatic));
}

if (kind == PropertyKind.Property)
{
ConsumeSemicolon();
return Finalize(node, new PropertyDefinition(key!, computed, value!, isStatic));
}

return Finalize(node, new MethodDefinition(key!, computed, (FunctionExpression)value!, kind, isStatic));
}

private ArrayList<ClassProperty> ParseClassElementList()
Expand Down
Loading