Fix super Method Calls in Class Private Methods (#9704)

This fixes an issue with the use of super method calls in class private methods. See https://github.com/babel/babel/issues/9580 for more info re: behavior of the bug.
This commit is contained in:
Tim McClure 2019-03-19 13:43:02 -04:00 committed by Nicolò Ribaudo
parent 48d66eb648
commit e068281e28
8 changed files with 137 additions and 11 deletions

View File

@ -501,10 +501,9 @@ function replaceThisContext(path, ref, superRef, file, loose) {
file,
getObjectRef() {
state.needsClassRef = true;
return ref;
return path.node.static ? ref : t.thisExpression();
},
});
replacer.isStatic = true;
replacer.replace();
if (path.isProperty()) {
path.traverse(thisContextVisitor, state);
@ -532,7 +531,7 @@ export function buildFieldsInitNodes(
const isField = prop.isProperty();
const isMethod = !isField;
if (isStatic) {
if (isStatic || (isMethod && isPrivate)) {
const replaced = replaceThisContext(prop, ref, superRef, state, loose);
needsClassRef = needsClassRef || replaced;
}

View File

@ -15,11 +15,12 @@ import * as t from "@babel/types";
*
* helpers.getPrototypeOf(CLASS.prototype)
*/
function getPrototypeOfExpression(objectRef, isStatic, file) {
function getPrototypeOfExpression(objectRef, isStatic, file, isPrivateMethod) {
objectRef = t.cloneNode(objectRef);
const targetRef = isStatic
? objectRef
: t.memberExpression(objectRef, t.identifier("prototype"));
const targetRef =
isStatic || isPrivateMethod
? objectRef
: t.memberExpression(objectRef, t.identifier("prototype"));
return t.callExpression(file.addHelper("getPrototypeOf"), [targetRef]);
}
@ -98,7 +99,12 @@ const specHandlers = {
get(superMember) {
return t.callExpression(this.file.addHelper("get"), [
getPrototypeOfExpression(this.getObjectRef(), this.isStatic, this.file),
getPrototypeOfExpression(
this.getObjectRef(),
this.isStatic,
this.file,
this.isPrivateMethod,
),
this.prop(superMember),
t.thisExpression(),
]);
@ -106,7 +112,12 @@ const specHandlers = {
set(superMember, value) {
return t.callExpression(this.file.addHelper("set"), [
getPrototypeOfExpression(this.getObjectRef(), this.isStatic, this.file),
getPrototypeOfExpression(
this.getObjectRef(),
this.isStatic,
this.file,
this.isPrivateMethod,
),
this.prop(superMember),
value,
t.thisExpression(),
@ -170,8 +181,8 @@ export default class ReplaceSupers {
const path = opts.methodPath;
this.methodPath = path;
this.isStatic =
path.isClassMethod({ static: true }) || path.isObjectMethod();
this.isStatic = path.isObjectMethod() || path.node.static;
this.isPrivateMethod = path.isPrivate() && path.isMethod();
this.file = opts.file;
this.superRef = opts.superRef;
@ -202,6 +213,7 @@ export default class ReplaceSupers {
memberExpressionToFunctions(this.methodPath, visitor, {
file: this.file,
isStatic: this.isStatic,
isPrivateMethod: this.isPrivateMethod,
getObjectRef: this.getObjectRef.bind(this),
superRef: this.superRef,
...handler,

View File

@ -0,0 +1,17 @@
class Base {
superMethod() {
return 1017;
}
}
class Sub extends Base {
#privateMethod() {
return super.superMethod();
}
publicMethod() {
return this.#privateMethod();
}
}
expect((new Sub()).publicMethod()).toEqual(1017);

View File

@ -0,0 +1,15 @@
class Base {
superMethod() {
return 1017;
}
}
class Sub extends Base {
#privateMethod() {
return super.superMethod();
}
publicMethod() {
return this.#privateMethod();
}
}

View File

@ -0,0 +1,26 @@
class Base {
superMethod() {
return 1017;
}
}
class Sub extends Base {
constructor(...args) {
super(...args);
Object.defineProperty(this, _privateMethod, {
value: _privateMethod2
});
}
publicMethod() {
return babelHelpers.classPrivateFieldLooseBase(this, _privateMethod)[_privateMethod]();
}
}
var _privateMethod = babelHelpers.classPrivateFieldLooseKey("privateMethod");
var _privateMethod2 = function _privateMethod2() {
return Base.prototype.superMethod.call(this);
};

View File

@ -0,0 +1,17 @@
class Base {
superMethod() {
return 1017;
}
}
class Sub extends Base {
#privateMethod() {
return super.superMethod();
}
publicMethod() {
return this.#privateMethod();
}
}
expect((new Sub()).publicMethod()).toEqual(1017);

View File

@ -0,0 +1,15 @@
class Base {
superMethod() {
return 1017;
}
}
class Sub extends Base {
#privateMethod() {
return super.superMethod();
}
publicMethod() {
return this.#privateMethod();
}
}

View File

@ -0,0 +1,25 @@
class Base {
superMethod() {
return 1017;
}
}
class Sub extends Base {
constructor(...args) {
super(...args);
_privateMethod.add(this);
}
publicMethod() {
return babelHelpers.classPrivateMethodGet(this, _privateMethod, _privateMethod2).call(this);
}
}
var _privateMethod = new WeakSet();
var _privateMethod2 = function _privateMethod2() {
return babelHelpers.get(babelHelpers.getPrototypeOf(this), "superMethod", this).call(this);
};