Fix nested classes reference private fields (#11405)

* Fix nested classes reference private fields

* Process only visible private fields when redeclaring

* Comments

* Skip class traversal if there are no private fields

* Handle redeclared private field in computed key
This commit is contained in:
Justin Ridgewell 2020-04-14 15:51:51 -04:00 committed by GitHub
parent aaced0156d
commit 9b48a8ead7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 598 additions and 11 deletions

View File

@ -85,24 +85,50 @@ const privateNameVisitor = {
const { privateNamesMap } = this;
const body = path.get("body.body");
const visiblePrivateNames = new Map(privateNamesMap);
const redeclared = [];
for (const prop of body) {
if (!prop.isPrivate()) {
continue;
if (!prop.isPrivate()) continue;
const { name } = prop.node.key.id;
visiblePrivateNames.delete(name);
redeclared.push(name);
}
if (!privateNamesMap.has(prop.node.key.id.name)) continue;
// This class redeclares the private name.
// So, we can only evaluate the things in the outer scope.
path.traverse(privateNameInnerVisitor, this);
path.skip();
break;
// If the class doesn't redeclare any private fields, we can continue with
// our overall traversal.
if (!redeclared.length) {
return;
}
// This class redeclares some private field. We need to process the outer
// environment with access to all the outer privates, then we can process
// the inner environment with only the still-visible outer privates.
path.get("superClass").traverse(privateNameVisitor, this);
path.get("body").traverse(privateNameNestedVisitor, {
...this,
redeclared,
});
path.traverse(privateNameVisitor, {
...this,
privateNamesMap: visiblePrivateNames,
});
// We'll eventually hit this class node again with the overall Class
// Features visitor, which'll process the redeclared privates.
path.skip();
},
};
// Traverses the outer portion of a class, without touching the class's inner
// scope, for private names.
const privateNameInnerVisitor = traverse.visitors.merge([
const privateNameNestedVisitor = traverse.visitors.merge([
{
PrivateName(path) {
const { redeclared } = this;
const { name } = path.node.id;
if (redeclared.includes(name)) path.skip();
},
},
{
PrivateName: privateNameVisitor.PrivateName,
},
@ -263,6 +289,8 @@ export function transformPrivateNamesUsage(
loose,
state,
) {
if (!privateNamesMap.size) return;
const body = path.get("body");
if (loose) {

View File

@ -0,0 +1,14 @@
class Foo {
#foo = 1;
test() {
class Nested {
#foo = 2;
[this.#foo]() {
}
}
this.#foo;
}
}

View File

@ -0,0 +1,43 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _foo, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var _babelHelpers$classPr;
_babelHelpers$classPr = babelHelpers.classPrivateFieldLooseBase(this, _foo2)[_foo2];
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
Object.defineProperty(this, _foo2, {
writable: true,
value: 2
});
}
babelHelpers.createClass(Nested, [{
key: _babelHelpers$classPr,
value: function () {}
}]);
return Nested;
}();
var _foo2 = babelHelpers.classPrivateFieldLooseKey("foo");
babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo];
}
}]);
return Foo;
}();
var _foo = babelHelpers.classPrivateFieldLooseKey("foo");

View File

@ -0,0 +1,12 @@
class Foo {
#foo = 1;
test() {
class Nested {
[this.#foo]() {
}
}
this.#foo;
}
}

View File

@ -0,0 +1,35 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _foo, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var _this = this;
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
}
babelHelpers.createClass(Nested, [{
key: babelHelpers.classPrivateFieldLooseBase(_this, _foo)[_foo],
value: function () {}
}]);
return Nested;
}();
babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo];
}
}]);
return Foo;
}();
var _foo = babelHelpers.classPrivateFieldLooseKey("foo");

View File

@ -0,0 +1,18 @@
class Foo {
#foo = 1;
#bar = 1;
test() {
class Nested {
#bar = 2;
test() {
this.#foo;
this.#bar;
}
}
this.#foo;
this.#bar;
}
}

View File

@ -0,0 +1,49 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _foo, {
writable: true,
value: 1
});
Object.defineProperty(this, _bar, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
Object.defineProperty(this, _bar2, {
writable: true,
value: 2
});
}
babelHelpers.createClass(Nested, [{
key: "test",
value: function test() {
babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo];
babelHelpers.classPrivateFieldLooseBase(this, _bar2)[_bar2];
}
}]);
return Nested;
}();
var _bar2 = babelHelpers.classPrivateFieldLooseKey("bar");
babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo];
babelHelpers.classPrivateFieldLooseBase(this, _bar)[_bar];
}
}]);
return Foo;
}();
var _foo = babelHelpers.classPrivateFieldLooseKey("foo");
var _bar = babelHelpers.classPrivateFieldLooseKey("bar");

View File

@ -0,0 +1,15 @@
class Foo {
#foo = 1;
test() {
class Nested {
#foo = 2;
test() {
this.#foo;
}
}
this.#foo;
}
}

View File

@ -0,0 +1,41 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _foo, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
Object.defineProperty(this, _foo2, {
writable: true,
value: 2
});
}
babelHelpers.createClass(Nested, [{
key: "test",
value: function test() {
babelHelpers.classPrivateFieldLooseBase(this, _foo2)[_foo2];
}
}]);
return Nested;
}();
var _foo2 = babelHelpers.classPrivateFieldLooseKey("foo");
babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo];
}
}]);
return Foo;
}();
var _foo = babelHelpers.classPrivateFieldLooseKey("foo");

View File

@ -0,0 +1,13 @@
class Foo {
#foo = 1;
test() {
class Nested {
test() {
this.#foo;
}
}
this.#foo;
}
}

View File

@ -0,0 +1,35 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _foo, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
}
babelHelpers.createClass(Nested, [{
key: "test",
value: function test() {
babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo];
}
}]);
return Nested;
}();
babelHelpers.classPrivateFieldLooseBase(this, _foo)[_foo];
}
}]);
return Foo;
}();
var _foo = babelHelpers.classPrivateFieldLooseKey("foo");

View File

@ -0,0 +1,14 @@
class Foo {
#foo = 1;
test() {
class Nested {
#foo = 2;
[this.#foo]() {
}
}
this.#foo;
}
}

View File

@ -0,0 +1,45 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
_foo.set(this, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var _babelHelpers$classPr;
_babelHelpers$classPr = babelHelpers.classPrivateFieldGet(this, _foo2);
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
_foo2.set(this, {
writable: true,
value: 2
});
}
babelHelpers.createClass(Nested, [{
key: _babelHelpers$classPr,
value: function () {}
}]);
return Nested;
}();
var _foo2 = new WeakMap();
babelHelpers.classPrivateFieldGet(this, _foo);
}
}]);
return Foo;
}();
var _foo = new WeakMap();

View File

@ -0,0 +1,12 @@
class Foo {
#foo = 1;
test() {
class Nested {
[this.#foo]() {
}
}
this.#foo;
}
}

View File

@ -0,0 +1,36 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
_foo.set(this, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var _this = this;
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
}
babelHelpers.createClass(Nested, [{
key: babelHelpers.classPrivateFieldGet(_this, _foo),
value: function () {}
}]);
return Nested;
}();
babelHelpers.classPrivateFieldGet(this, _foo);
}
}]);
return Foo;
}();
var _foo = new WeakMap();

View File

@ -0,0 +1,18 @@
class Foo {
#foo = 1;
#bar = 1;
test() {
class Nested {
#bar = 2;
test() {
this.#foo;
this.#bar;
}
}
this.#foo;
this.#bar;
}
}

View File

@ -0,0 +1,52 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
_foo.set(this, {
writable: true,
value: 1
});
_bar.set(this, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
_bar2.set(this, {
writable: true,
value: 2
});
}
babelHelpers.createClass(Nested, [{
key: "test",
value: function test() {
babelHelpers.classPrivateFieldGet(this, _foo);
babelHelpers.classPrivateFieldGet(this, _bar2);
}
}]);
return Nested;
}();
var _bar2 = new WeakMap();
babelHelpers.classPrivateFieldGet(this, _foo);
babelHelpers.classPrivateFieldGet(this, _bar);
}
}]);
return Foo;
}();
var _foo = new WeakMap();
var _bar = new WeakMap();

View File

@ -0,0 +1,15 @@
class Foo {
#foo = 1;
test() {
class Nested {
#foo = 2;
test() {
this.#foo;
}
}
this.#foo;
}
}

View File

@ -0,0 +1,43 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
_foo.set(this, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
_foo2.set(this, {
writable: true,
value: 2
});
}
babelHelpers.createClass(Nested, [{
key: "test",
value: function test() {
babelHelpers.classPrivateFieldGet(this, _foo2);
}
}]);
return Nested;
}();
var _foo2 = new WeakMap();
babelHelpers.classPrivateFieldGet(this, _foo);
}
}]);
return Foo;
}();
var _foo = new WeakMap();

View File

@ -0,0 +1,13 @@
class Foo {
#foo = 1;
test() {
class Nested {
test() {
this.#foo;
}
}
this.#foo;
}
}

View File

@ -0,0 +1,36 @@
var Foo = /*#__PURE__*/function () {
"use strict";
function Foo() {
babelHelpers.classCallCheck(this, Foo);
_foo.set(this, {
writable: true,
value: 1
});
}
babelHelpers.createClass(Foo, [{
key: "test",
value: function test() {
var Nested = /*#__PURE__*/function () {
function Nested() {
babelHelpers.classCallCheck(this, Nested);
}
babelHelpers.createClass(Nested, [{
key: "test",
value: function test() {
babelHelpers.classPrivateFieldGet(this, _foo);
}
}]);
return Nested;
}();
babelHelpers.classPrivateFieldGet(this, _foo);
}
}]);
return Foo;
}();
var _foo = new WeakMap();