Private class methods stage 3 (#8654)

* Add private method syntax support

* Add private method spec support

* Add private method loose support

* Throw error if static private method is used

* Add more isStatic & isMethod checks

* Remove `writable:false` from private method inits

`writable` is false by default.

* Add private method func obj equality check

* Throw if private accessor is used

* Add check for fields === private method loose mode

* Throw buildCodeFrameErrors instead of Errors

* Move obj destructuring inside for loop

* Remove "computed" from ClassPrivateMethod type def
This commit is contained in:
Tim McClure
2018-11-28 19:20:09 -05:00
committed by Justin Ridgewell
parent 6e39b58f8a
commit 0859535b62
40 changed files with 745 additions and 37 deletions

View File

@@ -0,0 +1,11 @@
class Foo {
constructor() {
this.publicField = this.#privateMethod();
}
#privateMethod() {
return 42;
}
}
expect((new Foo).publicField).toEqual(42);

View File

@@ -0,0 +1,9 @@
class Foo {
constructor() {
this.publicField = this.#privateMethod();
}
#privateMethod() {
return 42;
}
}

View File

@@ -0,0 +1,15 @@
var Foo = function Foo() {
"use strict";
babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _privateMethod, {
value: _privateMethod2
});
this.publicField = babelHelpers.classPrivateFieldLooseBase(this, _privateMethod)[_privateMethod]();
};
var _privateMethod = babelHelpers.classPrivateFieldLooseKey("privateMethod");
var _privateMethod2 = function _privateMethod2() {
return 42;
};

View File

@@ -0,0 +1,41 @@
class Foo {
constructor(status) {
this.status = status;
expect(() => this.#getStatus = null).toThrow(TypeError);
}
#getStatus() {
return this.status;
}
getCurrentStatus() {
return this.#getStatus();
}
setCurrentStatus(newStatus) {
this.status = newStatus;
}
getFakeStatus(fakeStatus) {
const getStatus = this.#getStatus;
return function () {
return getStatus.call({ status: fakeStatus });
};
}
getFakeStatusFunc() {
return {
status: 'fake-status',
getFakeStatus: this.#getStatus,
};
}
}
const f = new Foo('inactive');
expect(f.getCurrentStatus()).toBe('inactive');
f.setCurrentStatus('new-status');
expect(f.getCurrentStatus()).toBe('new-status');
expect(f.getFakeStatus('fake')()).toBe('fake');
expect(f.getFakeStatusFunc().getFakeStatus()).toBe('fake-status');

View File

@@ -0,0 +1,31 @@
class Foo {
constructor(status) {
this.status = status;
}
#getStatus() {
return this.status;
}
getCurrentStatus() {
return this.#getStatus();
}
setCurrentStatus(newStatus) {
this.status = newStatus;
}
getFakeStatus(fakeStatus) {
const fakeGetStatus = this.#getStatus;
return function() {
return fakeGetStatus.call({ status: fakeStatus });
};
}
getFakeStatusFunc() {
return {
status: 'fake-status',
getFakeStatus: this.#getStatus,
};
}
}

View File

@@ -0,0 +1,51 @@
var Foo =
/*#__PURE__*/
function () {
"use strict";
function Foo(status) {
babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _getStatus, {
value: _getStatus2
});
this.status = status;
}
babelHelpers.createClass(Foo, [{
key: "getCurrentStatus",
value: function getCurrentStatus() {
return babelHelpers.classPrivateFieldLooseBase(this, _getStatus)[_getStatus]();
}
}, {
key: "setCurrentStatus",
value: function setCurrentStatus(newStatus) {
this.status = newStatus;
}
}, {
key: "getFakeStatus",
value: function getFakeStatus(fakeStatus) {
var fakeGetStatus = babelHelpers.classPrivateFieldLooseBase(this, _getStatus)[_getStatus];
return function () {
return fakeGetStatus.call({
status: fakeStatus
});
};
}
}, {
key: "getFakeStatusFunc",
value: function getFakeStatusFunc() {
return {
status: 'fake-status',
getFakeStatus: babelHelpers.classPrivateFieldLooseBase(this, _getStatus)[_getStatus]
};
}
}]);
return Foo;
}();
var _getStatus = babelHelpers.classPrivateFieldLooseKey("getStatus");
var _getStatus2 = function _getStatus2() {
return this.status;
};

View File

@@ -0,0 +1,15 @@
let exfiltrated;
class Foo {
#privateMethod() {}
constructor() {
if (exfiltrated === undefined) {
exfiltrated = this.#privateMethod;
}
expect(exfiltrated).toStrictEqual(this.#privateMethod);
}
}
new Foo();
// check for private method function object equality
new Foo();

View File

@@ -0,0 +1,10 @@
let exfiltrated;
class Foo {
#privateMethod() {}
constructor() {
if (exfiltrated === undefined) {
exfiltrated = this.#privateMethod;
}
}
}

View File

@@ -0,0 +1,18 @@
var exfiltrated;
var Foo = function Foo() {
"use strict";
babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _privateMethod, {
value: _privateMethod2
});
if (exfiltrated === undefined) {
exfiltrated = babelHelpers.classPrivateFieldLooseBase(this, _privateMethod)[_privateMethod];
}
};
var _privateMethod = babelHelpers.classPrivateFieldLooseKey("privateMethod");
var _privateMethod2 = function _privateMethod2() {};

View File

@@ -0,0 +1,15 @@
{
"plugins": [
[
"external-helpers",
{
"helperVersion": "7.1.6"
}
],
["proposal-private-methods", { "loose": true }],
["proposal-class-properties", { "loose": true }],
"transform-classes",
"transform-block-scoping",
"syntax-class-properties"
]
}

View File

@@ -0,0 +1,11 @@
class Foo {
constructor() {
this.publicField = this.#privateMethod();
}
#privateMethod() {
return 42;
}
}
expect((new Foo).publicField).toEqual(42);

View File

@@ -0,0 +1,9 @@
class Foo {
constructor() {
this.publicField = this.#privateMethod();
}
#privateMethod() {
return 42;
}
}

View File

@@ -0,0 +1,15 @@
var Foo = function Foo() {
"use strict";
babelHelpers.classCallCheck(this, Foo);
_privateMethod.add(this);
this.publicField = babelHelpers.classPrivateMethodGet(this, _privateMethod, _privateMethod2).call(this);
};
var _privateMethod = new WeakSet();
var _privateMethod2 = function _privateMethod2() {
return 42;
};

View File

@@ -0,0 +1,41 @@
class Foo {
constructor(status) {
this.status = status;
expect(() => this.#getStatus = null).toThrow(TypeError);
}
#getStatus() {
return this.status;
}
getCurrentStatus() {
return this.#getStatus();
}
setCurrentStatus(newStatus) {
this.status = newStatus;
}
getFakeStatus(fakeStatus) {
const getStatus = this.#getStatus;
return function () {
return getStatus.call({ status: fakeStatus });
};
}
getFakeStatusFunc() {
return {
status: 'fake-status',
getFakeStatus: this.#getStatus,
};
}
}
const f = new Foo('inactive');
expect(f.getCurrentStatus()).toBe('inactive');
f.setCurrentStatus('new-status');
expect(f.getCurrentStatus()).toBe('new-status');
expect(f.getFakeStatus('fake')()).toBe('fake');
expect(f.getFakeStatusFunc().getFakeStatus()).toBe('fake-status');

View File

@@ -0,0 +1,31 @@
class Foo {
constructor(status) {
this.status = status;
}
#getStatus() {
return this.status;
}
getCurrentStatus() {
return this.#getStatus();
}
setCurrentStatus(newStatus) {
this.status = newStatus;
}
getFakeStatus(fakeStatus) {
const fakeGetStatus = this.#getStatus;
return function() {
return fakeGetStatus.call({ status: fakeStatus });
};
}
getFakeStatusFunc() {
return {
status: 'fake-status',
getFakeStatus: this.#getStatus,
};
}
}

View File

@@ -0,0 +1,50 @@
var Foo =
/*#__PURE__*/
function () {
"use strict";
function Foo(status) {
babelHelpers.classCallCheck(this, Foo);
_getStatus.add(this);
this.status = status;
}
babelHelpers.createClass(Foo, [{
key: "getCurrentStatus",
value: function getCurrentStatus() {
return babelHelpers.classPrivateMethodGet(this, _getStatus, _getStatus2).call(this);
}
}, {
key: "setCurrentStatus",
value: function setCurrentStatus(newStatus) {
this.status = newStatus;
}
}, {
key: "getFakeStatus",
value: function getFakeStatus(fakeStatus) {
var fakeGetStatus = babelHelpers.classPrivateMethodGet(this, _getStatus, _getStatus2);
return function () {
return fakeGetStatus.call({
status: fakeStatus
});
};
}
}, {
key: "getFakeStatusFunc",
value: function getFakeStatusFunc() {
return {
status: 'fake-status',
getFakeStatus: babelHelpers.classPrivateMethodGet(this, _getStatus, _getStatus2)
};
}
}]);
return Foo;
}();
var _getStatus = new WeakSet();
var _getStatus2 = function _getStatus2() {
return this.status;
};

View File

@@ -0,0 +1,15 @@
let exfiltrated;
class Foo {
#privateMethod() {}
constructor() {
if (exfiltrated === undefined) {
exfiltrated = this.#privateMethod;
}
expect(exfiltrated).toStrictEqual(this.#privateMethod);
}
}
new Foo();
// check for private method function object equality
new Foo();

View File

@@ -0,0 +1,10 @@
let exfiltrated;
class Foo {
#privateMethod() {}
constructor() {
if (exfiltrated === undefined) {
exfiltrated = this.#privateMethod;
}
}
}

View File

@@ -0,0 +1,17 @@
var exfiltrated;
var Foo = function Foo() {
"use strict";
babelHelpers.classCallCheck(this, Foo);
_privateMethod.add(this);
if (exfiltrated === undefined) {
exfiltrated = babelHelpers.classPrivateMethodGet(this, _privateMethod, _privateMethod2);
}
};
var _privateMethod = new WeakSet();
var _privateMethod2 = function _privateMethod2() {};

View File

@@ -0,0 +1,15 @@
{
"plugins": [
[
"external-helpers",
{
"helperVersion": "7.1.6"
}
],
"proposal-private-methods",
"proposal-class-properties",
"transform-classes",
"transform-block-scoping",
"syntax-class-properties"
]
}

View File

@@ -0,0 +1,3 @@
import runner from "@babel/helper-plugin-test-runner";
runner(__dirname);