Skip to content

Commit 6954aeb

Browse files
authored
Add more class parsing validation (#367)
* centralize class method parsing logic
1 parent 6b15c5a commit 6954aeb

File tree

5 files changed

+74
-155
lines changed

5 files changed

+74
-155
lines changed

src/Esprima/JavascriptParser.cs

Lines changed: 69 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public void Reset()
3232
FirstCoverInitializedNameError = null;
3333
IsAssignmentTarget = false;
3434
IsBindingElement = false;
35+
InClassFieldInit = false;
36+
InClassStaticBlock = false;
3537
InFunctionBody = false;
3638
InIteration = false;
3739
InSwitch = false;
@@ -65,6 +67,8 @@ public void ReleaseLargeBuffers()
6567
public bool IsAsync;
6668
public bool IsAssignmentTarget;
6769
public bool IsBindingElement;
70+
public bool InClassFieldInit;
71+
public bool InClassStaticBlock;
6872
public bool InFunctionBody;
6973
public bool InIteration;
7074
public bool InSwitch;
@@ -297,10 +301,10 @@ private protected string GetTokenRaw(in Token token)
297301
case TokenType.BooleanLiteral:
298302
var stringValue = (string) token.Value!;
299303
// Identifiers may contain escaped characters.
300-
// (In tolerant mode even identifers like "nul\u{6c}" may be accepted as keywords, null or boolean literals.)
301-
if (stringValue.Length == token.End - token.Start)
304+
// (In tolerant mode even identifiers like "nul\u{6c}" may be accepted as keywords, null or boolean literals.)
305+
if (!token.IsEscaped(stringValue))
302306
{
303-
return (string) token.Value!;
307+
return stringValue;
304308
}
305309
break;
306310

@@ -680,6 +684,10 @@ private protected virtual Expression ParsePrimaryExpression()
680684
{
681685
TolerateUnexpectedToken(_lookahead);
682686
}
687+
if ((_context.InClassFieldInit || _context.InClassStaticBlock) && "arguments".Equals(_lookahead._value))
688+
{
689+
TolerateError(Messages.ArgumentsNotAllowedInClassField);
690+
}
683691

684692
if (MatchAsyncFunction())
685693
{
@@ -934,8 +942,10 @@ private BlockStatement ParsePropertyMethod(ref ParsedParameters parameters, out
934942
_context.IsAssignmentTarget = false;
935943
_context.IsBindingElement = false;
936944

945+
var previousInClassStaticBlock = _context.InClassStaticBlock;
937946
var previousStrict = _context.Strict;
938947
var previousAllowStrictDirective = _context.AllowStrictDirective;
948+
_context.InClassStaticBlock = false;
939949
_context.AllowStrictDirective = parameters.Simple;
940950
var body = IsolateCoverGrammar(_parseFunctionSourceElements);
941951
hasStrictDirective = _context.Strict;
@@ -951,6 +961,7 @@ private BlockStatement ParsePropertyMethod(ref ParsedParameters parameters, out
951961

952962
_context.Strict = previousStrict;
953963
_context.AllowStrictDirective = previousAllowStrictDirective;
964+
_context.InClassStaticBlock = previousInClassStaticBlock;
954965

955966
return body;
956967
}
@@ -959,11 +970,13 @@ private FunctionExpression ParsePropertyMethodFunction(bool isAsync, bool isGene
959970
{
960971
var node = CreateNode();
961972

973+
var previousInClassStaticBlock = _context.InClassStaticBlock;
962974
var previousIsAsync = _context.IsAsync;
963975
var previousAllowYield = _context.AllowYield;
964976
var previousAllowSuperAccess = _context.AllowSuperAccess;
965977
var previousAllowSuperCall = _context.AllowSuperCall;
966978

979+
_context.InClassStaticBlock = false;
967980
_context.IsAsync = isAsync;
968981
_context.AllowYield = true;
969982
_context.AllowSuperAccess = true;
@@ -977,6 +990,7 @@ private FunctionExpression ParsePropertyMethodFunction(bool isAsync, bool isGene
977990
_context.AllowYield = previousAllowYield;
978991
_context.AllowSuperAccess = previousAllowSuperAccess;
979992
_context.AllowSuperCall = previousAllowSuperCall;
993+
_context.InClassStaticBlock = previousInClassStaticBlock;
980994

981995
return Finalize(node, new FunctionExpression(null, NodeList.From(ref parameters.Parameters), method, isGenerator, hasStrictDirective, isAsync));
982996
}
@@ -1008,11 +1022,10 @@ private Expression ParseObjectPropertyKey()
10081022
case TokenType.BooleanLiteral:
10091023
case TokenType.NullLiteral:
10101024
case TokenType.Keyword:
1011-
key = Finalize(node, new Identifier((string?) token.Value!));
1025+
key = Finalize(node, new Identifier((string) token.Value!));
10121026
break;
10131027

10141028
case TokenType.Punctuator when "[".Equals(token.Value):
1015-
10161029
key = IsolateCoverGrammar(_parseAssignmentExpression);
10171030
Expect("]");
10181031
break;
@@ -1609,7 +1622,7 @@ private Expression ParseNewExpression()
16091622
if (Match("."))
16101623
{
16111624
NextToken();
1612-
if (_lookahead.Type == TokenType.Identifier && _context.InFunctionBody && "target".Equals(_lookahead.Value))
1625+
if (_lookahead.Type == TokenType.Identifier && (_context.InFunctionBody || _context.InClassBody) && "target".Equals(_lookahead.Value))
16131626
{
16141627
var property = ParseIdentifierName();
16151628
expr = new MetaProperty(id, property);
@@ -1738,7 +1751,7 @@ private bool MatchImportMeta()
17381751
match = meta.Type == TokenType.Identifier && Equals(meta.Value, "meta");
17391752
if (match)
17401753
{
1741-
if (meta.End - meta.Start != "meta".Length)
1754+
if (meta.IsEscaped("meta"))
17421755
{
17431756
TolerateUnexpectedToken(meta, Messages.InvalidEscapedReservedWord);
17441757
}
@@ -2075,7 +2088,7 @@ private Expression ParseUnaryExpression()
20752088
}
20762089
else if (_context.IsAsync && MatchContextualKeyword("await"))
20772090
{
2078-
if (_lookahead.End - _lookahead.Start != "await".Length)
2091+
if (_lookahead.IsEscaped("await"))
20792092
{
20802093
TolerateUnexpectedToken(_lookahead, Messages.InvalidEscapedReservedWord);
20812094
}
@@ -4274,8 +4287,10 @@ private FunctionDeclaration ParseFunctionDeclaration(bool identifierIsOptional =
42744287
}
42754288
}
42764289

4290+
var previousInClassStaticBlock = _context.InClassStaticBlock;
42774291
var previousIsAsync = _context.IsAsync;
42784292
var previousAllowYield = _context.AllowYield;
4293+
_context.InClassStaticBlock = false;
42794294
_context.IsAsync = isAsync;
42804295
_context.AllowYield = !isGenerator;
42814296

@@ -4307,6 +4322,7 @@ private FunctionDeclaration ParseFunctionDeclaration(bool identifierIsOptional =
43074322
_context.Strict = previousStrict;
43084323
_context.IsAsync = previousIsAsync;
43094324
_context.AllowYield = previousAllowYield;
4325+
_context.InClassStaticBlock = previousInClassStaticBlock;
43104326

43114327
var functionDeclaration = Finalize(node, new FunctionDeclaration(id, parameters, body, isGenerator, hasStrictDirective, isAsync));
43124328
return functionDeclaration;
@@ -4334,8 +4350,10 @@ private FunctionExpression ParseFunctionExpression()
43344350
Expression? id = null;
43354351
Token? firstRestricted = null;
43364352

4353+
var previousInClassStaticBlock = _context.InClassStaticBlock;
43374354
var previousIsAsync = _context.IsAsync;
43384355
var previousAllowYield = _context.AllowYield;
4356+
_context.InClassStaticBlock = false;
43394357
_context.IsAsync = isAsync;
43404358
_context.AllowYield = !isGenerator;
43414359

@@ -4396,6 +4414,7 @@ private FunctionExpression ParseFunctionExpression()
43964414
_context.AllowStrictDirective = previousAllowStrictDirective;
43974415
_context.IsAsync = previousIsAsync;
43984416
_context.AllowYield = previousAllowYield;
4417+
_context.InClassStaticBlock = previousInClassStaticBlock;
43994418

44004419
return Finalize(node, new FunctionExpression((Identifier?) id, parameters, body, isGenerator, hasStrictDirective, isAsync));
44014420
}
@@ -4485,90 +4504,66 @@ TokenType.NumericLiteral or
44854504

44864505
private FunctionExpression ParseGetterMethod()
44874506
{
4488-
var node = CreateNode();
4489-
4490-
var previousIsAsync = _context.IsAsync;
4491-
var previousAllowYield = _context.AllowYield;
4492-
var previousAllowSuperAccess = _context.AllowSuperAccess;
4493-
var previousAllowSuperCall = _context.AllowSuperCall;
4494-
4495-
_context.IsAsync = false;
4496-
_context.AllowYield = true;
4497-
_context.AllowSuperAccess = true;
4498-
_context.AllowSuperCall = false;
4499-
var formalParameters = ParseFormalParameters();
4500-
if (formalParameters.Parameters.Count > 0)
4507+
var method = ParseMethod(isAsync: false, generator: false);
4508+
if (method.Params.Count > 0)
45014509
{
45024510
TolerateError(Messages.BadGetterArity);
45034511
}
45044512

4505-
var method = ParsePropertyMethod(ref formalParameters, out var hasStrictDirective);
4506-
4507-
_context.IsAsync = previousIsAsync;
4508-
_context.AllowYield = previousAllowYield;
4509-
_context.AllowSuperAccess = previousAllowSuperAccess;
4510-
_context.AllowSuperCall = previousAllowSuperCall;
4511-
4512-
return Finalize(node, new FunctionExpression(null, NodeList.From(ref formalParameters.Parameters), method, generator: false, hasStrictDirective, false));
4513+
return method;
45134514
}
45144515

45154516
private FunctionExpression ParseSetterMethod()
45164517
{
4517-
var node = CreateNode();
4518+
var method = ParseMethod(isAsync: false, generator: false);
45184519

4519-
var previousIsAsync = _context.IsAsync;
4520-
var previousAllowYield = _context.AllowYield;
4521-
var previousAllowSuperAccess = _context.AllowSuperAccess;
4522-
var previousAllowSuperCall = _context.AllowSuperCall;
4523-
4524-
_context.IsAsync = false;
4525-
_context.AllowYield = true;
4526-
_context.AllowSuperAccess = true;
4527-
_context.AllowSuperCall = false;
4528-
var formalParameters = ParseFormalParameters();
4529-
if (formalParameters.Parameters.Count != 1)
4520+
ref readonly var parameters = ref method.Params;
4521+
if (parameters.Count != 1)
45304522
{
45314523
TolerateError(Messages.BadSetterArity);
45324524
}
4533-
else if (formalParameters.Parameters[0] is RestElement)
4525+
else if (parameters[0] is RestElement)
45344526
{
45354527
TolerateError(Messages.BadSetterRestParameter);
45364528
}
45374529

4538-
var method = ParsePropertyMethod(ref formalParameters, out var hasStrictDirective);
4539-
4540-
_context.IsAsync = previousIsAsync;
4541-
_context.AllowYield = previousAllowYield;
4542-
_context.AllowSuperAccess = previousAllowSuperAccess;
4543-
_context.AllowSuperCall = previousAllowSuperCall;
4544-
4545-
return Finalize(node, new FunctionExpression(null, NodeList.From(ref formalParameters.Parameters), method, generator: false, hasStrictDirective, false));
4530+
return method;
45464531
}
45474532

45484533
private FunctionExpression ParseGeneratorMethod(bool isAsync)
4534+
{
4535+
return ParseMethod(isAsync, generator: true);
4536+
}
4537+
4538+
private FunctionExpression ParseMethod(bool isAsync, bool generator)
45494539
{
45504540
var node = CreateNode();
45514541

4542+
var previousInClassStaticBlock = _context.InClassStaticBlock;
45524543
var previousIsAsync = _context.IsAsync;
45534544
var previousAllowYield = _context.AllowYield;
45544545
var previousAllowSuperAccess = _context.AllowSuperAccess;
45554546
var previousAllowSuperCall = _context.AllowSuperCall;
45564547

4548+
_context.InClassStaticBlock = false;
45574549
_context.IsAsync = isAsync;
45584550
_context.AllowYield = true;
45594551
_context.AllowSuperAccess = true;
45604552
_context.AllowSuperCall = false;
4561-
var parameters = ParseFormalParameters();
45624553

4563-
_context.AllowYield = false;
4564-
var method = ParsePropertyMethod(ref parameters, out var hasStrictDirective);
4554+
var formalParameters = ParseFormalParameters();
4555+
4556+
_context.AllowYield = !generator;
4557+
4558+
var method = ParsePropertyMethod(ref formalParameters, out var hasStrictDirective);
45654559

45664560
_context.IsAsync = previousIsAsync;
45674561
_context.AllowYield = previousAllowYield;
45684562
_context.AllowSuperAccess = previousAllowSuperAccess;
45694563
_context.AllowSuperCall = previousAllowSuperCall;
4564+
_context.InClassStaticBlock = previousInClassStaticBlock;
45704565

4571-
return Finalize(node, new FunctionExpression(null, NodeList.From(ref parameters.Parameters), method, generator: true, hasStrictDirective, isAsync));
4566+
return Finalize(node, new FunctionExpression(null, NodeList.From(ref formalParameters.Parameters), method, generator, hasStrictDirective, isAsync));
45724567
}
45734568

45744569
// https://tc39.github.io/ecma262/#sec-generator-function-definitions
@@ -4636,8 +4631,10 @@ private StaticBlock ParseStaticBlock()
46364631

46374632
Expect("{");
46384633

4634+
var previousInClassStaticBlock = _context.InClassStaticBlock;
46394635
var previousAllowSuperAccess = _context.AllowSuperAccess;
46404636
var previousAllowSuperCall = _context.AllowSuperCall;
4637+
_context.InClassStaticBlock = true;
46414638
_context.AllowSuperAccess = true;
46424639
_context.AllowSuperCall = false;
46434640

@@ -4654,6 +4651,7 @@ private StaticBlock ParseStaticBlock()
46544651

46554652
_context.AllowSuperAccess = previousAllowSuperAccess;
46564653
_context.AllowSuperCall = previousAllowSuperCall;
4654+
_context.InClassStaticBlock = previousInClassStaticBlock;
46574655

46584656
Expect("}");
46594657

@@ -4713,6 +4711,8 @@ private ClassElement ParseClassElement(bool hasSuperClass, ref bool hasConstruct
47134711
var isPrivate = false;
47144712
var isAccessor = false;
47154713

4714+
var escapedStatic = false;
4715+
47164716
isGenerator = Match("*");
47174717
if (isGenerator)
47184718
{
@@ -4741,6 +4741,7 @@ private ClassElement ParseClassElement(bool hasSuperClass, ref bool hasConstruct
47414741
{
47424742
NextToken();
47434743
isStatic = true;
4744+
escapedStatic = token.IsEscaped("static");
47444745
kind = PropertyKind.Method;
47454746
goto ParseKey;
47464747
}
@@ -4750,6 +4751,7 @@ private ClassElement ParseClassElement(bool hasSuperClass, ref bool hasConstruct
47504751
goto ParseValue;
47514752
}
47524753

4754+
escapedStatic = token.IsEscaped("static");
47534755
isStatic = true;
47544756
}
47554757

@@ -4758,6 +4760,11 @@ private ClassElement ParseClassElement(bool hasSuperClass, ref bool hasConstruct
47584760
goto ParseKey;
47594761
}
47604762

4763+
if (MatchContextualKeyword("async") && _lookahead.IsEscaped("async"))
4764+
{
4765+
TolerateError(Messages.InvalidEscapedReservedWord);
4766+
}
4767+
47614768
switch ((string?) _lookahead.Value)
47624769
{
47634770
case "async":
@@ -4825,6 +4832,11 @@ private ClassElement ParseClassElement(bool hasSuperClass, ref bool hasConstruct
48254832
kind = Match("(") ? PropertyKind.Method : PropertyKind.Property;
48264833
}
48274834

4835+
if (isStatic && escapedStatic)
4836+
{
4837+
TolerateError(Messages.InvalidEscapedReservedWord);
4838+
}
4839+
48284840
if (!computed && !isPrivate)
48294841
{
48304842
if (isStatic)
@@ -4895,15 +4907,14 @@ private ClassElement ParseClassElement(bool hasSuperClass, ref bool hasConstruct
48954907
if (Match("="))
48964908
{
48974909
NextToken();
4898-
if (_lookahead.Type == TokenType.Identifier && (string?) _lookahead.Value == "arguments")
4899-
{
4900-
ThrowUnexpectedToken(_lookahead, Messages.ArgumentsNotAllowedInClassField);
4901-
}
49024910
var previousAllowSuperAccess = _context.AllowSuperAccess;
49034911
var previousAllowSuperCall = _context.AllowSuperCall;
4912+
var previousInClassFieldInit = _context.InClassFieldInit;
49044913
_context.AllowSuperAccess = true;
49054914
_context.AllowSuperCall = false;
4915+
_context.InClassFieldInit = true;
49064916
value = IsolateCoverGrammar(_parseAssignmentExpression);
4917+
_context.InClassFieldInit = previousInClassFieldInit;
49074918
_context.AllowSuperAccess = previousAllowSuperAccess;
49084919
_context.AllowSuperCall = previousAllowSuperCall;
49094920
}

src/Esprima/Messages.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public static class Messages
88
public const string BadGetterArity = "Getter must not have any formal parameters";
99
public const string BadSetterArity = "Setter must have exactly one formal parameter";
1010
public const string BadSetterRestParameter = "Setter function argument must not be a rest parameter";
11+
public const string CannotUseAwaitInClassStaticBlock = "Cannot use await in class static initialization block";
1112
public const string CannotUseImportMetaOutsideAModule = "Cannot use 'import.meta' outside a module";
1213
public const string CannotUseImportWithNew = "Cannot use new with import";
1314
public const string ConstructorIsAccessor = "Class constructor may not be an accessor";

src/Esprima/Token.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Configuration;
2-
using System.Numerics;
1+
using System.Numerics;
32
using System.Runtime.CompilerServices;
43
using System.Runtime.InteropServices;
54
using System.Text.RegularExpressions;
@@ -154,4 +153,6 @@ internal Token ChangeType(TokenType newType)
154153
{
155154
return new Token(newType, _value, Start, End, LineNumber, LineStart, Octal);
156155
}
156+
157+
internal bool IsEscaped(string value) => value.Length != End - Start;
157158
}

0 commit comments

Comments
 (0)