Use correct "this" in static fields (#9508)
This commit is contained in:
parent
cade33c647
commit
0b01b5217b
@ -1,5 +1,7 @@
|
||||
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 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(
|
||||
ref,
|
||||
superRef,
|
||||
props,
|
||||
privateNamesMap,
|
||||
state,
|
||||
@ -464,6 +497,11 @@ export function buildFieldsInitNodes(
|
||||
const isField = prop.isProperty();
|
||||
const isMethod = !isField;
|
||||
|
||||
if (isStatic && isField) {
|
||||
const replaced = replaceThisContext(prop, ref, superRef, state, loose);
|
||||
needsClassRef = needsClassRef || replaced;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case isStatic && isPrivate && isField && loose:
|
||||
needsClassRef = true;
|
||||
|
||||
@ -159,6 +159,7 @@ export function createClassFeaturePlugin({
|
||||
keysNodes = extractComputedKeys(ref, path, computedPaths, this.file);
|
||||
({ staticNodes, instanceNodes, wrapClass } = buildFieldsInitNodes(
|
||||
ref,
|
||||
path.node.superClass,
|
||||
props,
|
||||
privateNamesMap,
|
||||
state,
|
||||
|
||||
@ -51,13 +51,7 @@ export const environmentVisitor = {
|
||||
path.skip();
|
||||
},
|
||||
|
||||
Method(path) {
|
||||
skipAllButComputedKey(path);
|
||||
},
|
||||
|
||||
"ClassProperty|ClassPrivateProperty"(path) {
|
||||
// If the property is computed, we need to visit everything.
|
||||
if (path.node.static) return;
|
||||
"Method|ClassProperty|ClassPrivateProperty"(path) {
|
||||
skipAllButComputedKey(path);
|
||||
},
|
||||
};
|
||||
|
||||
13
packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/exec.js
vendored
Normal file
13
packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/exec.js
vendored
Normal 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);
|
||||
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
static #self = this;
|
||||
static #getA = () => this;
|
||||
}
|
||||
@ -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
|
||||
});
|
||||
13
packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/exec.js
vendored
Normal file
13
packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/exec.js
vendored
Normal 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);
|
||||
4
packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/input.js
vendored
Normal file
4
packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/input.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
static #self = this;
|
||||
static #getA = () => this;
|
||||
}
|
||||
14
packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/output.js
vendored
Normal file
14
packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/output.js
vendored
Normal 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
|
||||
};
|
||||
15
packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/exec.js
vendored
Normal file
15
packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/exec.js
vendored
Normal 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);
|
||||
@ -0,0 +1,9 @@
|
||||
class A {
|
||||
static prop = 1;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
static prop = 2;
|
||||
static propA = super.prop;
|
||||
static getPropA = () => super.prop;
|
||||
}
|
||||
@ -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;
|
||||
@ -0,0 +1,9 @@
|
||||
class A {
|
||||
static self = this;
|
||||
static getA = () => this;
|
||||
}
|
||||
|
||||
const { self, getA } = A;
|
||||
|
||||
expect(self).toBe(A);
|
||||
expect(getA()).toBe(A);
|
||||
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
static self = this;
|
||||
static getA = () => this;
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
var A = function A() {
|
||||
"use strict";
|
||||
|
||||
babelHelpers.classCallCheck(this, A);
|
||||
};
|
||||
|
||||
A.self = A;
|
||||
|
||||
A.getA = () => A;
|
||||
15
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/exec.js
vendored
Normal file
15
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/exec.js
vendored
Normal 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);
|
||||
9
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/input.js
vendored
Normal file
9
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/input.js
vendored
Normal 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;
|
||||
}
|
||||
26
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/output.js
vendored
Normal file
26
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/output.js
vendored
Normal 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));
|
||||
9
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/exec.js
vendored
Normal file
9
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/exec.js
vendored
Normal 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);
|
||||
4
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/input.js
vendored
Normal file
4
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/input.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
static self = this;
|
||||
static getA = () => this;
|
||||
}
|
||||
8
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/output.js
vendored
Normal file
8
packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/output.js
vendored
Normal 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);
|
||||
@ -1,19 +1,17 @@
|
||||
var _this = this;
|
||||
|
||||
(function () {
|
||||
class Foo {
|
||||
constructor() {
|
||||
var _this2 = this;
|
||||
var _this = this;
|
||||
|
||||
babelHelpers.defineProperty(this, "fn", function () {
|
||||
return console.log(_this2);
|
||||
return console.log(_this);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
constructor() {
|
||||
var _this3 = this;
|
||||
var _this2 = this;
|
||||
|
||||
babelHelpers.defineProperty(this, "fn", function () {
|
||||
return console.log(_this3);
|
||||
return console.log(_this2);
|
||||
});
|
||||
}
|
||||
|
||||
}, babelHelpers.defineProperty(_class, "fn", function () {
|
||||
return console.log(_this);
|
||||
return console.log(_class);
|
||||
}), _temp;
|
||||
});
|
||||
|
||||
(function () {
|
||||
class Baz {
|
||||
constructor(_force) {
|
||||
var _this4 = this;
|
||||
var _this3 = this;
|
||||
|
||||
babelHelpers.defineProperty(this, "fn", function () {
|
||||
return console.log(_this4);
|
||||
return console.log(_this3);
|
||||
});
|
||||
babelHelpers.defineProperty(this, "force", force);
|
||||
}
|
||||
@ -48,25 +46,23 @@ var _this = this;
|
||||
}
|
||||
|
||||
babelHelpers.defineProperty(Baz, "fn", function () {
|
||||
return console.log(_this);
|
||||
return console.log(Baz);
|
||||
});
|
||||
});
|
||||
|
||||
var qux = function () {
|
||||
var _this6 = this;
|
||||
|
||||
class Qux {
|
||||
constructor() {
|
||||
var _this5 = this;
|
||||
var _this4 = this;
|
||||
|
||||
babelHelpers.defineProperty(this, "fn", function () {
|
||||
return console.log(_this5);
|
||||
return console.log(_this4);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
babelHelpers.defineProperty(Qux, "fn", function () {
|
||||
return console.log(_this6);
|
||||
return console.log(Qux);
|
||||
});
|
||||
}.bind(this);
|
||||
|
||||
@ -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 _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 _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 _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() {
|
||||
"use strict";
|
||||
|
||||
var _this2 = this;
|
||||
|
||||
_classCallCheck(this, Test);
|
||||
|
||||
var Other =
|
||||
@ -54,6 +52,6 @@ var Test = function Test() {
|
||||
}(Test);
|
||||
|
||||
_defineProperty(Other, "a", function () {
|
||||
return _get(_getPrototypeOf(Test.prototype), "test", _this2);
|
||||
return _get(_getPrototypeOf(Other), "test", Other);
|
||||
});
|
||||
};
|
||||
|
||||
@ -200,7 +200,6 @@ function hoistFunctionEnvironment(
|
||||
child.skip();
|
||||
},
|
||||
ClassProperty(child) {
|
||||
if (child.node.static) return;
|
||||
child.skip();
|
||||
},
|
||||
CallExpression(child) {
|
||||
@ -453,7 +452,6 @@ function getThisBinding(thisEnvFn, inConstructor) {
|
||||
child.skip();
|
||||
},
|
||||
ClassProperty(child) {
|
||||
if (child.node.static) return;
|
||||
child.skip();
|
||||
},
|
||||
CallExpression(child) {
|
||||
@ -581,7 +579,6 @@ function getScopeInformation(fnPath) {
|
||||
|
||||
fnPath.traverse({
|
||||
ClassProperty(child) {
|
||||
if (child.node.static) return;
|
||||
child.skip();
|
||||
},
|
||||
Function(child) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user