diff options
Diffstat (limited to 'js/src/tests/non262/PrivateName')
18 files changed, 616 insertions, 0 deletions
diff --git a/js/src/tests/non262/PrivateName/browser.js b/js/src/tests/non262/PrivateName/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/PrivateName/browser.js diff --git a/js/src/tests/non262/PrivateName/error-locations.js b/js/src/tests/non262/PrivateName/error-locations.js new file mode 100644 index 0000000000..fb54faaefa --- /dev/null +++ b/js/src/tests/non262/PrivateName/error-locations.js @@ -0,0 +1,35 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +function assertLineAndColumn(str, line, column) { + try { + eval(str); + throw 'didn\'t syntaxerror, bad.' + } catch (e) { + assertEq(e instanceof SyntaxError, true); + assertEq(e.lineNumber, line); + assertEq(e.columnNumber, column); + } +} + +assertLineAndColumn(`class A { g() { return this.#x; }}`, 1, 28); +// Make sure we're reporting the first error, if there are multiple, in class +// exit context; +assertLineAndColumn( + `class A { g() { return this.#x; } y() { return this.#z + this.#y; } }`, + 1, 28); +assertLineAndColumn(`this.#x`, 1, 5); +// Make sure we're reporting the first error, if there are multiple, in +// non-class context; +assertLineAndColumn(`this.#x; this.#y; this.#z`, 1, 5); + +assertLineAndColumn( + `class A { +g() { return this.#x; }}`, + 2, 18); +assertLineAndColumn( + `class A { + +g() { return this.#x; } y() { return this.#y; }}`, + 3, 18); + +if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/error-outside-class.js b/js/src/tests/non262/PrivateName/error-outside-class.js new file mode 100644 index 0000000000..8ddb380872 --- /dev/null +++ b/js/src/tests/non262/PrivateName/error-outside-class.js @@ -0,0 +1,13 @@ + +// Can't reference a private field without an object +assertThrowsInstanceOf(() => eval('#x'), SyntaxError); + +// Can't reference a private field without an enclosing class +assertThrowsInstanceOf(() => eval('this.#x'), SyntaxError); + +// Can't reference a private field in a random function outside a class context +assertThrowsInstanceOf( + () => eval('function foo() { return this.#x'), SyntaxError); + + +if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/illegal-delete.js b/js/src/tests/non262/PrivateName/illegal-delete.js new file mode 100644 index 0000000000..84d527a378 --- /dev/null +++ b/js/src/tests/non262/PrivateName/illegal-delete.js @@ -0,0 +1,94 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +class A { + #x = {a: 1}; + b = null; + es(s) { + eval(s); + } +} + +var a = new A; +a.b = new A; + +assertThrowsInstanceOf(() => a.es('delete this.#x'), SyntaxError); +assertThrowsInstanceOf(() => a.es('delete (this.#x)'), SyntaxError); +assertThrowsInstanceOf(() => a.es('delete this?.#x'), SyntaxError); +assertThrowsInstanceOf(() => a.es('delete this?.b.#x'), SyntaxError); +// Should be OK +a.es('delete (0, this.#x.a)') +a.es('delete this?.b.#x.a') + + +// Make sure the above works in a different context, with emphasis on +// lazy/syntax parsing. +function eval_and_report(str) { + var classTest = ` + class B { + #x = {a: 1}; + b = null; + test() { + ${str}; + } + } + var b = new B; + b.b = new B; + b.test(); + `; + + var str = ` + function f(run) { + if (run) { + ${classTest} + } + } + f(run)`; + + + var throws = []; + // Evalutate in a new global; has the advantage that it makes successes + // idempotent. + var g = newGlobal(); + g.run = false; + + try { + // The idea is that this is a full parse + evaluate(classTest, {global: g}); + } catch (e) { + throws.push(e); + } + + try { + // The idea is this is a lazy parse; however, fields currently + // disable lazy parsing, so right now + evaluate(str, {global: g}); + } catch (e) { + throws.push(e); + } + + return throws; +} + +function assertSyntaxError(str) { + var exceptions = eval_and_report(str); + assertEq(exceptions.length, 2); + for (var e of exceptions) { + assertEq(/SyntaxError/.test(e.name), true); + } +} + +function assertNoSyntaxError(str) { + var exceptions = eval_and_report(str); + assertEq(exceptions.length, 0); +} + +assertSyntaxError('delete this.#x'); +assertSyntaxError('delete (this.#x)'); +assertSyntaxError('delete this?.#x'); +assertSyntaxError('delete this?.b.#x'); +// Should be OK +assertNoSyntaxError('delete (0, this.#x.a)') +assertNoSyntaxError('delete this?.b.#x.a') + + +if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/illegal-in-class-context.js b/js/src/tests/non262/PrivateName/illegal-in-class-context.js new file mode 100644 index 0000000000..3012e4bfe9 --- /dev/null +++ b/js/src/tests/non262/PrivateName/illegal-in-class-context.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Are private fields enabled? +let privateFields = false; +try { + Function('class C { #x; }'); + privateFields = true; +} catch (exc) { + assertEq(exc instanceof SyntaxError, true); +} + +if (!privateFields) { + assertThrowsInstanceOf(() => eval(`class A { #x }`), SyntaxError); + assertThrowsInstanceOf(() => eval(`class A { #x=10 }`), SyntaxError); +} else { + assertThrowsInstanceOf(() => eval(`class A { #x; #x; }`), SyntaxError); +} + +// No computed private fields +assertThrowsInstanceOf( + () => eval(`var x = "foo"; class A { #[x] = 20; }`), SyntaxError); + + +if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/illegal-in-identifier-context.js b/js/src/tests/non262/PrivateName/illegal-in-identifier-context.js new file mode 100644 index 0000000000..e7e232bf69 --- /dev/null +++ b/js/src/tests/non262/PrivateName/illegal-in-identifier-context.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Private names can't appear in contexts where plain identifiers are expected. + +// Private names as binding identifiers. +assertThrowsInstanceOf(() => eval(`var #a;`), SyntaxError); +assertThrowsInstanceOf(() => eval(`let #a;`), SyntaxError); +assertThrowsInstanceOf(() => eval(`const #a = 0;`), SyntaxError); +assertThrowsInstanceOf(() => eval(`function #a(){}`), SyntaxError); +assertThrowsInstanceOf(() => eval(`function f(#a){}`), SyntaxError); + +// With escape sequences (leading and non-leading case). +assertThrowsInstanceOf(() => eval(String.raw`var #\u0061;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`var #a\u0061;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`let #\u0061;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`let #a\u0061;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`const #\u0061 = 0;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`const #a\u0061 = 0;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`function #\u0061(){}`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`function #a\u0061(){}`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`function f(#\u0061){}`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`function f(#a\u0061){}`), SyntaxError); + + +// Private names as label identifiers. +assertThrowsInstanceOf(() => eval(`#a: ;`), SyntaxError); + +// With escape sequences (leading and non-leading case). +assertThrowsInstanceOf(() => eval(String.raw`#\u0061: ;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`#a\u0061: ;`), SyntaxError); + + +// Private names as identifier references. +assertThrowsInstanceOf(() => eval(`#a = 0;`), SyntaxError); +assertThrowsInstanceOf(() => eval(`typeof #a;`), SyntaxError); + +// With escape sequences (leading and non-leading case). +assertThrowsInstanceOf(() => eval(String.raw`#\u0061 = 0;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`#a\u0061 = 0;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`typeof #\u0061;`), SyntaxError); +assertThrowsInstanceOf(() => eval(String.raw`typeof #a\u0061;`), SyntaxError); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/illegal-in-object-context.js b/js/src/tests/non262/PrivateName/illegal-in-object-context.js new file mode 100644 index 0000000000..9cc1488376 --- /dev/null +++ b/js/src/tests/non262/PrivateName/illegal-in-object-context.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Private names aren't valid in object literals. + +assertThrowsInstanceOf(() => eval(`var o = {#a: 0};`), SyntaxError); +assertThrowsInstanceOf(() => eval(`var o = {#a};`), SyntaxError); +assertThrowsInstanceOf(() => eval(`var o = {#a(){}};`), SyntaxError); +assertThrowsInstanceOf(() => eval(`var o = {get #a(){}};`), SyntaxError); +assertThrowsInstanceOf(() => eval(`var o = {set #a(v){}};`), SyntaxError); +assertThrowsInstanceOf(() => eval(`var o = {*#a(v){}};`), SyntaxError); +assertThrowsInstanceOf(() => eval(`var o = {async #a(v){}};`), SyntaxError); +assertThrowsInstanceOf(() => eval(`var o = {async *#a(v){}};`), SyntaxError); + +if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/lexical-presence.js b/js/src/tests/non262/PrivateName/lexical-presence.js new file mode 100644 index 0000000000..4ec5c310c4 --- /dev/null +++ b/js/src/tests/non262/PrivateName/lexical-presence.js @@ -0,0 +1,55 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +// Verify that private fields are enabled. +class A { + #x; +}; + +function assertThrowsSyntaxError(str) { + assertThrowsInstanceOf(() => eval(str), SyntaxError); // Direct Eval + assertThrowsInstanceOf(() => (1, eval)(str), SyntaxError); // Indirect Eval + assertThrowsInstanceOf(() => Function(str), SyntaxError); // Function +} + +assertThrowsSyntaxError(` + class B { + g() { + return this.#x; + } + }; +`); + +assertThrowsSyntaxError(` +with (new class A { #x; }) { + class B { + #y; + constructor() { return this.#x; } + } + } +`); + +// Make sure we don't create a generic binding for #x. +with(new class { + #x = 12; +}) { + assertEq('#x' in this, false); +} + +// Try to fetch a non-existing field using different +// compilation contexts +function assertNonExisting(fetchCode) { + source = `var X = class { + b() { + ${fetchCode} + } + } + var a = new X; + a.b()` + assertThrowsInstanceOf(() => eval(source), SyntaxError); +} + +assertNonExisting(`return eval("this.#x")"`); +assertNonExisting(`return (1,eval)("this.#x")`); +assertNonExisting(`var f = Function("this.#x"); return f();`); + +if (typeof reportCompare === 'function') reportCompare(0, 0);
\ No newline at end of file diff --git a/js/src/tests/non262/PrivateName/modify-non-extensible.js b/js/src/tests/non262/PrivateName/modify-non-extensible.js new file mode 100644 index 0000000000..b57f82b38e --- /dev/null +++ b/js/src/tests/non262/PrivateName/modify-non-extensible.js @@ -0,0 +1,42 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +// Returns the argument in the constructor to allow stamping private fields into +// arbitrary objects. +class OverrideBase { + constructor(o) { + return o; + } +}; + +class A extends OverrideBase { + #a = 1; + g() { + return this.#a + } + + static gs(o) { + return o.#a; + } + static inca(obj) { + obj.#a++; + } +} + +var obj = {}; +Object.seal(obj); +new A(obj); // Add #a to obj, but not g. +assertEq('g' in obj, false); +assertEq(A.gs(obj), 1); +A.inca(obj); +assertEq(A.gs(obj), 2); + +// Ensure that the object remains non-extensible +obj.h = 'hi' +assertEq('h' in obj, false); + + +Object.freeze(obj); +A.inca(obj); // Despite being frozen, private names are modifiable. +assertEq(A.gs(obj), 3); + +if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/names.js b/js/src/tests/non262/PrivateName/names.js new file mode 100644 index 0000000000..f71f1ac19d --- /dev/null +++ b/js/src/tests/non262/PrivateName/names.js @@ -0,0 +1,31 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +var C = class { + static #field = () => 'Test262'; + static field = () => 'Test262'; + #instance = () => 'Test262'; + instance = () => 'Test262'; + + static accessPrivateField() { + return this.#field; + } + + accessPrivateInstanceField() { + return this.#instance; + } + + static accessField() { + return this.field; + } + + accessInstanceField() { + return this.instance; + } +} +assertEq(C.accessPrivateField().name, '#field') +assertEq(C.accessField().name, 'field'); +var c = new C; +assertEq(c.accessPrivateInstanceField().name, '#instance'); +assertEq(c.accessInstanceField().name, 'instance'); + +if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/nested-class-name-used.js b/js/src/tests/non262/PrivateName/nested-class-name-used.js new file mode 100644 index 0000000000..cc1a77f375 --- /dev/null +++ b/js/src/tests/non262/PrivateName/nested-class-name-used.js @@ -0,0 +1,33 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +// AllPrivateIdentifiersValid uses only lexical string names, not +// the dynamic private names; which means the below is not a syntax +// error but is instead a TypeError on access. + +class A { + #x = 10; + f() { + class B { + g() { + return this.#x; // note: #x isn't declared in this class, but + // the enclosing. + } + }; + this.y = new B; + } + constructor() { + this.f(); + } + g() { + return this.y.g(); + } +}; + +a = new A; +try { + a.g(); +} catch (e) { + assertEq(e instanceof TypeError, true); +} + +if (typeof reportCompare === 'function') reportCompare(0, 0);
\ No newline at end of file diff --git a/js/src/tests/non262/PrivateName/not-iterable.js b/js/src/tests/non262/PrivateName/not-iterable.js new file mode 100644 index 0000000000..59c6169079 --- /dev/null +++ b/js/src/tests/non262/PrivateName/not-iterable.js @@ -0,0 +1,39 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options +// +// Ensure PrivateNames aren't iterable. + +class O { + #x = 123; + gx() { + return this.#x; + } +} +var o = new O; + +assertEq(o.gx(), 123); + +assertEq(Object.keys(o).length, 0); +assertEq(Object.getOwnPropertyNames(o).length, 0); +assertEq(Object.getOwnPropertySymbols(o).length, 0); +assertEq(Reflect.ownKeys(o).length, 0); + +var forIn = []; +for (var pk in o) { + forIn.push(pk); +} +assertEq(forIn.length, 0); + +// Proxy case +var proxy = new Proxy(o, {}); +assertEq(Object.keys(proxy).length, 0); +assertEq(Object.getOwnPropertyNames(proxy).length, 0); +assertEq(Object.getOwnPropertySymbols(proxy).length, 0); +assertEq(Reflect.ownKeys(proxy).length, 0); + +for (var pk in proxy) { + forIn.push(pk); +} +assertEq(forIn.length, 0); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/proxy-1.js b/js/src/tests/non262/PrivateName/proxy-1.js new file mode 100644 index 0000000000..5a70841450 --- /dev/null +++ b/js/src/tests/non262/PrivateName/proxy-1.js @@ -0,0 +1,21 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +class A { + #x = 10; + g() { + return this.#x; + } +}; + +var p = new Proxy(new A, {}); +var completed = false; +try { + p.g(); + completed = true; +} catch (e) { + assertEq(e instanceof TypeError, true); +} +assertEq(completed, false); + + +if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/non262/PrivateName/proxy-ccw.js b/js/src/tests/non262/PrivateName/proxy-ccw.js new file mode 100644 index 0000000000..e68426b93d --- /dev/null +++ b/js/src/tests/non262/PrivateName/proxy-ccw.js @@ -0,0 +1,66 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +// Validate CCWs and proxies +class Base { + constructor(o) { + return o; + } +} + +class A extends Base { + x1 = 12; + #x = 10; + static gx(o) { + return o.#x; + } + static sx(o, x) { + o.#x = x; + } + static hasx(o) { + try { + o.#x; + return true; + } catch { + return false; + } + } +} + + +var g = newGlobal({newCompartment: true}); +g.A = A; + +// cross_compartment_target is a cross compartment wrapper to an empty object. +var cross_compartment_target = g.eval('this.x = {}; this.x'); + +// #x gets stamped into the target of the CCW. +new A(cross_compartment_target); +assertEq(A.hasx(cross_compartment_target), true); + +// Can we update and read from this compartment? +assertEq(A.gx(cross_compartment_target), 10); +var o = {test: 12}; +A.sx(cross_compartment_target, o); +assertEq(A.gx(cross_compartment_target), o); + +// Can we read and update from the other compartment? +assertEq(g.eval('this.A.gx(this.x)'), o); +var y = g.eval('this.y = {test: 13}; this.A.sx(this.x, this.y); this.y'); +assertEq(g.eval('this.A.gx(this.x)'), y); +assertEq(A.gx(cross_compartment_target), y); + + +if (typeof nukeCCW === 'function') { + // Nuke the CCW. Now things should throw. + nukeCCW(cross_compartment_target); + var threw = true; + try { + A.gx(cross_compartment_target); + threw = false; + } catch (e) { + } + assertEq(threw, true); +} + + +if (typeof reportCompare === 'function') reportCompare(0, 0);
\ No newline at end of file diff --git a/js/src/tests/non262/PrivateName/proxy-init-set.js b/js/src/tests/non262/PrivateName/proxy-init-set.js new file mode 100644 index 0000000000..0d94ec0492 --- /dev/null +++ b/js/src/tests/non262/PrivateName/proxy-init-set.js @@ -0,0 +1,75 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-options + +// Ensure that the distinction between Proxy Init and Proxy Set holds + +function assertThrowsTypeError(f) { + var type; + try { + f(); + } catch (ex) { + type = ex.name; + } + assertEq(type, 'TypeError'); +} + + + +var target = {}; +var p1 = new Proxy(target, {}); +var p2 = new Proxy(target, {}); + +class Base { + constructor(o) { + return o; + } +} + +class A extends Base { + #field = 10; + static gf(o) { + return o.#field; + } + static sf(o) { + o.#field = 15; + } +} + +class B extends Base { + #field = 25; + static gf(o) { + return o.#field; + } + static sf(o) { + o.#field = 20; + } +} + +// Verify field handling on the proxy we install it on. +new A(p1); +assertEq(A.gf(p1), 10); +A.sf(p1) +assertEq(A.gf(p1), 15); + +// Despite P1 being stamped with A's field, it shouldn't +// be sufficient to set B's field. +assertThrowsTypeError(() => B.sf(p1)); +assertThrowsTypeError(() => B.gf(p1)); +assertThrowsTypeError(() => B.sf(p1)); +new B(p1); +assertEq(B.gf(p1), 25); +B.sf(p1); +assertEq(B.gf(p1), 20); + +// A's field should't be on the target +assertThrowsTypeError(() => A.gf(target)); + +// Can't set the field, doesn't exist +assertThrowsTypeError(() => A.sf(p2)); + +// Definitely can't get the field, doesn't exist. +assertThrowsTypeError(() => A.gf(p2)); + +// Still should't be on the target. +assertThrowsTypeError(() => A.gf(target)); + +if (typeof reportCompare === 'function') reportCompare(0, 0);
\ No newline at end of file diff --git a/js/src/tests/non262/PrivateName/read-private-eval.js b/js/src/tests/non262/PrivateName/read-private-eval.js new file mode 100644 index 0000000000..1f96f2ac5c --- /dev/null +++ b/js/src/tests/non262/PrivateName/read-private-eval.js @@ -0,0 +1,13 @@ +// |reftest| shell-option(--enable-private-fields) skip-if(!xulRuntime.shell) -- requires shell-option + +class A { + #x = 14; + g() { + return eval('this.#x'); + } +} + +a = new A; +assertEq(a.g(), 14); + +if (typeof reportCompare === 'function') reportCompare(0, 0);
\ No newline at end of file diff --git a/js/src/tests/non262/PrivateName/shell.js b/js/src/tests/non262/PrivateName/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/PrivateName/shell.js diff --git a/js/src/tests/non262/PrivateName/unicode-names.js b/js/src/tests/non262/PrivateName/unicode-names.js new file mode 100644 index 0000000000..25eeffff4a --- /dev/null +++ b/js/src/tests/non262/PrivateName/unicode-names.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!xulRuntime.shell) + +source = `class A { + // Ensure this name parses. Failure would be an InternalError: Buffer too + // small + #℘; +}`; + +try { + Function(source); +} catch (e) { + assertEq(getRealmConfiguration()['privateFields'], false); + assertEq(e instanceof SyntaxError, true); +} + +if (typeof reportCompare === 'function') reportCompare(0, 0);
\ No newline at end of file |