Skip to content

Commit

Permalink
Add private accessors spec support
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-mc committed Dec 24, 2018
1 parent ba5120c commit ee8b26f
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 27 deletions.
88 changes: 69 additions & 19 deletions packages/babel-helper-create-class-features-plugin/src/fields.js
Expand Up @@ -43,15 +43,19 @@ export function buildPrivateNamesNodes(privateNamesMap, loose, state) {
// In spec mode, only instance fields need a "private name" initializer
// because static fields are directly assigned to a variable in the
// buildPrivateStaticFieldInitSpec function.
const { id, static: isStatic, method: isMethod } = value;
const { id, static: isStatic, method: isMethod, getId, setId } = value;
if (loose) {
initNodes.push(
template.statement.ast`
var ${id} = ${state.addHelper("classPrivateFieldLooseKey")}("${name}")
`,
);
} else if (isMethod && !isStatic) {
initNodes.push(template.statement.ast`var ${id} = new WeakSet();`);
if (getId || setId) {
initNodes.push(template.statement.ast`var ${id} = new WeakMap();`);
} else {
initNodes.push(template.statement.ast`var ${id} = new WeakSet();`);
}
} else if (!isStatic) {
initNodes.push(template.statement.ast`var ${id} = new WeakMap();`);
}
Expand Down Expand Up @@ -133,48 +137,65 @@ const privateNameHandlerSpec = {
static: isStatic,
method: isMethod,
methodId,
getId,
} = privateNamesMap.get(name);

if (isStatic && !isMethod) {
return t.callExpression(
file.addHelper("classStaticPrivateFieldSpecGet"),
[this.receiver(member), t.cloneNode(classRef), t.cloneNode(id)],
);
} else if (isMethod) {
}
if (isMethod) {
if (getId) {
return t.callExpression(file.addHelper("classPrivateFieldGet"), [
this.receiver(member),
t.cloneNode(id),
]);
}
return t.callExpression(file.addHelper("classPrivateMethodGet"), [
this.receiver(member),
t.cloneNode(id),
t.cloneNode(methodId),
]);
} else {
return t.callExpression(file.addHelper("classPrivateFieldGet"), [
this.receiver(member),
t.cloneNode(id),
]);
}
return t.callExpression(file.addHelper("classPrivateFieldGet"), [
this.receiver(member),
t.cloneNode(id),
]);
},

set(member, value) {
const { classRef, privateNamesMap, file } = this;
const { name } = member.node.property.id;
const { id, static: isStatic, method: isMethod } = privateNamesMap.get(
name,
);
const {
id,
static: isStatic,
method: isMethod,
setId,
} = privateNamesMap.get(name);

if (isStatic && !isMethod) {
return t.callExpression(
file.addHelper("classStaticPrivateFieldSpecSet"),
[this.receiver(member), t.cloneNode(classRef), t.cloneNode(id), value],
);
} else if (isMethod) {
}
if (isMethod) {
if (setId) {
return t.callExpression(file.addHelper("classPrivateFieldSet"), [
this.receiver(member),
t.cloneNode(id),
value,
]);
}
return t.callExpression(file.addHelper("classPrivateMethodSet"), []);
} else {
return t.callExpression(file.addHelper("classPrivateFieldSet"), [
this.receiver(member),
t.cloneNode(id),
value,
]);
}
return t.callExpression(file.addHelper("classPrivateFieldSet"), [
this.receiver(member),
t.cloneNode(id),
value,
]);
},

call(member, args) {
Expand Down Expand Up @@ -321,8 +342,37 @@ function buildPrivateMethodInitLoose(ref, prop, privateNamesMap) {
}

function buildPrivateInstanceMethodInitSpec(ref, prop, privateNamesMap) {
const { id } = privateNamesMap.get(prop.node.key.id.name);
const privateName = privateNamesMap.get(prop.node.key.id.name);
const { id, getId, setId, initAdded } = privateName;
if (initAdded) return;

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

if (getId && setId) {
return template.statement.ast`
${id}.set(${ref}, {
get: ${getId.name},
set: ${setId.name}
});
`;
} else if (getId && !setId) {
return template.statement.ast`
${id}.set(${ref}, {
get: ${getId.name}
});
`;
} else if (!getId && setId) {
return template.statement.ast`
${id}.set(${ref}, {
set: ${setId.name}
});
`;
}
}
return template.statement.ast`${id}.add(${ref})`;
}

Expand Down
17 changes: 9 additions & 8 deletions packages/babel-helpers/src/helpers.js
Expand Up @@ -1050,7 +1050,7 @@ helpers.classPrivateFieldGet = helper("7.0.0-beta.0")`
}
var descriptor = privateMap.get(receiver);
if (descriptor.get) {
return descriptor.get();
return descriptor.get.call(receiver);
}
return descriptor.value;
}
Expand All @@ -1062,15 +1062,16 @@ helpers.classPrivateFieldSet = helper("7.0.0-beta.0")`
throw new TypeError("attempted to set private field on non-instance");
}
var descriptor = privateMap.get(receiver);
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");
}
if (descriptor.set) {
descriptor.set(value);
descriptor.set.call(receiver, value);
} 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");
}
descriptor.value = value;
return value;
}
Expand Down
@@ -0,0 +1,60 @@
class Foo {
#privateField = "top secret string";

constructor() {
this.publicField = "not secret string";
}

get #privateFieldValue() {
return this.#privateField;
}

set #privateFieldValue(newValue) {
this.#privateField = newValue;
}

publicGetPrivateField() {
return this.#privateFieldValue;
}

publicSetPrivateField(newValue) {
this.#privateFieldValue = newValue;
}

get publicFieldValue() {
return this.publicField;
}

set publicFieldValue(newValue) {
this.publicField = newValue;
}

testUpdates() {
this.#privateField = 0;
this.publicField = 0;
this.#privateFieldValue = this.#privateFieldValue++;
this.publicFieldValue = this.publicFieldValue++;
expect(this.#privateField).toEqual(this.publicField);

++this.#privateFieldValue;
++this.publicFieldValue;
expect(this.#privateField).toEqual(this.publicField);

this.#privateFieldValue += 1;
this.publicFieldValue += 1;
expect(this.#privateField).toEqual(this.publicField);

this.#privateFieldValue = -(this.#privateFieldValue ** this.#privateFieldValue);
this.publicFieldValue = -(this.publicFieldValue ** this.publicFieldValue);
expect(this.#privateField).toEqual(this.publicField);
}
}

const foo = new Foo();

expect(foo.publicGetPrivateField()).toEqual("top secret string");

foo.publicSetPrivateField("new secret string");
expect(foo.publicGetPrivateField()).toEqual("new secret string");

foo.testUpdates();
@@ -0,0 +1,47 @@
class Foo {
#privateField = "top secret string";

constructor() {
this.publicField = "not secret string";
}

get #privateFieldValue() {
return this.#privateField;
}

set #privateFieldValue(newValue) {
this.#privateField = newValue;
}

publicGetPrivateField() {
return this.#privateFieldValue;
}

publicSetPrivateField(newValue) {
this.#privateFieldValue = newValue;
}

get publicFieldValue() {
return this.publicField;
}

set publicFieldValue(newValue) {
this.publicField = newValue;
}

testUpdates() {
this.#privateField = 0;
this.publicField = 0;
this.#privateFieldValue = this.#privateFieldValue++;
this.publicFieldValue = this.publicFieldValue++;

++this.#privateFieldValue;
++this.publicFieldValue;

this.#privateFieldValue += 1;
this.publicFieldValue += 1;

this.#privateFieldValue = -(this.#privateFieldValue ** this.#privateFieldValue);
this.publicFieldValue = -(this.publicFieldValue ** this.publicFieldValue);
}
}
@@ -0,0 +1,70 @@
var Foo =
/*#__PURE__*/
function () {
"use strict";

function Foo() {
babelHelpers.classCallCheck(this, Foo);

_privateField.set(this, {
writable: true,
value: "top secret string"
});

_privateFieldValue.set(this, {
get: _get_privateFieldValue,
set: _set_privateFieldValue
});

this.publicField = "not secret string";
}

babelHelpers.createClass(Foo, [{
key: "publicGetPrivateField",
value: function publicGetPrivateField() {
return babelHelpers.classPrivateFieldGet(this, _privateFieldValue);
}
}, {
key: "publicSetPrivateField",
value: function publicSetPrivateField(newValue) {
babelHelpers.classPrivateFieldSet(this, _privateFieldValue, newValue);
}
}, {
key: "testUpdates",
value: function testUpdates() {
var _this$privateFieldVal, _this$privateFieldVal2;

babelHelpers.classPrivateFieldSet(this, _privateField, 0);
this.publicField = 0;
babelHelpers.classPrivateFieldSet(this, _privateFieldValue, (babelHelpers.classPrivateFieldSet(this, _privateFieldValue, (_this$privateFieldVal2 = +babelHelpers.classPrivateFieldGet(this, _privateFieldValue)) + 1), _this$privateFieldVal2));
this.publicFieldValue = this.publicFieldValue++;
babelHelpers.classPrivateFieldSet(this, _privateFieldValue, +babelHelpers.classPrivateFieldGet(this, _privateFieldValue) + 1);
++this.publicFieldValue;
babelHelpers.classPrivateFieldSet(this, _privateFieldValue, babelHelpers.classPrivateFieldGet(this, _privateFieldValue) + 1);
this.publicFieldValue += 1;
babelHelpers.classPrivateFieldSet(this, _privateFieldValue, -(babelHelpers.classPrivateFieldGet(this, _privateFieldValue) ** babelHelpers.classPrivateFieldGet(this, _privateFieldValue)));
this.publicFieldValue = -(this.publicFieldValue ** this.publicFieldValue);
}
}, {
key: "publicFieldValue",
get: function () {
return this.publicField;
},
set: function (newValue) {
this.publicField = newValue;
}
}]);
return Foo;
}();

var _privateField = new WeakMap();

var _privateFieldValue = new WeakMap();

var _get_privateFieldValue = function () {
return babelHelpers.classPrivateFieldGet(this, _privateField);
};

var _set_privateFieldValue = function (newValue) {
babelHelpers.classPrivateFieldSet(this, _privateField, newValue);
};

0 comments on commit ee8b26f

Please sign in to comment.