Skip to content

Commit 7b41ac0

Browse files
lahmaclaude
andauthored
Default source to "<anonymous>" in PrepareScript/PrepareModule (#2379)
PrepareScript and PrepareModule passed null source through to the parser as-is, while Evaluate/Execute defaulted null source to "<anonymous>". This inconsistency meant nodes from prepared scripts had Location.SourceFile == null instead of "<anonymous>", causing breakpoint matching issues when debugging tools used the source identifier. Fixes #2378 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c56643c commit 7b41ac0

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

Jint.Tests/Runtime/Debugger/BreakPointTests.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,95 @@ public void BreakPointBreaksAtPosition()
133133
Assert.True(didBreak);
134134
}
135135

136+
[Fact]
137+
public void BreakPointBreaksAtPositionWithPreparedScript()
138+
{
139+
string script = @"let x = 1, y = 2;
140+
if (x === 1)
141+
{
142+
x++; y *= 2;
143+
}";
144+
145+
var engine = new Engine(options => options.DebugMode());
146+
147+
bool didBreak = false;
148+
engine.Debugger.Break += (sender, info) =>
149+
{
150+
Assert.Equal(4, info.Location.Start.Line);
151+
Assert.Equal(5, info.Location.Start.Column);
152+
didBreak = true;
153+
return StepMode.None;
154+
};
155+
156+
engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5));
157+
var prepared = Engine.PrepareScript(script);
158+
engine.Execute(prepared);
159+
Assert.True(didBreak);
160+
}
161+
162+
[Fact]
163+
public void BreakPointBreaksInCorrectSourceWithPreparedScript()
164+
{
165+
string script1 = @"let x = 1, y = 2;
166+
if (x === 1)
167+
{
168+
x++; y *= 2;
169+
}";
170+
171+
string script2 = @"function test(x)
172+
{
173+
return x + 2;
174+
}";
175+
176+
string script3 = @"const z = 3;
177+
test(z);";
178+
179+
var engine = new Engine(options => { options.DebugMode(); });
180+
181+
engine.Debugger.BreakPoints.Set(new BreakPoint("script2", 3, 0));
182+
183+
bool didBreak = false;
184+
engine.Debugger.Break += (sender, info) =>
185+
{
186+
Assert.Equal("script2", info.Location.SourceFile);
187+
Assert.Equal(3, info.Location.Start.Line);
188+
Assert.Equal(0, info.Location.Start.Column);
189+
didBreak = true;
190+
return StepMode.None;
191+
};
192+
193+
engine.Execute(Engine.PrepareScript(script1, "script1"));
194+
Assert.False(didBreak);
195+
196+
engine.Execute(Engine.PrepareScript(script2, "script2"));
197+
Assert.False(didBreak);
198+
199+
engine.Execute(Engine.PrepareScript(script3, "script3"));
200+
Assert.True(didBreak);
201+
}
202+
203+
[Fact]
204+
public void BreakPointBreaksWithPreparedScriptDefaultSource()
205+
{
206+
string script = @"let x = 1;
207+
x++;";
208+
209+
var engine = new Engine(options => options.DebugMode());
210+
211+
bool didBreak = false;
212+
engine.Debugger.Break += (sender, info) =>
213+
{
214+
Assert.Equal("<anonymous>", info.Location.SourceFile);
215+
didBreak = true;
216+
return StepMode.None;
217+
};
218+
219+
engine.Debugger.BreakPoints.Set(new BreakPoint("<anonymous>", 2, 0));
220+
var prepared = Engine.PrepareScript(script);
221+
engine.Execute(prepared);
222+
Assert.True(didBreak);
223+
}
224+
136225
[Fact]
137226
public void BreakPointBreaksInCorrectSource()
138227
{

Jint.Tests/Runtime/ScriptModulePreparationTests.ScriptPreparation.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,15 @@ public void CompiledRegexShouldProduceSameResultAsNonCompiled()
8080
public void PrepareScriptShouldNotLeakAcornimaException()
8181
{
8282
var ex = Assert.Throws<ScriptPreparationException>(() => Engine.PrepareScript("class A { } A().#nonexistent = 1;"));
83-
ex.Message.Should().Be("Could not prepare script: Private field '#nonexistent' must be declared in an enclosing class (1:17)");
83+
ex.Message.Should().Be("Could not prepare script: Private field '#nonexistent' must be declared in an enclosing class (<anonymous>:1:17)");
8484
ex.InnerException.Should().BeOfType<SyntaxErrorException>();
8585
}
8686

8787
[Fact]
8888
public void PrepareModuleShouldNotLeakAcornimaException()
8989
{
9090
var ex = Assert.Throws<ScriptPreparationException>(() => Engine.PrepareModule("class A { } A().#nonexistent = 1;"));
91-
ex.Message.Should().Be("Could not prepare script: Private field '#nonexistent' must be declared in an enclosing class (1:17)");
91+
ex.Message.Should().Be("Could not prepare script: Private field '#nonexistent' must be declared in an enclosing class (<anonymous>:1:17)");
9292
ex.InnerException.Should().BeOfType<SyntaxErrorException>();
9393
}
9494
}

Jint/Engine.Ast.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public partial class Engine
1717
/// </remarks>
1818
public static Prepared<Script> PrepareScript(string code, string? source = null, bool strict = false, ScriptPreparationOptions? options = null)
1919
{
20+
source ??= "<anonymous>";
2021
options ??= ScriptPreparationOptions.Default;
2122

2223
var astAnalyzer = new AstAnalyzer(options);
@@ -53,6 +54,7 @@ public static Prepared<Script> PrepareScript(string code, string? source = null,
5354
/// </remarks>
5455
public static Prepared<Module> PrepareModule(string code, string? source = null, ModulePreparationOptions? options = null)
5556
{
57+
source ??= "<anonymous>";
5658
options ??= ModulePreparationOptions.Default;
5759

5860
var astAnalyzer = new AstAnalyzer(options);

0 commit comments

Comments
 (0)