Skip to content
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
3 changes: 2 additions & 1 deletion benchmarks/Acornima.Benchmarks/FileParsingBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public class FileParsingBenchmark
{ "jquery-1.9.1", null! },
{ "yui-3.12.0", null! },
{ "jquery.mobile-1.4.2", null! },
{ "angular-1.2.5", null! }
{ "angular-1.2.5", null! },
{ "angular-1.7.9", null! }
};

private Parser _acornimaParser = null!;
Expand Down
3 changes: 3 additions & 0 deletions src/Acornima/Parser.Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ internal Expression ParseMaybeAssign(ref DestructuringErrors destructuringErrors
}

// Parse a ternary conditional (`?:`) operator.
[MethodImpl((MethodImplOptions)512 /* AggressiveOptimization */)]
private Expression ParseMaybeConditional(ref DestructuringErrors destructuringErrors, ExpressionContext context)
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/expression.js > `pp.parseMaybeConditional = function`
Expand All @@ -279,6 +280,7 @@ private Expression ParseMaybeConditional(ref DestructuringErrors destructuringEr
}

// Start the precedence parser.
[MethodImpl((MethodImplOptions)512 /* AggressiveOptimization */)]
private Expression ParseMaybeBinary(ref DestructuringErrors destructuringErrors, ExpressionContext context)
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/expression.js > `pp.parseExprOps = function`
Expand Down Expand Up @@ -595,6 +597,7 @@ private Expression ParseExprSubscripts(ref DestructuringErrors destructuringErro
return result;
}

[MethodImpl((MethodImplOptions)512 /* AggressiveOptimization */)]
private Expression ParseSubscripts(in Marker startMarker, Expression baseExpr, bool noCalls, ExpressionContext context)
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/expression.js > `pp.parseSubscripts = function`
Expand Down
2 changes: 2 additions & 0 deletions src/Acornima/Parser.ParseUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal bool Eat(TokenType type)
}

// Tests whether parsed token is a contextual keyword.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsContextual(string name)
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/parseutil.js > `pp.isContextual = function`
Expand Down Expand Up @@ -65,6 +66,7 @@ private void ExpectContextual(string name)
}

// Test whether a semicolon can be inserted at the current position.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CanInsertSemicolon()
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/parseutil.js > `pp.canInsertSemicolon = function`
Expand Down
81 changes: 43 additions & 38 deletions src/Acornima/Parser.Statement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ private bool IsUsingKeyword(bool isFor, out VariableDeclarationKind kind)
// regular expression literal. This is to handle cases like
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
// does not help.
[MethodImpl((MethodImplOptions)512 /* AggressiveOptimization */)]
private Statement ParseStatement(StatementContext context, bool topLevel = false)
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/statement.js > `pp.parseStatement = function`
Expand Down Expand Up @@ -1355,10 +1356,12 @@ private Node ParseClassElement(bool constructorAllowsSuper)
var hasDecorators = _tokenizer._type == TokenType.At;
var decorators = hasDecorators ? ParseDecorators() : default;

bool isStatic, isAsync, isGenerator, isAccessor = false;
string? keyName = null;
string keyword;
var kind = PropertyKind.Unknown;

var isStatic = EatContextual(keyword = "static");
isStatic = EatContextual(keyword = "static");
if (isStatic)
{
// Parse static init block
Expand All @@ -1374,68 +1377,70 @@ private Node ParseClassElement(bool constructorAllowsSuper)

if (!(IsClassElementNameStart() || _tokenizer._type == TokenType.Star))
{
isStatic = false;
isStatic = isAsync = isGenerator = false;
keyName = keyword;
goto ParseElementName;
}
}

var isAsync = keyName is null && _tokenizerOptions._ecmaVersion >= EcmaVersion.ES8 && EatContextual(keyword = "async");
isAsync = _tokenizerOptions._ecmaVersion >= EcmaVersion.ES8 && EatContextual(keyword = "async");
if (isAsync)
{
if (!((IsClassElementNameStart() || _tokenizer._type == TokenType.Star) && !CanInsertSemicolon()))
{
isAsync = false;
isAsync = isGenerator = false;
keyName = keyword;
goto ParseElementName;
}
}

var isGenerator = keyName is null
// NOTE: We only need to check the ECMA version in the case of async generators
// since non-async generators were introduced in ES6, together with classes.
&& (!isAsync || _tokenizerOptions._ecmaVersion >= EcmaVersion.ES9)
&& Eat(TokenType.Star);
isGenerator = _tokenizerOptions._ecmaVersion >= EcmaVersion.ES9 && Eat(TokenType.Star);
goto ParseElementName;
}

var isAccessor = false;
var kind = PropertyKind.Unknown;
isGenerator = Eat(TokenType.Star);
if (isGenerator)
{
goto ParseElementName;
}

if (keyName is null && !isAsync && !isGenerator)
if (EatContextual(keyword = "get"))
{
if (EatContextual(keyword = "get"))
if (IsClassElementNameStart())
{
if (IsClassElementNameStart())
{
kind = PropertyKind.Get;
}
else
{
keyName = keyword;
}
kind = PropertyKind.Get;
}
else if (EatContextual(keyword = "set"))
else
{
if (IsClassElementNameStart())
{
kind = PropertyKind.Set;
}
else
{
keyName = keyword;
}
keyName = keyword;
}
else if (_tokenizerOptions.AllowDecorators() && EatContextual(keyword = "accessor"))
}
else if (EatContextual(keyword = "set"))
{
if (IsClassElementNameStart())
{
if (!CanInsertSemicolon() && IsClassElementNameStart())
{
isAccessor = true;
}
else
{
keyName = keyword;
}
kind = PropertyKind.Set;
}
else
{
keyName = keyword;
}
}
else if (_tokenizerOptions.AllowDecorators() && EatContextual(keyword = "accessor"))
{
if (!CanInsertSemicolon() && IsClassElementNameStart())
{
isAccessor = true;
}
else
{
keyName = keyword;
}
}

// Parse element name
ParseElementName:
Expression key;
bool computed;
if (keyName is null)
Expand Down
4 changes: 4 additions & 0 deletions src/Acornima/Tokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ private void ReadToken(TokenContext currentContext)
}
}

[MethodImpl((MethodImplOptions)512 /* AggressiveOptimization */)]
internal bool TryReadToken(int cp)
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/tokenize.js > `pp.readToken = function`, `pp.getTokenFromCode = function`
Expand Down Expand Up @@ -318,6 +319,7 @@ private void SkipLineComment(int startSkip, CommentKind kind, OnCommentHandler?

// Called at the start of the parse and after every token. Skips
// whitespace and comments.
[MethodImpl((MethodImplOptions)512 /* AggressiveOptimization */)]
private void SkipSpace(OnCommentHandler? onComment)
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/tokenize.js > `pp.skipSpace = function`
Expand Down Expand Up @@ -1519,6 +1521,7 @@ private int ReadHexChar(int len, int start, bool isVariableLength = false)
//
// Incrementally adds only escaped chars, adding other chunks as-is
// as a micro-optimization.
[MethodImpl((MethodImplOptions)512 /* AggressiveOptimization */)]
private ReadOnlySpan<char> ReadWord1()
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/tokenize.js > `pp.readWord1 = function`
Expand Down Expand Up @@ -1578,6 +1581,7 @@ private ReadOnlySpan<char> ReadWord1()

// Read an identifier or keyword token. Will check for reserved
// words when necessary.
[MethodImpl((MethodImplOptions)512 /* AggressiveOptimization */)]
private bool ReadWord()
{
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/tokenize.js > `pp.readWord = function`
Expand Down
2 changes: 1 addition & 1 deletion test/Acornima.Tests/ParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void CanHandleDeepRecursion()
#if DEBUG
const int depth = 395;
#else
const int depth = 610;
const int depth = 900;
#endif
var input = $"if ({new string('(', depth)}true{new string(')', depth)}) {{ }}";
parser.ParseScript(input);
Expand Down