Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(class-properties): replace new.target in static properties with undefined #13560

Merged
merged 7 commits into from Jul 16, 2021
33 changes: 33 additions & 0 deletions packages/babel-helper-create-class-features-plugin/src/fields.ts
@@ -1,4 +1,5 @@
import { template, traverse, types as t } from "@babel/core";
import type { NodePath } from "@babel/traverse";
import ReplaceSupers, {
environmentVisitor,
} from "@babel/helper-replace-supers";
Expand Down Expand Up @@ -770,6 +771,38 @@ export function buildFieldsInitNodes(
needsClassRef = needsClassRef || replaced;
}

// if there are `new.target` in static field
// we should replace it with `undefined`
if (isStatic && isField) {
// fix issue #12737
prop.traverse({
MetaProperty(path: NodePath<t.MetaProperty>) {
const meta = path.get("meta");
const property = path.get("property");
const { scope } = path;

if (
meta.isIdentifier({ name: "new" }) &&
property.isIdentifier({ name: "target" })
) {
const func = path.findParent(path => {
if (path.isClass()) return true;
if (path.isFunction() && !path.isArrowFunctionExpression()) {
return true;
}
return false;
});
existentialism marked this conversation as resolved.
Show resolved Hide resolved
if (!func.findParent(path => path === prop)) {
// if func is not child of prop, which means
// `new.target` is leaked to the upper `new.target` values.
// thus we should replace it with `undefined` (`void 0` here)
path.replaceWith(scope.buildUndefinedNode());
}
}
},
});
}

switch (true) {
case isStaticBlock:
staticNodes.push(
Expand Down
@@ -0,0 +1,9 @@
class Foo {
constructor() {
this.Bar = class {
static p = new.target
}
}
}

expect((new Foo).Bar.p).toBeUndefined()
@@ -0,0 +1,20 @@
class Foo {
constructor() {
this.Bar = class {
static p = new.target
static p1 = class { constructor() { new.target } } // should not replace
static p2 = new function () { new.target } // should not replace
static p3 = () => { new.target } // should replace
static p4 = function () { new.target } // should not replace
q = new.target // should not replace
}
}

test = function() {
new.target;
};

test2 = () => {
new.target;
}
}
@@ -0,0 +1,7 @@
{
"plugins": [
"transform-new-target",
"transform-arrow-functions",
["proposal-class-properties", { "loose": true }]
]
}
@@ -0,0 +1,35 @@
class Foo {
constructor() {
var _newtarget = this.constructor,
_class,
_temp;

this.test = function _target() {
this instanceof _target ? this.constructor : void 0;
};

this.test2 = function () {
_newtarget;
};

this.Bar = (_temp = _class = class _target2 {
constructor() {
this.q = this.constructor;
} // should not replace


}, _class.p = void 0, _class.p1 = class _target3 {
constructor() {
this.constructor;
}

}, _class.p2 = new function _target4() {
this instanceof _target4 ? this.constructor : void 0;
}(), _class.p3 = function () {
void 0;
}, _class.p4 = function _target5() {
this instanceof _target5 ? this.constructor : void 0;
}, _temp);
}

}
@@ -0,0 +1,9 @@
class Foo {
constructor() {
this.Bar = class {
static p = new.target
}
}
}

expect((new Foo).Bar.p).toBeUndefined()
@@ -1,4 +1,15 @@
class Foo {
constructor() {
this.Bar = class {
static p = new.target
static p1 = class { constructor() { new.target } } // should not replace
static p2 = new function () { new.target } // should not replace
static p3 = () => { new.target } // should replace
static p4 = function () { new.target } // should not replace
q = new.target // should not replace
}
}

test = function() {
new.target;
};
Expand Down
Expand Up @@ -2,6 +2,6 @@
"plugins": [
"transform-new-target",
"transform-arrow-functions",
["proposal-class-properties", { "loose": true }]
["proposal-class-properties", { "loose": false }]
colinaaa marked this conversation as resolved.
Show resolved Hide resolved
]
}
@@ -1,14 +1,33 @@
class Foo {
constructor() {
var _newtarget = this.constructor;
var _newtarget = this.constructor,
_class,
_temp;

this.test = function _target() {
babelHelpers.defineProperty(this, "test", function _target() {
this instanceof _target ? this.constructor : void 0;
};

this.test2 = function () {
});
babelHelpers.defineProperty(this, "test2", function () {
_newtarget;
};
});
this.Bar = (_temp = _class = class _target2 {
constructor() {
babelHelpers.defineProperty(this, "q", this.constructor);
} // should not replace


}, babelHelpers.defineProperty(_class, "p", void 0), babelHelpers.defineProperty(_class, "p1", class _target3 {
constructor() {
this.constructor;
}

}), babelHelpers.defineProperty(_class, "p2", new function _target4() {
this instanceof _target4 ? this.constructor : void 0;
}()), babelHelpers.defineProperty(_class, "p3", function () {
void 0;
}), babelHelpers.defineProperty(_class, "p4", function _target5() {
this instanceof _target5 ? this.constructor : void 0;
}), _temp);
}

}