Skip to content

Commit

Permalink
fix: apply toPropertyKey when defining class members (#15182)
Browse files Browse the repository at this point in the history
Fixes #15178
  • Loading branch information
JLHwung committed Nov 11, 2022
1 parent df601f9 commit dafa8bd
Show file tree
Hide file tree
Showing 14 changed files with 295 additions and 8 deletions.
Expand Up @@ -6,7 +6,7 @@
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
Object.defineProperty(target, babelHelpers.toPropertyKey(descriptor.key), descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
Expand Down
5 changes: 4 additions & 1 deletion packages/babel-helpers/src/helpers.ts
Expand Up @@ -78,13 +78,14 @@ helpers.classCallCheck = helper("7.0.0-beta.0")`
`;

helpers.createClass = helper("7.0.0-beta.0")`
import toPropertyKey from "toPropertyKey";
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i ++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor);
}
}
Expand Down Expand Up @@ -137,7 +138,9 @@ helpers.defaults = helper("7.0.0-beta.0")`
`;

helpers.defineProperty = helper("7.0.0-beta.0")`
import toPropertyKey from "toPropertyKey";
export default function _defineProperty(obj, key, value) {
key = toPropertyKey(key);
// Shortcircuit the slow defineProperty path when possible.
// We are trying to avoid issues where setters defined on the
// prototype cause side effects under the fast path of simple
Expand Down
@@ -0,0 +1,17 @@
const foo = { [Symbol.toPrimitive]: () => "foo" };

expect((class { static [foo] = 0 }).foo).toBe(0);
expect((class { static [foo](){ return 0; } }).foo()).toBe(0);
expect((class { static get [foo](){ return 0; } }).foo).toBe(0);
expect((class { static set [foo](v){ return v; } }).foo = 0).toBe(0);

expect((new class { [foo] = 0 }).foo).toBe(0);

const arrayLike = { [Symbol.toPrimitive] : () => [] };

expect(() => class { static [arrayLike] = 0 }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static get [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static set [arrayLike](v){ return v; } }).toThrowError("@@toPrimitive must return a primitive value.");

expect(() => new class { [arrayLike] = 0 }).toThrowError("@@toPrimitive must return a primitive value.");
@@ -0,0 +1,17 @@
const foo = { [Symbol.toPrimitive]: () => "foo" };

expect((class { static [foo] = 0 }).foo).toBe(0);
expect((class { static [foo](){ return 0; } }).foo()).toBe(0);
expect((class { static get [foo](){ return 0; } }).foo).toBe(0);
expect((class { static set [foo](v){ return v; } }).foo = 0).toBe(0);

expect((new class { [foo] = 0 }).foo).toBe(0);

const arrayLike = { [Symbol.toPrimitive] : () => [] };

expect(() => class { static [arrayLike] = 0 }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static get [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => class { static set [arrayLike](v){ return v; } }).toThrowError("@@toPrimitive must return a primitive value.");

expect(() => new class { [arrayLike] = 0 }).toThrowError("@@toPrimitive must return a primitive value.");
@@ -0,0 +1,122 @@
var _class;
var foo = {
[Symbol.toPrimitive]: () => "foo"
};
expect((_class = /*#__PURE__*/babelHelpers.createClass(function _class() {
"use strict";

babelHelpers.classCallCheck(this, _class);
}), babelHelpers.defineProperty(_class, foo, 0), _class).foo).toBe(0);
expect( /*#__PURE__*/function () {
"use strict";

function _class2() {
babelHelpers.classCallCheck(this, _class2);
}
babelHelpers.createClass(_class2, null, [{
key: foo,
value: function () {
return 0;
}
}]);
return _class2;
}().foo()).toBe(0);
expect( /*#__PURE__*/function () {
"use strict";

function _class3() {
babelHelpers.classCallCheck(this, _class3);
}
babelHelpers.createClass(_class3, null, [{
key: foo,
get: function () {
return 0;
}
}]);
return _class3;
}().foo).toBe(0);
expect( /*#__PURE__*/function () {
"use strict";

function _class4() {
babelHelpers.classCallCheck(this, _class4);
}
babelHelpers.createClass(_class4, null, [{
key: foo,
set: function (v) {
return v;
}
}]);
return _class4;
}().foo = 0).toBe(0);
expect(new ( /*#__PURE__*/function () {
"use strict";

function _class6() {
babelHelpers.classCallCheck(this, _class6);
babelHelpers.defineProperty(this, foo, 0);
}
return babelHelpers.createClass(_class6);
}())().foo).toBe(0);
var arrayLike = {
[Symbol.toPrimitive]: () => []
};
expect(() => {
var _class7;
return _class7 = /*#__PURE__*/babelHelpers.createClass(function _class7() {
"use strict";

babelHelpers.classCallCheck(this, _class7);
}), babelHelpers.defineProperty(_class7, arrayLike, 0), _class7;
}).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => /*#__PURE__*/function () {
"use strict";

function _class8() {
babelHelpers.classCallCheck(this, _class8);
}
babelHelpers.createClass(_class8, null, [{
key: arrayLike,
value: function () {
return 0;
}
}]);
return _class8;
}()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => /*#__PURE__*/function () {
"use strict";

function _class9() {
babelHelpers.classCallCheck(this, _class9);
}
babelHelpers.createClass(_class9, null, [{
key: arrayLike,
get: function () {
return 0;
}
}]);
return _class9;
}()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => /*#__PURE__*/function () {
"use strict";

function _class10() {
babelHelpers.classCallCheck(this, _class10);
}
babelHelpers.createClass(_class10, null, [{
key: arrayLike,
set: function (v) {
return v;
}
}]);
return _class10;
}()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new ( /*#__PURE__*/function () {
"use strict";

function _class12() {
babelHelpers.classCallCheck(this, _class12);
babelHelpers.defineProperty(this, arrayLike, 0);
}
return babelHelpers.createClass(_class12);
}())()).toThrowError("@@toPrimitive must return a primitive value.");
Expand Up @@ -20,7 +20,9 @@ describe("plugin ordering", () => {
plugins: [proposalClassProperties, proposalClassStaticBlock],
}).code,
).toMatchInlineSnapshot(`
"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 _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, \\"string\\"); return typeof key === \\"symbol\\" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== \\"object\\" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || \\"default\\"); if (typeof res !== \\"object\\") return res; throw new TypeError(\\"@@toPrimitive must return a primitive value.\\"); } return (hint === \\"string\\" ? String : Number)(input); }
class Foo {}
Foo.foo = Foo.bar;
_defineProperty(Foo, \\"bar\\", 42);"
Expand Down
@@ -0,0 +1,11 @@
const foo = { [Symbol.toPrimitive]: () => "foo" };

expect((new class { [foo](){ return 0; } }).foo()).toBe(0);
expect((new class { get [foo](){ return 0; } }).foo).toBe(0);
expect((new class { set [foo](v){ return v; } }).foo = 0).toBe(0);

const arrayLike = { [Symbol.toPrimitive] : () => [] };

expect(() => new class { [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new class { get [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new class { set [arrayLike](v){ return v; } }).toThrowError("@@toPrimitive must return a primitive value.");
@@ -0,0 +1,11 @@
const foo = { [Symbol.toPrimitive]: () => "foo" };

expect((new class { [foo](){ return 0; } }).foo()).toBe(0);
expect((new class { get [foo](){ return 0; } }).foo).toBe(0);
expect((new class { set [foo](v){ return v; } }).foo = 0).toBe(0);

const arrayLike = { [Symbol.toPrimitive] : () => [] };

expect(() => new class { [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new class { get [arrayLike](){ return 0; } }).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new class { set [arrayLike](v){ return v; } }).toThrowError("@@toPrimitive must return a primitive value.");
@@ -0,0 +1,90 @@
var foo = {
[Symbol.toPrimitive]: () => "foo"
};
expect(new ( /*#__PURE__*/function () {
"use strict";

function _class() {
babelHelpers.classCallCheck(this, _class);
}
babelHelpers.createClass(_class, [{
key: foo,
value: function value() {
return 0;
}
}]);
return _class;
}())().foo()).toBe(0);
expect(new ( /*#__PURE__*/function () {
"use strict";

function _class2() {
babelHelpers.classCallCheck(this, _class2);
}
babelHelpers.createClass(_class2, [{
key: foo,
get: function get() {
return 0;
}
}]);
return _class2;
}())().foo).toBe(0);
expect(new ( /*#__PURE__*/function () {
"use strict";

function _class3() {
babelHelpers.classCallCheck(this, _class3);
}
babelHelpers.createClass(_class3, [{
key: foo,
set: function set(v) {
return v;
}
}]);
return _class3;
}())().foo = 0).toBe(0);
var arrayLike = {
[Symbol.toPrimitive]: () => []
};
expect(() => new ( /*#__PURE__*/function () {
"use strict";

function _class4() {
babelHelpers.classCallCheck(this, _class4);
}
babelHelpers.createClass(_class4, [{
key: arrayLike,
value: function value() {
return 0;
}
}]);
return _class4;
}())()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new ( /*#__PURE__*/function () {
"use strict";

function _class5() {
babelHelpers.classCallCheck(this, _class5);
}
babelHelpers.createClass(_class5, [{
key: arrayLike,
get: function get() {
return 0;
}
}]);
return _class5;
}())()).toThrowError("@@toPrimitive must return a primitive value.");
expect(() => new ( /*#__PURE__*/function () {
"use strict";

function _class6() {
babelHelpers.classCallCheck(this, _class6);
}
babelHelpers.createClass(_class6, [{
key: arrayLike,
set: function set(v) {
return v;
}
}]);
return _class6;
}())()).toThrowError("@@toPrimitive must return a primitive value.");

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit dafa8bd

Please sign in to comment.