Skip to content

Commit 27c39c5

Browse files
authored
Class private properties (babel#7842)
* Update member-expression-to-functions 1. Babel using British spellings, so `memoise` 2. Provide a helper `AssignmentMemoiser` class, which will assign the memo'd value with the `n`th access. * Private properties! * Fixes * Tests * Update helper name * Fix privates that reference other privates * Don't extend a builtin * Rebase
1 parent 70eb206 commit 27c39c5

File tree

177 files changed

+3053
-92
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

177 files changed

+3053
-92
lines changed

packages/babel-helper-member-expression-to-functions/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const visitor = {
2626

2727
// The helper requires three special methods on state: `get`, `set`, and
2828
// `call`.
29-
// Optionally, a special `memoize` method may be defined, which gets
29+
// Optionally, a special `memoise` method may be defined, which gets
3030
// called if the member is in a self-referential update expression.
3131
// Everything else will be passed through as normal.
3232
const state = {
@@ -55,10 +55,10 @@ const state = {
5555
);
5656
},
5757

58-
memoize(memberPath) {
58+
memoise(memberPath) {
5959
const { node } = memberPath;
6060
if (node.computed) {
61-
MEMOIZED.set(node, ...);
61+
MEMOISED.set(node, ...);
6262
}
6363
},
6464

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

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,39 @@
11
import * as t from "@babel/types";
22

3+
class AssignmentMemoiser {
4+
constructor() {
5+
this._map = new WeakMap();
6+
}
7+
8+
has(key) {
9+
return this._map.has(key);
10+
}
11+
12+
get(key) {
13+
if (!this.has(key)) return;
14+
15+
const record = this._map.get(key);
16+
const { value } = record;
17+
18+
record.count--;
19+
if (record.count === 0) {
20+
// The `count` access is the outermost function call (hopefully), so it
21+
// does the assignment.
22+
return t.assignmentExpression("=", value, key);
23+
}
24+
return value;
25+
}
26+
27+
set(key, value, count) {
28+
return this._map.set(key, { count, value });
29+
}
30+
}
31+
332
const handle = {
33+
memoise() {
34+
// noop.
35+
},
36+
437
handle(member) {
538
const { node, parent, parentPath } = member;
639

@@ -9,11 +42,10 @@ const handle = {
942
if (parentPath.isUpdateExpression({ argument: node })) {
1043
const { operator, prefix } = parent;
1144

12-
// Give the state handler a chance to memoize the member,
13-
// since we'll reference it twice.
14-
if (this.memoize) {
15-
this.memoize(member);
16-
}
45+
// Give the state handler a chance to memoise the member, since we'll
46+
// reference it twice. The second access (the set) should do the memo
47+
// assignment.
48+
this.memoise(member, 2);
1749

1850
const value = t.binaryExpression(
1951
operator[0],
@@ -44,11 +76,10 @@ const handle = {
4476
let value = right;
4577

4678
if (operator !== "=") {
47-
// Give the state handler a chance to memoize the member,
48-
// since we'll reference it twice.
49-
if (this.memoize) {
50-
this.memoize(member);
51-
}
79+
// Give the state handler a chance to memoise the member, since we'll
80+
// reference it twice. The second access (the set) should do the memo
81+
// assignment.
82+
this.memoise(member, 2);
5283

5384
value = t.binaryExpression(
5485
operator.slice(0, -1),
@@ -79,11 +110,12 @@ const handle = {
79110
// it wishes to be transformed.
80111
// Additionally, the caller must pass in a state object with at least
81112
// get, set, and call methods.
82-
// Optionally, a memoize method may be defined on the state, which will be
113+
// Optionally, a memoise method may be defined on the state, which will be
83114
// called when the member is a self-referential update.
84115
export default function memberExpressionToFunctions(path, visitor, state) {
85116
path.traverse(visitor, {
86-
...state,
87117
...handle,
118+
...state,
119+
memoiser: new AssignmentMemoiser(),
88120
});
89121
}

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

Lines changed: 29 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,8 @@ const visitor = traverse.visitors.merge([
7070
},
7171
]);
7272

73-
const memoized = new WeakMap();
7473
const specHandlers = {
75-
memoize(superMember) {
74+
memoise(superMember, count) {
7675
const { scope, node } = superMember;
7776
const { computed, property } = node;
7877
if (!computed) {
@@ -84,44 +83,34 @@ const specHandlers = {
8483
return;
8584
}
8685

87-
memoized.set(property, memo);
86+
this.memoiser.set(property, memo, count);
8887
},
8988

90-
get(superMember) {
89+
prop(superMember) {
9190
const { computed, property } = superMember.node;
92-
const thisExpr = t.thisExpression();
91+
if (this.memoiser.has(property)) {
92+
return t.cloneNode(this.memoiser.get(property));
93+
}
9394

94-
let prop;
95-
if (computed && memoized.has(property)) {
96-
prop = t.cloneNode(memoized.get(property));
97-
} else {
98-
prop = computed ? property : t.stringLiteral(property.name);
95+
if (computed) {
96+
return t.cloneNode(property);
9997
}
10098

99+
return t.stringLiteral(property.name);
100+
},
101+
102+
get(superMember) {
101103
return t.callExpression(this.file.addHelper("get"), [
102104
getPrototypeOfExpression(this.getObjectRef(), this.isStatic, this.file),
103-
prop,
104-
thisExpr,
105+
this.prop(superMember),
106+
t.thisExpression(),
105107
]);
106108
},
107109

108110
set(superMember, value) {
109-
const { computed, property } = superMember.node;
110-
111-
let prop;
112-
if (computed && memoized.has(property)) {
113-
prop = t.assignmentExpression(
114-
"=",
115-
t.cloneNode(memoized.get(property)),
116-
property,
117-
);
118-
} else {
119-
prop = computed ? property : t.stringLiteral(property.name);
120-
}
121-
122111
return t.callExpression(this.file.addHelper("set"), [
123112
getPrototypeOfExpression(this.getObjectRef(), this.isStatic, this.file),
124-
prop,
113+
this.prop(superMember),
125114
value,
126115
t.thisExpression(),
127116
t.booleanLiteral(superMember.isInStrictMode()),
@@ -134,12 +123,21 @@ const specHandlers = {
134123
};
135124

136125
const looseHandlers = {
137-
memoize: specHandlers.memoize,
138-
call: specHandlers.call,
126+
...specHandlers,
127+
128+
prop(superMember) {
129+
const { property } = superMember.node;
130+
if (this.memoiser.has(property)) {
131+
return t.cloneNode(this.memoiser.get(property));
132+
}
133+
134+
return t.cloneNode(property);
135+
},
139136

140137
get(superMember) {
141138
const { isStatic, superRef } = this;
142-
const { property, computed } = superMember.node;
139+
const { computed } = superMember.node;
140+
const prop = this.prop(superMember);
143141

144142
let object;
145143
if (isStatic) {
@@ -155,29 +153,12 @@ const looseHandlers = {
155153
: t.memberExpression(t.identifier("Object"), t.identifier("prototype"));
156154
}
157155

158-
let prop;
159-
if (computed && memoized.has(property)) {
160-
prop = t.cloneNode(memoized.get(property));
161-
} else {
162-
prop = property;
163-
}
164-
165156
return t.memberExpression(object, prop, computed);
166157
},
167158

168159
set(superMember, value) {
169-
const { property, computed } = superMember.node;
170-
171-
let prop;
172-
if (computed && memoized.has(property)) {
173-
prop = t.assignmentExpression(
174-
"=",
175-
t.cloneNode(memoized.get(property)),
176-
property,
177-
);
178-
} else {
179-
prop = property;
180-
}
160+
const { computed } = superMember.node;
161+
const prop = this.prop(superMember);
181162

182163
return t.assignmentExpression(
183164
"=",

packages/babel-helpers/src/helpers.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,3 +967,38 @@ helpers.applyDecoratedDescriptor = () => template.program.ast`
967967
return desc;
968968
}
969969
`;
970+
971+
helpers.classPrivateFieldLooseKey = () => template.program.ast`
972+
var id = 0;
973+
export default function _classPrivateFieldKey(name) {
974+
return "__private_" + (id++) + "_" + name;
975+
}
976+
`;
977+
978+
helpers.classPrivateFieldLooseBase = () => template.program.ast`
979+
export default function _classPrivateFieldBase(receiver, privateKey) {
980+
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
981+
throw new TypeError("attempted to use private field on non-instance");
982+
}
983+
return receiver;
984+
}
985+
`;
986+
987+
helpers.classPrivateFieldGet = () => template.program.ast`
988+
export default function _classPrivateFieldGet(receiver, privateMap) {
989+
if (!privateMap.has(receiver)) {
990+
throw new TypeError("attempted to get private field on non-instance");
991+
}
992+
return privateMap.get(receiver);
993+
}
994+
`;
995+
996+
helpers.classPrivateFieldSet = () => template.program.ast`
997+
export default function _classPrivateFieldSet(receiver, privateMap, value) {
998+
if (!privateMap.has(receiver)) {
999+
throw new TypeError("attempted to set private field on non-instance");
1000+
}
1001+
privateMap.set(receiver, value);
1002+
return value;
1003+
}
1004+
`;

packages/babel-plugin-proposal-class-properties/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
],
1111
"dependencies": {
1212
"@babel/helper-function-name": "7.0.0-beta.47",
13+
"@babel/helper-member-expression-to-functions": "7.0.0-beta.47",
14+
"@babel/helper-optimise-call-expression": "7.0.0-beta.47",
1315
"@babel/helper-plugin-utils": "7.0.0-beta.47",
1416
"@babel/helper-replace-supers": "7.0.0-beta.47",
1517
"@babel/plugin-syntax-class-properties": "7.0.0-beta.47"

0 commit comments

Comments
 (0)