Skip to content

Commit

Permalink
destructuring private fields with array pattern / object patte… (babe…
Browse files Browse the repository at this point in the history
…l#10017)

* destructuring private fields with array pattern / object pattern

* wip: new test cases

* destrucuring and rest for private properties

* test case for loose private-loose

* add transform-desturcturing for exec

* update test case

* remove getPrototypeOf imports from get and set

* wip: destructure super assignment

* throw "Destructuring to a super field is not supported yet."

* fix tests and fix assignment pattern

* remove CallExpression from AssignmentPattern
  • Loading branch information
tanhauhau authored and AdamRamberg committed Jul 17, 2019
1 parent 560df5e commit 93baa84
Show file tree
Hide file tree
Showing 88 changed files with 970 additions and 4 deletions.
13 changes: 13 additions & 0 deletions packages/babel-helper-create-class-features-plugin/src/fields.js
Expand Up @@ -211,6 +211,19 @@ const privateNameHandlerSpec = {
]);
},

destructureSet(member) {
const { privateNamesMap, file } = this;
const { name } = member.node.property.id;
const { id } = privateNamesMap.get(name);
return t.memberExpression(
t.callExpression(file.addHelper("classPrivateFieldDestructureSet"), [
this.receiver(member),
t.cloneNode(id),
]),
t.identifier("value"),
);
},

call(member, args) {
// The first access (the get) should do the memo assignment.
this.memoise(member, 1);
Expand Down
28 changes: 28 additions & 0 deletions packages/babel-helper-member-expression-to-functions/src/index.js
Expand Up @@ -100,6 +100,34 @@ const handle = {
return;
}

// { KEY: MEMBER } = OBJ -> { KEY: _destructureSet(MEMBER) } = OBJ
// { KEY: MEMBER = _VALUE } = OBJ -> { KEY: _destructureSet(MEMBER) = _VALUE } = OBJ
// {...MEMBER} -> {..._destructureSet(MEMBER)}
//
// [MEMBER] = ARR -> [_destructureSet(MEMBER)] = ARR
// [MEMBER = _VALUE] = ARR -> [_destructureSet(MEMBER) = _VALUE] = ARR
// [...MEMBER] -> [..._destructureSet(MEMBER)]
if (
// { KEY: MEMBER } = OBJ
(parentPath.isObjectProperty({ value: node }) &&
parentPath.parentPath.isObjectPattern()) ||
// { KEY: MEMBER = _VALUE } = OBJ
(parentPath.isAssignmentPattern({ left: node }) &&
parentPath.parentPath.isObjectProperty({ value: parent }) &&
parentPath.parentPath.parentPath.isObjectPattern()) ||
// [MEMBER] = ARR
parentPath.isArrayPattern() ||
// [MEMBER = _VALUE] = ARR
(parentPath.isAssignmentPattern({ left: node }) &&
parentPath.parentPath.isArrayPattern()) ||
// {...MEMBER}
// [...MEMBER]
parentPath.isRestElement()
) {
member.replaceWith(this.destructureSet(member));
return;
}

// MEMBER -> _get(MEMBER)
member.replaceWith(this.get(member));
},
Expand Down
13 changes: 13 additions & 0 deletions packages/babel-helper-replace-supers/src/index.js
Expand Up @@ -125,6 +125,12 @@ const specHandlers = {
]);
},

destructureSet(superMember) {
throw superMember.buildCodeFrameError(
`Destructuring to a super field is not supported yet.`,
);
},

call(superMember, args) {
return optimiseCall(this.get(superMember), t.thisExpression(), args);
},
Expand Down Expand Up @@ -174,6 +180,13 @@ const looseHandlers = {
value,
);
},

destructureSet(superMember) {
const { computed } = superMember.node;
const prop = this.prop(superMember);

return t.memberExpression(t.thisExpression(), prop, computed);
},
};

export default class ReplaceSupers {
Expand Down
30 changes: 28 additions & 2 deletions packages/babel-helpers/src/helpers.js
Expand Up @@ -720,7 +720,6 @@ helpers.superPropBase = helper("7.0.0-beta.0")`
`;

helpers.get = helper("7.0.0-beta.0")`
import getPrototypeOf from "getPrototypeOf";
import superPropBase from "superPropBase";
export default function _get(target, property, receiver) {
Expand All @@ -745,7 +744,6 @@ helpers.get = helper("7.0.0-beta.0")`
`;

helpers.set = helper("7.0.0-beta.0")`
import getPrototypeOf from "getPrototypeOf";
import superPropBase from "superPropBase";
import defineProperty from "defineProperty";
Expand Down Expand Up @@ -1123,6 +1121,34 @@ helpers.classPrivateFieldSet = helper("7.0.0-beta.0")`
}
`;

helpers.classPrivateFieldDestructureSet = helper("7.4.4")`
export default function _classPrivateFieldDestructureSet(receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to set private field on non-instance");
}
var descriptor = privateMap.get(receiver);
if (descriptor.set) {
if (!("__destrObj" in descriptor)) {
descriptor.__destrObj = {
set value(v) {
descriptor.set.call(receiver, v)
},
};
}
return descriptor.__destrObj;
} else {
if (!descriptor.writable) {
// This should only throw in strict mode, but class bodies are
// always strict and private fields can only be used inside
// class bodies.
throw new TypeError("attempted to set read only private field");
}
return descriptor;
}
}
`;

helpers.classStaticPrivateFieldSpecGet = helper("7.0.2")`
export default function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) {
if (receiver !== classConstructor) {
Expand Down
@@ -0,0 +1,17 @@
class Foo {
#client

constructor(props) {
this.#client = 1;
;([this.x = this.#client, this.#client, this.y = this.#client] = props);
}

getClient() {
return this.#client;
}
}

const foo = new Foo([undefined, 'bar']);
expect(foo.getClient()).toBe('bar');
expect(foo.x).toBe(1);
expect(foo.y).toBe('bar');
@@ -0,0 +1,8 @@
class Foo {
#client

constructor(props) {
this.#client = 1;
([this.x = this.#client, this.#client, this.y = this.#client] = props);
}
}
@@ -0,0 +1,13 @@
var Foo = function Foo(props) {
"use strict";

babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _client, {
writable: true,
value: void 0
});
babelHelpers.classPrivateFieldLooseBase(this, _client)[_client] = 1;
[this.x = babelHelpers.classPrivateFieldLooseBase(this, _client)[_client], babelHelpers.classPrivateFieldLooseBase(this, _client)[_client], this.y = babelHelpers.classPrivateFieldLooseBase(this, _client)[_client]] = props;
};

var _client = babelHelpers.classPrivateFieldLooseKey("client");
@@ -0,0 +1,15 @@
class Foo {
#client

constructor(props) {
let x;
;([x, ...this.#client] = props);
}

getClient() {
return this.#client;
}
}

const foo = new Foo(['foo', 'bar', 'baz', 'quu']);
expect(foo.getClient()).toEqual(['bar', 'baz', 'quu']);
@@ -0,0 +1,7 @@
class Foo {
#client

constructor(props) {
([x, ...this.#client] = props);
}
}
@@ -0,0 +1,12 @@
var Foo = function Foo(props) {
"use strict";

babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _client, {
writable: true,
value: void 0
});
[x, ...babelHelpers.classPrivateFieldLooseBase(this, _client)[_client]] = props;
};

var _client = babelHelpers.classPrivateFieldLooseKey("client");
@@ -0,0 +1,14 @@
class Foo {
#client

constructor(props) {
([this.#client = 5] = props);
}

getClient() {
return this.#client;
}
}

const foo = new Foo([]);
expect(foo.getClient()).toEqual(5);
@@ -0,0 +1,7 @@
class Foo {
#client

constructor(props) {
([this.#client = 5] = props);
}
}
@@ -0,0 +1,12 @@
var Foo = function Foo(props) {
"use strict";

babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _client, {
writable: true,
value: void 0
});
[babelHelpers.classPrivateFieldLooseBase(this, _client)[_client] = 5] = props;
};

var _client = babelHelpers.classPrivateFieldLooseKey("client");
@@ -0,0 +1,14 @@
class Foo {
#client

constructor(props) {
;([this.#client] = props);
}

getClient() {
return this.#client;
}
}

const foo = new Foo(['bar']);
expect(foo.getClient()).toBe('bar');
@@ -0,0 +1,7 @@
class Foo {
#client

constructor(props) {
([this.#client] = props);
}
}
@@ -0,0 +1,12 @@
var Foo = function Foo(props) {
"use strict";

babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _client, {
writable: true,
value: void 0
});
[babelHelpers.classPrivateFieldLooseBase(this, _client)[_client]] = props;
};

var _client = babelHelpers.classPrivateFieldLooseKey("client");
@@ -0,0 +1,17 @@
class Foo {
#client

constructor(props) {
this.#client = 'foo';
;({ x: this.x = this.#client, y: this.#client, z: this.z = this.#client } = props)
}

getClient() {
return this.#client;
}
}

const foo = new Foo({ y: 'bar' });
expect(foo.getClient()).toBe('bar');
expect(foo.x).toBe('foo');
expect(foo.z).toBe('bar');
@@ -0,0 +1,8 @@
class Foo {
#client

constructor(props) {
this.#client = 'foo';
({ x: this.x = this.#client, y: this.#client, z: this.z = this.#client } = props)
}
}
@@ -0,0 +1,17 @@
var Foo = function Foo(props) {
"use strict";

babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _client, {
writable: true,
value: void 0
});
babelHelpers.classPrivateFieldLooseBase(this, _client)[_client] = 'foo';
({
x: this.x = babelHelpers.classPrivateFieldLooseBase(this, _client)[_client],
y: babelHelpers.classPrivateFieldLooseBase(this, _client)[_client],
z: this.z = babelHelpers.classPrivateFieldLooseBase(this, _client)[_client]
} = props);
};

var _client = babelHelpers.classPrivateFieldLooseKey("client");
@@ -0,0 +1,15 @@
class Foo {
#client

constructor(props) {
let x;
;({ x, ...this.#client } = props)
}

getClient() {
return this.#client;
}
}

const foo = new Foo({ x: 'foo', y: 'bar', z: 'baz' });
expect(foo.getClient()).toEqual({ y: 'bar', z: 'baz' });
@@ -0,0 +1,15 @@
{
"plugins": [
"transform-destructuring",
[
"external-helpers",
{
"helperVersion": "7.4.4"
}
],
"proposal-class-properties",
"transform-classes",
"transform-block-scoping",
"syntax-class-properties"
]
}
@@ -0,0 +1,7 @@
class Foo {
#client

constructor(props) {
({ x, ...this.#client } = props)
}
}
@@ -0,0 +1,15 @@
var Foo = function Foo(props) {
"use strict";

babelHelpers.classCallCheck(this, Foo);
Object.defineProperty(this, _client, {
writable: true,
value: void 0
});
({
x,
...babelHelpers.classPrivateFieldLooseBase(this, _client)[_client]
} = props);
};

var _client = babelHelpers.classPrivateFieldLooseKey("client");

0 comments on commit 93baa84

Please sign in to comment.