Decorators normative changes (#52582)

This commit is contained in:
Ron Buckton
2023-02-09 14:48:45 -05:00
committed by GitHub
parent 0ef9e8eac9
commit 6c9792aa87
114 changed files with 1890 additions and 1043 deletions

View File

@@ -704,7 +704,7 @@ describe("unittests:: evaluation:: esDecorators", () => {
});
});
// Disabled pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
describe.skip(".access", () => {
describe(".access", () => {
describe("for: class", () => {
it("is not set", () => {
const { context } = exec`
@@ -716,7 +716,7 @@ describe("unittests:: evaluation:: esDecorators", () => {
});
});
describe("for: method", () => {
it("is { get }", () => {
it("is { has, get }", () => {
const { context } = exec`
export let context;
export class C {
@@ -725,10 +725,11 @@ describe("unittests:: evaluation:: esDecorators", () => {
}
`;
assert.isObject(context.access);
assert.hasAllKeys(context.access, ["get"]);
assert.hasAllKeys(context.access, ["has", "get"]);
assert.isFunction(context.access.has);
assert.isFunction(context.access.get);
});
it("accesses value using 'this'", () => {
it("test public element presence via .has", () => {
const { context, C } = exec`
export let context;
export class C {
@@ -736,27 +737,56 @@ describe("unittests:: evaluation:: esDecorators", () => {
static method() {}
}
`;
assert.strictEqual(context.access.get.call(C), C.method);
const obj = { method() {} };
assert.strictEqual(context.access.get.call(obj), obj.method);
assert.isTrue(context.access.has(C));
assert.isTrue(context.access.has({ method() {} }));
assert.isFalse(context.access.has({ }));
});
it("can access value for private name", () => {
const { context, C } = exec`
it("test private element presence via .has", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static #method() {}
}
export class D {
static #method() {}
}
`;
assert.isFunction(context.access.get.call(C));
const obj = { ["#method"]() {} };
assert.throws(() => context.access.get.call(obj));
assert.isTrue(context.access.has(C));
assert.isFalse(context.access.has(D));
assert.isFalse(context.access.has({ }));
});
it("read public element of argument", () => {
const { context, C } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static method() {}
}
`;
assert.strictEqual(context.access.get(C), C.method);
const obj = { method() {} };
assert.isTrue(context.access.has(obj));
assert.strictEqual(context.access.get(obj), obj.method);
});
it("read private element of argument", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static #method() {}
}
export class D {
static #method() {}
}
`;
assert.isFunction(context.access.get(C));
assert.throws(() => context.access.get(D));
assert.throws(() => context.access.get({ ["#method"]() {} }));
});
});
describe("for: getter", () => {
it("is { get }", () => {
it("is { has, get }", () => {
const { context } = exec`
export let context;
export class C {
@@ -765,10 +795,11 @@ describe("unittests:: evaluation:: esDecorators", () => {
}
`;
assert.isObject(context.access);
assert.hasAllKeys(context.access, ["get"]);
assert.hasAllKeys(context.access, ["has", "get"]);
assert.isFunction(context.access.has);
assert.isFunction(context.access.get);
});
it("accesses value using 'this'", () => {
it("test public element presence via .has", () => {
const { context, C } = exec`
export let context;
export class C {
@@ -776,27 +807,56 @@ describe("unittests:: evaluation:: esDecorators", () => {
static get x() { return 1; }
}
`;
assert.strictEqual(context.access.get.call(C), 1);
const obj = { x: 2 };
assert.strictEqual(context.access.get.call(obj), 2);
assert.isTrue(context.access.has(C));
assert.isTrue(context.access.has({
get x() { return 2; }
}));
assert.isFalse(context.access.has({ }));
});
it("can access value for private name", () => {
it("test private element presence via .has", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static #method() {}
}
export class D {
static #method() {}
}
`;
assert.isTrue(context.access.has(C));
assert.isFalse(context.access.has(D));
assert.isFalse(context.access.has({ }));
});
it("read public element of argument", () => {
const { context, C } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static get x() { return 1; }
}
`;
assert.strictEqual(context.access.get(C), 1);
assert.strictEqual(context.access.get({ x: 2 }), 2);
});
it("read private element of argument", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static get #x() { return 1; }
}
export class D {
static get #x() { return 1; }
}
`;
assert.strictEqual(context.access.get.call(C), 1);
const obj = { "#x": 2 };
assert.throws(() => context.access.get.call(obj));
assert.strictEqual(context.access.get(C), 1);
assert.throws(() => context.access.get(D));
assert.throws(() => context.access.get({ "#x": 2 }));
});
});
describe("for: setter", () => {
it("is { set }", () => {
it("is { has, set }", () => {
const { context } = exec`
export let context;
export class C {
@@ -805,10 +865,38 @@ describe("unittests:: evaluation:: esDecorators", () => {
}
`;
assert.isObject(context.access);
assert.hasAllKeys(context.access, ["set"]);
assert.hasAllKeys(context.access, ["has", "set"]);
assert.isFunction(context.access.has);
assert.isFunction(context.access.set);
});
it("accesses value using 'this'", () => {
it("test public element presence via .has", () => {
const { context, C } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static set x(v: number) { }
}
`;
assert.isTrue(context.access.has(C));
assert.isTrue(context.access.has({ x: 2 }));
assert.isFalse(context.access.has({ }));
});
it("test private element presence via .has", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static set #x(v: number) { }
}
export class D {
static set #x(v: number) { }
}
`;
assert.isTrue(context.access.has(C));
assert.isFalse(context.access.has(D));
assert.isFalse(context.access.has({ "#x": 2 }));
});
it("write public element of argument", () => {
const { context, C } = exec`
export let context;
export class C {
@@ -817,29 +905,31 @@ describe("unittests:: evaluation:: esDecorators", () => {
static y: number;
}
`;
context.access.set.call(C, 1);
context.access.set(C, 1);
assert.strictEqual(C.y, 1);
const obj = { x: 2 };
context.access.set.call(obj, 3);
context.access.set(obj, 3);
assert.strictEqual(obj.x, 3);
});
it("can access value for private name", () => {
const { context, C } = exec`
it("write private element of argument", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static set #x(v: number) {}
}
export class D {
static set #x(v: number) {}
}
`;
context.access.set.call(C, 1);
const obj = { "#x": 2 };
assert.throws(() => context.access.set.call(obj, 3));
context.access.set(C, 1);
assert.throws(() => context.access.set(D, 3));
assert.throws(() => context.access.set({ "#x": 2 }, 3));
});
});
describe("for: field", () => {
it("is { get, set }", () => {
it("is { has, get, set }", () => {
const { context } = exec`
export let context;
export class C {
@@ -848,11 +938,39 @@ describe("unittests:: evaluation:: esDecorators", () => {
}
`;
assert.isObject(context.access);
assert.hasAllKeys(context.access, ["get", "set"]);
assert.hasAllKeys(context.access, ["has", "get", "set"]);
assert.isFunction(context.access.has);
assert.isFunction(context.access.get);
assert.isFunction(context.access.set);
});
it("accesses value using 'this'", () => {
it("test public element presence via .has", () => {
const { context, C } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static x: number = 1;
}
`;
assert.isTrue(context.access.has(C));
assert.isTrue(context.access.has({ x: 2 }));
assert.isFalse(context.access.has({ }));
});
it("test private element presence via .has", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static #x: number = 1;
}
export class D {
static #x: number = 1;
}
`;
assert.isTrue(context.access.has(C));
assert.isFalse(context.access.has(D));
assert.isFalse(context.access.has({ "#x": 2 }));
});
it("read/write public element of argument", () => {
const { context, C } = exec`
export let context;
export class C {
@@ -861,36 +979,41 @@ describe("unittests:: evaluation:: esDecorators", () => {
}
`;
assert.strictEqual(context.access.get.call(C), 1);
context.access.set.call(C, 2);
assert.strictEqual(context.access.get(C), 1);
context.access.set(C, 2);
assert.strictEqual(C.x, 2);
const obj = { x: 2 };
assert.strictEqual(context.access.get.call(obj), 2);
context.access.set.call(obj, 3);
assert.strictEqual(context.access.get(obj), 2);
context.access.set(obj, 3);
assert.strictEqual(obj.x, 3);
});
it("can access value for private name", () => {
const { context, C } = exec`
it("read/write private element of argument", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static #x: number = 1;
static getX() { return this.#x; }
}
export class D {
static #x: number = 1;
}
`;
assert.strictEqual(context.access.get.call(C), 1);
context.access.set.call(C, 2);
assert.strictEqual(context.access.get(C), 1);
context.access.set(C, 2);
assert.strictEqual(C.getX(), 2);
const obj = { "#x": 2 };
assert.throws(() => context.access.get.call(obj));
assert.throws(() => context.access.set.call(obj, 3));
assert.throws(() => context.access.get(D));
assert.throws(() => context.access.set(D, 3));
assert.throws(() => context.access.get({ "#x": 2 }));
assert.throws(() => context.access.set({ "#x": 2 }, 3));
});
});
describe("for: auto-accessor", () => {
it("is { get, set }", () => {
it("is { has, get, set }", () => {
const { context } = exec`
export let context;
export class C {
@@ -898,11 +1021,39 @@ describe("unittests:: evaluation:: esDecorators", () => {
static accessor x: number;
}
`;
assert.hasAllKeys(context.access, ["get", "set"]);
assert.hasAllKeys(context.access, ["has", "get", "set"]);
assert.isFunction(context.access.has);
assert.isFunction(context.access.get);
assert.isFunction(context.access.set);
});
it("accesses value using 'this'", () => {
it("test public element presence via .has", () => {
const { context, C } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static accessor x: number = 1;
}
`;
assert.isTrue(context.access.has(C));
assert.isTrue(context.access.has({ x: 2 }));
assert.isFalse(context.access.has({ }));
});
it("test private element presence via .has", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static accessor #x: number = 1;
}
export class D {
static accessor #x: number = 1;
}
`;
assert.isTrue(context.access.has(C));
assert.isFalse(context.access.has(D));
assert.isFalse(context.access.has({ "#x": 2 }));
});
it("read/write public element of argument", () => {
const { context, C } = exec`
export let context;
export class C {
@@ -911,32 +1062,37 @@ describe("unittests:: evaluation:: esDecorators", () => {
}
`;
assert.strictEqual(context.access.get.call(C), 1);
context.access.set.call(C, 2);
assert.strictEqual(context.access.get(C), 1);
context.access.set(C, 2);
assert.strictEqual(C.x, 2);
const obj = { x: 2 };
assert.strictEqual(context.access.get.call(obj), 2);
context.access.set.call(obj, 3);
assert.strictEqual(context.access.get(obj), 2);
context.access.set(obj, 3);
assert.strictEqual(obj.x, 3);
});
it("can access value for private name", () => {
const { context, C } = exec`
it("read/write private element of argument", () => {
const { context, C, D } = exec`
export let context;
export class C {
@((t, c) => { context = c; })
static accessor #x: number = 1;
static getX() { return this.#x; }
}
export class D {
static accessor #x: number = 1;
}
`;
assert.strictEqual(context.access.get.call(C), 1);
context.access.set.call(C, 2);
assert.strictEqual(context.access.get(C), 1);
context.access.set(C, 2);
assert.strictEqual(C.getX(), 2);
const obj = { "#x": 2 };
assert.throws(() => context.access.get.call(obj));
assert.throws(() => context.access.set.call(obj, 3));
assert.throws(() => context.access.get(D));
assert.throws(() => context.access.set(D, 3));
assert.throws(() => context.access.get({ "#x": 2 }));
assert.throws(() => context.access.set({ "#x": 2 }, 3));
});
});
});
@@ -2223,13 +2379,12 @@ describe("unittests:: evaluation:: esDecorators", () => {
});
// see https://github.com/tc39/proposal-decorators#access-and-metadata-sidechanneling
// Disabled, pending the outcome of https://github.com/tc39/proposal-decorators/issues/494
it.skip(`dependency injection (${targetName})`, () => {
it(`dependency injection (${targetName})`, () => {
const { result } = exec`
const INJECTIONS = new WeakMap<object, { injectionKey: string, set: (this: any, value: any) => void }[]>();
const INJECTIONS = new WeakMap<object, { injectionKey: string, set: (object: any, value: any) => void }[]>();
function createInjections() {
const injections: { injectionKey: string, set: (this: any, value: any) => void }[] = [];
const injections: { injectionKey: string, set: (object: any, value: any) => void }[] = [];
function injectable<T extends new (...args: any) => any>(Class: T, context: ClassDecoratorContext<T>) {
INJECTIONS.set(Class, injections);
@@ -2259,7 +2414,7 @@ describe("unittests:: evaluation:: esDecorators", () => {
let instance = new Class();
for (const { injectionKey, set } of INJECTIONS.get(Class) || []) {
set.call(instance, this.lookup(injectionKey));
set(instance, this.lookup(injectionKey));
}
return instance;