diff --git a/features.txt b/features.txt index 79f909f462..595e470733 100644 --- a/features.txt +++ b/features.txt @@ -88,6 +88,10 @@ upsert # https://github.com/tc39/proposal-immutable-arraybuffer immutable-arraybuffer +# Non-extensible Applies to Private +# https://github.com/tc39/proposal-nonextensible-applies-to-private +nonextensible-applies-to-private + ## Standard language features # # Language features that have been included in a published version of the diff --git a/test/language/statements/class/elements/private-class-field-on-frozen-objects.js b/test/language/statements/class/elements/private-class-field-on-frozen-objects.js deleted file mode 100644 index 9517976c10..0000000000 --- a/test/language/statements/class/elements/private-class-field-on-frozen-objects.js +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2019 Caio Lima. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -description: It is possible to add private fields on frozen objects -esid: sec-define-field -info: | - DefineField(receiver, fieldRecord) - ... - 8. If fieldName is a Private Name, - a. Perform ? PrivateFieldAdd(fieldName, receiver, initValue). - 9. Else, - a. Assert: IsPropertyKey(fieldName) is true. - b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue). - 10. Return. -features: [class, class-fields-private, class-fields-public] -flags: [onlyStrict] ----*/ - -class Test { - f = this; - #g = (Object.freeze(this), "Test262"); - - get g() { - return this.#g; - } -} - -let t = new Test(); -assert.sameValue(t.f, t); -assert.sameValue(t.g, "Test262"); diff --git a/test/language/statements/class/elements/private-class-field-on-nonextensible-objects.js b/test/language/statements/class/elements/private-class-field-on-nonextensible-objects.js new file mode 100644 index 0000000000..ada3868916 --- /dev/null +++ b/test/language/statements/class/elements/private-class-field-on-nonextensible-objects.js @@ -0,0 +1,117 @@ +// Copyright (C) 2019 Caio Lima. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: It is not possible to add private fields on non-extensible objects +esid: sec-define-field +info: | + 1.1 PrivateFieldAdd ( O, P, value ) + 1. If O.[[Extensible]] is false, throw a TypeError exception. + ... + +features: + - class + - class-fields-private + - class-fields-public + - nonextensible-applies-to-private +flags: [onlyStrict] +---*/ + +// Analogous to +// test/language/statements/class/subclass/private-class-field-on-nonextensible-return-override.js + +class NonExtensibleBase { + constructor(seal) { + if (seal) Object.preventExtensions(this); + } +} + + +// extend superclass with private instance data field +class ClassWithPrivateField extends NonExtensibleBase { + #val; + + constructor(seal) { + super(seal); + this.#val = 42; + } + val() { + return this.#val; + } +} + +const t = new ClassWithPrivateField(false); +// extensible objects can be extended +assert.sameValue(t.val(), 42); + +// where superclass prevented extensions & subclass extended +assert.throws(TypeError, function () { + new ClassWithPrivateField(true); +}); + + +// extend superclass with private instance method +class ClassWithPrivateMethod extends NonExtensibleBase { + constructor(seal) { + super(seal); + } + // private methods are on the instance, so will fail + #privateMethod() { + return 42; + }; + // public methods are on the prototype, so are ok. + publicMethod() { + return this.#privateMethod(); + } +} + +const m = new ClassWithPrivateMethod(false); +// extensible objects can be extended +assert.sameValue(m.publicMethod(), 42); + +// where superclass prevented extensions & subclass extended +assert.throws(TypeError, function () { + new ClassWithPrivateMethod(true); +}); + + +// extend superclass with private instance accessor +class ClassWithPrivateAccessor extends NonExtensibleBase { + constructor(seal) { + super(seal); + } + // private accessors are on the instance, so will fail + get #privateAccessor() { + return 42; + }; + // public accessors are on the prototype, so are ok. + get publicAccessor() { + return this.#privateAccessor; + } +} + +const a = new ClassWithPrivateAccessor(false); +// extensible objects can be extended +assert.sameValue(m.publicAccessor, 42); + +// where superclass prevented extensions & subclass extended +assert.throws(TypeError, function () { + new ClassWithPrivateAccessor(true); +}); + + +// base class private instance data field +class TestNonExtensibleData { + #g = (Object.preventExtensions(this), "Test262"); +} + +assert.throws(TypeError, function () { + new TestNonExtensibleData(); +}); + +// base class with private static data field +assert.throws(TypeError, function () { + class TestNonExtensibleStaticData { + static #g = (Object.preventExtensions(TestNonExtensibleStaticData), "Test262"); + } +}); diff --git a/test/language/statements/class/subclass/private-class-field-on-nonextensible-return-override.js b/test/language/statements/class/subclass/private-class-field-on-nonextensible-return-override.js new file mode 100644 index 0000000000..d8e7034a7d --- /dev/null +++ b/test/language/statements/class/subclass/private-class-field-on-nonextensible-return-override.js @@ -0,0 +1,100 @@ +// Copyright (C) 2019 Caio Lima. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: It is not possible to add private fields on non-extensible objects via return override +esid: sec-define-field +info: | + 1.1 PrivateFieldAdd ( O, P, value ) + 1. If O.[[Extensible]] is false, throw a TypeError exception. + ... + +features: + - class + - class-fields-private + - class-fields-public + - nonextensible-applies-to-private +flags: [onlyStrict] +---*/ + +// Analogous to +// test/language/statements/class/elements/private-class-field-on-nonextensible-objects.js + +class TrojanBase { + constructor(obj) { + return obj; + } +} + + +// extend superclass with private instance data field +class ClassWithPrivateField extends TrojanBase { + #val; + + constructor(obj) { + super(obj); + this.#val = 42; + } + val() { + return this.#val; + } +} + +const t = new ClassWithPrivateField({}); +// extensible objects can be extended +assert.sameValue(t.val(), 42); + +// where superclass prevented extensions & subclass extended +assert.throws(TypeError, function () { + new ClassWithPrivateField(Object.preventExtensions({})); +}); + + +// extend superclass with private instance method +class ClassWithPrivateMethod extends TrojanBase { + constructor(obj) { + super(obj); + } + // private methods are on the instance, so will fail + #privateMethod() { + return 42; + }; + // public methods are on the prototype, so are ok. + publicMethod() { + return this.#privateMethod(); + } +} + +const m = new ClassWithPrivateMethod({}); +// extensible objects can be extended +assert.sameValue(m.publicMethod(), 42); + +// where superclass prevented extensions & subclass extended +assert.throws(TypeError, function () { + new ClassWithPrivateMethod(Object.preventExtensions({})); +}); + + +// extend superclass with private instance accessor +class ClassWithPrivateAccessor extends TrojanBase { + constructor(obj) { + super(obj); + } + // private accessors are on the instance, so will fail + get #privateAccessor() { + return 42; + }; + // public accessors are on the prototype, so are ok. + get publicAccessor() { + return this.#privateAccessor; + } +} + +const a = new ClassWithPrivateAccessor({}); +// extensible objects can be extended +assert.sameValue(m.publicAccessor, 42); + +// where superclass prevented extensions & subclass extended +assert.throws(TypeError, function () { + new ClassWithPrivateAccessor(Object.preventExtensions({})); +}); diff --git a/test/staging/sm/PrivateName/modify-non-extensible.js b/test/staging/sm/PrivateName/modify-non-extensible.js index b3e7283cbb..f122f9a3b8 100644 --- a/test/staging/sm/PrivateName/modify-non-extensible.js +++ b/test/staging/sm/PrivateName/modify-non-extensible.js @@ -2,6 +2,11 @@ // This code is governed by the BSD license found in the LICENSE file. /*--- +features: + - class + - class-fields-private + - class-fields-public + - nonextensible-applies-to-private flags: - noStrict description: | @@ -32,8 +37,8 @@ class A extends OverrideBase { } var obj = {}; -Object.seal(obj); new A(obj); // Add #a to obj, but not g. +Object.seal(obj); assert.sameValue('g' in obj, false); assert.sameValue(A.gs(obj), 1); A.inca(obj); @@ -67,8 +72,10 @@ assert.sameValue(A.gs(proxy), 2) var target = { a: 10 }; Object.freeze(target); -new A(target); -assert.sameValue(Object.isFrozen(target), true) +assert.throws(TypeError, function () { + new A(target); +}); +assert.sameValue(Object.isFrozen(target), true); var getOwnKeys = []; var proxy = new Proxy(target, { @@ -80,4 +87,3 @@ var proxy = new Proxy(target, { Object.isFrozen(proxy); assert.sameValue(getOwnKeys.length, 1); -