Skip to content

Commit

Permalink
[WIP] Private static accessors strict
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-mc committed Jul 14, 2019
1 parent 36f6ccd commit 1de0bb8
Show file tree
Hide file tree
Showing 16 changed files with 379 additions and 18 deletions.
61 changes: 53 additions & 8 deletions packages/babel-helper-create-class-features-plugin/src/fields.js
Expand Up @@ -142,9 +142,10 @@ const privateNameHandlerSpec = {
} = privateNamesMap.get(name);

if (isStatic) {
const helperName = isMethod
? "classStaticPrivateMethodGet"
: "classStaticPrivateFieldSpecGet";
const helperName =
isMethod && !(getId || setId)
? "classStaticPrivateMethodGet"
: "classStaticPrivateFieldSpecGet";

return t.callExpression(file.addHelper(helperName), [
this.receiver(member),
Expand Down Expand Up @@ -180,12 +181,14 @@ const privateNameHandlerSpec = {
static: isStatic,
method: isMethod,
setId,
getId,
} = privateNamesMap.get(name);

if (isStatic) {
const helperName = isMethod
? "classStaticPrivateMethodSet"
: "classStaticPrivateFieldSpecSet";
const helperName =
isMethod && !(getId || setId)
? "classStaticPrivateMethodSet"
: "classStaticPrivateFieldSpecSet";

return t.callExpression(file.addHelper(helperName), [
this.receiver(member),
Expand Down Expand Up @@ -287,8 +290,47 @@ function buildPrivateInstanceFieldInitSpec(ref, prop, privateNamesMap) {
}

function buildPrivateStaticFieldInitSpec(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;
const value = prop.node.value || prop.scope.buildUndefinedNode();
if (!prop.isProperty() && (initAdded || !(getId || setId))) return;

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

if (getId && setId) {
return template.statement.ast`
var ${id.name} = {
// configurable is false by default
// enumerable is false by default
// writable is false by default
get: ${getId.name},
set: ${setId.name}
}
`;
} else if (getId && !setId) {
return template.statement.ast`
var ${id.name} = {
// configurable is false by default
// enumerable is false by default
// writable is false by default
get: ${getId.name}
}
`;
} else if (!getId && setId) {
return template.statement.ast`
var ${id.name} = {
// configurable is false by default
// enumerable is false by default
// writable is false by default
set: ${setId.name}
}
`;
}
}

return template.statement.ast`
var ${id} = {
Expand Down Expand Up @@ -602,6 +644,9 @@ export function buildFieldsInitNodes(
case isStatic && isPrivate && isMethod && !loose:
needsClassRef = true;
staticNodes.push(
buildPrivateStaticFieldInitSpec(prop, privateNamesMap),
);
staticNodes.unshift(
buildPrivateMethodDeclaration(prop, privateNamesMap, loose),
);
break;
Expand Down Expand Up @@ -633,7 +678,7 @@ export function buildFieldsInitNodes(
}

return {
staticNodes,
staticNodes: staticNodes.filter(Boolean),
instanceNodes: instanceNodes.filter(Boolean),
wrapClass(path) {
for (const prop of props) {
Expand Down
20 changes: 14 additions & 6 deletions packages/babel-helpers/src/helpers.js
Expand Up @@ -1128,6 +1128,9 @@ helpers.classStaticPrivateFieldSpecGet = helper("7.0.2")`
if (receiver !== classConstructor) {
throw new TypeError("Private static access of wrong provenance");
}
if (descriptor.get) {
return descriptor.get.call(receiver);
}
return descriptor.value;
}
`;
Expand All @@ -1137,13 +1140,18 @@ helpers.classStaticPrivateFieldSpecSet = helper("7.0.2")`
if (receiver !== classConstructor) {
throw new TypeError("Private static access of wrong provenance");
}
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.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;
}
descriptor.value = value;
return value;
}
`;
Expand Down
Expand Up @@ -16,12 +16,12 @@ class B extends A {

}

var _getA = function _getA() {
return babelHelpers.get(babelHelpers.getPrototypeOf(B), "a", this);
};

var _getB = function _getB() {
return this.b;
};

var _getA = function _getA() {
return babelHelpers.get(babelHelpers.getPrototypeOf(B), "a", this);
};

var [getA, getB] = B.extract();
@@ -0,0 +1,23 @@
class Cl {
static #PRIVATE_STATIC_FIELD = "top secret string";

static get #privateStaticFieldValue() {
return Cl.#PRIVATE_STATIC_FIELD;
}

static set #privateStaticFieldValue(newValue) {
Cl.#PRIVATE_STATIC_FIELD = `Updated: ${newValue}`;
}

static getValue() {
return Cl.#privateStaticFieldValue;
}

static setValue() {
Cl.#privateStaticFieldValue = "dank";
}
}

expect(Cl.getValue()).toEqual("top secret string");
Cl.setValue();
expect(Cl.getValue()).toEqual("Updated: dank");
@@ -0,0 +1,19 @@
class Cl {
static #PRIVATE_STATIC_FIELD = "top secret string";

static get #privateStaticFieldValue() {
return Cl.#PRIVATE_STATIC_FIELD;
}

static set #privateStaticFieldValue(newValue) {
Cl.#PRIVATE_STATIC_FIELD = `Updated: ${newValue}`;
}

static getValue() {
return Cl.#privateStaticFieldValue;
}

static setValue() {
Cl.#privateStaticFieldValue = "dank";
}
}
@@ -0,0 +1,27 @@
class Cl {
static getValue() {
return babelHelpers.classStaticPrivateFieldSpecGet(Cl, Cl, _privateStaticFieldValue);
}

static setValue() {
babelHelpers.classStaticPrivateFieldSpecSet(Cl, Cl, _privateStaticFieldValue, "dank");
}

}

var _set_privateStaticFieldValue = function (newValue) {
babelHelpers.classStaticPrivateFieldSpecSet(Cl, Cl, _PRIVATE_STATIC_FIELD, `Updated: ${newValue}`);
};

var _get_privateStaticFieldValue = function () {
return babelHelpers.classStaticPrivateFieldSpecGet(Cl, Cl, _PRIVATE_STATIC_FIELD);
};

var _PRIVATE_STATIC_FIELD = {
writable: true,
value: "top secret string"
};
var _privateStaticFieldValue = {
get: _get_privateStaticFieldValue,
set: _set_privateStaticFieldValue
};
@@ -0,0 +1,13 @@
class Cl {
static #PRIVATE_STATIC_FIELD = 0;

static set #privateStaticFieldValue(newValue) {
Cl.#PRIVATE_STATIC_FIELD = newValue;
}

static getPrivateStaticFieldValue() {
return Cl.#privateStaticFieldValue;
}
}

expect(Cl.getPrivateStaticFieldValue()).toBeUndefined();
@@ -0,0 +1,11 @@
class Cl {
static #PRIVATE_STATIC_FIELD = 0;

static set #privateStaticFieldValue(newValue) {
Cl.#PRIVATE_STATIC_FIELD = newValue;
}

static getPrivateStaticFieldValue() {
return Cl.#privateStaticFieldValue;
}
}
@@ -0,0 +1,18 @@
class Cl {
static getPrivateStaticFieldValue() {
return babelHelpers.classStaticPrivateFieldSpecGet(Cl, Cl, _privateStaticFieldValue);
}

}

var _set_privateStaticFieldValue = function (newValue) {
babelHelpers.classStaticPrivateFieldSpecSet(Cl, Cl, _PRIVATE_STATIC_FIELD, newValue);
};

var _PRIVATE_STATIC_FIELD = {
writable: true,
value: 0
};
var _privateStaticFieldValue = {
set: _set_privateStaticFieldValue
};
@@ -0,0 +1,14 @@
{
"plugins": [
[
"external-helpers",
{
"helperVersion": "7.1000.0"
}
],
"proposal-private-methods",
"proposal-class-properties",
"transform-block-scoping",
"syntax-class-properties"
]
}
@@ -0,0 +1,13 @@
class Cl {
static #PRIVATE_STATIC_FIELD = 0;

static get #privateStaticFieldValue() {
return Cl.#PRIVATE_STATIC_FIELD;
}

static setPrivateStaticFieldValue() {
Cl.#privateStaticFieldValue = 1;
}
}

expect(() => Cl.setPrivateStaticFieldValue()).toThrow(TypeError);
@@ -0,0 +1,11 @@
class Cl {
static #PRIVATE_STATIC_FIELD = 0;

static get #privateStaticFieldValue() {
return Cl.#PRIVATE_STATIC_FIELD;
}

static setPrivateStaticFieldValue() {
Cl.#privateStaticFieldValue = 1;
}
}
@@ -0,0 +1,18 @@
class Cl {
static setPrivateStaticFieldValue() {
babelHelpers.classStaticPrivateFieldSpecSet(Cl, Cl, _privateStaticFieldValue, 1);
}

}

var _get_privateStaticFieldValue = function () {
return babelHelpers.classStaticPrivateFieldSpecGet(Cl, Cl, _PRIVATE_STATIC_FIELD);
};

var _PRIVATE_STATIC_FIELD = {
writable: true,
value: 0
};
var _privateStaticFieldValue = {
get: _get_privateStaticFieldValue
};
@@ -0,0 +1,46 @@
class Cl {
static #privateField = "top secret string";
static publicField = "not secret string";

static get #privateFieldValue() {
return Cl.#privateField;
}

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

static publicGetPrivateField() {
return Cl.#privateFieldValue;
}

static publicSetPrivateField(newValue) {
Cl.#privateFieldValue = newValue;
}

static get publicFieldValue() {
return Cl.publicField;
}

static set publicFieldValue(newValue) {
Cl.publicField = newValue;
}

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

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

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

Cl.testUpdates();

0 comments on commit 1de0bb8

Please sign in to comment.