Skip to content
Open
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
18 changes: 15 additions & 3 deletions tests/Starling.Js.Test262.Tests/Test262Runner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,23 @@ private static bool IsParseLevel(Exception ex) =>

/// <summary>Read the <c>name</c> property of a thrown error value (e.g.
/// "TypeError"); null when the throw isn't an error-shaped object.</summary>
private static string? ErrorName(JsValue value)
internal static string? ErrorName(JsValue value)
{
if (!value.IsObject) return null;
var n = value.AsObject.Get("name");
return n.IsString ? n.AsString : null;
var obj = value.AsObject;
// Built-in errors expose their type via `name` (TypeError.prototype.name
// etc.). The harness's Test262Error and other plain error classes set no
// `name` at all, so fall back to the constructor's name — which is what
// the official test262 runner matches a negative `type` against.
var n = obj.Get("name");
if (n.IsString) return n.AsString;
var ctor = obj.Get("constructor");
if (ctor.IsObject)
{
var cn = ctor.AsObject.Get("name");
if (cn.IsString) return cn.AsString;
}
return null;
}

private static string ExtractMessage(Exception ex) =>
Expand Down
51 changes: 51 additions & 0 deletions tests/Starling.Js.Test262.Tests/Test262RunnerErrorNameTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Starling.Js.Bytecode;
using Starling.Js.Parse;
using Starling.Js.Runtime;

namespace Starling.Js.Test262.Tests;

/// <summary>
/// Pins how the runner classifies a thrown error against a negative test's
/// <c>type</c>. The harness's <c>Test262Error</c> is a plain function with no
/// <c>name</c> property, so a negative <c>type: Test262Error</c> can only be
/// matched by the thrown value's constructor name.
/// </summary>
[TestClass]
public class Test262RunnerErrorNameTests
{
[TestMethod]
public void Test262Error_instance_resolves_by_constructor_name()
{
var value = Eval("function Test262Error(m){ this.message = m || ''; } new Test262Error('boom');");
Assert.AreEqual("Test262Error", Test262Runner.ErrorName(value));
}

[TestMethod]
public void Builtin_error_resolves_by_name_property()
{
var value = Eval("new TypeError('x');");
Assert.AreEqual("TypeError", Test262Runner.ErrorName(value));
}

[TestMethod]
public void Name_property_wins_over_constructor_name()
{
// `name` is present, so it is used even though the constructor (Object)
// has a different name.
var value = Eval("({ name: 'Custom' });");
Assert.AreEqual("Custom", Test262Runner.ErrorName(value));
}

[TestMethod]
public void Thrown_primitive_has_no_error_name()
{
var value = Eval("'not an error';");
Assert.IsNull(Test262Runner.ErrorName(value));
}

private static JsValue Eval(string source)
{
var chunk = JsCompiler.CompileForEval(new JsParser(source).ParseProgram());
return new JsVm(new JsRuntime()).Run(chunk);
}
}
Loading