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

Disallow reinitializing private elements #13601

Merged
merged 33 commits into from Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d909242
Reproduced error for member properties
komyg Jul 19, 2021
5d643dd
Fixed tests
komyg Jul 26, 2021
53e82d8
Added private fields check
komyg Jul 26, 2021
9ecbcf3
Updated field initializtion
komyg Jul 28, 2021
90f39e5
Updated helpers
komyg Jul 28, 2021
ce05c09
Added helper call
komyg Jul 30, 2021
06300db
Fixed unit test
komyg Jul 30, 2021
0f5a4e7
Removed commented code
komyg Aug 2, 2021
086c12d
Handle unavailable helper
komyg Aug 2, 2021
a975111
Updated error statement
komyg Aug 2, 2021
454c2cc
Updated error text
komyg Aug 2, 2021
dbc25c4
Added private method helper
komyg Aug 2, 2021
5770b46
Update packages/babel-helpers/src/helpers.js
komyg Aug 5, 2021
2e6f07a
Added babel 8 breaking guard
komyg Aug 5, 2021
2d6c4a2
Merge branch 'fix/bug-13299-class-members' of github.com:komyg/babel …
komyg Aug 5, 2021
aa4677d
Merge branch 'main' into fix/bug-13299-class-members
komyg Aug 5, 2021
13f8014
Fixed typings error
komyg Aug 5, 2021
894b70a
Fixed return types
komyg Aug 6, 2021
90087c2
Added babel 8 check
komyg Aug 6, 2021
7ba6f5e
Added check redeclaration helper
komyg Aug 6, 2021
3e81c56
Fixed test
komyg Aug 6, 2021
3e28e0d
Updated var name
komyg Aug 6, 2021
932b2ef
Fixed tests
komyg Aug 6, 2021
c275da7
Handle private accessor method
komyg Aug 6, 2021
01e2e0b
Overwritten tests
komyg Aug 7, 2021
4047989
Updated error message
komyg Aug 12, 2021
8e5e404
Update packages/babel-helper-create-class-features-plugin/src/fields.ts
komyg Aug 16, 2021
1f6b26c
Update packages/babel-helper-create-class-features-plugin/src/fields.ts
komyg Aug 16, 2021
eca092f
Merge branch 'main' into fix/bug-13299-class-members
komyg Aug 16, 2021
d187a77
Update packages/babel-helper-create-class-features-plugin/src/fields.ts
komyg Aug 18, 2021
1d33cf2
Update packages/babel-helper-create-class-features-plugin/src/fields.ts
komyg Aug 18, 2021
f9eb76a
Update packages/babel-helper-create-class-features-plugin/src/fields.ts
komyg Aug 18, 2021
ebf193c
Clone id nodes
komyg Aug 18, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
105 changes: 92 additions & 13 deletions packages/babel-helper-create-class-features-plugin/src/fields.ts
Expand Up @@ -510,16 +510,33 @@ function buildPrivateInstanceFieldInitSpec(
ref: t.Expression,
prop: NodePath<t.ClassPrivateProperty>,
privateNamesMap: PrivateNamesMap,
state,
) {
const { id } = privateNamesMap.get(prop.node.key.id.name);
const value = prop.node.value || prop.scope.buildUndefinedNode();

return template.statement.ast`${t.cloneNode(id)}.set(${ref}, {
// configurable is always false for private elements
// enumerable is always false for private elements
writable: true,
value: ${value},
})`;
if (!process.env.BABEL_8_BREAKING) {
if (!state.availableHelper("classPrivateFieldInitSpec")) {
return template.statement.ast`${t.cloneNode(id)}.set(${ref}, {
// configurable is always false for private elements
// enumerable is always false for private elements
writable: true,
value: ${value},
})`;
}
}

const helper = state.addHelper("classPrivateFieldInitSpec");
const expr = t.callExpression(helper, [
t.thisExpression(),
t.cloneNode(id),
template.expression.ast`{
writable: true,
value: ${value}
}`,
]);

return t.expressionStatement(expr);
komyg marked this conversation as resolved.
Show resolved Hide resolved
}

function buildPrivateStaticFieldInitSpec(
Expand Down Expand Up @@ -602,27 +619,87 @@ function buildPrivateInstanceMethodInitSpec(
ref: t.Expression,
prop: NodePath<t.ClassPrivateMethod>,
privateNamesMap: PrivateNamesMap,
state,
) {
const privateName = privateNamesMap.get(prop.node.key.id.name);
const { id, getId, setId, initAdded } = privateName;
const { getId, setId, initAdded } = privateName;

if (initAdded) return;

const isAccessor = getId || setId;
if (isAccessor) {
privateNamesMap.set(prop.node.key.id.name, {
...privateName,
initAdded: true,
});
return buildPrivateAccessorInitialization(
ref,
prop,
privateNamesMap,
state,
);
}

return template.statement.ast`
return buildPrivateInstanceMethodInitalization(
ref,
prop,
privateNamesMap,
state,
);
}

function buildPrivateAccessorInitialization(
ref: t.Expression,
prop: NodePath<t.ClassPrivateMethod>,
privateNamesMap: PrivateNamesMap,
state,
) {
const privateName = privateNamesMap.get(prop.node.key.id.name);
const { id, getId, setId } = privateName;

privateNamesMap.set(prop.node.key.id.name, {
...privateName,
initAdded: true,
});

if (!process.env.BABEL_8_BREAKING) {
if (!state.availableHelper("classPrivateFieldInitSpec")) {
return template.statement.ast`
${id}.set(${ref}, {
get: ${getId ? getId.name : prop.scope.buildUndefinedNode()},
set: ${setId ? setId.name : prop.scope.buildUndefinedNode()}
});
`;
}
}
return template.statement.ast`${id}.add(${ref})`;

const helper = state.addHelper("classPrivateFieldInitSpec");
const expr = t.callExpression(helper, [
t.thisExpression(),
t.cloneNode(id),
template.expression.ast`{
get: ${getId ? getId.name : prop.scope.buildUndefinedNode()},
set: ${setId ? setId.name : prop.scope.buildUndefinedNode()}
}`,
]);

return t.expressionStatement(expr);
komyg marked this conversation as resolved.
Show resolved Hide resolved
}

function buildPrivateInstanceMethodInitalization(
ref: t.Expression,
prop: NodePath<t.ClassPrivateMethod>,
privateNamesMap: PrivateNamesMap,
state,
) {
const privateName = privateNamesMap.get(prop.node.key.id.name);
const { id } = privateName;

if (!process.env.BABEL_8_BREAKING) {
if (!state.availableHelper("classPrivateMethodInitSpec")) {
return template.statement.ast`${id}.add(${ref})`;
}
}

const helper = state.addHelper("classPrivateMethodInitSpec");
const expr = t.callExpression(helper, [t.thisExpression(), t.cloneNode(id)]);
return t.expressionStatement(expr);
komyg marked this conversation as resolved.
Show resolved Hide resolved
}

function buildPublicFieldInitLoose(
Expand Down Expand Up @@ -927,6 +1004,7 @@ export function buildFieldsInitNodes(
// @ts-expect-error checked in switch
prop,
privateNamesMap,
state,
),
);
break;
Expand Down Expand Up @@ -955,6 +1033,7 @@ export function buildFieldsInitNodes(
// @ts-expect-error checked in switch
prop,
privateNamesMap,
state,
),
);
pureStaticNodes.push(
Expand Down
@@ -0,0 +1,19 @@
class Base {
constructor(obj) {
return obj;
}
}

class Derived extends Base {
get #foo() {
return 'bar';
}

set #foo(value) {
this.#foo = value;
}

static get(obj) {
return obj.#foo();
}
}
@@ -0,0 +1,6 @@
{
"presets": [["env", { "shippedProposals": true }]],
"targets": {
"chrome": "75"
}
}
@@ -0,0 +1,31 @@
class Base {
constructor(obj) {
return obj;
}

}

var _foo = /*#__PURE__*/new WeakMap();

class Derived extends Base {
constructor(...args) {
super(...args);
babelHelpers.classPrivateFieldInitSpec(this, _foo, {
get: _get_foo,
set: _set_foo
});
}

static get(obj) {
return babelHelpers.classPrivateFieldGet(obj, _foo).call(obj);
}

}

function _get_foo() {
return 'bar';
}

function _set_foo(value) {
babelHelpers.classPrivateFieldSet(this, _foo, value);
}
@@ -0,0 +1,13 @@
class Base {
constructor(obj) {
return obj;
}
}

let counter = 0;
class Derived extends Base {
#foo = ++counter;
static get(obj) {
return obj.#foo;
}
}
@@ -0,0 +1,6 @@
{
"presets": [["env", { "shippedProposals": true }]],
"targets": {
"chrome": "75"
}
}
@@ -0,0 +1,25 @@
class Base {
constructor(obj) {
return obj;
}

}

let counter = 0;

var _foo = /*#__PURE__*/new WeakMap();

class Derived extends Base {
constructor(...args) {
super(...args);
babelHelpers.classPrivateFieldInitSpec(this, _foo, {
writable: true,
value: ++counter
});
}

static get(obj) {
return babelHelpers.classPrivateFieldGet(obj, _foo);
}

}
@@ -0,0 +1,15 @@
class Base {
constructor(obj) {
return obj;
}
}

class Derived extends Base {
#foo() {
return 'bar';
}

static get(obj) {
return obj.#foo();
}
}
@@ -0,0 +1,6 @@
{
"presets": [["env", { "shippedProposals": true }]],
"targets": {
"chrome": "75"
}
}
@@ -0,0 +1,24 @@
class Base {
constructor(obj) {
return obj;
}

}

var _foo = /*#__PURE__*/new WeakSet();

class Derived extends Base {
constructor(...args) {
super(...args);
babelHelpers.classPrivateMethodInitSpec(this, _foo);
}

static get(obj) {
return babelHelpers.classPrivateMethodGet(obj, _foo, _foo2).call(obj);
}

}

function _foo2() {
return 'bar';
}
Expand Up @@ -2,7 +2,7 @@ var _privateMethod = /*#__PURE__*/new WeakSet();

class X {
constructor() {
_privateMethod.add(this);
babelHelpers.classPrivateMethodInitSpec(this, _privateMethod);
}

}
Expand Down
Expand Up @@ -3,8 +3,7 @@ var _foo = /*#__PURE__*/new WeakSet();
class A extends B {
constructor(...args) {
super(...args);

_foo.add(this);
babelHelpers.classPrivateMethodInitSpec(this, _foo);
}

}
Expand Down
26 changes: 26 additions & 0 deletions packages/babel-helpers/src/helpers.ts
Expand Up @@ -2022,6 +2022,32 @@ helpers.classPrivateMethodGet = helper("7.1.6")`
}
`;

helpers.checkPrivateRedeclaration = helper("7.14.1")`
export default function _checkPrivateRedeclaration(obj, privateCollection) {
if (privateCollection.has(obj)) {
throw new TypeError("Cannot initialize the same private elements twice on an object");
}
}
`;

helpers.classPrivateFieldInitSpec = helper("7.14.1")`
import checkPrivateRedeclaration from "checkPrivateRedeclaration";

export default function _classPrivateFieldInitSpec(obj, privateMap, value) {
checkPrivateRedeclaration(obj, privateMap);
privateMap.set(obj, value);
}
`;

helpers.classPrivateMethodInitSpec = helper("7.14.1")`
import checkPrivateRedeclaration from "checkPrivateRedeclaration";

export default function _classPrivateMethodInitSpec(obj, privateSet) {
checkPrivateRedeclaration(obj, privateSet);
privateSet.add(obj);
}
`;

if (!process.env.BABEL_8_BREAKING) {
// Use readOnlyError instead
helpers.classPrivateMethodSet = helper("7.1.6")`
Expand Down
Expand Up @@ -4,11 +4,10 @@ class C {
constructor() {
var _babelHelpers$classPr;

_m.set(this, {
babelHelpers.classPrivateFieldInitSpec(this, _m, {
writable: true,
value: void 0
});

const o = null;
const n = this;
const p = o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldGet(o, _m).call(o, ...c, 1);
Expand Down
Expand Up @@ -4,11 +4,10 @@ class C {
constructor() {
var _babelHelpers$classPr;

_m.set(this, {
babelHelpers.classPrivateFieldInitSpec(this, _m, {
writable: true,
value: void 0
});

const o = null;
const n = this;
const p = o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldGet(o, _m).call(o, ...c, 1);
Expand Down
Expand Up @@ -2,7 +2,7 @@ var _g = /*#__PURE__*/new WeakSet();

class C {
constructor() {
_g.add(this);
babelHelpers.classPrivateMethodInitSpec(this, _g);
}

}
Expand Down