diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/decorators | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/decorators')
-rw-r--r-- | js/src/jit-test/tests/decorators/accessor-decorators.js | 189 | ||||
-rw-r--r-- | js/src/jit-test/tests/decorators/accessors.js | 86 | ||||
-rw-r--r-- | js/src/jit-test/tests/decorators/addInitializer.js | 39 | ||||
-rw-r--r-- | js/src/jit-test/tests/decorators/class-decorators.js | 101 | ||||
-rw-r--r-- | js/src/jit-test/tests/decorators/decorator-this.js | 36 | ||||
-rw-r--r-- | js/src/jit-test/tests/decorators/field-decorators.js | 104 | ||||
-rw-r--r-- | js/src/jit-test/tests/decorators/getter-setter-decorators.js | 68 | ||||
-rw-r--r-- | js/src/jit-test/tests/decorators/method-decorators.js | 122 | ||||
-rw-r--r-- | js/src/jit-test/tests/decorators/syntax.js | 172 |
9 files changed, 917 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/decorators/accessor-decorators.js b/js/src/jit-test/tests/decorators/accessor-decorators.js new file mode 100644 index 0000000000..33e9c8a728 --- /dev/null +++ b/js/src/jit-test/tests/decorators/accessor-decorators.js @@ -0,0 +1,189 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") + +load(libdir + "asserts.js"); + +let dec1Called = false; + +// This explicitly tests the case where undefined is returned. +function dec(value, context) { + dec1Called = true; + // return undefined +} + +function decorate_getter(value, context) { + return { + get: function() { + return 2 * value.get.call(this); + } + }; +} + +function decorate_setter(value, context) { + return { + set: function(x) { + return value.set.call(this, 2*x); + } + }; +} + +function decorate_initializer(value, context) { + return { + init: function(initialValue) { + return 2 * initialValue; + } + }; +} + +function checkDecoratorContext(kind, isPrivate, isStatic, name) { + return (value, context) => { + assertEq(typeof value, "object"); + assertEq(typeof value.get, "function"); + assertEq(typeof value.set, "function"); + assertEq(context.kind, kind); + assertEq(typeof context.access, "object"); + assertEq(context.private, isPrivate); + assertEq(context.static, isStatic); + assertEq(context.name, name); + if (isStatic) { + assertEq(typeof context.addInitializer, "undefined"); + } else { + assertEq(typeof context.addInitializer, "function"); + } + // return undefined + } +} + +class C { + @dec accessor x = 1; + @decorate_getter accessor x2 = 1; + @decorate_setter @decorate_getter accessor x3 = 1; + @decorate_initializer accessor x4 = 1; + @decorate_initializer @decorate_initializer accessor x5 = 1; + @decorate_setter @decorate_getter @decorate_initializer accessor x6 = 1; + @checkDecoratorContext("accessor", true, false, "x8 accessor storage") accessor x8 = 1; + @checkDecoratorContext("accessor", true, true, "x9 accessor storage") static accessor x9 = 1; + @checkDecoratorContext("accessor", true, false, "#x10 accessor storage") accessor #x10 = 1; +} + +let c = new C(); +assertEq(dec1Called, true); +assertEq(c.x, 1); +c.x = 2; +assertEq(c.x, 2); +assertEq(c.x2, 2); +assertEq(c.x3, 2); +c.x3 = 4; +assertEq(c.x3, 16); +assertEq(c.x4, 2); +assertEq(c.x5, 4); +assertEq(c.x6, 4); +c.x6 = 4; +assertEq(c.x6, 16); + +class D { + @decorate_initializer accessor #x = 1; + @decorate_getter accessor #x2 = 1; + @decorate_setter @decorate_getter accessor #x3 = 1; + + getX() { + return this.#x; + } + + setX(v) { + this.#x = v; + } + + getX2() { + return this.#x2; + } + + setX2(v) { + this.#x2 = v; + } + + getX3() { + return this.#x3; + } + + setX3(v) { + this.#x3 = v; + } +} + +let d = new D(); +assertEq(d.getX(), 2); +d.setX(4); +assertEq(d.getX(), 4); +assertEq(d.getX2(), 2); +d.setX2(4); +assertEq(d.getX2(), 8); +assertEq(d.getX3(), 2); +d.setX3(4); +assertEq(d.getX3(), 16); + +class E { + @decorate_getter static accessor x = 1; +} + +assertEq(E.x, 2); +E.x = 2; +assertEq(E.x, 4); + +class F { + @decorate_getter static accessor #x = 1; + + getX() { + return F.#x; + } + + setX(v) { + F.#x = v; + } +} +let f = new F(); +assertEq(f.getX(), 2); +f.setX(4); +assertEq(f.getX(), 8); + +assertThrowsInstanceOf(() => { + class G { + @(() => { return "hello!"; }) accessor x; + } +}, TypeError), "Returning a value other than undefined or a callable throws."; + +assertThrowsInstanceOf(() => { + class G { + @(() => { return {get: "hello!"}; }) accessor x; + } +}, TypeError), "Returning a value other than undefined or a callable throws."; + +assertThrowsInstanceOf(() => { + class G { + @(() => { return {set: "hello!"}; }) accessor x; + } +}, TypeError), "Returning a value other than undefined or a callable throws."; + +assertThrowsInstanceOf(() => { + class G { + @(() => { return {init: "hello!"}; }) accessor x; + } +}, TypeError), "Returning a value other than undefined or a callable throws."; + +const decoratorOrder = []; +function makeOrderedDecorator(order) { + return function (value, context) { + decoratorOrder.push(order); + return value; + } +} + +class H { + @makeOrderedDecorator(1) @makeOrderedDecorator(2) @makeOrderedDecorator(3) + accessor x = 1; +} + +let h = new H(); +assertEq(decoratorOrder.length, 3); +assertEq(decoratorOrder[0], 3); +assertEq(decoratorOrder[1], 2); +assertEq(decoratorOrder[2], 1); diff --git a/js/src/jit-test/tests/decorators/accessors.js b/js/src/jit-test/tests/decorators/accessors.js new file mode 100644 index 0000000000..660c6e79df --- /dev/null +++ b/js/src/jit-test/tests/decorators/accessors.js @@ -0,0 +1,86 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") + +load(libdir + "asserts.js"); + +function assertAccessorDescriptor(object, name) { + var desc = Object.getOwnPropertyDescriptor(object, name); + assertEq(desc.configurable, true, "The value of `desc.configurable` is `true`"); + assertEq(desc.enumerable, false, "The value of `desc.enumerable` is `false`"); + assertEq(typeof desc.get, 'function', "`typeof desc.get` is `'function'`"); + assertEq(typeof desc.set, 'function', "`typeof desc.set` is `'function'`"); + assertEq( + 'prototype' in desc.get, + false, + "The result of `'prototype' in desc.get` is `false`" + ); + assertEq( + 'prototype' in desc.set, + false, + "The result of `'prototype' in desc.set` is `false`" + ); +} + +class C { + accessor x; +} + +assertAccessorDescriptor(C.prototype, 'x'); + +let c = new C(); +assertEq(c.x, undefined, "The value of `c.x` is `undefined` after construction"); +c.x = 2; +assertEq(c.x, 2, "The value of `c.x` is `2`, after executing `c.x = 2;`"); + +class D { + accessor x = 1; +} + +assertAccessorDescriptor(D.prototype, 'x'); + +let d = new D(); +assertEq(d.x, 1, "The value of `d.x` is `1` after construction"); +d.x = 2; +assertEq(d.x, 2, "The value of `d.x` is `2`, after executing `d.x = 2;`"); + +class E { + accessor #x = 1; + + getX() { + return this.#x; + } + + setX(v) { + this.#x = v; + } +} + +let e = new E(); +assertEq(e.getX(), 1, "The value of `e.x` is `1` after construction"); +e.setX(2); +assertEq(e.getX(), 2, "The value of `e.x` is `2`, after executing `e.setX(2);`"); + +class F { + static accessor x = 1; +} + +assertEq(F.x, 1, "The value of `F.x` is `1` after construction"); +F.x = 2; +assertEq(F.x, 2, "The value of `F.x` is `2`, after executing `F.x = 2;`"); + +assertAccessorDescriptor(F, 'x'); + +class G { + static accessor #x = 1; + + getX() { + return G.#x; + } + + setX(v) { + G.#x = v; + } +} +g = new G(); +assertEq(g.getX(), 1, "The value of `g.getX()` is `1` after construction"); +g.setX(2); +assertEq(g.getX(), 2, "The value of `g.getX()` is `2`, after executing `g.setX(2)`"); diff --git a/js/src/jit-test/tests/decorators/addInitializer.js b/js/src/jit-test/tests/decorators/addInitializer.js new file mode 100644 index 0000000000..334de8f721 --- /dev/null +++ b/js/src/jit-test/tests/decorators/addInitializer.js @@ -0,0 +1,39 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") +load(libdir + "asserts.js"); + +let extraInitializerCalled = {}; + +function checkDecoratorContext(kind, isPrivate, isStatic, name) { + return function (value, context) { + if (kind == "field") { + assertEq(value, undefined); + } else if (kind == "accessor") { + assertEq(typeof value, "object"); + assertEq(typeof value.get, "function"); + assertEq(typeof value.set, "function"); + } + assertEq(context.kind, kind); + assertEq(typeof context.access, "object"); + assertEq(context.private, isPrivate); + assertEq(context.static, isStatic); + assertEq(context.name, name); + assertEq(typeof context.addInitializer, "function"); + context.addInitializer(() => {extraInitializerCalled[context.name] = true;}); + // return undefined + } +} + +class C { + @checkDecoratorContext("field", false, false, "x") x; + @checkDecoratorContext("accessor", true, false, "y accessor storage") accessor y; + @checkDecoratorContext("method", false, false, "f") f() {}; + @checkDecoratorContext("method", false, false, 1) 1() {}; + @checkDecoratorContext("method", false, false, 2) 2n() {}; +} + +let c = new C(); +assertEq(extraInitializerCalled["x"], true); +assertEq(extraInitializerCalled["y accessor storage"], true); +assertEq(extraInitializerCalled["f"], true); +assertEq(extraInitializerCalled["1"], true); +assertEq(extraInitializerCalled["2"], true); diff --git a/js/src/jit-test/tests/decorators/class-decorators.js b/js/src/jit-test/tests/decorators/class-decorators.js new file mode 100644 index 0000000000..f9b120ff45 --- /dev/null +++ b/js/src/jit-test/tests/decorators/class-decorators.js @@ -0,0 +1,101 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") + +load(libdir + "asserts.js"); + +let dec1Called = false; + +// This explicitly tests the case where undefined is returned. +function dec1(value, context) { + dec1Called = true; + // returns undefined +} + +function dec2(value, context) { + return class extends value { + constructor(...args) { + super(...args); + } + + x2 = true; + } +} + +function checkDecoratorContext(name) { + return function (value, context) { + assertEq(typeof value, "function"); + assertEq(context.kind, "class"); + assertEq(context.name, name); + assertEq(typeof context.addInitializer, "undefined"); + // return undefined + } +} + +@dec1 class C1 {}; +assertEq(dec1Called, true); + +@dec2 class C2 { + x1 = true; +} + +let c2 = new C2(); +assertEq(c2.x1, true); +assertEq(c2.x2, true); + +let c3 = new @dec2 class { + x1 = true; +} + +assertEq(c3.x1, true); +assertEq(c3.x2, true); + +@checkDecoratorContext("D") class D {} +let d2 = new @checkDecoratorContext(undefined) class {}; + +class E { + static #dec1(value, context) { + return class extends value { + constructor(...args) { + super(...args); + } + + x2 = true; + } + } + static { + this.F = @E.#dec1 class { + x1 = true; + } + } +} + +let f = new E.F(); +assertEq(f.x1, true); +assertEq(f.x2, true); + +assertThrowsInstanceOf(() => { + @(() => { return "hello!"; }) class G {} +}, TypeError), "Returning a value other than undefined or a callable throws."; + +assertThrowsInstanceOf(() => { + class G { + static #dec1() {} + static { + @G.#dec1 class G {} + } + } +}, ReferenceError), "can't access lexical declaration 'G' before initialization"; + +const decoratorOrder = []; +function makeOrderedDecorator(order) { + return function (value, context) { + decoratorOrder.push(order); + return value; + } +} + +@makeOrderedDecorator(1) @makeOrderedDecorator(2) @makeOrderedDecorator(3) +class H {} +assertEq(decoratorOrder.length, 3); +assertEq(decoratorOrder[0], 3); +assertEq(decoratorOrder[1], 2); +assertEq(decoratorOrder[2], 1); diff --git a/js/src/jit-test/tests/decorators/decorator-this.js b/js/src/jit-test/tests/decorators/decorator-this.js new file mode 100644 index 0000000000..b545729ca4 --- /dev/null +++ b/js/src/jit-test/tests/decorators/decorator-this.js @@ -0,0 +1,36 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") + +load(libdir + "asserts.js"); + +let globalDecCalled = false; +function globalDec(value, context) { + globalDecCalled = true; + assertEq(this, globalThis); +} + +// Forward declare c to be able to check it inside of C when running decorators. +let c; +let classDecCalled = false; +class C { + classDec(value, context) { + classDecCalled = true; + // At this point, `this` is an instance of C + assertEq(c, this); + return function(initialValue) { + // At this point, `this` is an instance of D + assertEq(this instanceof D, true); + return initialValue; + } + } +} + +c = new C(); + +class D { + @globalDec x1; + @c.classDec x2; +} + +let d = new D(); +assertEq(globalDecCalled, true); +assertEq(classDecCalled, true); diff --git a/js/src/jit-test/tests/decorators/field-decorators.js b/js/src/jit-test/tests/decorators/field-decorators.js new file mode 100644 index 0000000000..7b02f35ef9 --- /dev/null +++ b/js/src/jit-test/tests/decorators/field-decorators.js @@ -0,0 +1,104 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") + +load(libdir + "asserts.js"); + +let dec1Called = false; +let dec2Called = false; + +// This explicitly tests the case where undefined is returned. +function dec1(value, context) { + dec1Called = true; + // returns undefined +} + +function dec2(value, context) { + dec2Called = true; + return (initialValue) => 2; +} + +function dec3(value, context) { + return (initialValue) => initialValue*2; +} + +function checkDecoratorContext(kind, isPrivate, isStatic, name) { + return (value, context) => { + assertEq(value, undefined); + assertEq(context.kind, kind); + assertEq(typeof context.access, "object"); + assertEq(context.private, isPrivate); + assertEq(context.static, isStatic); + assertEq(context.name, name); + if (isStatic) { + assertEq(typeof context.addInitializer, "undefined"); + } else { + assertEq(typeof context.addInitializer, "function"); + } + } +} + +class C { + @dec1 x; + @dec1 x2 = 1; + @checkDecoratorContext("field", false, false, "x3") x3; + @checkDecoratorContext("field", false, true, "x4") static x4; + @checkDecoratorContext("field", true, false, "#x5") #x5; + @dec2 x6; + @dec3 x7 = 1; + @dec2 @dec3 x8 = 1; + @dec2 static x9; + @dec2 #x10; + getX10 = () => { + return this.#x10; + }; + @dec3 #x11 = 2; + getX11 = () => { + return this.#x11; + }; + @dec1 42 = 1; + @dec2 [43]; + @dec2 static #x12 = 1; + getX12() { + return C.#x12; + } +} + + +let c = new C(); +assertEq(dec1Called, true); +assertEq(c.x2, 1); +assertEq(dec2Called, true); +assertEq(c.x6, 2); +assertEq(c.x7, 2); +assertEq(c.x8, 2); +assertEq(C.x9, 2); +assertEq(c.getX10(), 2); +assertEq(c.getX11(), 4); +assertEq(c[42], 1); +assertEq(c[43], 2); +assertEq(c.getX12(), 2); + +assertThrowsInstanceOf(() => { + class D { + @(() => { return "hello!"; }) f(x) { return x; } + } +}, TypeError), "Returning a value other than undefined or a callable throws."; + +const decoratorOrder = []; +function makeOrderedDecorator(order) { + return function (value, context) { + decoratorOrder.push(order); + return value; + } +} + +class D { + @makeOrderedDecorator(1) @makeOrderedDecorator(2) @makeOrderedDecorator(3) + x = 1; +} + +let d = new D(); +assertEq(decoratorOrder.length, 3); +assertEq(decoratorOrder[0], 3); +assertEq(decoratorOrder[1], 2); +assertEq(decoratorOrder[2], 1); +assertEq(d.x, 1); diff --git a/js/src/jit-test/tests/decorators/getter-setter-decorators.js b/js/src/jit-test/tests/decorators/getter-setter-decorators.js new file mode 100644 index 0000000000..e3c370f500 --- /dev/null +++ b/js/src/jit-test/tests/decorators/getter-setter-decorators.js @@ -0,0 +1,68 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") + +load(libdir + "asserts.js"); + +let dec1Called = false; + +// This explicitly tests the case where undefined is returned. +function dec(value, context) { + dec1Called = true; + // return undefined +} + +function decorate_getter(value, context) { + return function() { + return 2 * value.call(this); + } +} + +function decorate_setter(value, context) { + return function(x) { + return value.call(this, 2*x); + } +} + +function checkDecoratorContext(kind, isPrivate, isStatic, name) { + return (value, context) => { + assertEq(typeof value.call, "function"); + assertEq(context.kind, kind); + assertEq(typeof context.access, "object"); + assertEq(context.private, isPrivate); + assertEq(context.static, isStatic); + assertEq(context.name, name); + if (isStatic) { + assertEq(typeof context.addInitializer, "undefined"); + } else { + assertEq(typeof context.addInitializer, "function"); + } + } +} + +class C { + setter5Value; + + @dec get getter1() { return "unmodified"; } + @checkDecoratorContext("getter", false, false, "getter2") get getter2() { } + @checkDecoratorContext("getter", false, true, "getter3") static get getter3() { } + @checkDecoratorContext("getter", true, false, "#getter4") get #getter4() { } + @decorate_getter get getter5() { return 1; } + + @dec set setter1(value) { } + @checkDecoratorContext("setter", false, false, "setter2") set setter2(value) { } + @checkDecoratorContext("setter", false, true, "setter3") static set setter3(value) { } + @checkDecoratorContext("setter", true, false, "#setter4") set #setter4(value) { } + @decorate_setter set setter5(value) { this.setter5Value = value; } +} + +let c = new C(); +assertEq(dec1Called, true); +assertEq(c.getter1, "unmodified"); +assertEq(c.getter5, 2); +c.setter5 = 1; +assertEq(c.setter5Value, 2); + +assertThrowsInstanceOf(() => { + class D { + @(() => { return "hello!"; }) get f() { return "unmodified"; } + } +}, TypeError), "Returning a value other than undefined or a callable throws."; diff --git a/js/src/jit-test/tests/decorators/method-decorators.js b/js/src/jit-test/tests/decorators/method-decorators.js new file mode 100644 index 0000000000..59708aa70b --- /dev/null +++ b/js/src/jit-test/tests/decorators/method-decorators.js @@ -0,0 +1,122 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") + +load(libdir + "asserts.js"); + +let dec1Called = false; + +// This explicitly tests the case where undefined is returned. +function dec1(value, context) { + dec1Called = true; + // return undefined +} + +function dec2(value, context) { + return (x) => "replaced: " + x; +} + +function dec3(value, context) { + return (x) => "decorated: " + value.call(this, x); +} + +let decorators = { + "dec4": (value, context) => { + return (x) => "decorated: " + x; + }, + "more": { + "deeply": { + "nested": { + "dec5": (value, context) => { + return (x) => "decorated: " + x; + } + } + } + } +}; + +function dec6(arg) { + return (value, context) => { + return (x) => arg + " decorated: " + value.call(this, x); + } +} + +function checkDecoratorContext(kind, isPrivate, isStatic, name) { + return (value, context) => { + assertEq(typeof value.call, "function"); + assertEq(context.kind, kind); + assertEq(typeof context.access, "object"); + assertEq(context.private, isPrivate); + assertEq(context.static, isStatic); + assertEq(context.name, name); + if (isStatic) { + assertEq(typeof context.addInitializer, "undefined"); + } else { + assertEq(typeof context.addInitializer, "function"); + } + } +} + +class C { + @dec1 f1(x) { return "called with: " + x; } + @dec2 f2(x) { return "called with: " + x; } + @dec1 @dec2 f3(x) { return "called with: " + x; } + @dec1 @dec2 @dec3 f4(x) { return "called with: " + x; } + @decorators.dec4 f5(x) { return "called with: " + x; } + @decorators.more.deeply.nested.dec5 f6(x) { return "called with: " + x; } + @(() => { }) f7(x) { return "called with: " + x; } + @((value, context) => { return (x) => "decorated: " + x; }) f8(x) { return "called with: " + x; } + @dec6("hello!") f9(x) { return "called with: " + x; } + + static dec7(value, context) { return function(x) { return "replaced: " + x; } } + static #dec8(value, context) { return function(x) { return "replaced: " + x; } } + + static { + this.D = class { + @C.dec7 static f10(x) { return "called with: " + x; } + @C.#dec8 static f11(x) { return "called with: " + x; } + } + } + + @checkDecoratorContext("method", false, false, "f12") f12(x) { return x; } + @checkDecoratorContext("method", false, true, "f13") static f13(x) { return x; } + @checkDecoratorContext("method", true, false, "#f14") #f14(x) { return x; } +} + +let c = new C(); +assertEq(dec1Called, true); +assertEq(c.f1("value"), "called with: value"); +assertEq(c.f2("value"), "replaced: value"); +assertEq(c.f3("value"), "replaced: value"); +assertEq(c.f4("value"), "replaced: value"); +assertEq(c.f5("value"), "decorated: value"); +assertEq(c.f6("value"), "decorated: value"); +assertEq(c.f7("value"), "called with: value"); +assertEq(c.f8("value"), "decorated: value"); +assertEq(c.f9("value"), "hello! decorated: called with: value"); +assertEq(C.D.f10("value"), "replaced: value"); +assertEq(C.D.f11("value"), "replaced: value"); + +assertThrowsInstanceOf(() => { + class D { + @(() => { return "hello!"; }) f(x) { return x; } + } +}, TypeError), "Returning a value other than undefined or a callable throws."; + +const decoratorOrder = []; +function makeOrderedDecorator(order) { + return function (value, context) { + decoratorOrder.push(order); + return value; + } +} + +class E { + @makeOrderedDecorator(1) @makeOrderedDecorator(2) @makeOrderedDecorator(3) + f(x) { return x; } +} + +let e = new E(); +assertEq(e.f("value"), "value"); +assertEq(decoratorOrder.length, 3); +assertEq(decoratorOrder[0], 3); +assertEq(decoratorOrder[1], 2); +assertEq(decoratorOrder[2], 1); diff --git a/js/src/jit-test/tests/decorators/syntax.js b/js/src/jit-test/tests/decorators/syntax.js new file mode 100644 index 0000000000..635e30bf7b --- /dev/null +++ b/js/src/jit-test/tests/decorators/syntax.js @@ -0,0 +1,172 @@ +// |jit-test| skip-if: !getBuildConfiguration("decorators") + +load(libdir + "asserts.js"); + +Reflect.parse("class c {@dec1 field = false;}"); +Reflect.parse("class c {@dec1 @dec2 @dec3 field = false;}"); +Reflect.parse("class c {@dec1 @dec2 @dec3('a') static field = false;}"); +Reflect.parse("class c {@dec1 method() {};}"); +Reflect.parse("class c {@dec1 @dec2 @dec3 method() {};}"); +Reflect.parse("class c {@dec1 @dec2 @dec3 static method() {};}"); +Reflect.parse("class c {@dec1 @dec2('a') @dec3 method(...args) {};}"); +Reflect.parse("class c {@((a, b, c) => {}) method(a, b) {};}"); +Reflect.parse("class c {@((a, b, c) => {}) @dec2('a', 'b') @dec3 method(a, b) {};}"); +Reflect.parse("@dec1 class c {}"); +Reflect.parse("@dec1 @dec2 @dec3 class c {}"); +Reflect.parse("@dec1('a') @(() => {}) @dec3 class c {}"); +Reflect.parse("@dec1('a') @(() => {}) @dec3 class c {@((a, b, c) => {}) @dec2('a', 'b') @dec3 method(a, b) {};}"); +Reflect.parse("x = @dec class { #x }"); +Reflect.parse("x = (class A { }, @dec class { })"); +Reflect.parse("@dec1 class A extends @dec2 class B extends @dec3 class {} {} {}"); +Reflect.parse("class c {@dec1.dec2.dec3 method() {};}"); +Reflect.parse("class c {@dec1 @dec2.dec3.dec4 @dec5 method() {};}"); +Reflect.parse("class c {@dec1('a') @dec2.dec3.dec4 @dec5 method() {};}"); +Reflect.parse("class c {@dec1('a') @dec2.dec3.dec4('b', 'c') @dec5 method() {};}"); +Reflect.parse("class c {@dec1('a') @dec2.dec3.dec4('b', 'c') @dec5.dec6 method() {};}"); +Reflect.parse("@dec1.dec2 class c {}"); +Reflect.parse("@dec1.dec2 @dec3.dec4 @dec5 class c {}"); +Reflect.parse("@dec1.dec2('a') @(() => {}) @dec4 class c {}"); +Reflect.parse("@dec1.dec2('a') @(() => {}) @dec4 class c {@((a, b, c) => {}) @dec5.dec6('a', 'b') @dec7 method(a, b) {};}"); +Reflect.parse("class c {accessor field = false;}"); +Reflect.parse("class c {static accessor field = false;}"); +Reflect.parse("class c {@dec1 @dec2 accessor field = false;}"); +Reflect.parse("class c {@dec1 @dec2 @dec3 static accessor field = false;}"); +Reflect.parse("let accessor = false;"); +Reflect.parse("class accessor {accessor = false;}"); +Reflect.parse("class c {accessor = false;}"); +Reflect.parse("class c {accessor\n= false;}"); +Reflect.parse("class c {accessor\nfield = false;}"); +Reflect.parse("class C {accessor\n foo() {}}"); +Reflect.parse("class c {accessor; }"); +Reflect.parse("class c {accessor\nset field(a) {}}"); +Reflect.parse("class c {accessor\nasync field(a) {}}"); +Reflect.parse("class c {accessor\n* field(a) {}}"); +Reflect.parse("{accessor, field(a)}"); +Reflect.parse("class c {static accessor x = 1;}"); +Reflect.parse("class c {accessor #y = 2;}"); +Reflect.parse("class c {static dec1(){}; static {@c.dec1 class d{@c.dec1 field}}}"); +Reflect.parse("class c {static #dec1(){}; static {@c.#dec1 class d{@c.#dec1 field}}}"); +Reflect.parse("class c {@as field = false;}"); +Reflect.parse("class c {@accessor field = false;}"); +Reflect.parse("class c {@assert field = false;}"); +Reflect.parse("class c {@async field = false;}"); +Reflect.parse("class c {@await field = false;}"); +Reflect.parse("class c {@each field = false;}"); +Reflect.parse("class c {@from field = false;}"); +Reflect.parse("class c {@get field = false;}"); +Reflect.parse("class c {@meta field = false;}"); +Reflect.parse("class c {@of field = false;}"); +Reflect.parse("class c {@set field = false;}"); +Reflect.parse("class c {@target field = false;}"); + +assertThrowsInstanceOf(() => Reflect.parse("class c {@ method() {};}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@((a, b, c => {}) method(a, b) {};}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@dec1 static @dec2 method(a, b) {};}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("@dec1 let x = 1"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("@dec1 f(a) {}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("@dec1 () => {}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("@class class { x; }"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("@for class { x; }"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@dec1. method() {};}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("@dec1. class c {method() {};}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("@dec1.(a) class c {method() {};}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {accessor method() {};}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {accessor @dec1 field = false;}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {accessor, }"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("({ accessor field: 10 })"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("({ accessor foo });"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("({ accessor foo = 10 } = {});"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {async accessor foo() {}}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {accessor set field(a) {}}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {accessor *field(a) {}}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {accessor *field(a) {}}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@dec[0] x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@new dec() x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@super x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@super() x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@super.dec() x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@import \"decorator\" x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@dec`template` x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@import.meta.url x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@new.target x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@obj.first?.second x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@class x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@function() x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@{} x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@[] x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@\"string\" x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@`string` x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@/[a-z]+/ x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@true x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@1n x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@this x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@null x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@... x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@let x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@static x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@yield x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@if x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@enum x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@implements x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@foo().bar.prop x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@foo.bar().prop x}"), SyntaxError); +assertThrowsInstanceOf(() => Reflect.parse("class c {@foo.bar().prop() x}"), SyntaxError); + +// assert we have the right syntax tree +const syntax = Reflect.parse("class c {}"); +assertEq(syntax.type, "Program"); +assertEq(syntax.body.length, 1); +assertEq(syntax.body[0].type, "ClassStatement"); +assertEq(syntax.body[0].id.type, "Identifier"); +assertEq(syntax.body[0].id.name, "c"); +assertEq(syntax.body[0].decorators, null); + +// assert decrorators on a class +const syntax2 = Reflect.parse("@dec @dec2 class c {}"); +assertEq(syntax2.type, "Program"); +assertEq(syntax2.body.length, 1); +assertEq(syntax2.body[0].decorators.type, "SequenceExpression"); +assertEq(syntax2.body[0].decorators.expressions.length, 2); +assertEq(syntax2.body[0].decorators.expressions[0].type, "Identifier"); +assertEq(syntax2.body[0].decorators.expressions[0].name, "dec"); +assertEq(syntax2.body[0].decorators.expressions[1].type, "Identifier"); +assertEq(syntax2.body[0].decorators.expressions[1].name, "dec2"); + +// assert decorators on class fields +const syntax3 = Reflect.parse("class c {@dec1 @dec2 field = false;}"); +assertEq(syntax3.type, "Program"); +assertEq(syntax3.body.length, 1); +assertEq(syntax3.body[0].decorators, null); +assertEq(syntax3.body[0].body[0].type, "ClassField"); +assertEq(syntax3.body[0].body[0].decorators.type, "SequenceExpression"); +assertEq(syntax3.body[0].body[0].decorators.expressions.length, 2); +assertEq(syntax3.body[0].body[0].decorators.expressions[0].type, "Identifier"); +assertEq(syntax3.body[0].body[0].decorators.expressions[0].name, "dec1"); +assertEq(syntax3.body[0].body[0].decorators.expressions[1].type, "Identifier"); +assertEq(syntax3.body[0].body[0].decorators.expressions[1].name, "dec2"); + +// assert decorators on accessors +const syntax4 = Reflect.parse("class c {@dec1 @dec2 accessor field = false;}"); +assertEq(syntax3.type, "Program"); +assertEq(syntax3.body.length, 1); +assertEq(syntax3.body[0].decorators, null); +assertEq(syntax3.body[0].body[0].type, "ClassField"); +assertEq(syntax3.body[0].body[0].decorators.type, "SequenceExpression"); +assertEq(syntax3.body[0].body[0].decorators.expressions.length, 2); +assertEq(syntax3.body[0].body[0].decorators.expressions[0].type, "Identifier"); +assertEq(syntax3.body[0].body[0].decorators.expressions[0].name, "dec1"); +assertEq(syntax3.body[0].body[0].decorators.expressions[1].type, "Identifier"); +assertEq(syntax3.body[0].body[0].decorators.expressions[1].name, "dec2"); + +// assert decorators on methods +const syntax5 = Reflect.parse("class c {@dec1 @dec2 method() {};}"); +assertEq(syntax5.type, "Program"); +assertEq(syntax5.body.length, 1); +assertEq(syntax5.body[0].decorators, null); +assertEq(syntax5.body[0].body[0].type, "ClassMethod"); +assertEq(syntax5.body[0].body[0].decorators.type, "SequenceExpression"); +assertEq(syntax5.body[0].body[0].decorators.expressions.length, 2); +assertEq(syntax5.body[0].body[0].decorators.expressions[0].type, "Identifier"); +assertEq(syntax5.body[0].body[0].decorators.expressions[0].name, "dec1"); +assertEq(syntax5.body[0].body[0].decorators.expressions[1].type, "Identifier"); +assertEq(syntax5.body[0].body[0].decorators.expressions[1].name, "dec2"); |