Use correct "this" in static fields (#9508)

This commit is contained in:
Nicolò Ribaudo 2019-02-24 08:14:49 +01:00 committed by GitHub
parent cade33c647
commit 0b01b5217b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 268 additions and 34 deletions

View File

@ -1,5 +1,7 @@
import { template, traverse, types as t } from "@babel/core"; import { template, traverse, types as t } from "@babel/core";
import { environmentVisitor } from "@babel/helper-replace-supers"; import ReplaceSupers, {
environmentVisitor,
} from "@babel/helper-replace-supers";
import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions"; import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions";
import optimiseCall from "@babel/helper-optimise-call-expression"; import optimiseCall from "@babel/helper-optimise-call-expression";
@ -445,8 +447,39 @@ function buildPrivateInstanceMethodDeclaration(prop, privateNamesMap) {
]); ]);
} }
const thisContextVisitor = traverse.visitors.merge([
{
ThisExpression(path, state) {
state.needsClassRef = true;
path.replaceWith(t.cloneNode(state.classRef));
},
},
environmentVisitor,
]);
function replaceThisContext(path, ref, superRef, file, loose) {
const state = { classRef: ref, needsClassRef: false };
const replacer = new ReplaceSupers({
methodPath: path,
isLoose: loose,
superRef,
file,
getObjectRef() {
state.needsClassRef = true;
return ref;
},
});
replacer.isStatic = true;
replacer.replace();
path.traverse(thisContextVisitor, state);
return state.needsClassRef;
}
export function buildFieldsInitNodes( export function buildFieldsInitNodes(
ref, ref,
superRef,
props, props,
privateNamesMap, privateNamesMap,
state, state,
@ -464,6 +497,11 @@ export function buildFieldsInitNodes(
const isField = prop.isProperty(); const isField = prop.isProperty();
const isMethod = !isField; const isMethod = !isField;
if (isStatic && isField) {
const replaced = replaceThisContext(prop, ref, superRef, state, loose);
needsClassRef = needsClassRef || replaced;
}
switch (true) { switch (true) {
case isStatic && isPrivate && isField && loose: case isStatic && isPrivate && isField && loose:
needsClassRef = true; needsClassRef = true;

View File

@ -159,6 +159,7 @@ export function createClassFeaturePlugin({
keysNodes = extractComputedKeys(ref, path, computedPaths, this.file); keysNodes = extractComputedKeys(ref, path, computedPaths, this.file);
({ staticNodes, instanceNodes, wrapClass } = buildFieldsInitNodes( ({ staticNodes, instanceNodes, wrapClass } = buildFieldsInitNodes(
ref, ref,
path.node.superClass,
props, props,
privateNamesMap, privateNamesMap,
state, state,

View File

@ -51,13 +51,7 @@ export const environmentVisitor = {
path.skip(); path.skip();
}, },
Method(path) { "Method|ClassProperty|ClassPrivateProperty"(path) {
skipAllButComputedKey(path);
},
"ClassProperty|ClassPrivateProperty"(path) {
// If the property is computed, we need to visit everything.
if (path.node.static) return;
skipAllButComputedKey(path); skipAllButComputedKey(path);
}, },
}; };

View File

@ -0,0 +1,13 @@
class A {
static #self = this;
static #getA = () => this;
static extract() {
return { self: A.#self, getA: A.#getA };
}
}
const { self, getA } = A.extract();
expect(self).toBe(A);
expect(getA()).toBe(A);

View File

@ -0,0 +1,4 @@
class A {
static #self = this;
static #getA = () => this;
}

View File

@ -0,0 +1,18 @@
var A = function A() {
"use strict";
babelHelpers.classCallCheck(this, A);
};
var _self = babelHelpers.classPrivateFieldLooseKey("self");
var _getA = babelHelpers.classPrivateFieldLooseKey("getA");
Object.defineProperty(A, _self, {
writable: true,
value: A
});
Object.defineProperty(A, _getA, {
writable: true,
value: () => A
});

View File

@ -0,0 +1,13 @@
class A {
static #self = this;
static #getA = () => this;
static extract() {
return { self: A.#self, getA: A.#getA };
}
}
const { self, getA } = A.extract();
expect(self).toBe(A);
expect(getA()).toBe(A);

View File

@ -0,0 +1,4 @@
class A {
static #self = this;
static #getA = () => this;
}

View File

@ -0,0 +1,14 @@
var A = function A() {
"use strict";
babelHelpers.classCallCheck(this, A);
};
var _self = {
writable: true,
value: A
};
var _getA = {
writable: true,
value: () => A
};

View File

@ -0,0 +1,15 @@
class A {
static prop = 1;
}
class B extends A {
static prop = 2;
static propA = super.prop;
static getPropA = () => super.prop;
}
const { prop, propA, getPropA } = B;
expect(prop).toBe(2);
expect(propA).toBe(1);
expect(getPropA()).toBe(1);

View File

@ -0,0 +1,9 @@
class A {
static prop = 1;
}
class B extends A {
static prop = 2;
static propA = super.prop;
static getPropA = () => super.prop;
}

View File

@ -0,0 +1,27 @@
var A = function A() {
"use strict";
babelHelpers.classCallCheck(this, A);
};
A.prop = 1;
var B =
/*#__PURE__*/
function (_A) {
"use strict";
babelHelpers.inherits(B, _A);
function B() {
babelHelpers.classCallCheck(this, B);
return babelHelpers.possibleConstructorReturn(this, babelHelpers.getPrototypeOf(B).apply(this, arguments));
}
return B;
}(A);
B.prop = 2;
B.propA = A.prop;
B.getPropA = () => A.prop;

View File

@ -0,0 +1,9 @@
class A {
static self = this;
static getA = () => this;
}
const { self, getA } = A;
expect(self).toBe(A);
expect(getA()).toBe(A);

View File

@ -0,0 +1,4 @@
class A {
static self = this;
static getA = () => this;
}

View File

@ -0,0 +1,9 @@
var A = function A() {
"use strict";
babelHelpers.classCallCheck(this, A);
};
A.self = A;
A.getA = () => A;

View File

@ -0,0 +1,15 @@
class A {
static prop = 1;
}
class B extends A {
static prop = 2;
static propA = super.prop;
static getPropA = () => super.prop;
}
const { prop, propA, getPropA } = B;
expect(prop).toBe(2);
expect(propA).toBe(1);
expect(getPropA()).toBe(1);

View File

@ -0,0 +1,9 @@
class A {
static prop = 1;
}
class B extends A {
static prop = 2;
static propA = super.prop;
static getPropA = () => super.prop;
}

View File

@ -0,0 +1,26 @@
var A = function A() {
"use strict";
babelHelpers.classCallCheck(this, A);
};
babelHelpers.defineProperty(A, "prop", 1);
var B =
/*#__PURE__*/
function (_A) {
"use strict";
babelHelpers.inherits(B, _A);
function B() {
babelHelpers.classCallCheck(this, B);
return babelHelpers.possibleConstructorReturn(this, babelHelpers.getPrototypeOf(B).apply(this, arguments));
}
return B;
}(A);
babelHelpers.defineProperty(B, "prop", 2);
babelHelpers.defineProperty(B, "propA", babelHelpers.get(babelHelpers.getPrototypeOf(B), "prop", B));
babelHelpers.defineProperty(B, "getPropA", () => babelHelpers.get(babelHelpers.getPrototypeOf(B), "prop", B));

View File

@ -0,0 +1,9 @@
class A {
static self = this;
static getA = () => this;
}
const { self, getA } = A;
expect(self).toBe(A);
expect(getA()).toBe(A);

View File

@ -0,0 +1,4 @@
class A {
static self = this;
static getA = () => this;
}

View File

@ -0,0 +1,8 @@
var A = function A() {
"use strict";
babelHelpers.classCallCheck(this, A);
};
babelHelpers.defineProperty(A, "self", A);
babelHelpers.defineProperty(A, "getA", () => A);

View File

@ -1,19 +1,17 @@
var _this = this;
(function () { (function () {
class Foo { class Foo {
constructor() { constructor() {
var _this2 = this; var _this = this;
babelHelpers.defineProperty(this, "fn", function () { babelHelpers.defineProperty(this, "fn", function () {
return console.log(_this2); return console.log(_this);
}); });
} }
} }
babelHelpers.defineProperty(Foo, "fn", function () { babelHelpers.defineProperty(Foo, "fn", function () {
return console.log(_this); return console.log(Foo);
}); });
}); });
@ -22,25 +20,25 @@ var _this = this;
return _temp = _class = class Bar { return _temp = _class = class Bar {
constructor() { constructor() {
var _this3 = this; var _this2 = this;
babelHelpers.defineProperty(this, "fn", function () { babelHelpers.defineProperty(this, "fn", function () {
return console.log(_this3); return console.log(_this2);
}); });
} }
}, babelHelpers.defineProperty(_class, "fn", function () { }, babelHelpers.defineProperty(_class, "fn", function () {
return console.log(_this); return console.log(_class);
}), _temp; }), _temp;
}); });
(function () { (function () {
class Baz { class Baz {
constructor(_force) { constructor(_force) {
var _this4 = this; var _this3 = this;
babelHelpers.defineProperty(this, "fn", function () { babelHelpers.defineProperty(this, "fn", function () {
return console.log(_this4); return console.log(_this3);
}); });
babelHelpers.defineProperty(this, "force", force); babelHelpers.defineProperty(this, "force", force);
} }
@ -48,25 +46,23 @@ var _this = this;
} }
babelHelpers.defineProperty(Baz, "fn", function () { babelHelpers.defineProperty(Baz, "fn", function () {
return console.log(_this); return console.log(Baz);
}); });
}); });
var qux = function () { var qux = function () {
var _this6 = this;
class Qux { class Qux {
constructor() { constructor() {
var _this5 = this; var _this4 = this;
babelHelpers.defineProperty(this, "fn", function () { babelHelpers.defineProperty(this, "fn", function () {
return console.log(_this5); return console.log(_this4);
}); });
} }
} }
babelHelpers.defineProperty(Qux, "fn", function () { babelHelpers.defineProperty(Qux, "fn", function () {
return console.log(_this6); return console.log(Qux);
}); });
}.bind(this); }.bind(this);

View File

@ -8,21 +8,19 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function"
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Test = function Test() { var Test = function Test() {
"use strict"; "use strict";
var _this2 = this;
_classCallCheck(this, Test); _classCallCheck(this, Test);
var Other = var Other =
@ -54,6 +52,6 @@ var Test = function Test() {
}(Test); }(Test);
_defineProperty(Other, "a", function () { _defineProperty(Other, "a", function () {
return _get(_getPrototypeOf(Test.prototype), "test", _this2); return _get(_getPrototypeOf(Other), "test", Other);
}); });
}; };

View File

@ -200,7 +200,6 @@ function hoistFunctionEnvironment(
child.skip(); child.skip();
}, },
ClassProperty(child) { ClassProperty(child) {
if (child.node.static) return;
child.skip(); child.skip();
}, },
CallExpression(child) { CallExpression(child) {
@ -453,7 +452,6 @@ function getThisBinding(thisEnvFn, inConstructor) {
child.skip(); child.skip();
}, },
ClassProperty(child) { ClassProperty(child) {
if (child.node.static) return;
child.skip(); child.skip();
}, },
CallExpression(child) { CallExpression(child) {
@ -581,7 +579,6 @@ function getScopeInformation(fnPath) {
fnPath.traverse({ fnPath.traverse({
ClassProperty(child) { ClassProperty(child) {
if (child.node.static) return;
child.skip(); child.skip();
}, },
Function(child) { Function(child) {