[static private] Use explicit descriptors instead of an object (#8620)
This is similar to ec69b4bb1256c061ac76f53dfed09c4283ec6a31, which was about private instance fields. Private properties can be non-writable (thanks to decorators), or have get/set accessors. If we stored this information on the `privateClass` object, we would need to always use `Object.getOwnPropertyDescriptor` before reading or writing a property because accessors need to be called with the correct `this` context (it should be the actual class, not the object hat stores the private properties). This commit simplifies that operation a bit by removing the container object. It also have another advantage, which instance fields already have thanks to the use of separate weakmaps: unused private static fields can be tree-shaken away or garbage-collected, while properties of an object can't. Also, they can be easilier minified.
This commit is contained in:
parent
f6643d1804
commit
c5279eeca4
@ -1055,24 +1055,26 @@ helpers.classPrivateFieldSet = helper("7.0.0-beta.0")`
|
||||
`;
|
||||
|
||||
helpers.classStaticPrivateFieldSpecGet = helper("7.0.1")`
|
||||
export default function _classStaticPrivateFieldSpecGet(
|
||||
receiver, classConstructor, privateClass, privateId
|
||||
) {
|
||||
export default function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) {
|
||||
if (receiver !== classConstructor) {
|
||||
throw new TypeError("Private static access of wrong provenance");
|
||||
}
|
||||
return privateClass[privateId];
|
||||
return descriptor.value;
|
||||
}
|
||||
`;
|
||||
|
||||
helpers.classStaticPrivateFieldSpecSet = helper("7.0.1")`
|
||||
export default function _classStaticPrivateFieldSpecSet(
|
||||
receiver, classConstructor, privateClass, privateId, value
|
||||
) {
|
||||
export default function _classStaticPrivateFieldSpecSet(receiver, classConstructor, descriptor, value) {
|
||||
if (receiver !== classConstructor) {
|
||||
throw new TypeError("Private static access of wrong provenance");
|
||||
}
|
||||
privateClass[privateId] = value;
|
||||
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;
|
||||
}
|
||||
`;
|
||||
|
||||
@ -164,40 +164,27 @@ export default declare((api, options) => {
|
||||
...privateNameHandlerSpec,
|
||||
|
||||
get(member) {
|
||||
const { file, name, privateClassId, classRef } = this;
|
||||
const { file, privateId, classRef } = this;
|
||||
|
||||
return t.callExpression(
|
||||
file.addHelper("classStaticPrivateFieldSpecGet"),
|
||||
[
|
||||
this.receiver(member),
|
||||
classRef,
|
||||
privateClassId,
|
||||
t.stringLiteral(name),
|
||||
],
|
||||
[this.receiver(member), t.cloneNode(classRef), t.cloneNode(privateId)],
|
||||
);
|
||||
},
|
||||
|
||||
set(member, value) {
|
||||
const { file, name, privateClassId, classRef } = this;
|
||||
const { file, privateId, classRef } = this;
|
||||
|
||||
return t.callExpression(
|
||||
file.addHelper("classStaticPrivateFieldSpecSet"),
|
||||
[
|
||||
this.receiver(member),
|
||||
classRef,
|
||||
privateClassId,
|
||||
t.stringLiteral(name),
|
||||
t.cloneNode(classRef),
|
||||
t.cloneNode(privateId),
|
||||
value,
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
call(member, args) {
|
||||
// The first access (the get) should do the memo assignment.
|
||||
this.memoise(member, 1);
|
||||
|
||||
return optimiseCall(this.get(member), this.receiver(member), args);
|
||||
},
|
||||
};
|
||||
|
||||
function buildClassPropertySpec(ref, path, state) {
|
||||
@ -308,50 +295,32 @@ export default declare((api, options) => {
|
||||
state,
|
||||
);
|
||||
|
||||
const staticNodesToAdd = [keyDecl, buildInit()];
|
||||
return [staticNodesToAdd];
|
||||
return [keyDecl, buildInit()];
|
||||
}
|
||||
|
||||
function buildClassStaticPrivatePropertySpec(
|
||||
ref,
|
||||
path,
|
||||
state,
|
||||
privateClassId,
|
||||
) {
|
||||
const { scope, parentPath } = path;
|
||||
const { key, value } = path.node;
|
||||
const { name } = key.id;
|
||||
const staticNodesToAdd = [];
|
||||
|
||||
if (!privateClassId) {
|
||||
// Create a private static "host" object if it does not exist
|
||||
privateClassId = path.scope.generateUidIdentifier(ref.name + "Statics");
|
||||
staticNodesToAdd.push(
|
||||
template.statement`const PRIVATE_CLASS_ID = Object.create(null);`({
|
||||
PRIVATE_CLASS_ID: privateClassId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
function buildClassStaticPrivatePropertySpec(ref, path, state) {
|
||||
const { parentPath, scope } = path;
|
||||
const { name } = path.node.key.id;
|
||||
|
||||
const privateId = scope.generateUidIdentifier(name);
|
||||
memberExpressionToFunctions(parentPath, privateNameVisitor, {
|
||||
name,
|
||||
privateClassId,
|
||||
privateId,
|
||||
classRef: ref,
|
||||
file: state,
|
||||
...staticPrivatePropertyHandlerSpec,
|
||||
});
|
||||
|
||||
staticNodesToAdd.push(
|
||||
t.expressionStatement(
|
||||
t.callExpression(state.addHelper("defineProperty"), [
|
||||
privateClassId,
|
||||
t.stringLiteral(name),
|
||||
value || scope.buildUndefinedNode(),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
return [staticNodesToAdd, privateClassId];
|
||||
return [
|
||||
template.statement.ast`
|
||||
var ${privateId} = {
|
||||
// configurable is always false for private elements
|
||||
// enumerable is always false for private elements
|
||||
writable: true,
|
||||
value: ${path.node.value || scope.buildUndefinedNode()}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
const buildClassProperty = loose
|
||||
@ -461,21 +430,16 @@ export default declare((api, options) => {
|
||||
}
|
||||
}
|
||||
let p = 0;
|
||||
let privateClassId;
|
||||
for (const prop of props) {
|
||||
if (prop.node.static) {
|
||||
if (prop.isPrivate()) {
|
||||
let staticNodesToAdd;
|
||||
[
|
||||
staticNodesToAdd,
|
||||
privateClassId,
|
||||
] = buildClassStaticPrivateProperty(
|
||||
t.cloneNode(ref),
|
||||
prop,
|
||||
state,
|
||||
privateClassId,
|
||||
staticNodes.push(
|
||||
...buildClassStaticPrivateProperty(
|
||||
t.cloneNode(ref),
|
||||
prop,
|
||||
state,
|
||||
),
|
||||
);
|
||||
staticNodes.push(...staticNodesToAdd);
|
||||
} else {
|
||||
staticNodes.push(
|
||||
buildClassProperty(t.cloneNode(ref), prop, state),
|
||||
|
||||
@ -7,7 +7,7 @@ class Foo {
|
||||
}
|
||||
|
||||
static test() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "foo");
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _foo);
|
||||
}
|
||||
|
||||
test() {
|
||||
@ -16,8 +16,9 @@ class Foo {
|
||||
|
||||
}
|
||||
|
||||
var _FooStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_FooStatics, "foo", "foo");
|
||||
var _foo = {
|
||||
writable: true,
|
||||
value: "foo"
|
||||
};
|
||||
|
||||
var _bar = new WeakMap();
|
||||
|
||||
@ -1,20 +1,16 @@
|
||||
export default (param => {
|
||||
var _class, _temp;
|
||||
var _class, _temp, _props;
|
||||
|
||||
return function () {
|
||||
_temp = _class = class App {
|
||||
getParam() {
|
||||
return param;
|
||||
}
|
||||
return _temp = _class = class App {
|
||||
getParam() {
|
||||
return param;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var _classStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_classStatics, "props", {
|
||||
}, _props = {
|
||||
writable: true,
|
||||
value: {
|
||||
prop1: 'prop1',
|
||||
prop2: 'prop2'
|
||||
});
|
||||
return _temp;
|
||||
}();
|
||||
}
|
||||
}, _temp;
|
||||
});
|
||||
|
||||
@ -1,38 +1,32 @@
|
||||
function classFactory() {
|
||||
var _class, _temp;
|
||||
var _class, _temp, _foo, _bar;
|
||||
|
||||
return function () {
|
||||
_temp = _class = class Foo {
|
||||
constructor() {
|
||||
_foo.set(this, {
|
||||
writable: true,
|
||||
value: "foo"
|
||||
});
|
||||
}
|
||||
return _temp = _class = class Foo {
|
||||
constructor() {
|
||||
_foo.set(this, {
|
||||
writable: true,
|
||||
value: "foo"
|
||||
});
|
||||
}
|
||||
|
||||
instance() {
|
||||
return babelHelpers.classPrivateFieldGet(this, _foo);
|
||||
}
|
||||
instance() {
|
||||
return babelHelpers.classPrivateFieldGet(this, _foo);
|
||||
}
|
||||
|
||||
static() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, _class, _classStatics, "bar");
|
||||
}
|
||||
static() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, _class, _bar);
|
||||
}
|
||||
|
||||
static instance(inst) {
|
||||
return babelHelpers.classPrivateFieldGet(inst, _foo);
|
||||
}
|
||||
static instance(inst) {
|
||||
return babelHelpers.classPrivateFieldGet(inst, _foo);
|
||||
}
|
||||
|
||||
static static() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, _class, _classStatics, "bar");
|
||||
}
|
||||
static static() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, _class, _bar);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var _foo = new WeakMap();
|
||||
|
||||
var _classStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_classStatics, "bar", "bar");
|
||||
return _temp;
|
||||
}();
|
||||
}, _foo = new WeakMap(), _bar = {
|
||||
writable: true,
|
||||
value: "bar"
|
||||
}, _temp;
|
||||
}
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
var _class, _temp;
|
||||
var _class, _temp, _test;
|
||||
|
||||
call(function () {
|
||||
_temp = _class = class {};
|
||||
|
||||
var _classStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_classStatics, "test", true);
|
||||
return _temp;
|
||||
}());
|
||||
call((_temp = _class = class {}, _test = {
|
||||
writable: true,
|
||||
value: true
|
||||
}, _temp));
|
||||
export default class _class2 {}
|
||||
|
||||
var _class2Statics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_class2Statics, "test", true);
|
||||
var _test2 = {
|
||||
writable: true,
|
||||
value: true
|
||||
};
|
||||
;
|
||||
|
||||
@ -1,18 +1,14 @@
|
||||
function withContext(ComposedComponent) {
|
||||
var _class, _temp;
|
||||
var _class, _temp, _propTypes;
|
||||
|
||||
return function () {
|
||||
_temp = _class = class WithContext extends Component {};
|
||||
|
||||
var _classStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_classStatics, "propTypes", {
|
||||
return _temp = _class = class WithContext extends Component {}, _propTypes = {
|
||||
writable: true,
|
||||
value: {
|
||||
context: PropTypes.shape({
|
||||
addCss: PropTypes.func,
|
||||
setTitle: PropTypes.func,
|
||||
setMeta: PropTypes.func
|
||||
})
|
||||
});
|
||||
return _temp;
|
||||
}();
|
||||
}
|
||||
}, _temp;
|
||||
}
|
||||
|
||||
@ -10,14 +10,15 @@ function () {
|
||||
babelHelpers.createClass(Foo, [{
|
||||
key: "test",
|
||||
value: function test(x) {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "foo").call(Foo, x);
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _foo).call(Foo, x);
|
||||
}
|
||||
}]);
|
||||
return Foo;
|
||||
}();
|
||||
|
||||
var _FooStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_FooStatics, "foo", function (x) {
|
||||
return x;
|
||||
});
|
||||
var _foo = {
|
||||
writable: true,
|
||||
value: function (x) {
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
export class MyClass {}
|
||||
|
||||
var _MyClassStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_MyClassStatics, "property", value);
|
||||
var _property = {
|
||||
writable: true,
|
||||
value: value
|
||||
};
|
||||
export default class MyClass2 {}
|
||||
|
||||
var _MyClass2Statics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_MyClass2Statics, "property", value);
|
||||
var _property2 = {
|
||||
writable: true,
|
||||
value: value
|
||||
};
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
var _class, _temp;
|
||||
var _class, _temp, _num;
|
||||
|
||||
var Foo = function () {
|
||||
_temp = _class = class Foo {};
|
||||
|
||||
var _classStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_classStatics, "num", 0);
|
||||
return _temp;
|
||||
}();
|
||||
var Foo = (_temp = _class = class Foo {}, _num = {
|
||||
writable: true,
|
||||
value: 0
|
||||
}, _temp);
|
||||
|
||||
@ -1,35 +1,37 @@
|
||||
class Base {
|
||||
static getThis() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(this, Base, _BaseStatics, "foo");
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(this, Base, _foo);
|
||||
}
|
||||
|
||||
static updateThis(val) {
|
||||
return babelHelpers.classStaticPrivateFieldSpecSet(this, Base, _BaseStatics, "foo", val);
|
||||
return babelHelpers.classStaticPrivateFieldSpecSet(this, Base, _foo, val);
|
||||
}
|
||||
|
||||
static getClass() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Base, Base, _BaseStatics, "foo");
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Base, Base, _foo);
|
||||
}
|
||||
|
||||
static updateClass(val) {
|
||||
return babelHelpers.classStaticPrivateFieldSpecSet(Base, Base, _BaseStatics, "foo", val);
|
||||
return babelHelpers.classStaticPrivateFieldSpecSet(Base, Base, _foo, val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var _BaseStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_BaseStatics, "foo", 1);
|
||||
var _foo = {
|
||||
writable: true,
|
||||
value: 1
|
||||
};
|
||||
|
||||
class Sub1 extends Base {
|
||||
static update(val) {
|
||||
return babelHelpers.classStaticPrivateFieldSpecSet(this, Sub1, _Sub1Statics, "foo", val);
|
||||
return babelHelpers.classStaticPrivateFieldSpecSet(this, Sub1, _foo2, val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var _Sub1Statics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_Sub1Statics, "foo", 2);
|
||||
var _foo2 = {
|
||||
writable: true,
|
||||
value: 2
|
||||
};
|
||||
|
||||
class Sub2 extends Base {}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
class Foo {
|
||||
static test() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "bar");
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||
}
|
||||
|
||||
test() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "bar");
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var _FooStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_FooStatics, "bar", void 0);
|
||||
var _bar = {
|
||||
writable: true,
|
||||
value: void 0
|
||||
};
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
class Foo {
|
||||
static test() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "bar");
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||
}
|
||||
|
||||
test() {
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "bar");
|
||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var _FooStatics = Object.create(null);
|
||||
|
||||
babelHelpers.defineProperty(_FooStatics, "bar", "foo");
|
||||
var _bar = {
|
||||
writable: true,
|
||||
value: "foo"
|
||||
};
|
||||
expect("bar" in Foo).toBe(false);
|
||||
expect(Foo.test()).toBe("foo");
|
||||
expect(Foo.test()).toBe("foo");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user