blob: 7efeb16000873400d9b23ea05abcb70365a47004 [file] [edit]
function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error('bad value: ' + actual + ' expected: ' + expected);
}
// Anonymous functions and classes in non-computed class field initializers
// get their names inferred statically (no SetFunctionName at runtime).
class C {
f = () => 1;
g = function () { return 2; };
h = class {};
#p = () => 3;
static s = () => 4;
"str x" = () => 5;
0 = () => 6;
n = class { static name() { return 7; } };
getP() { return this.#p; }
}
for (var i = 0; i < 1e4; ++i) {
var c = new C();
shouldBe(c.f.name, 'f');
shouldBe(c.g.name, 'g');
shouldBe(c.h.name, 'h');
shouldBe(c.getP().name, '#p');
shouldBe(c['str x'].name, 'str x');
shouldBe(c[0].name, '0');
}
shouldBe(C.s.name, 's');
// A static "name" method shadows the inferred class name.
shouldBe(typeof new C().n.name, 'function');
shouldBe(new C().n.name(), 7);
// Property descriptor must match runtime SetFunctionName semantics.
{
let c = new C();
let desc = Object.getOwnPropertyDescriptor(c.f, 'name');
shouldBe(desc.value, 'f');
shouldBe(desc.writable, false);
shouldBe(desc.enumerable, false);
shouldBe(desc.configurable, true);
}
// Own property names and their order.
{
let c = new C();
shouldBe(Object.getOwnPropertyNames(c.f).join(','), 'length,name');
shouldBe(Object.keys(c.f).length, 0);
shouldBe(c.f.hasOwnProperty('name'), true);
}
// delete fn.name falls through to Function.prototype.name.
{
let c = new C();
shouldBe(delete c.f.name, true);
shouldBe(c.f.name, '');
shouldBe(c.f.hasOwnProperty('name'), false);
}
// Writing is not allowed (ReadOnly), but defineProperty works (configurable).
{
let c = new C();
let threw = false;
try {
(function () { 'use strict'; c.f.name = 'changed'; })();
} catch (e) {
threw = e instanceof TypeError;
}
shouldBe(threw, true);
shouldBe(c.f.name, 'f');
Object.defineProperty(c.f, 'name', { value: 'redefined' });
shouldBe(c.f.name, 'redefined');
}
// Instances are independent: reifying one must not affect another.
{
let a = new C();
shouldBe(a.f.name, 'f');
let b = new C();
Object.defineProperty(a.f, 'name', { value: 'mutated' });
shouldBe(b.f.name, 'f');
shouldBe(a.f.name, 'mutated');
}
// length is still derived from the function itself, not the field.
{
let c = new C();
shouldBe(c.g.length, 0);
shouldBe(c.f.length, 0);
}
// bind reads the inferred name.
{
let c = new C();
shouldBe(c.f.bind(null).name, 'bound f');
}
// Error.stack shows the inferred name.
{
class S { e = () => new Error(); }
let stack = new S().e().stack;
shouldBe(stack.split('\n')[0].split('@')[0], 'e');
}
// Computed field names still go through runtime SetFunctionName.
{
function make(key) {
class D { [key] = () => 1; }
return new D();
}
shouldBe(make('alpha').alpha.name, 'alpha');
shouldBe(make('beta').beta.name, 'beta');
let sym = Symbol('x');
shouldBe(make(sym)[sym].name, '[x]');
}
// Named function expressions in field initializers keep their own name.
{
class E { m = function inner() {}; }
shouldBe(new E().m.name, 'inner');
}
// Fields whose initializer is not a function are unaffected.
{
class F { v = 42; w = 'hi'; }
let f = new F();
shouldBe(f.v, 42);
shouldBe(f.w, 'hi');
}