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
7 changes: 5 additions & 2 deletions Jint.Tests/Parser/JavascriptParserTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Jint.Tests.Parsing;
using Jint.Runtime;

namespace Jint.Tests.Parsing;

public class JavascriptParserTests
{
Expand Down Expand Up @@ -163,7 +165,8 @@ public void ShouldProvideLocationForMultiLinesStringLiterals()
[Fact]
public void ShouldThrowErrorForInvalidLeftHandOperation()
{
Assert.Throws<SyntaxErrorException>(() => new Engine().Execute("~ (WE0=1)--- l('1');"));
var ex = Assert.Throws<JavaScriptException>(() => new Engine().Execute("~ (WE0=1)--- l('1');"));
Assert.Equal("Invalid left-hand side expression in postfix operation (<anonymous>:1:4)", ex.Message);
}


Expand Down
30 changes: 30 additions & 0 deletions Jint.Tests/Runtime/ClassTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Jint.Runtime;

namespace Jint.Tests.Runtime;

public class ClassTests
Expand Down Expand Up @@ -45,4 +47,32 @@ get doubleWidth () {
Assert.Equal(10, engine.Evaluate("board.width"));
Assert.Equal(20, engine.Evaluate("board.doubleWidth "));
}

[Fact]
public void PrivateMemberAccessOutsideOfClass()
{
var ex = Assert.Throws<JavaScriptException>(() => new Engine().Evaluate
(
"""
class A { }
new A().#nonexistent = 1;
"""
));

Assert.Equal("Private field '#nonexistent' must be declared in an enclosing class (<anonymous>:2:9)", ex.Message);
}

[Fact]
public void PrivateMemberAccessAgainstUnknownMemberInConstructor()
{
var ex = Assert.Throws<JavaScriptException>(() => new Engine().Evaluate
(
"""
class A { constructor() { #nonexistent = 2; } }
new A();
"""
));

Assert.Equal("Unexpected identifier '#nonexistent' (<anonymous>:1:27)", ex.Message);
}
}
14 changes: 7 additions & 7 deletions Jint.Tests/Runtime/EngineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1046,11 +1046,11 @@ public void ShouldGetParseErrorLocation()
{
engine.Evaluate("1.2+ new", "jQuery.js");
}
catch (SyntaxErrorException e)
catch (JavaScriptException e)
{
Assert.Equal(1, e.LineNumber);
Assert.Equal(8, e.Column);
Assert.Equal("jQuery.js", e.SourceFile);
Assert.Equal(1, e.Location.Start.Line);
Assert.Equal(8, e.Location.Start.Column);
Assert.Equal("jQuery.js", e.Location.SourceFile);
}
}
#region DateParsingAndStrings
Expand Down Expand Up @@ -1314,7 +1314,7 @@ public void ShouldNotAllowDuplicateProtoProperty()
{
var code = "if({ __proto__: [], __proto__:[] } instanceof Array) {}";

Exception ex = Assert.Throws<SyntaxErrorException>(() => _engine.Execute(code, new ScriptParsingOptions { Tolerant = false }));
Exception ex = Assert.Throws<JavaScriptException>(() => _engine.Execute(code, new ScriptParsingOptions { Tolerant = false }));
Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message);

ex = Assert.Throws<JavaScriptException>(() => _engine.Execute($"eval('{code}')"));
Expand Down Expand Up @@ -2865,8 +2865,8 @@ public void ShouldObeyScriptLevelStrictModeInFunctions()
Assert.Equal("Cannot delete property 'prototype' of function Boolean() { [native code] }", ex.Message);

const string source2 = "'use strict'; delete foobar;";
var ex2 = Assert.Throws<SyntaxErrorException>(() => engine.Evaluate(source2));
Assert.Equal("Delete of an unqualified identifier in strict mode", ex2.Description);
ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate(source2));
Assert.Equal("Delete of an unqualified identifier in strict mode (<anonymous>:1:22)", ex.Message);
}

[Fact]
Expand Down
8 changes: 4 additions & 4 deletions Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ internal void ResetCallStack()
/// </summary>
public JsValue Evaluate(string code, string? source = null)
{
var script = _defaultParser.ParseScript(code, source ?? "<anonymous>", _isStrict);
var script = _defaultParser.ParseScriptGuarded(Realm, code, source ?? "<anonymous>", _isStrict);
return Evaluate(new Prepared<Script>(script, _defaultParser.Options));
}

Expand All @@ -357,7 +357,7 @@ public JsValue Evaluate(string code, ScriptParsingOptions parsingOptions)
public JsValue Evaluate(string code, string source, ScriptParsingOptions parsingOptions)
{
var parser = GetParserFor(parsingOptions);
var script = parser.ParseScript(code, source, _isStrict);
var script = parser.ParseScriptGuarded(Realm, code, source, _isStrict);
return Evaluate(new Prepared<Script>(script, parser.Options));
}

Expand All @@ -372,7 +372,7 @@ public JsValue Evaluate(in Prepared<Script> preparedScript)
/// </summary>
public Engine Execute(string code, string? source = null)
{
var script = _defaultParser.ParseScript(code, source ?? "<anonymous>", _isStrict);
var script = _defaultParser.ParseScriptGuarded(Realm, code, source ?? "<anonymous>", _isStrict);
return Execute(new Prepared<Script>(script, _defaultParser.Options));
}

Expand All @@ -388,7 +388,7 @@ public Engine Execute(string code, ScriptParsingOptions parsingOptions)
public Engine Execute(string code, string source, ScriptParsingOptions parsingOptions)
{
var parser = GetParserFor(parsingOptions);
var script = parser.ParseScript(code, source, _isStrict);
var script = parser.ParseScriptGuarded(Realm, code, source, _isStrict);
return Execute(new Prepared<Script>(script, parser.Options));
}

Expand Down
42 changes: 42 additions & 0 deletions Jint/Extensions/AcornimaExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Jint.Runtime;

namespace Jint;

internal static class AcornimaExtensions
{
public static Script ParseScriptGuarded(this Parser parser, Realm realm, string code, string? source = null, bool strict = false)
{
try
{
return parser.ParseScript(code, source, strict);
}
catch (ParseErrorException e)
{
ExceptionHelper.ThrowSyntaxError(realm, e.Message, ToLocation(e, source));
return default;
}
}

public static Module ParseModuleGuarded(this Parser parser, Engine engine, string code, string? source = null)
{
try
{
return parser.ParseModule(code, source);
}
catch (ParseErrorException ex)
{
ExceptionHelper.ThrowSyntaxError(engine.Realm, $"Error while loading module: error in module '{source}': {ex.Error}", ToLocation(ex, source));
return default;
}
catch (Exception)
{
ExceptionHelper.ThrowJavaScriptException(engine, $"Could not load module {source}", AstExtensions.DefaultLocation);
return default;
}
}

private static SourceLocation ToLocation(ParseErrorException ex, string? source)
{
return SourceLocation.From(Position.From(ex.LineNumber, ex.Column), Position.From(ex.LineNumber, ex.Column), source);
}
}
2 changes: 1 addition & 1 deletion Jint/Native/Function/ClassDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static ClassDefinition()
// generate missing constructor AST only once
static MethodDefinition CreateConstructorMethodDefinition(Parser parser, string source)
{
var script = parser.ParseScript(source);
var script = parser.ParseScriptGuarded(new Engine().Realm, source);
var classDeclaration = (ClassDeclaration) script.Body[0];
return (MethodDefinition) classDeclaration.Body.Body[0];
}
Expand Down
16 changes: 1 addition & 15 deletions Jint/Native/Function/EvalFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,7 @@ internal JsValue PerformEval(JsValue x, Realm callerRealm, bool strictCaller, bo
CheckPrivateFields = false
};
var parser = _engine.GetParserFor(adjustedParserOptions);
try
{
script = parser.ParseScript(x.ToString(), strict: strictCaller);
}
catch (ParseErrorException e)
{
if (string.Equals(e.Error.Code, "InvalidLhsInAssignment", StringComparison.Ordinal))
{
ExceptionHelper.ThrowReferenceError(callerRealm, (string?) null);
}
else
{
ExceptionHelper.ThrowSyntaxError(callerRealm, e.Message);
}
}
script = parser.ParseScriptGuarded(_engine.Realm, x.ToString(), strict: strictCaller);

var body = script.Body;
if (body.Count == 0)
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Function/FunctionInstance.Dynamic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ internal Function CreateDynamicFunction(
parserOptions = parserOptions with { AllowReturnOutsideFunction = true };
}
Parser parser = new(parserOptions);
function = (IFunction) parser.ParseScript(functionExpression, strict: _engine._isStrict).Body[0];
function = (IFunction) parser.ParseScriptGuarded(callerRealm, functionExpression, strict: _engine._isStrict).Body[0];
}
catch (ParseErrorException ex)
{
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/ShadowRealm/ShadowRealm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ internal JsValue PerformShadowRealmEval(string sourceText, ParserOptions parserO
Script script;
try
{
script = parser.ParseScript(sourceText, strict: _engine._isStrict);
script = parser.ParseScriptGuarded(callerRealm, sourceText, strict: _engine._isStrict);
}
catch (ParseErrorException e)
{
Expand Down
16 changes: 1 addition & 15 deletions Jint/Runtime/Modules/ModuleFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,9 @@ public static class ModuleFactory
public static Module BuildSourceTextModule(Engine engine, ResolvedSpecifier resolved, string code, ModuleParsingOptions? parsingOptions = null)
{
var source = resolved.Uri?.LocalPath ?? resolved.Key;
AstModule module;
var parserOptions = (parsingOptions ?? ModuleParsingOptions.Default).GetParserOptions();
var parser = new Parser(parserOptions);
try
{
module = parser.ParseModule(code, source);
}
catch (ParseErrorException ex)
{
ExceptionHelper.ThrowSyntaxError(engine.Realm, $"Error while loading module: error in module '{source}': {ex.Error}");
module = null;
}
catch (Exception)
{
ExceptionHelper.ThrowJavaScriptException(engine, $"Could not load module {source}", AstExtensions.DefaultLocation);
module = null;
}
var module = parser.ParseModuleGuarded(engine, code, source);

return BuildSourceTextModule(engine, new Prepared<AstModule>(module, parserOptions));
}
Expand Down