test(es/decorators): Split decorator-tests (#9119)

This commit is contained in:
Donny/강동윤 2024-07-02 10:50:23 +09:00 committed by GitHub
parent f8a33e3800
commit 07c3054847
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
148 changed files with 4308 additions and 0 deletions

View File

@ -0,0 +1,45 @@
import typescript from 'typescript'
import fs from 'fs'
import module from 'module'
const require = module.createRequire(import.meta.url)
const ts = fs.readFileSync('./tests/decorator-tests/decorator-tests.ts', 'utf8')
// Convert TypeScript to JavaScript for testing JavaScript VMs
console.log('Converting TypeScript to JavaScript...')
const program = typescript.createProgram({
rootNames: ['./tests/decorator-tests/decorator-tests.ts'],
options: { target: typescript.ScriptTarget.ESNext }
});
// console.log('program', program)
const mainFile = program.getSourceFile('./tests/decorator-tests/decorator-tests.ts')
// console.log('mainFile', mainFile)
const testVarStmt = mainFile.statements.find((s) => s.kind === typescript.SyntaxKind.VariableStatement)
// console.log('testVarStmt', testVarStmt)
const testVarDecl = testVarStmt.declarationList.declarations[0]
// console.log('testVarDecl', testVarDecl)
const properties = testVarDecl.initializer.properties
// console.log('properties', properties)
fs.rmdirSync(`tests/decorator-evanw-split`, { recursive: true });
fs.mkdirSync(`tests/decorator-evanw-split`, { recursive: true });
for (const property of properties) {
let name = property.name.getText(mainFile)
const value = property.initializer.getText(mainFile)
name = name.replace(/['\(\)"']+/g, '').replace(/[\:; ]+/g, '-')
// console.log('name', name)
// console.log('value', value)
const code = `(${value})()`;
const transpiiled = typescript.transpile(code, { target: typescript.ScriptTarget.ESNext })
fs.writeFileSync(`tests/decorator-evanw-split/${name}.js`, transpiiled)
}

View File

@ -0,0 +1,37 @@
(() => {
const dec = (key, getName, setName) => (target, ctx) => {
assertEq(() => typeof target.get, 'function');
assertEq(() => typeof target.set, 'function');
assertEq(() => target.get.name, getName);
assertEq(() => target.set.name, setName);
assertEq(() => ctx.kind, 'accessor');
assertEq(() => ctx.name, key);
assertEq(() => ctx.static, false);
assertEq(() => ctx.private, false);
assertEq(() => ctx.access.has({ [key]: false }), true);
assertEq(() => ctx.access.has({ bar: true }), false);
assertEq(() => ctx.access.get({ [key]: 123 }), 123);
assertEq(() => {
const obj = {};
ctx.access.set(obj, 123);
return obj[key];
}, 123);
};
const bar = Symbol('bar');
const baz = Symbol();
class Foo {
@dec('foo', 'get foo', 'set foo')
accessor foo = 0;
@dec(bar, 'get [bar]', 'set [bar]')
accessor [bar] = 0;
@dec(baz, 'get ', 'set ')
accessor [baz] = 0;
}
var obj = new Foo;
obj.foo = 321;
assertEq(() => obj.foo, 321);
obj[bar] = 4321;
assertEq(() => obj[bar], 4321);
obj[baz] = 54321;
assertEq(() => obj[baz], 54321);
})();

View File

@ -0,0 +1,37 @@
(() => {
let lateAsserts;
const dec = (target, ctx) => {
assertEq(() => typeof target.get, 'function');
assertEq(() => typeof target.set, 'function');
assertEq(() => target.get.name, 'get #foo');
assertEq(() => target.set.name, 'set #foo');
assertEq(() => ctx.kind, 'accessor');
assertEq(() => ctx.name, '#foo');
assertEq(() => ctx.static, false);
assertEq(() => ctx.private, true);
lateAsserts = () => {
assertEq(() => ctx.access.has(new Foo), true);
assertEq(() => ctx.access.has({}), false);
assertEq(() => ctx.access.get(new Foo), 0);
assertEq(() => {
const obj = new Foo;
ctx.access.set(obj, 123);
return get$foo(obj);
}, 123);
};
};
let get$foo;
let set$foo;
class Foo {
@dec
accessor #foo = 0;
static {
get$foo = x => x.#foo;
set$foo = (x, y) => { x.#foo = y; };
}
}
lateAsserts();
var obj = new Foo;
assertEq(() => set$foo(obj, 321), undefined);
assertEq(() => get$foo(obj), 321);
})();

View File

@ -0,0 +1,35 @@
(() => {
let lateAsserts;
const dec = (target, ctx) => {
assertEq(() => typeof target.get, 'function');
assertEq(() => typeof target.set, 'function');
assertEq(() => target.get.name, 'get #foo');
assertEq(() => target.set.name, 'set #foo');
assertEq(() => ctx.kind, 'accessor');
assertEq(() => ctx.name, '#foo');
assertEq(() => ctx.static, true);
assertEq(() => ctx.private, true);
lateAsserts = () => {
assertEq(() => ctx.access.has(Foo), true);
assertEq(() => ctx.access.has({}), false);
assertEq(() => ctx.access.get(Foo), 0);
assertEq(() => {
ctx.access.set(Foo, 123);
return get$foo(Foo);
}, 123);
};
};
let get$foo;
let set$foo;
class Foo {
@dec
static accessor #foo = 0;
static {
get$foo = x => x.#foo;
set$foo = (x, y) => { x.#foo = y; };
}
}
lateAsserts();
assertEq(() => set$foo(Foo, 321), undefined);
assertEq(() => get$foo(Foo), 321);
})();

View File

@ -0,0 +1,36 @@
(() => {
const dec = (key, getName, setName) => (target, ctx) => {
assertEq(() => typeof target.get, 'function');
assertEq(() => typeof target.set, 'function');
assertEq(() => target.get.name, getName);
assertEq(() => target.set.name, setName);
assertEq(() => ctx.kind, 'accessor');
assertEq(() => ctx.name, key);
assertEq(() => ctx.static, true);
assertEq(() => ctx.private, false);
assertEq(() => ctx.access.has({ [key]: false }), true);
assertEq(() => ctx.access.has({ bar: true }), false);
assertEq(() => ctx.access.get({ [key]: 123 }), 123);
assertEq(() => {
const obj = {};
ctx.access.set(obj, 123);
return obj[key];
}, 123);
};
const bar = Symbol('bar');
const baz = Symbol();
class Foo {
@dec('foo', 'get foo', 'set foo')
static accessor foo = 0;
@dec(bar, 'get [bar]', 'set [bar]')
static accessor [bar] = 0;
@dec(baz, 'get ', 'set ')
static accessor [baz] = 0;
}
Foo.foo = 321;
assertEq(() => Foo.foo, 321);
Foo[bar] = 4321;
assertEq(() => Foo[bar], 4321);
Foo[baz] = 54321;
assertEq(() => Foo[baz], 54321);
})();

View File

@ -0,0 +1,22 @@
(() => {
let oldAddInitializer;
let got;
const dec = (target, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
accessor foo;
}
assertEq(() => got, undefined);
const instance = new Foo;
assertEq(() => got.this, instance);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,22 @@
(() => {
let oldAddInitializer;
let got;
const dec = (target, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
accessor #foo;
}
assertEq(() => got, undefined);
const instance = new Foo;
assertEq(() => got.this, instance);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,20 @@
(() => {
let oldAddInitializer;
let got;
const dec = (target, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
static accessor #foo;
}
assertEq(() => got.this, Foo);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,20 @@
(() => {
let oldAddInitializer;
let got;
const dec = (target, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
static accessor foo;
}
assertEq(() => got.this, Foo);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (target, ctx) => {
return null;
};
class Foo {
@dec
accessor foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (target, ctx) => {
return null;
};
class Foo {
@dec
accessor #foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (target, ctx) => {
return null;
};
class Foo {
@dec
static accessor #foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (target, ctx) => {
return null;
};
class Foo {
@dec
static accessor foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,23 @@
(() => {
let get;
let set;
const dec = (target, ctx) => {
function init(x) {
assertEq(() => this instanceof Foo, true);
return x + 1;
}
get = function () { return target.get.call(this) * 10; };
set = function (x) { target.set.call(this, x * 2); };
return { get, set, init };
};
class Foo {
@dec
accessor foo = 123;
}
assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo').get, get);
assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo').set, set);
var obj = new Foo;
assertEq(() => obj.foo, (123 + 1) * 10);
obj.foo = 321;
assertEq(() => obj.foo, (321 * 2) * 10);
})();

View File

@ -0,0 +1,27 @@
(() => {
let get;
let set;
const dec = (target, ctx) => {
function init(x) {
assertEq(() => this instanceof Foo, true);
return x + 1;
}
get = function () { return target.get.call(this) * 10; };
set = function (x) { target.set.call(this, x * 2); };
return { get, set, init };
};
let get$foo;
let set$foo;
class Foo {
@dec
accessor #foo = 123;
static {
get$foo = x => x.#foo;
set$foo = (x, y) => { x.#foo = y; };
}
}
var obj = new Foo;
assertEq(() => get$foo(obj), (123 + 1) * 10);
assertEq(() => set$foo(obj, 321), undefined);
assertEq(() => get$foo(obj), (321 * 2) * 10);
})();

View File

@ -0,0 +1,28 @@
(() => {
let foo;
let get;
let set;
const dec = (target, ctx) => {
function init(x) {
assertEq(() => this, foo);
return x + 1;
}
get = function () { return target.get.call(this) * 10; };
set = function (x) { target.set.call(this, x * 2); };
return { get, set, init };
};
let get$foo;
let set$foo;
class Foo {
static {
foo = Foo;
get$foo = x => x.#foo;
set$foo = (x, y) => { x.#foo = y; };
}
@dec
static accessor #foo = 123;
}
assertEq(() => get$foo(Foo), (123 + 1) * 10);
assertEq(() => set$foo(Foo, 321), undefined);
assertEq(() => get$foo(Foo), (321 * 2) * 10);
})();

View File

@ -0,0 +1,26 @@
(() => {
let foo;
let get;
let set;
const dec = (target, ctx) => {
function init(x) {
assertEq(() => this, foo);
return x + 1;
}
get = function () { return target.get.call(this) * 10; };
set = function (x) { target.set.call(this, x * 2); };
return { get, set, init };
};
class Foo {
static {
foo = Foo;
}
@dec
static accessor foo = 123;
}
assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo').get, get);
assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo').set, set);
assertEq(() => Foo.foo, (123 + 1) * 10);
Foo.foo = 321;
assertEq(() => Foo.foo, (321 * 2) * 10);
})();

View File

@ -0,0 +1,23 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
const Foo = (x => x)(
@dec('')
class {
});
assertEq(() => Foo, old);
const Bar = (x => x)(
@dec('Baz')
class Baz {
});
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,23 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
const [Foo =
@dec('Foo')
class {
}] = [];
assertEq(() => Foo, old);
const [Bar =
@dec('Baz')
class Baz {
}] = [];
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,25 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
let Foo;
[Foo =
@dec('Foo')
class {
}] = [];
assertEq(() => Foo, old);
let Bar;
[Bar =
@dec('Baz')
class Baz {
}] = [];
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,25 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
let Foo;
Foo =
@dec('Foo')
class {
};
assertEq(() => Foo, old);
let Bar;
Bar =
@dec('Baz')
class Baz {
};
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,25 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
let Foo;
({ Foo =
@dec('Foo')
class {
} } = {});
assertEq(() => Foo, old);
let Bar;
({ Bar =
@dec('Baz')
class Baz {
} } = {});
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,29 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
class Class {
accessor Foo =
@dec('Foo')
class {
};
}
const Foo = new Class().Foo;
assertEq(() => Foo, old);
class Class2 {
accessor Bar =
@dec('Baz')
class Baz {
};
}
const Bar = new Class2().Bar;
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,29 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
class Class {
Foo =
@dec('Foo')
class {
};
}
const Foo = new Class().Foo;
assertEq(() => Foo, old);
class Class2 {
Bar =
@dec('Baz')
class Baz {
};
}
const Bar = new Class2().Bar;
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,23 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
const { Foo =
@dec('Foo')
class {
} } = {};
assertEq(() => Foo, old);
const { Bar =
@dec('Baz')
class Baz {
} } = {};
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,27 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
const obj = {
Foo:
@dec('Foo')
class {
},
};
assertEq(() => obj.Foo, old);
const obj2 = {
Bar:
@dec('Baz')
class Baz {
},
};
assertEq(() => obj2.Bar, old);
})();

View File

@ -0,0 +1,27 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
class Class {
static accessor Foo =
@dec('Foo')
class {
};
}
assertEq(() => Class.Foo, old);
class Class2 {
static accessor Bar =
@dec('Baz')
class Baz {
};
}
assertEq(() => Class2.Bar, old);
})();

View File

@ -0,0 +1,27 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
class Class {
static Foo =
@dec('Foo')
class {
};
}
assertEq(() => Class.Foo, old);
class Class2 {
static Bar =
@dec('Baz')
class Baz {
};
}
assertEq(() => Class2.Bar, old);
})();

View File

@ -0,0 +1,23 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
const Foo =
@dec('Foo')
class {
};
assertEq(() => Foo, old);
const Bar =
@dec('Baz')
class Baz {
};
assertEq(() => Bar, old);
})();

View File

@ -0,0 +1,17 @@
(() => {
let old;
const dec = (name) => (cls, ctx) => {
assertEq(() => typeof cls, 'function');
assertEq(() => cls.name, name);
assertEq(() => ctx.kind, 'class');
assertEq(() => ctx.name, name);
assertEq(() => 'static' in ctx, false);
assertEq(() => 'private' in ctx, false);
assertEq(() => 'access' in ctx, false);
old = cls;
};
@dec('Foo')
class Foo {
}
assertEq(() => Foo, old);
})();

View File

@ -0,0 +1,19 @@
(() => {
let oldAddInitializer;
let got;
const dec = (cls, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
@dec
@dec
class Foo {
}
assertEq(() => got.this, Foo);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,32 @@
(() => {
const log = [];
let Bar;
let Baz;
const dec1 = (cls, ctx) => {
log.push(2);
Bar = function () {
log.push(4);
return new cls;
};
return Bar;
};
const dec2 = (cls, ctx) => {
log.push(1);
Baz = function () {
log.push(5);
return new cls;
};
return Baz;
};
log.push(0);
@dec1
@dec2
class Foo {
constructor() { log.push(6); }
}
log.push(3);
new Foo;
log.push(7);
assertEq(() => Foo, Bar);
assertEq(() => log + '', '0,1,2,3,4,5,6,7');
})();

View File

@ -0,0 +1,10 @@
(() => {
assertThrows(() => {
const dec = (cls, ctx) => {
return null;
};
@dec
class Foo {
}
}, TypeError);
})();

View File

@ -0,0 +1,10 @@
(() => {
assertThrows(() => {
const dec = (cls, ctx) => {
return {};
};
@dec
class Foo {
}
}, TypeError);
})();

View File

@ -0,0 +1,44 @@
(() => {
const fns = [];
const capture = (fn) => {
fns.push(fn);
return () => { };
};
const originalFoo = (
@(capture(() => Foo))
class Foo {
@(capture(() => Foo))
method() { }
@(capture(() => Foo))
static method() { }
@(capture(() => Foo))
field;
@(capture(() => Foo))
static field;
@(capture(() => Foo))
get getter() { return; }
@(capture(() => Foo))
static get getter() { return; }
@(capture(() => Foo))
set setter(x) { }
@(capture(() => Foo))
static set setter(x) { }
@(capture(() => Foo))
accessor accessor;
@(capture(() => Foo))
static accessor accessor;
});
// Decorators on the class itself should reference a global called "Foo",
// which should still be a reference error. This is because a class
// expression runs "DecoratorListEvaluation" in the outer environment and
// then passes the evaluated decorators to "ClassDefinitionEvaluation".
const firstFn = fns.shift();
assertThrows(() => firstFn(), ReferenceError);
// All other decorators should reference the classBinding called "Foo",
// which should now be initialized. This is because all other decorators
// are evaluated within "ClassDefinitionEvaluation" while the running
// execution context's environment is the nested class environment.
for (const fn of fns) {
assertEq(() => fn(), originalFoo);
}
})();

View File

@ -0,0 +1,61 @@
(() => {
const fns = [];
const capture = (fn) => {
fns.push(fn);
// Note: As far as I can tell, early reference to the class name should
// throw a reference error because:
//
// 1. Class decorators run first in the top-level scope before entering
// BindingClassDeclarationEvaluation.
//
// 2. Class element decorators run in ClassDefinitionEvaluation, which
// runs ClassElementEvaluation for each class element before eventually
// running classEnv.InitializeBinding(classBinding, F).
//
assertThrows(() => fn(), ReferenceError);
return () => { };
};
@(capture(() => Foo))
class Foo {
@(capture(() => Foo))
method() { }
@(capture(() => Foo))
static method() { }
@(capture(() => Foo))
field;
@(capture(() => Foo))
static field;
@(capture(() => Foo))
get getter() { return; }
@(capture(() => Foo))
static get getter() { return; }
@(capture(() => Foo))
set setter(x) { }
@(capture(() => Foo))
static set setter(x) { }
@(capture(() => Foo))
accessor accessor;
@(capture(() => Foo))
static accessor accessor;
}
const originalFoo = Foo;
// Once we get here, these should all reference the now-initialized class,
// either through classBinding (for class element decorators) or through
// className (for decorators on the class itself).
for (const fn of fns) {
assertEq(() => fn(), originalFoo);
}
// Mutating a class binding is allowed in JavaScript. Let's test what
// happens when we do this.
Foo = null;
// As far as I can tell, class decorators should observe this change because
// they are evaluated in the top-level scope.
const firstFn = fns.shift();
assertEq(() => firstFn(), null);
// But I believe class element decorators should not observe this change
// because they are evaluated in the environment that exists for the
// duration of ClassDefinitionEvaluation (i.e. classEnv, not env).
for (const fn of fns) {
assertEq(() => fn(), originalFoo);
}
})();

View File

@ -0,0 +1,39 @@
(() => {
const log = [];
const foo = (n) => {
log.push(n);
return () => { };
};
const computed = {
get method() { log.push(log.length); return Symbol('method'); },
get field() { log.push(log.length); return Symbol('field'); },
get getter() { log.push(log.length); return Symbol('getter'); },
get setter() { log.push(log.length); return Symbol('setter'); },
get accessor() { log.push(log.length); return Symbol('accessor'); },
};
(
@foo(0)
class extends (foo(1), Object) {
@foo(2)
[computed.method]() { }
@foo(4)
static [computed.method]() { }
@foo(6)
[computed.field];
@foo(8)
static [computed.field];
@foo(10)
get [computed.getter]() { return; }
@foo(12)
static get [computed.getter]() { return; }
@foo(14)
set [computed.setter](x) { }
@foo(16)
static set [computed.setter](x) { }
@foo(18)
accessor [computed.accessor];
@foo(20)
static accessor [computed.accessor];
});
assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21');
})();

View File

@ -0,0 +1,38 @@
(() => {
const log = [];
const foo = (n) => {
log.push(n);
return () => { };
};
const computed = {
get method() { log.push(log.length); return Symbol('method'); },
get field() { log.push(log.length); return Symbol('field'); },
get getter() { log.push(log.length); return Symbol('getter'); },
get setter() { log.push(log.length); return Symbol('setter'); },
get accessor() { log.push(log.length); return Symbol('accessor'); },
};
@foo(0)
class Foo extends (foo(1), Object) {
@foo(2)
[computed.method]() { }
@foo(4)
static [computed.method]() { }
@foo(6)
[computed.field];
@foo(8)
static [computed.field];
@foo(10)
get [computed.getter]() { return; }
@foo(12)
static get [computed.getter]() { return; }
@foo(14)
set [computed.setter](x) { }
@foo(16)
static set [computed.setter](x) { }
@foo(18)
accessor [computed.accessor];
@foo(20)
static accessor [computed.accessor];
}
assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21');
})();

View File

@ -0,0 +1,48 @@
(() => {
const fns = [];
const capture = (fn) => {
fns.push(fn);
return () => { };
};
class Outer {
static #foo = 0;
static {
(
@(capture(() => Outer.#foo + 0))
class Foo {
#foo = 10;
@(capture(() => new Foo().#foo + 1))
method() { }
@(capture(() => new Foo().#foo + 2))
static method() { }
@(capture(() => new Foo().#foo + 3))
field;
@(capture(() => new Foo().#foo + 4))
static field;
@(capture(() => new Foo().#foo + 5))
get getter() { return; }
@(capture(() => new Foo().#foo + 6))
static get getter() { return; }
@(capture(() => new Foo().#foo + 7))
set setter(x) { }
@(capture(() => new Foo().#foo + 8))
static set setter(x) { }
@(capture(() => new Foo().#foo + 9))
accessor accessor;
@(capture(() => new Foo().#foo + 10))
static accessor accessor;
});
}
}
// Accessing the outer "#foo" on "Outer" from within the class decorator
// should succeed. Class decorators are evaluated in the outer private
// environment before entering "ClassDefinitionEvaluation".
//
// Accessing the inner "#foo" on "Foo" from within any of the class element
// decorators should also succeed. Class element decorators are evaluated
// in the inner private environment inside "ClassDefinitionEvaluation".
const log = [];
for (const fn of fns)
log.push(fn());
assertEq(() => '' + log, '0,11,12,13,14,15,16,17,18,19,20');
})();

View File

@ -0,0 +1,56 @@
(() => {
const fns = [];
const capture = (fn) => {
fns.push(fn);
return () => { };
};
class Dummy {
static #foo = NaN;
static {
@(capture(() => new Foo().#foo + 0))
class Foo {
#foo = 10;
@(capture(() => new Foo().#foo + 1))
method() { }
@(capture(() => new Foo().#foo + 2))
static method() { }
@(capture(() => new Foo().#foo + 3))
field;
@(capture(() => new Foo().#foo + 4))
static field;
@(capture(() => new Foo().#foo + 5))
get getter() { return; }
@(capture(() => new Foo().#foo + 6))
static get getter() { return; }
@(capture(() => new Foo().#foo + 7))
set setter(x) { }
@(capture(() => new Foo().#foo + 8))
static set setter(x) { }
@(capture(() => new Foo().#foo + 9))
accessor accessor;
@(capture(() => new Foo().#foo + 10))
static accessor accessor;
}
}
}
// Accessing "#foo" in the class decorator should fail. The "#foo" should
// refer to the outer "#foo", not the inner "#foo".
const firstFn = fns.shift();
assertEq(() => {
try {
firstFn();
throw new Error('Expected a TypeError to be thrown');
}
catch (err) {
if (err instanceof TypeError)
return true;
throw err;
}
}, true);
// Accessing "#foo" from any of the class element decorators should succeed.
// Each "#foo" should refer to the inner "#foo", not the outer "#foo".
const log = [];
for (const fn of fns)
log.push(fn());
assertEq(() => '' + log, '11,12,13,14,15,16,17,18,19,20');
})();

View File

@ -0,0 +1,37 @@
(() => {
const log = [];
class Dummy {
static #foo(n) {
log.push(n);
return () => { };
}
static {
const dummy = this;
(
@(dummy.#foo(0))
class extends (dummy.#foo(1), Object) {
@(dummy.#foo(2))
method() { }
@(dummy.#foo(3))
static method() { }
@(dummy.#foo(4))
field;
@(dummy.#foo(5))
static field;
@(dummy.#foo(6))
get getter() { return; }
@(dummy.#foo(7))
static get getter() { return; }
@(dummy.#foo(8))
set setter(x) { }
@(dummy.#foo(9))
static set setter(x) { }
@(dummy.#foo(10))
accessor accessor;
@(dummy.#foo(11))
static accessor accessor;
});
}
}
assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11');
})();

View File

@ -0,0 +1,36 @@
(() => {
const log = [];
class Dummy {
static #foo(n) {
log.push(n);
return () => { };
}
static {
const dummy = this;
@(dummy.#foo(0))
class Foo extends (dummy.#foo(1), Object) {
@(dummy.#foo(2))
method() { }
@(dummy.#foo(3))
static method() { }
@(dummy.#foo(4))
field;
@(dummy.#foo(5))
static field;
@(dummy.#foo(6))
get getter() { return; }
@(dummy.#foo(7))
static get getter() { return; }
@(dummy.#foo(8))
set setter(x) { }
@(dummy.#foo(9))
static set setter(x) { }
@(dummy.#foo(10))
accessor accessor;
@(dummy.#foo(11))
static accessor accessor;
}
}
}
assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11');
})();

View File

@ -0,0 +1,32 @@
(async () => {
const log = [];
const dummy = () => { };
async function wrapper() {
(
@(log.push(await Promise.resolve(0)), dummy)
class extends (log.push(await Promise.resolve(1)), Object) {
@(log.push(await Promise.resolve(2)), dummy)
method() { }
@(log.push(await Promise.resolve(3)), dummy)
static method() { }
@(log.push(await Promise.resolve(4)), dummy)
field;
@(log.push(await Promise.resolve(5)), dummy)
static field;
@(log.push(await Promise.resolve(6)), dummy)
get getter() { return; }
@(log.push(await Promise.resolve(7)), dummy)
static get getter() { return; }
@(log.push(await Promise.resolve(8)), dummy)
set setter(x) { }
@(log.push(await Promise.resolve(9)), dummy)
static set setter(x) { }
@(log.push(await Promise.resolve(10)), dummy)
accessor accessor;
@(log.push(await Promise.resolve(11)), dummy)
static accessor accessor;
});
}
await wrapper();
assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11');
})();

View File

@ -0,0 +1,31 @@
(async () => {
const log = [];
const dummy = () => { };
async function wrapper() {
@(log.push(await Promise.resolve(0)), dummy)
class Foo extends (log.push(await Promise.resolve(1)), Object) {
@(log.push(await Promise.resolve(2)), dummy)
method() { }
@(log.push(await Promise.resolve(3)), dummy)
static method() { }
@(log.push(await Promise.resolve(4)), dummy)
field;
@(log.push(await Promise.resolve(5)), dummy)
static field;
@(log.push(await Promise.resolve(6)), dummy)
get getter() { return; }
@(log.push(await Promise.resolve(7)), dummy)
static get getter() { return; }
@(log.push(await Promise.resolve(8)), dummy)
set setter(x) { }
@(log.push(await Promise.resolve(9)), dummy)
static set setter(x) { }
@(log.push(await Promise.resolve(10)), dummy)
accessor accessor;
@(log.push(await Promise.resolve(11)), dummy)
static accessor accessor;
}
}
await wrapper();
assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11');
})();

View File

@ -0,0 +1,37 @@
(() => {
const log = [];
const dummy = () => { };
const ctx = {
foo(n) {
log.push(n);
}
};
function wrapper() {
(
@(assertEq(() => this.foo(0), undefined), dummy)
class extends (assertEq(() => this.foo(1), undefined), Object) {
@(assertEq(() => this.foo(2), undefined), dummy)
method() { }
@(assertEq(() => this.foo(3), undefined), dummy)
static method() { }
@(assertEq(() => this.foo(4), undefined), dummy)
field;
@(assertEq(() => this.foo(5), undefined), dummy)
static field;
@(assertEq(() => this.foo(6), undefined), dummy)
get getter() { return; }
@(assertEq(() => this.foo(7), undefined), dummy)
static get getter() { return; }
@(assertEq(() => this.foo(8), undefined), dummy)
set setter(x) { }
@(assertEq(() => this.foo(9), undefined), dummy)
static set setter(x) { }
@(assertEq(() => this.foo(10), undefined), dummy)
accessor accessor;
@(assertEq(() => this.foo(11), undefined), dummy)
static accessor accessor;
});
}
wrapper.call(ctx);
assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11');
})();

View File

@ -0,0 +1,36 @@
(() => {
const log = [];
const dummy = () => { };
const ctx = {
foo(n) {
log.push(n);
}
};
function wrapper() {
@(assertEq(() => this.foo(0), undefined), dummy)
class Foo extends (assertEq(() => this.foo(1), undefined), Object) {
@(assertEq(() => this.foo(2), undefined), dummy)
method() { }
@(assertEq(() => this.foo(3), undefined), dummy)
static method() { }
@(assertEq(() => this.foo(4), undefined), dummy)
field;
@(assertEq(() => this.foo(5), undefined), dummy)
static field;
@(assertEq(() => this.foo(6), undefined), dummy)
get getter() { return; }
@(assertEq(() => this.foo(7), undefined), dummy)
static get getter() { return; }
@(assertEq(() => this.foo(8), undefined), dummy)
set setter(x) { }
@(assertEq(() => this.foo(9), undefined), dummy)
static set setter(x) { }
@(assertEq(() => this.foo(10), undefined), dummy)
accessor accessor;
@(assertEq(() => this.foo(11), undefined), dummy)
static accessor accessor;
}
}
wrapper.call(ctx);
assertEq(() => '' + log, '0,1,2,3,4,5,6,7,8,9,10,11');
})();

View File

@ -0,0 +1,68 @@
(() => {
let counter = 0;
const dec = (_, ctx) => {
ctx.metadata[ctx.name] = counter++;
};
const Foo =
@dec
class {
@dec
instanceField;
@dec
accessor instanceAccessor;
@dec
instanceMethod() { }
@dec
get instanceGetter() { return; }
@dec
set instanceSetter(_) { }
@dec
static staticField;
@dec
static accessor staticAccessor;
@dec
static staticMethod() { }
@dec
static get staticGetter() { return; }
@dec
static set staticSetter(_) { }
}, Bar =
@dec
class extends Foo {
@dec
#instanceField;
@dec
accessor #instanceAccessor;
@dec
#instanceMethod() { }
@dec
get #instanceGetter() { return; }
@dec
set #instanceSetter(_) { }
@dec
static #staticField;
@dec
static accessor #staticAccessor;
@dec
static #staticMethod() { }
@dec
static get #staticGetter() { return; }
@dec
static set #staticSetter(_) { }
};
const order = (meta) => '' + [
meta['staticAccessor'], meta['staticMethod'], meta['staticGetter'], meta['staticSetter'],
meta['#staticAccessor'], meta['#staticMethod'], meta['#staticGetter'], meta['#staticSetter'],
meta['instanceAccessor'], meta['instanceMethod'], meta['instanceGetter'], meta['instanceSetter'],
meta['#instanceAccessor'], meta['#instanceMethod'], meta['#instanceGetter'], meta['#instanceSetter'],
meta['staticField'], meta['#staticField'],
meta['instanceField'], meta['#instanceField'],
meta['Foo'], meta['Bar'],
];
const foo = Foo[Symbol.metadata];
const bar = Bar[Symbol.metadata];
assertEq(() => order(foo), '0,1,2,3,,,,,4,5,6,7,,,,,8,,9,,10,');
assertEq(() => Object.getPrototypeOf(foo), null);
assertEq(() => order(bar), '0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21');
assertEq(() => Object.getPrototypeOf(bar), foo);
})();

View File

@ -0,0 +1,67 @@
(() => {
let counter = 0;
const dec = (_, ctx) => {
ctx.metadata[ctx.name] = counter++;
};
@dec
class Foo {
@dec
instanceField;
@dec
accessor instanceAccessor;
@dec
instanceMethod() { }
@dec
get instanceGetter() { return; }
@dec
set instanceSetter(_) { }
@dec
static staticField;
@dec
static accessor staticAccessor;
@dec
static staticMethod() { }
@dec
static get staticGetter() { return; }
@dec
static set staticSetter(_) { }
}
@dec
class Bar extends Foo {
@dec
#instanceField;
@dec
accessor #instanceAccessor;
@dec
#instanceMethod() { }
@dec
get #instanceGetter() { return; }
@dec
set #instanceSetter(_) { }
@dec
static #staticField;
@dec
static accessor #staticAccessor;
@dec
static #staticMethod() { }
@dec
static get #staticGetter() { return; }
@dec
static set #staticSetter(_) { }
}
const order = (meta) => '' + [
meta['staticAccessor'], meta['staticMethod'], meta['staticGetter'], meta['staticSetter'],
meta['#staticAccessor'], meta['#staticMethod'], meta['#staticGetter'], meta['#staticSetter'],
meta['instanceAccessor'], meta['instanceMethod'], meta['instanceGetter'], meta['instanceSetter'],
meta['#instanceAccessor'], meta['#instanceMethod'], meta['#instanceGetter'], meta['#instanceSetter'],
meta['staticField'], meta['#staticField'],
meta['instanceField'], meta['#instanceField'],
meta['Foo'], meta['Bar'],
];
const foo = Foo[Symbol.metadata];
const bar = Bar[Symbol.metadata];
assertEq(() => order(foo), '0,1,2,3,,,,,4,5,6,7,,,,,8,,9,,10,');
assertEq(() => Object.getPrototypeOf(foo), null);
assertEq(() => order(bar), '0,1,2,3,11,12,13,14,4,5,6,7,15,16,17,18,8,19,9,20,10,21');
assertEq(() => Object.getPrototypeOf(bar), foo);
})();

View File

@ -0,0 +1,30 @@
(() => {
const dec = (key) => (value, ctx) => {
assertEq(() => value, undefined);
assertEq(() => ctx.kind, 'field');
assertEq(() => ctx.name, key);
assertEq(() => ctx.static, false);
assertEq(() => ctx.private, false);
assertEq(() => ctx.access.has({ [key]: false }), true);
assertEq(() => ctx.access.has({ bar: true }), false);
assertEq(() => ctx.access.get({ [key]: 123 }), 123);
assertEq(() => {
const obj = {};
ctx.access.set(obj, 321);
return obj[key];
}, 321);
};
const bar = Symbol('bar');
const baz = Symbol();
class Foo {
@dec('foo')
foo = 123;
@dec(bar)
[bar] = 123;
@dec(baz)
[baz] = 123;
}
assertEq(() => new Foo().foo, 123);
assertEq(() => new Foo()[bar], 123);
assertEq(() => new Foo()[baz], 123);
})();

View File

@ -0,0 +1,28 @@
(() => {
let lateAsserts;
const dec = (value, ctx) => {
assertEq(() => value, undefined);
assertEq(() => ctx.kind, 'field');
assertEq(() => ctx.name, '#foo');
assertEq(() => ctx.static, false);
assertEq(() => ctx.private, true);
lateAsserts = () => {
assertEq(() => ctx.access.has(new Foo), true);
assertEq(() => ctx.access.has({}), false);
assertEq(() => ctx.access.get(new Foo), 123);
assertEq(() => {
const obj = new Foo;
ctx.access.set(obj, 321);
return get$foo(obj);
}, 321);
};
};
let get$foo;
class Foo {
@dec
#foo = 123;
static { get$foo = x => x.#foo; }
}
assertEq(() => get$foo(new Foo()), 123);
lateAsserts();
})();

View File

@ -0,0 +1,27 @@
(() => {
let lateAsserts;
const dec = (value, ctx) => {
assertEq(() => value, undefined);
assertEq(() => ctx.kind, 'field');
assertEq(() => ctx.name, '#foo');
assertEq(() => ctx.static, true);
assertEq(() => ctx.private, true);
lateAsserts = () => {
assertEq(() => ctx.access.has(Foo), true);
assertEq(() => ctx.access.has({}), false);
assertEq(() => ctx.access.get(Foo), 123);
assertEq(() => {
ctx.access.set(Foo, 321);
return get$foo(Foo);
}, 321);
};
};
let get$foo;
class Foo {
@dec
static #foo = 123;
static { get$foo = x => x.#foo; }
}
assertEq(() => get$foo(Foo), 123);
lateAsserts();
})();

View File

@ -0,0 +1,30 @@
(() => {
const dec = (key) => (value, ctx) => {
assertEq(() => value, undefined);
assertEq(() => ctx.kind, 'field');
assertEq(() => ctx.name, key);
assertEq(() => ctx.static, true);
assertEq(() => ctx.private, false);
assertEq(() => ctx.access.has({ [key]: false }), true);
assertEq(() => ctx.access.has({ bar: true }), false);
assertEq(() => ctx.access.get({ [key]: 123 }), 123);
assertEq(() => {
const obj = {};
ctx.access.set(obj, 321);
return obj[key];
}, 321);
};
const bar = Symbol('bar');
const baz = Symbol();
class Foo {
@dec('foo')
static foo = 123;
@dec(bar)
static [bar] = 123;
@dec(baz)
static [baz] = 123;
}
assertEq(() => Foo.foo, 123);
assertEq(() => Foo[bar], 123);
assertEq(() => Foo[baz], 123);
})();

View File

@ -0,0 +1,22 @@
(() => {
let oldAddInitializer;
let got;
const dec = (value, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
foo;
}
assertEq(() => got, undefined);
const instance = new Foo;
assertEq(() => got.this, instance);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,22 @@
(() => {
let oldAddInitializer;
let got;
const dec = (value, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
#foo;
}
assertEq(() => got, undefined);
const instance = new Foo;
assertEq(() => got.this, instance);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,20 @@
(() => {
let oldAddInitializer;
let got;
const dec = (value, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
static #foo;
}
assertEq(() => got.this, Foo);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,20 @@
(() => {
let oldAddInitializer;
let got;
const dec = (value, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
static foo;
}
assertEq(() => got.this, Foo);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,22 @@
(() => {
const log = [];
const dec1 = (value, ctx) => {
log.push(2);
return () => log.push(4);
};
const dec2 = (value, ctx) => {
log.push(1);
return () => log.push(5);
};
log.push(0);
class Foo {
@dec1
@dec2
foo = 123;
}
log.push(3);
var obj = new Foo();
log.push(6);
assertEq(() => obj.foo, 6);
assertEq(() => log + '', '0,1,2,3,4,5,6');
})();

View File

@ -0,0 +1,24 @@
(() => {
const log = [];
const dec1 = (value, ctx) => {
log.push(2);
return () => log.push(4);
};
const dec2 = (value, ctx) => {
log.push(1);
return () => log.push(5);
};
log.push(0);
let get$foo;
class Foo {
@dec1
@dec2
#foo = 123;
static { get$foo = x => x.#foo; }
}
log.push(3);
var obj = new Foo();
log.push(6);
assertEq(() => get$foo(obj), 6);
assertEq(() => log + '', '0,1,2,3,4,5,6');
})();

View File

@ -0,0 +1,22 @@
(() => {
const log = [];
const dec1 = (value, ctx) => {
log.push(2);
return () => log.push(3);
};
const dec2 = (value, ctx) => {
log.push(1);
return () => log.push(4);
};
log.push(0);
let get$foo;
class Foo {
@dec1
@dec2
static #foo = 123;
static { get$foo = x => x.#foo; }
}
log.push(5);
assertEq(() => get$foo(Foo), 5);
assertEq(() => log + '', '0,1,2,3,4,5');
})();

View File

@ -0,0 +1,20 @@
(() => {
const log = [];
const dec1 = (value, ctx) => {
log.push(2);
return () => log.push(3);
};
const dec2 = (value, ctx) => {
log.push(1);
return () => log.push(4);
};
log.push(0);
class Foo {
@dec1
@dec2
static foo = 123;
}
log.push(5);
assertEq(() => Foo.foo, 5);
assertEq(() => log + '', '0,1,2,3,4,5');
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (value, ctx) => {
return null;
};
class Foo {
@dec
foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (value, ctx) => {
return null;
};
class Foo {
@dec
#foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (value, ctx) => {
return null;
};
class Foo {
@dec
static #foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (value, ctx) => {
return null;
};
class Foo {
@dec
static foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (value, ctx) => {
return {};
};
class Foo {
@dec
foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (value, ctx) => {
return {};
};
class Foo {
@dec
#foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (value, ctx) => {
return {};
};
class Foo {
@dec
static #foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (value, ctx) => {
return {};
};
class Foo {
@dec
static foo;
}
}, TypeError);
})();

View File

@ -0,0 +1,20 @@
(() => {
let log = [];
const dec = (value, ctx) => {
return function (x) {
assertEq(() => this instanceof Foo, true);
return log.push('foo' in this, 'bar' in this, x);
};
};
class Foo {
@dec
foo = 123;
@dec
bar;
}
assertEq(() => log + '', '');
var obj = new Foo;
assertEq(() => obj.foo, 3);
assertEq(() => obj.bar, 6);
assertEq(() => log + '', 'false,false,123,true,false,');
})();

View File

@ -0,0 +1,30 @@
(() => {
let log = [];
const dec = (value, ctx) => {
return function (x) {
assertEq(() => this instanceof Foo, true);
return log.push(has$foo(this), has$bar(this), x);
};
};
let has$foo;
let has$bar;
let get$foo;
let get$bar;
class Foo {
@dec
#foo = 123;
@dec
#bar;
static {
has$foo = x => #foo in x;
has$bar = x => #bar in x;
get$foo = x => x.#foo;
get$bar = x => x.#bar;
}
}
assertEq(() => log + '', '');
var obj = new Foo;
assertEq(() => get$foo(obj), 3);
assertEq(() => get$bar(obj), 6);
assertEq(() => log + '', 'false,false,123,true,false,');
})();

View File

@ -0,0 +1,31 @@
(() => {
let foo;
let log = [];
const dec = (value, ctx) => {
return function (x) {
assertEq(() => this, foo);
return log.push(has$foo(this), has$bar(this), x);
};
};
assertEq(() => log + '', '');
let has$foo;
let has$bar;
let get$foo;
let get$bar;
class Foo {
static {
foo = Foo;
has$foo = x => #foo in x;
has$bar = x => #bar in x;
get$foo = x => x.#foo;
get$bar = x => x.#bar;
}
@dec
static #foo = 123;
@dec
static #bar;
}
assertEq(() => get$foo(Foo), 3);
assertEq(() => get$bar(Foo), 6);
assertEq(() => log + '', 'false,false,123,true,false,');
})();

View File

@ -0,0 +1,23 @@
(() => {
let foo;
let log = [];
const dec = (value, ctx) => {
return function (x) {
assertEq(() => this, foo);
return log.push('foo' in this, 'bar' in this, x);
};
};
assertEq(() => log + '', '');
class Foo {
static {
foo = Foo;
}
@dec
static foo = 123;
@dec
static bar;
}
assertEq(() => Foo.foo, 3);
assertEq(() => Foo.bar, 6);
assertEq(() => log + '', 'false,false,123,true,false,');
})();

View File

@ -0,0 +1,28 @@
(() => {
const dec = (key, name) => (fn, ctx) => {
assertEq(() => typeof fn, 'function');
assertEq(() => fn.name, name);
assertEq(() => ctx.kind, 'getter');
assertEq(() => ctx.name, key);
assertEq(() => ctx.static, false);
assertEq(() => ctx.private, false);
assertEq(() => ctx.access.has({ [key]: false }), true);
assertEq(() => ctx.access.has({ bar: true }), false);
assertEq(() => ctx.access.get({ [key]: 123 }), 123);
assertEq(() => 'set' in ctx.access, false);
};
const bar = Symbol('bar');
const baz = Symbol();
class Foo {
bar = 123;
@dec('foo', 'get foo')
get foo() { return this.bar; }
@dec(bar, 'get [bar]')
get [bar]() { return this.bar; }
@dec(baz, 'get ')
get [baz]() { return this.bar; }
}
assertEq(() => new Foo().foo, 123);
assertEq(() => new Foo()[bar], 123);
assertEq(() => new Foo()[baz], 123);
})();

View File

@ -0,0 +1,26 @@
(() => {
let lateAsserts;
const dec = (fn, ctx) => {
assertEq(() => typeof fn, 'function');
assertEq(() => fn.name, 'get #foo');
assertEq(() => ctx.kind, 'getter');
assertEq(() => ctx.name, '#foo');
assertEq(() => ctx.static, false);
assertEq(() => ctx.private, true);
lateAsserts = () => {
assertEq(() => ctx.access.has(new Foo), true);
assertEq(() => ctx.access.has({}), false);
assertEq(() => ctx.access.get(new Foo), 123);
assertEq(() => 'set' in ctx.access, false);
};
};
let get$foo;
class Foo {
#bar = 123;
@dec
get #foo() { return this.#bar; }
static { get$foo = x => x.#foo; }
}
assertEq(() => get$foo(new Foo), 123);
lateAsserts();
})();

View File

@ -0,0 +1,26 @@
(() => {
let lateAsserts;
const dec = (fn, ctx) => {
assertEq(() => typeof fn, 'function');
assertEq(() => fn.name, 'get #foo');
assertEq(() => ctx.kind, 'getter');
assertEq(() => ctx.name, '#foo');
assertEq(() => ctx.static, true);
assertEq(() => ctx.private, true);
lateAsserts = () => {
assertEq(() => ctx.access.has(Foo), true);
assertEq(() => ctx.access.has({}), false);
assertEq(() => ctx.access.get(Foo), 123);
assertEq(() => 'set' in ctx.access, false);
};
};
let get$foo;
class Foo {
static #bar = 123;
@dec
static get #foo() { return this.#bar; }
static { get$foo = x => x.#foo; }
}
assertEq(() => get$foo(Foo), 123);
lateAsserts();
})();

View File

@ -0,0 +1,28 @@
(() => {
const dec = (key, name) => (fn, ctx) => {
assertEq(() => typeof fn, 'function');
assertEq(() => fn.name, name);
assertEq(() => ctx.kind, 'getter');
assertEq(() => ctx.name, key);
assertEq(() => ctx.static, true);
assertEq(() => ctx.private, false);
assertEq(() => ctx.access.has({ [key]: false }), true);
assertEq(() => ctx.access.has({ bar: true }), false);
assertEq(() => ctx.access.get({ [key]: 123 }), 123);
assertEq(() => 'set' in ctx.access, false);
};
const bar = Symbol('bar');
const baz = Symbol();
class Foo {
static bar = 123;
@dec('foo', 'get foo')
static get foo() { return this.bar; }
@dec(bar, 'get [bar]')
static get [bar]() { return this.bar; }
@dec(baz, 'get ')
static get [baz]() { return this.bar; }
}
assertEq(() => Foo.foo, 123);
assertEq(() => Foo[bar], 123);
assertEq(() => Foo[baz], 123);
})();

View File

@ -0,0 +1,22 @@
(() => {
let oldAddInitializer;
let got;
const dec = (fn, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
get foo() { return; }
}
assertEq(() => got, undefined);
const instance = new Foo;
assertEq(() => got.this, instance);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,22 @@
(() => {
let oldAddInitializer;
let got;
const dec = (fn, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
get #foo() { return; }
}
assertEq(() => got, undefined);
const instance = new Foo;
assertEq(() => got.this, instance);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,20 @@
(() => {
let oldAddInitializer;
let got;
const dec = (fn, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
static get #foo() { return; }
}
assertEq(() => got.this, Foo);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,20 @@
(() => {
let oldAddInitializer;
let got;
const dec = (fn, ctx) => {
ctx.addInitializer(function (...args) {
got = { this: this, args };
});
if (oldAddInitializer)
assertThrows(() => oldAddInitializer(() => { }), TypeError);
assertThrows(() => ctx.addInitializer({}), TypeError);
oldAddInitializer = ctx.addInitializer;
};
class Foo {
@dec
@dec
static get foo() { return; }
}
assertEq(() => got.this, Foo);
assertEq(() => got.args.length, 0);
})();

View File

@ -0,0 +1,32 @@
(() => {
const log = [];
let bar;
let baz;
const dec1 = (fn, ctx) => {
log.push(2);
bar = function () {
log.push(4);
return fn.call(this);
};
return bar;
};
const dec2 = (fn, ctx) => {
log.push(1);
baz = function () {
log.push(5);
return fn.call(this);
};
return baz;
};
log.push(0);
class Foo {
@dec1
@dec2
get foo() { return log.push(6); }
}
log.push(3);
new Foo().foo;
log.push(7);
assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo').get, bar);
assertEq(() => log + '', '0,1,2,3,4,5,6,7');
})();

View File

@ -0,0 +1,33 @@
(() => {
const log = [];
let bar;
let baz;
const dec1 = (fn, ctx) => {
log.push(2);
bar = function () {
log.push(4);
return fn.call(this);
};
return bar;
};
const dec2 = (fn, ctx) => {
log.push(1);
baz = function () {
log.push(5);
return fn.call(this);
};
return baz;
};
log.push(0);
let get$foo;
class Foo {
@dec1
@dec2
get #foo() { return log.push(6); }
static { get$foo = x => x.#foo; }
}
log.push(3);
assertEq(() => get$foo(new Foo), 7);
log.push(7);
assertEq(() => log + '', '0,1,2,3,4,5,6,7');
})();

View File

@ -0,0 +1,33 @@
(() => {
const log = [];
let bar;
let baz;
const dec1 = (fn, ctx) => {
log.push(2);
bar = function () {
log.push(4);
return fn.call(this);
};
return bar;
};
const dec2 = (fn, ctx) => {
log.push(1);
baz = function () {
log.push(5);
return fn.call(this);
};
return baz;
};
log.push(0);
let get$foo;
class Foo {
@dec1
@dec2
static get #foo() { return log.push(6); }
static { get$foo = x => x.#foo; }
}
log.push(3);
assertEq(() => get$foo(Foo), 7);
log.push(7);
assertEq(() => log + '', '0,1,2,3,4,5,6,7');
})();

View File

@ -0,0 +1,32 @@
(() => {
const log = [];
let bar;
let baz;
const dec1 = (fn, ctx) => {
log.push(2);
bar = function () {
log.push(4);
return fn.call(this);
};
return bar;
};
const dec2 = (fn, ctx) => {
log.push(1);
baz = function () {
log.push(5);
return fn.call(this);
};
return baz;
};
log.push(0);
class Foo {
@dec1
@dec2
static get foo() { return log.push(6); }
}
log.push(3);
Foo.foo;
log.push(7);
assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo').get, bar);
assertEq(() => log + '', '0,1,2,3,4,5,6,7');
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (fn, ctx) => {
return null;
};
class Foo {
@dec
get foo() { return; }
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (fn, ctx) => {
return null;
};
class Foo {
@dec
get #foo() { return; }
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (fn, ctx) => {
return null;
};
class Foo {
@dec
static get #foo() { return; }
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (fn, ctx) => {
return null;
};
class Foo {
@dec
static get foo() { return; }
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (fn, ctx) => {
return {};
};
class Foo {
@dec
get foo() { return; }
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (fn, ctx) => {
return {};
};
class Foo {
@dec
get #foo() { return; }
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (fn, ctx) => {
return {};
};
class Foo {
@dec
static get #foo() { return; }
}
}, TypeError);
})();

View File

@ -0,0 +1,11 @@
(() => {
assertThrows(() => {
const dec = (fn, ctx) => {
return {};
};
class Foo {
@dec
static get foo() { return; }
}
}, TypeError);
})();

View File

@ -0,0 +1,14 @@
(() => {
let bar;
const dec = (fn, ctx) => {
bar = function () { return fn.call(this) + 1; };
return bar;
};
class Foo {
bar = 123;
@dec
get foo() { return this.bar; }
}
assertEq(() => Object.getOwnPropertyDescriptor(Foo.prototype, 'foo').get, bar);
assertEq(() => new Foo().foo, 124);
})();

View File

@ -0,0 +1,15 @@
(() => {
let bar;
const dec = (fn, ctx) => {
bar = function () { return fn.call(this) + 1; };
return bar;
};
let get$foo;
class Foo {
#bar = 123;
@dec
get #foo() { return this.#bar; }
static { get$foo = x => x.#foo; }
}
assertEq(() => get$foo(new Foo), 124);
})();

View File

@ -0,0 +1,15 @@
(() => {
let bar;
const dec = (fn, ctx) => {
bar = function () { return fn.call(this) + 1; };
return bar;
};
let get$foo;
class Foo {
static #bar = 123;
@dec
static get #foo() { return this.#bar; }
static { get$foo = x => x.#foo; }
}
assertEq(() => get$foo(Foo), 124);
})();

View File

@ -0,0 +1,14 @@
(() => {
let bar;
const dec = (fn, ctx) => {
bar = function () { return fn.call(this) + 1; };
return bar;
};
class Foo {
static bar = 123;
@dec
static get foo() { return this.bar; }
}
assertEq(() => Object.getOwnPropertyDescriptor(Foo, 'foo').get, bar);
assertEq(() => Foo.foo, 124);
})();

View File

@ -0,0 +1,236 @@
(() => {
const log = [];
// Class decorators
const classDec1 = (cls, ctxClass) => {
log.push('c2');
if (!assertEq(() => typeof ctxClass.addInitializer, 'function'))
return;
ctxClass.addInitializer(() => log.push('c5'));
ctxClass.addInitializer(() => log.push('c6'));
};
const classDec2 = (cls, ctxClass) => {
log.push('c1');
if (!assertEq(() => typeof ctxClass.addInitializer, 'function'))
return;
ctxClass.addInitializer(() => log.push('c3'));
ctxClass.addInitializer(() => log.push('c4'));
};
// Method decorators
const methodDec1 = (fn, ctxMethod) => {
log.push('m2');
if (!assertEq(() => typeof ctxMethod.addInitializer, 'function'))
return;
ctxMethod.addInitializer(() => log.push('m5'));
ctxMethod.addInitializer(() => log.push('m6'));
};
const methodDec2 = (fn, ctxMethod) => {
log.push('m1');
if (!assertEq(() => typeof ctxMethod.addInitializer, 'function'))
return;
ctxMethod.addInitializer(() => log.push('m3'));
ctxMethod.addInitializer(() => log.push('m4'));
};
const staticMethodDec1 = (fn, ctxStaticMethod) => {
log.push('M2');
if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function'))
return;
ctxStaticMethod.addInitializer(() => log.push('M5'));
ctxStaticMethod.addInitializer(() => log.push('M6'));
};
const staticMethodDec2 = (fn, ctxStaticMethod) => {
log.push('M1');
if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function'))
return;
ctxStaticMethod.addInitializer(() => log.push('M3'));
ctxStaticMethod.addInitializer(() => log.push('M4'));
};
// Field decorators
const fieldDec1 = (value, ctxField) => {
log.push('f2');
if (!assertEq(() => typeof ctxField.addInitializer, 'function'))
return;
ctxField.addInitializer(() => log.push('f5'));
ctxField.addInitializer(() => log.push('f6'));
return () => { log.push('f7'); };
};
const fieldDec2 = (value, ctxField) => {
log.push('f1');
if (!assertEq(() => typeof ctxField.addInitializer, 'function'))
return;
ctxField.addInitializer(() => log.push('f3'));
ctxField.addInitializer(() => log.push('f4'));
return () => { log.push('f8'); };
};
const staticFieldDec1 = (value, ctxStaticField) => {
log.push('F2');
if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function'))
return;
ctxStaticField.addInitializer(() => log.push('F5'));
ctxStaticField.addInitializer(() => log.push('F6'));
return () => { log.push('F7'); };
};
const staticFieldDec2 = (value, ctxStaticField) => {
log.push('F1');
if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function'))
return;
ctxStaticField.addInitializer(() => log.push('F3'));
ctxStaticField.addInitializer(() => log.push('F4'));
return () => { log.push('F8'); };
};
// Getter decorators
const getterDec1 = (fn, ctxGetter) => {
log.push('g2');
if (!assertEq(() => typeof ctxGetter.addInitializer, 'function'))
return;
ctxGetter.addInitializer(() => log.push('g5'));
ctxGetter.addInitializer(() => log.push('g6'));
};
const getterDec2 = (fn, ctxGetter) => {
log.push('g1');
if (!assertEq(() => typeof ctxGetter.addInitializer, 'function'))
return;
ctxGetter.addInitializer(() => log.push('g3'));
ctxGetter.addInitializer(() => log.push('g4'));
};
const staticGetterDec1 = (fn, ctxStaticGetter) => {
log.push('G2');
if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function'))
return;
ctxStaticGetter.addInitializer(() => log.push('G5'));
ctxStaticGetter.addInitializer(() => log.push('G6'));
};
const staticGetterDec2 = (fn, ctxStaticGetter) => {
log.push('G1');
if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function'))
return;
ctxStaticGetter.addInitializer(() => log.push('G3'));
ctxStaticGetter.addInitializer(() => log.push('G4'));
};
// Setter decorators
const setterDec1 = (fn, ctxSetter) => {
log.push('s2');
if (!assertEq(() => typeof ctxSetter.addInitializer, 'function'))
return;
ctxSetter.addInitializer(() => log.push('s5'));
ctxSetter.addInitializer(() => log.push('s6'));
};
const setterDec2 = (fn, ctxSetter) => {
log.push('s1');
if (!assertEq(() => typeof ctxSetter.addInitializer, 'function'))
return;
ctxSetter.addInitializer(() => log.push('s3'));
ctxSetter.addInitializer(() => log.push('s4'));
};
const staticSetterDec1 = (fn, ctxStaticSetter) => {
log.push('S2');
if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function'))
return;
ctxStaticSetter.addInitializer(() => log.push('S5'));
ctxStaticSetter.addInitializer(() => log.push('S6'));
};
const staticSetterDec2 = (fn, ctxStaticSetter) => {
log.push('S1');
if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function'))
return;
ctxStaticSetter.addInitializer(() => log.push('S3'));
ctxStaticSetter.addInitializer(() => log.push('S4'));
};
// Auto-accessor decorators
const accessorDec1 = (target, ctxAccessor) => {
log.push('a2');
if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function'))
return;
ctxAccessor.addInitializer(() => log.push('a5'));
ctxAccessor.addInitializer(() => log.push('a6'));
return { init() { log.push('a7'); } };
};
const accessorDec2 = (target, ctxAccessor) => {
log.push('a1');
if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function'))
return;
ctxAccessor.addInitializer(() => log.push('a3'));
ctxAccessor.addInitializer(() => log.push('a4'));
return { init() { log.push('a8'); } };
};
const staticAccessorDec1 = (target, ctxStaticAccessor) => {
log.push('A2');
if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function'))
return;
ctxStaticAccessor.addInitializer(() => log.push('A5'));
ctxStaticAccessor.addInitializer(() => log.push('A6'));
return { init() { log.push('A7'); } };
};
const staticAccessorDec2 = (target, ctxStaticAccessor) => {
log.push('A1');
if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function'))
return;
ctxStaticAccessor.addInitializer(() => log.push('A3'));
ctxStaticAccessor.addInitializer(() => log.push('A4'));
return { init() { log.push('A8'); } };
};
log.push('start');
const Foo =
@classDec1
@classDec2
class extends (log.push('extends'), Object) {
static { log.push('static:start'); }
constructor() {
log.push('ctor:start');
super();
log.push('ctor:end');
}
@methodDec1
@methodDec2
#method() { }
@staticMethodDec1
@staticMethodDec2
static #staticMethod() { }
@fieldDec1
@fieldDec2
#field;
@staticFieldDec1
@staticFieldDec2
static #staticField;
@getterDec1
@getterDec2
get #getter() { return; }
@staticGetterDec1
@staticGetterDec2
static get #staticGetter() { return; }
@setterDec1
@setterDec2
set #setter(x) { }
@staticSetterDec1
@staticSetterDec2
static set #staticSetter(x) { }
@accessorDec1
@accessorDec2
accessor #accessor;
@staticAccessorDec1
@staticAccessorDec2
static accessor #staticAccessor;
static { log.push('static:end'); }
};
log.push('after');
new Foo;
log.push('end');
assertEq(() => log + '', 'start,extends,' +
'M1,M2,G1,G2,S1,S2,A1,A2,' + // For each element e of staticElements if e.[[Kind]] is not field
'm1,m2,g1,g2,s1,s2,a1,a2,' + // For each element e of instanceElements if e.[[Kind]] is not field
'F1,F2,' + // For each element e of staticElements if e.[[Kind]] is field
'f1,f2,' + // For each element e of instanceElements if e.[[Kind]] is field
'c1,c2,' + // ApplyDecoratorsToClassDefinition
'M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,' + // For each element initializer of staticMethodExtraInitializers
'static:start,' + // For each element elementRecord of staticElements
'F7,F8,F3,F4,F5,F6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'A7,A8,A3,A4,A5,A6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'static:end,' + // For each element elementRecord of staticElements
'c3,c4,c5,c6,' + // For each element initializer of classExtraInitializers
'after,' +
'ctor:start,' +
'm3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,' + // For each element initializer of constructor.[[Initializers]] (a.k.a. instanceMethodExtraInitializers)
'f7,f8,f3,f4,f5,f6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'a7,a8,a3,a4,a5,a6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'ctor:end,' +
'end');
})();

View File

@ -0,0 +1,235 @@
(() => {
const log = [];
// Class decorators
const classDec1 = (cls, ctxClass) => {
log.push('c2');
if (!assertEq(() => typeof ctxClass.addInitializer, 'function'))
return;
ctxClass.addInitializer(() => log.push('c5'));
ctxClass.addInitializer(() => log.push('c6'));
};
const classDec2 = (cls, ctxClass) => {
log.push('c1');
if (!assertEq(() => typeof ctxClass.addInitializer, 'function'))
return;
ctxClass.addInitializer(() => log.push('c3'));
ctxClass.addInitializer(() => log.push('c4'));
};
// Method decorators
const methodDec1 = (fn, ctxMethod) => {
log.push('m2');
if (!assertEq(() => typeof ctxMethod.addInitializer, 'function'))
return;
ctxMethod.addInitializer(() => log.push('m5'));
ctxMethod.addInitializer(() => log.push('m6'));
};
const methodDec2 = (fn, ctxMethod) => {
log.push('m1');
if (!assertEq(() => typeof ctxMethod.addInitializer, 'function'))
return;
ctxMethod.addInitializer(() => log.push('m3'));
ctxMethod.addInitializer(() => log.push('m4'));
};
const staticMethodDec1 = (fn, ctxStaticMethod) => {
log.push('M2');
if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function'))
return;
ctxStaticMethod.addInitializer(() => log.push('M5'));
ctxStaticMethod.addInitializer(() => log.push('M6'));
};
const staticMethodDec2 = (fn, ctxStaticMethod) => {
log.push('M1');
if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function'))
return;
ctxStaticMethod.addInitializer(() => log.push('M3'));
ctxStaticMethod.addInitializer(() => log.push('M4'));
};
// Field decorators
const fieldDec1 = (value, ctxField) => {
log.push('f2');
if (!assertEq(() => typeof ctxField.addInitializer, 'function'))
return;
ctxField.addInitializer(() => log.push('f5'));
ctxField.addInitializer(() => log.push('f6'));
return () => { log.push('f7'); };
};
const fieldDec2 = (value, ctxField) => {
log.push('f1');
if (!assertEq(() => typeof ctxField.addInitializer, 'function'))
return;
ctxField.addInitializer(() => log.push('f3'));
ctxField.addInitializer(() => log.push('f4'));
return () => { log.push('f8'); };
};
const staticFieldDec1 = (value, ctxStaticField) => {
log.push('F2');
if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function'))
return;
ctxStaticField.addInitializer(() => log.push('F5'));
ctxStaticField.addInitializer(() => log.push('F6'));
return () => { log.push('F7'); };
};
const staticFieldDec2 = (value, ctxStaticField) => {
log.push('F1');
if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function'))
return;
ctxStaticField.addInitializer(() => log.push('F3'));
ctxStaticField.addInitializer(() => log.push('F4'));
return () => { log.push('F8'); };
};
// Getter decorators
const getterDec1 = (fn, ctxGetter) => {
log.push('g2');
if (!assertEq(() => typeof ctxGetter.addInitializer, 'function'))
return;
ctxGetter.addInitializer(() => log.push('g5'));
ctxGetter.addInitializer(() => log.push('g6'));
};
const getterDec2 = (fn, ctxGetter) => {
log.push('g1');
if (!assertEq(() => typeof ctxGetter.addInitializer, 'function'))
return;
ctxGetter.addInitializer(() => log.push('g3'));
ctxGetter.addInitializer(() => log.push('g4'));
};
const staticGetterDec1 = (fn, ctxStaticGetter) => {
log.push('G2');
if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function'))
return;
ctxStaticGetter.addInitializer(() => log.push('G5'));
ctxStaticGetter.addInitializer(() => log.push('G6'));
};
const staticGetterDec2 = (fn, ctxStaticGetter) => {
log.push('G1');
if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function'))
return;
ctxStaticGetter.addInitializer(() => log.push('G3'));
ctxStaticGetter.addInitializer(() => log.push('G4'));
};
// Setter decorators
const setterDec1 = (fn, ctxSetter) => {
log.push('s2');
if (!assertEq(() => typeof ctxSetter.addInitializer, 'function'))
return;
ctxSetter.addInitializer(() => log.push('s5'));
ctxSetter.addInitializer(() => log.push('s6'));
};
const setterDec2 = (fn, ctxSetter) => {
log.push('s1');
if (!assertEq(() => typeof ctxSetter.addInitializer, 'function'))
return;
ctxSetter.addInitializer(() => log.push('s3'));
ctxSetter.addInitializer(() => log.push('s4'));
};
const staticSetterDec1 = (fn, ctxStaticSetter) => {
log.push('S2');
if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function'))
return;
ctxStaticSetter.addInitializer(() => log.push('S5'));
ctxStaticSetter.addInitializer(() => log.push('S6'));
};
const staticSetterDec2 = (fn, ctxStaticSetter) => {
log.push('S1');
if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function'))
return;
ctxStaticSetter.addInitializer(() => log.push('S3'));
ctxStaticSetter.addInitializer(() => log.push('S4'));
};
// Auto-accessor decorators
const accessorDec1 = (target, ctxAccessor) => {
log.push('a2');
if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function'))
return;
ctxAccessor.addInitializer(() => log.push('a5'));
ctxAccessor.addInitializer(() => log.push('a6'));
return { init() { log.push('a7'); } };
};
const accessorDec2 = (target, ctxAccessor) => {
log.push('a1');
if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function'))
return;
ctxAccessor.addInitializer(() => log.push('a3'));
ctxAccessor.addInitializer(() => log.push('a4'));
return { init() { log.push('a8'); } };
};
const staticAccessorDec1 = (target, ctxStaticAccessor) => {
log.push('A2');
if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function'))
return;
ctxStaticAccessor.addInitializer(() => log.push('A5'));
ctxStaticAccessor.addInitializer(() => log.push('A6'));
return { init() { log.push('A7'); } };
};
const staticAccessorDec2 = (target, ctxStaticAccessor) => {
log.push('A1');
if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function'))
return;
ctxStaticAccessor.addInitializer(() => log.push('A3'));
ctxStaticAccessor.addInitializer(() => log.push('A4'));
return { init() { log.push('A8'); } };
};
log.push('start');
@classDec1
@classDec2
class Foo extends (log.push('extends'), Object) {
static { log.push('static:start'); }
constructor() {
log.push('ctor:start');
super();
log.push('ctor:end');
}
@methodDec1
@methodDec2
#method() { }
@staticMethodDec1
@staticMethodDec2
static #staticMethod() { }
@fieldDec1
@fieldDec2
#field;
@staticFieldDec1
@staticFieldDec2
static #staticField;
@getterDec1
@getterDec2
get #getter() { return; }
@staticGetterDec1
@staticGetterDec2
static get #staticGetter() { return; }
@setterDec1
@setterDec2
set #setter(x) { }
@staticSetterDec1
@staticSetterDec2
static set #staticSetter(x) { }
@accessorDec1
@accessorDec2
accessor #accessor;
@staticAccessorDec1
@staticAccessorDec2
static accessor #staticAccessor;
static { log.push('static:end'); }
}
log.push('after');
new Foo;
log.push('end');
assertEq(() => log + '', 'start,extends,' +
'M1,M2,G1,G2,S1,S2,A1,A2,' + // For each element e of staticElements if e.[[Kind]] is not field
'm1,m2,g1,g2,s1,s2,a1,a2,' + // For each element e of instanceElements if e.[[Kind]] is not field
'F1,F2,' + // For each element e of staticElements if e.[[Kind]] is field
'f1,f2,' + // For each element e of instanceElements if e.[[Kind]] is field
'c1,c2,' + // ApplyDecoratorsToClassDefinition
'M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,' + // For each element initializer of staticMethodExtraInitializers
'static:start,' + // For each element elementRecord of staticElements
'F7,F8,F3,F4,F5,F6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'A7,A8,A3,A4,A5,A6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'static:end,' + // For each element elementRecord of staticElements
'c3,c4,c5,c6,' + // For each element initializer of classExtraInitializers
'after,' +
'ctor:start,' +
'm3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,' + // For each element initializer of constructor.[[Initializers]] (a.k.a. instanceMethodExtraInitializers)
'f7,f8,f3,f4,f5,f6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'a7,a8,a3,a4,a5,a6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'ctor:end,' +
'end');
})();

View File

@ -0,0 +1,236 @@
(() => {
const log = [];
// Class decorators
const classDec1 = (cls, ctxClass) => {
log.push('c2');
if (!assertEq(() => typeof ctxClass.addInitializer, 'function'))
return;
ctxClass.addInitializer(() => log.push('c5'));
ctxClass.addInitializer(() => log.push('c6'));
};
const classDec2 = (cls, ctxClass) => {
log.push('c1');
if (!assertEq(() => typeof ctxClass.addInitializer, 'function'))
return;
ctxClass.addInitializer(() => log.push('c3'));
ctxClass.addInitializer(() => log.push('c4'));
};
// Method decorators
const methodDec1 = (fn, ctxMethod) => {
log.push('m2');
if (!assertEq(() => typeof ctxMethod.addInitializer, 'function'))
return;
ctxMethod.addInitializer(() => log.push('m5'));
ctxMethod.addInitializer(() => log.push('m6'));
};
const methodDec2 = (fn, ctxMethod) => {
log.push('m1');
if (!assertEq(() => typeof ctxMethod.addInitializer, 'function'))
return;
ctxMethod.addInitializer(() => log.push('m3'));
ctxMethod.addInitializer(() => log.push('m4'));
};
const staticMethodDec1 = (fn, ctxStaticMethod) => {
log.push('M2');
if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function'))
return;
ctxStaticMethod.addInitializer(() => log.push('M5'));
ctxStaticMethod.addInitializer(() => log.push('M6'));
};
const staticMethodDec2 = (fn, ctxStaticMethod) => {
log.push('M1');
if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function'))
return;
ctxStaticMethod.addInitializer(() => log.push('M3'));
ctxStaticMethod.addInitializer(() => log.push('M4'));
};
// Field decorators
const fieldDec1 = (value, ctxField) => {
log.push('f2');
if (!assertEq(() => typeof ctxField.addInitializer, 'function'))
return;
ctxField.addInitializer(() => log.push('f5'));
ctxField.addInitializer(() => log.push('f6'));
return () => { log.push('f7'); };
};
const fieldDec2 = (value, ctxField) => {
log.push('f1');
if (!assertEq(() => typeof ctxField.addInitializer, 'function'))
return;
ctxField.addInitializer(() => log.push('f3'));
ctxField.addInitializer(() => log.push('f4'));
return () => { log.push('f8'); };
};
const staticFieldDec1 = (value, ctxStaticField) => {
log.push('F2');
if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function'))
return;
ctxStaticField.addInitializer(() => log.push('F5'));
ctxStaticField.addInitializer(() => log.push('F6'));
return () => { log.push('F7'); };
};
const staticFieldDec2 = (value, ctxStaticField) => {
log.push('F1');
if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function'))
return;
ctxStaticField.addInitializer(() => log.push('F3'));
ctxStaticField.addInitializer(() => log.push('F4'));
return () => { log.push('F8'); };
};
// Getter decorators
const getterDec1 = (fn, ctxGetter) => {
log.push('g2');
if (!assertEq(() => typeof ctxGetter.addInitializer, 'function'))
return;
ctxGetter.addInitializer(() => log.push('g5'));
ctxGetter.addInitializer(() => log.push('g6'));
};
const getterDec2 = (fn, ctxGetter) => {
log.push('g1');
if (!assertEq(() => typeof ctxGetter.addInitializer, 'function'))
return;
ctxGetter.addInitializer(() => log.push('g3'));
ctxGetter.addInitializer(() => log.push('g4'));
};
const staticGetterDec1 = (fn, ctxStaticGetter) => {
log.push('G2');
if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function'))
return;
ctxStaticGetter.addInitializer(() => log.push('G5'));
ctxStaticGetter.addInitializer(() => log.push('G6'));
};
const staticGetterDec2 = (fn, ctxStaticGetter) => {
log.push('G1');
if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function'))
return;
ctxStaticGetter.addInitializer(() => log.push('G3'));
ctxStaticGetter.addInitializer(() => log.push('G4'));
};
// Setter decorators
const setterDec1 = (fn, ctxSetter) => {
log.push('s2');
if (!assertEq(() => typeof ctxSetter.addInitializer, 'function'))
return;
ctxSetter.addInitializer(() => log.push('s5'));
ctxSetter.addInitializer(() => log.push('s6'));
};
const setterDec2 = (fn, ctxSetter) => {
log.push('s1');
if (!assertEq(() => typeof ctxSetter.addInitializer, 'function'))
return;
ctxSetter.addInitializer(() => log.push('s3'));
ctxSetter.addInitializer(() => log.push('s4'));
};
const staticSetterDec1 = (fn, ctxStaticSetter) => {
log.push('S2');
if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function'))
return;
ctxStaticSetter.addInitializer(() => log.push('S5'));
ctxStaticSetter.addInitializer(() => log.push('S6'));
};
const staticSetterDec2 = (fn, ctxStaticSetter) => {
log.push('S1');
if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function'))
return;
ctxStaticSetter.addInitializer(() => log.push('S3'));
ctxStaticSetter.addInitializer(() => log.push('S4'));
};
// Auto-accessor decorators
const accessorDec1 = (target, ctxAccessor) => {
log.push('a2');
if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function'))
return;
ctxAccessor.addInitializer(() => log.push('a5'));
ctxAccessor.addInitializer(() => log.push('a6'));
return { init() { log.push('a7'); } };
};
const accessorDec2 = (target, ctxAccessor) => {
log.push('a1');
if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function'))
return;
ctxAccessor.addInitializer(() => log.push('a3'));
ctxAccessor.addInitializer(() => log.push('a4'));
return { init() { log.push('a8'); } };
};
const staticAccessorDec1 = (target, ctxStaticAccessor) => {
log.push('A2');
if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function'))
return;
ctxStaticAccessor.addInitializer(() => log.push('A5'));
ctxStaticAccessor.addInitializer(() => log.push('A6'));
return { init() { log.push('A7'); } };
};
const staticAccessorDec2 = (target, ctxStaticAccessor) => {
log.push('A1');
if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function'))
return;
ctxStaticAccessor.addInitializer(() => log.push('A3'));
ctxStaticAccessor.addInitializer(() => log.push('A4'));
return { init() { log.push('A8'); } };
};
log.push('start');
const Foo =
@classDec1
@classDec2
class extends (log.push('extends'), Object) {
static { log.push('static:start'); }
constructor() {
log.push('ctor:start');
super();
log.push('ctor:end');
}
@methodDec1
@methodDec2
method() { }
@staticMethodDec1
@staticMethodDec2
static method() { }
@fieldDec1
@fieldDec2
field;
@staticFieldDec1
@staticFieldDec2
static field;
@getterDec1
@getterDec2
get getter() { return; }
@staticGetterDec1
@staticGetterDec2
static get getter() { return; }
@setterDec1
@setterDec2
set setter(x) { }
@staticSetterDec1
@staticSetterDec2
static set setter(x) { }
@accessorDec1
@accessorDec2
accessor accessor;
@staticAccessorDec1
@staticAccessorDec2
static accessor accessor;
static { log.push('static:end'); }
};
log.push('after');
new Foo;
log.push('end');
assertEq(() => log + '', 'start,extends,' +
'M1,M2,G1,G2,S1,S2,A1,A2,' + // For each element e of staticElements if e.[[Kind]] is not field
'm1,m2,g1,g2,s1,s2,a1,a2,' + // For each element e of instanceElements if e.[[Kind]] is not field
'F1,F2,' + // For each element e of staticElements if e.[[Kind]] is field
'f1,f2,' + // For each element e of instanceElements if e.[[Kind]] is field
'c1,c2,' + // ApplyDecoratorsToClassDefinition
'M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,' + // For each element initializer of staticMethodExtraInitializers
'static:start,' + // For each element elementRecord of staticElements
'F7,F8,F3,F4,F5,F6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'A7,A8,A3,A4,A5,A6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'static:end,' + // For each element elementRecord of staticElements
'c3,c4,c5,c6,' + // For each element initializer of classExtraInitializers
'after,' +
'ctor:start,' +
'm3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,' + // For each element initializer of constructor.[[Initializers]] (a.k.a. instanceMethodExtraInitializers)
'f7,f8,f3,f4,f5,f6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'a7,a8,a3,a4,a5,a6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'ctor:end,' +
'end');
})();

View File

@ -0,0 +1,235 @@
(() => {
const log = [];
// Class decorators
const classDec1 = (cls, ctxClass) => {
log.push('c2');
if (!assertEq(() => typeof ctxClass.addInitializer, 'function'))
return;
ctxClass.addInitializer(() => log.push('c5'));
ctxClass.addInitializer(() => log.push('c6'));
};
const classDec2 = (cls, ctxClass) => {
log.push('c1');
if (!assertEq(() => typeof ctxClass.addInitializer, 'function'))
return;
ctxClass.addInitializer(() => log.push('c3'));
ctxClass.addInitializer(() => log.push('c4'));
};
// Method decorators
const methodDec1 = (fn, ctxMethod) => {
log.push('m2');
if (!assertEq(() => typeof ctxMethod.addInitializer, 'function'))
return;
ctxMethod.addInitializer(() => log.push('m5'));
ctxMethod.addInitializer(() => log.push('m6'));
};
const methodDec2 = (fn, ctxMethod) => {
log.push('m1');
if (!assertEq(() => typeof ctxMethod.addInitializer, 'function'))
return;
ctxMethod.addInitializer(() => log.push('m3'));
ctxMethod.addInitializer(() => log.push('m4'));
};
const staticMethodDec1 = (fn, ctxStaticMethod) => {
log.push('M2');
if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function'))
return;
ctxStaticMethod.addInitializer(() => log.push('M5'));
ctxStaticMethod.addInitializer(() => log.push('M6'));
};
const staticMethodDec2 = (fn, ctxStaticMethod) => {
log.push('M1');
if (!assertEq(() => typeof ctxStaticMethod.addInitializer, 'function'))
return;
ctxStaticMethod.addInitializer(() => log.push('M3'));
ctxStaticMethod.addInitializer(() => log.push('M4'));
};
// Field decorators
const fieldDec1 = (value, ctxField) => {
log.push('f2');
if (!assertEq(() => typeof ctxField.addInitializer, 'function'))
return;
ctxField.addInitializer(() => log.push('f5'));
ctxField.addInitializer(() => log.push('f6'));
return () => { log.push('f7'); };
};
const fieldDec2 = (value, ctxField) => {
log.push('f1');
if (!assertEq(() => typeof ctxField.addInitializer, 'function'))
return;
ctxField.addInitializer(() => log.push('f3'));
ctxField.addInitializer(() => log.push('f4'));
return () => { log.push('f8'); };
};
const staticFieldDec1 = (value, ctxStaticField) => {
log.push('F2');
if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function'))
return;
ctxStaticField.addInitializer(() => log.push('F5'));
ctxStaticField.addInitializer(() => log.push('F6'));
return () => { log.push('F7'); };
};
const staticFieldDec2 = (value, ctxStaticField) => {
log.push('F1');
if (!assertEq(() => typeof ctxStaticField.addInitializer, 'function'))
return;
ctxStaticField.addInitializer(() => log.push('F3'));
ctxStaticField.addInitializer(() => log.push('F4'));
return () => { log.push('F8'); };
};
// Getter decorators
const getterDec1 = (fn, ctxGetter) => {
log.push('g2');
if (!assertEq(() => typeof ctxGetter.addInitializer, 'function'))
return;
ctxGetter.addInitializer(() => log.push('g5'));
ctxGetter.addInitializer(() => log.push('g6'));
};
const getterDec2 = (fn, ctxGetter) => {
log.push('g1');
if (!assertEq(() => typeof ctxGetter.addInitializer, 'function'))
return;
ctxGetter.addInitializer(() => log.push('g3'));
ctxGetter.addInitializer(() => log.push('g4'));
};
const staticGetterDec1 = (fn, ctxStaticGetter) => {
log.push('G2');
if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function'))
return;
ctxStaticGetter.addInitializer(() => log.push('G5'));
ctxStaticGetter.addInitializer(() => log.push('G6'));
};
const staticGetterDec2 = (fn, ctxStaticGetter) => {
log.push('G1');
if (!assertEq(() => typeof ctxStaticGetter.addInitializer, 'function'))
return;
ctxStaticGetter.addInitializer(() => log.push('G3'));
ctxStaticGetter.addInitializer(() => log.push('G4'));
};
// Setter decorators
const setterDec1 = (fn, ctxSetter) => {
log.push('s2');
if (!assertEq(() => typeof ctxSetter.addInitializer, 'function'))
return;
ctxSetter.addInitializer(() => log.push('s5'));
ctxSetter.addInitializer(() => log.push('s6'));
};
const setterDec2 = (fn, ctxSetter) => {
log.push('s1');
if (!assertEq(() => typeof ctxSetter.addInitializer, 'function'))
return;
ctxSetter.addInitializer(() => log.push('s3'));
ctxSetter.addInitializer(() => log.push('s4'));
};
const staticSetterDec1 = (fn, ctxStaticSetter) => {
log.push('S2');
if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function'))
return;
ctxStaticSetter.addInitializer(() => log.push('S5'));
ctxStaticSetter.addInitializer(() => log.push('S6'));
};
const staticSetterDec2 = (fn, ctxStaticSetter) => {
log.push('S1');
if (!assertEq(() => typeof ctxStaticSetter.addInitializer, 'function'))
return;
ctxStaticSetter.addInitializer(() => log.push('S3'));
ctxStaticSetter.addInitializer(() => log.push('S4'));
};
// Auto-accessor decorators
const accessorDec1 = (target, ctxAccessor) => {
log.push('a2');
if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function'))
return;
ctxAccessor.addInitializer(() => log.push('a5'));
ctxAccessor.addInitializer(() => log.push('a6'));
return { init() { log.push('a7'); } };
};
const accessorDec2 = (target, ctxAccessor) => {
log.push('a1');
if (!assertEq(() => typeof ctxAccessor.addInitializer, 'function'))
return;
ctxAccessor.addInitializer(() => log.push('a3'));
ctxAccessor.addInitializer(() => log.push('a4'));
return { init() { log.push('a8'); } };
};
const staticAccessorDec1 = (target, ctxStaticAccessor) => {
log.push('A2');
if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function'))
return;
ctxStaticAccessor.addInitializer(() => log.push('A5'));
ctxStaticAccessor.addInitializer(() => log.push('A6'));
return { init() { log.push('A7'); } };
};
const staticAccessorDec2 = (target, ctxStaticAccessor) => {
log.push('A1');
if (!assertEq(() => typeof ctxStaticAccessor.addInitializer, 'function'))
return;
ctxStaticAccessor.addInitializer(() => log.push('A3'));
ctxStaticAccessor.addInitializer(() => log.push('A4'));
return { init() { log.push('A8'); } };
};
log.push('start');
@classDec1
@classDec2
class Foo extends (log.push('extends'), Object) {
static { log.push('static:start'); }
constructor() {
log.push('ctor:start');
super();
log.push('ctor:end');
}
@methodDec1
@methodDec2
method() { }
@staticMethodDec1
@staticMethodDec2
static method() { }
@fieldDec1
@fieldDec2
field;
@staticFieldDec1
@staticFieldDec2
static field;
@getterDec1
@getterDec2
get getter() { return; }
@staticGetterDec1
@staticGetterDec2
static get getter() { return; }
@setterDec1
@setterDec2
set setter(x) { }
@staticSetterDec1
@staticSetterDec2
static set setter(x) { }
@accessorDec1
@accessorDec2
accessor accessor;
@staticAccessorDec1
@staticAccessorDec2
static accessor accessor;
static { log.push('static:end'); }
}
log.push('after');
new Foo;
log.push('end');
assertEq(() => log + '', 'start,extends,' +
'M1,M2,G1,G2,S1,S2,A1,A2,' + // For each element e of staticElements if e.[[Kind]] is not field
'm1,m2,g1,g2,s1,s2,a1,a2,' + // For each element e of instanceElements if e.[[Kind]] is not field
'F1,F2,' + // For each element e of staticElements if e.[[Kind]] is field
'f1,f2,' + // For each element e of instanceElements if e.[[Kind]] is field
'c1,c2,' + // ApplyDecoratorsToClassDefinition
'M3,M4,M5,M6,G3,G4,G5,G6,S3,S4,S5,S6,' + // For each element initializer of staticMethodExtraInitializers
'static:start,' + // For each element elementRecord of staticElements
'F7,F8,F3,F4,F5,F6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'A7,A8,A3,A4,A5,A6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'static:end,' + // For each element elementRecord of staticElements
'c3,c4,c5,c6,' + // For each element initializer of classExtraInitializers
'after,' +
'ctor:start,' +
'm3,m4,m5,m6,g3,g4,g5,g6,s3,s4,s5,s6,' + // For each element initializer of constructor.[[Initializers]] (a.k.a. instanceMethodExtraInitializers)
'f7,f8,f3,f4,f5,f6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'a7,a8,a3,a4,a5,a6,' + // InitializeFieldOrAccessor + For each element initializer of elementRecord.[[ExtraInitializers]]
'ctor:end,' +
'end');
})();

Some files were not shown because too many files have changed in this diff Show More