Skip to content

Commit 2bba4a5

Browse files
fix: Deleting super property should throw (#15223)
Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com> Fixes #15169
1 parent 00783f5 commit 2bba4a5

13 files changed

Lines changed: 305 additions & 5 deletions

File tree

packages/babel-helper-member-expression-to-functions/src/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@ const handle = {
247247
} else if (parentIsCall) {
248248
// `(a?.#b)()` to `(a == null ? void 0 : a.#b.bind(a))()`
249249
member.replaceWith(this.boundGet(member));
250+
} else if (
251+
this.delete &&
252+
parentPath.isUnaryExpression({ operator: "delete" })
253+
) {
254+
parentPath.replaceWith(this.delete(member));
250255
} else {
251256
member.replaceWith(this.get(member));
252257
}
@@ -491,6 +496,12 @@ const handle = {
491496
return;
492497
}
493498

499+
// delete MEMBER -> _delete(MEMBER)
500+
if (this.delete && parentPath.isUnaryExpression({ operator: "delete" })) {
501+
parentPath.replaceWith(this.delete(member));
502+
return;
503+
}
504+
494505
// for (MEMBER of ARR)
495506
// for (MEMBER in ARR)
496507
// { KEY: MEMBER } = OBJ -> { KEY: _destructureSet(MEMBER) } = OBJ
@@ -562,6 +573,9 @@ export interface Handler<State> {
562573
member: Member,
563574
args: t.OptionalCallExpression["arguments"],
564575
): t.Expression;
576+
// TODO(Babel 8): Consider making this required, since `.get` doesn't
577+
// really work as a fallback for `.delete`
578+
delete?(this: HandlerState<State> & State, member: Member): t.Expression;
565579
}
566580

567581
export interface HandlerState<State = {}> extends Handler<State> {

packages/babel-helper-replace-supers/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"@babel/helper-environment-visitor": "workspace:^",
1818
"@babel/helper-member-expression-to-functions": "workspace:^",
1919
"@babel/helper-optimise-call-expression": "workspace:^",
20+
"@babel/template": "workspace:^",
2021
"@babel/traverse": "workspace:^",
2122
"@babel/types": "workspace:^"
2223
},

packages/babel-helper-replace-supers/src/index.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import type { NodePath, Scope } from "@babel/traverse";
2-
import traverse from "@babel/traverse";
1+
import type { File } from "@babel/core";
2+
import environmentVisitor from "@babel/helper-environment-visitor";
33
import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions";
44
import type { HandlerState } from "@babel/helper-member-expression-to-functions";
55
import optimiseCall from "@babel/helper-optimise-call-expression";
6-
import environmentVisitor from "@babel/helper-environment-visitor";
6+
import template from "@babel/template";
7+
import traverse from "@babel/traverse";
8+
import type { NodePath, Scope } from "@babel/traverse";
79
import {
810
assignmentExpression,
911
booleanLiteral,
@@ -16,7 +18,6 @@ import {
1618
thisExpression,
1719
} from "@babel/types";
1820
import type * as t from "@babel/types";
19-
import type { File } from "@babel/core";
2021

2122
// TODO (Babel 8): Don't export this.
2223
export {
@@ -107,7 +108,13 @@ type SuperMember = NodePath<
107108
interface SpecHandler
108109
extends Pick<
109110
Handler,
110-
"get" | "set" | "destructureSet" | "call" | "optionalCall" | "memoise"
111+
| "memoise"
112+
| "get"
113+
| "set"
114+
| "destructureSet"
115+
| "call"
116+
| "optionalCall"
117+
| "delete"
111118
> {
112119
_get(
113120
this: Handler & SpecHandler,
@@ -240,6 +247,23 @@ const specHandlers: SpecHandler = {
240247
true,
241248
);
242249
},
250+
251+
delete(this: Handler & SpecHandler, superMember: SuperMember) {
252+
if (superMember.node.computed) {
253+
return sequenceExpression([
254+
callExpression(this.file.addHelper("toPropertyKey"), [
255+
cloneNode(superMember.node.property),
256+
]),
257+
template.expression.ast`
258+
function () { throw new ReferenceError("'delete super[expr]' is invalid"); }()
259+
`,
260+
]);
261+
} else {
262+
return template.expression.ast`
263+
function () { throw new ReferenceError("'delete super.prop' is invalid"); }()
264+
`;
265+
}
266+
},
243267
};
244268

245269
const looseHandlers = {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
expect(() => {
2+
new class { y = delete super.x; };
3+
}).toThrow(ReferenceError);
4+
5+
expect(() => {
6+
new class { y = delete super[0]; };
7+
}).toThrow(ReferenceError);
8+
9+
expect(() => {
10+
class X1 { static y = delete super.x; }
11+
}).toThrow(ReferenceError);
12+
13+
expect(() => {
14+
class X2 { static y = delete super[0]; }
15+
}).toThrow(ReferenceError);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
new class { y = delete super.x; };
2+
3+
new class { y = delete super[0]; };
4+
5+
class X1 { static y = delete super.x; }
6+
7+
class X2 { static y = delete super[0]; }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
new ( /*#__PURE__*/function () {
2+
"use strict";
3+
4+
function _class2() {
5+
babelHelpers.classCallCheck(this, _class2);
6+
babelHelpers.defineProperty(this, "y", function () {
7+
throw new ReferenceError("'delete super.prop' is invalid");
8+
}());
9+
}
10+
return babelHelpers.createClass(_class2);
11+
}())();
12+
new ( /*#__PURE__*/function () {
13+
"use strict";
14+
15+
function _class4() {
16+
babelHelpers.classCallCheck(this, _class4);
17+
babelHelpers.defineProperty(this, "y", (babelHelpers.toPropertyKey(0), function () {
18+
throw new ReferenceError("'delete super[expr]' is invalid");
19+
}()));
20+
}
21+
return babelHelpers.createClass(_class4);
22+
}())();
23+
var X1 = /*#__PURE__*/babelHelpers.createClass(function X1() {
24+
"use strict";
25+
26+
babelHelpers.classCallCheck(this, X1);
27+
});
28+
babelHelpers.defineProperty(X1, "y", function () {
29+
throw new ReferenceError("'delete super.prop' is invalid");
30+
}());
31+
var X2 = /*#__PURE__*/babelHelpers.createClass(function X2() {
32+
"use strict";
33+
34+
babelHelpers.classCallCheck(this, X2);
35+
});
36+
babelHelpers.defineProperty(X2, "y", (babelHelpers.toPropertyKey(0), function () {
37+
throw new ReferenceError("'delete super[expr]' is invalid");
38+
}()));
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// [Symbol.toPrimitive] must be called if exist
2+
var counter = 0;
3+
expect(() => {
4+
(new class {
5+
f() {
6+
delete super[{
7+
[Symbol.toPrimitive]: function() { ++counter; return 0; },
8+
}];
9+
}
10+
}).f();
11+
}).toThrow(ReferenceError);
12+
expect(counter).toBe(1);
13+
14+
// [Symbol.toPrimitive] must return a primitive value
15+
expect(() => {
16+
(new class {
17+
f() {
18+
delete super[{
19+
[Symbol.toPrimitive]: function() { return {}; },
20+
}];
21+
}
22+
}).f();
23+
}).toThrow(TypeError);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// [Symbol.toPrimitive] must be called if exist
2+
var counter = 0;
3+
(new class {
4+
f() {
5+
delete super[{
6+
[Symbol.toPrimitive]: function() { ++counter; return 0; },
7+
}];
8+
}
9+
}).f();
10+
11+
// [Symbol.toPrimitive] must return a primitive value
12+
(new class {
13+
f() {
14+
delete super[{
15+
[Symbol.toPrimitive]: function() { return {}; },
16+
}];
17+
}
18+
}).f();
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// [Symbol.toPrimitive] must be called if exist
2+
var counter = 0;
3+
new ( /*#__PURE__*/function () {
4+
"use strict";
5+
6+
function _class() {
7+
babelHelpers.classCallCheck(this, _class);
8+
}
9+
babelHelpers.createClass(_class, [{
10+
key: "f",
11+
value: function f() {
12+
babelHelpers.toPropertyKey({
13+
[Symbol.toPrimitive]: function () {
14+
++counter;
15+
return 0;
16+
}
17+
}), function () {
18+
throw new ReferenceError("'delete super[expr]' is invalid");
19+
}();
20+
}
21+
}]);
22+
return _class;
23+
}())().f();
24+
25+
// [Symbol.toPrimitive] must return a primitive value
26+
new ( /*#__PURE__*/function () {
27+
"use strict";
28+
29+
function _class2() {
30+
babelHelpers.classCallCheck(this, _class2);
31+
}
32+
babelHelpers.createClass(_class2, [{
33+
key: "f",
34+
value: function f() {
35+
babelHelpers.toPropertyKey({
36+
[Symbol.toPrimitive]: function () {
37+
return {};
38+
}
39+
}), function () {
40+
throw new ReferenceError("'delete super[expr]' is invalid");
41+
}();
42+
}
43+
}]);
44+
return _class2;
45+
}())().f();
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
expect(() => {
2+
(new class {
3+
f() { delete super.x; }
4+
}).f();
5+
}).toThrow(ReferenceError);
6+
7+
expect(() => {
8+
(new class {
9+
f() { delete super[0]; }
10+
}).f();
11+
}).toThrow(ReferenceError);
12+
13+
// [expr] should be evaluated
14+
var counter = 0;
15+
expect(() => {
16+
(new class {
17+
f() { delete super[++counter]; }
18+
}).f();
19+
}).toThrow(ReferenceError);
20+
expect(counter).toBe(1);
21+
22+
// TypeError before ReferenceError
23+
expect(() => {
24+
(new class {
25+
f() { delete super[0()]; }
26+
}).f();
27+
}).toThrow(TypeError);

0 commit comments

Comments
 (0)