[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")`
|
helpers.classStaticPrivateFieldSpecGet = helper("7.0.1")`
|
||||||
export default function _classStaticPrivateFieldSpecGet(
|
export default function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) {
|
||||||
receiver, classConstructor, privateClass, privateId
|
|
||||||
) {
|
|
||||||
if (receiver !== classConstructor) {
|
if (receiver !== classConstructor) {
|
||||||
throw new TypeError("Private static access of wrong provenance");
|
throw new TypeError("Private static access of wrong provenance");
|
||||||
}
|
}
|
||||||
return privateClass[privateId];
|
return descriptor.value;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
helpers.classStaticPrivateFieldSpecSet = helper("7.0.1")`
|
helpers.classStaticPrivateFieldSpecSet = helper("7.0.1")`
|
||||||
export default function _classStaticPrivateFieldSpecSet(
|
export default function _classStaticPrivateFieldSpecSet(receiver, classConstructor, descriptor, value) {
|
||||||
receiver, classConstructor, privateClass, privateId, value
|
|
||||||
) {
|
|
||||||
if (receiver !== classConstructor) {
|
if (receiver !== classConstructor) {
|
||||||
throw new TypeError("Private static access of wrong provenance");
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -164,40 +164,27 @@ export default declare((api, options) => {
|
|||||||
...privateNameHandlerSpec,
|
...privateNameHandlerSpec,
|
||||||
|
|
||||||
get(member) {
|
get(member) {
|
||||||
const { file, name, privateClassId, classRef } = this;
|
const { file, privateId, classRef } = this;
|
||||||
|
|
||||||
return t.callExpression(
|
return t.callExpression(
|
||||||
file.addHelper("classStaticPrivateFieldSpecGet"),
|
file.addHelper("classStaticPrivateFieldSpecGet"),
|
||||||
[
|
[this.receiver(member), t.cloneNode(classRef), t.cloneNode(privateId)],
|
||||||
this.receiver(member),
|
|
||||||
classRef,
|
|
||||||
privateClassId,
|
|
||||||
t.stringLiteral(name),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
set(member, value) {
|
set(member, value) {
|
||||||
const { file, name, privateClassId, classRef } = this;
|
const { file, privateId, classRef } = this;
|
||||||
|
|
||||||
return t.callExpression(
|
return t.callExpression(
|
||||||
file.addHelper("classStaticPrivateFieldSpecSet"),
|
file.addHelper("classStaticPrivateFieldSpecSet"),
|
||||||
[
|
[
|
||||||
this.receiver(member),
|
this.receiver(member),
|
||||||
classRef,
|
t.cloneNode(classRef),
|
||||||
privateClassId,
|
t.cloneNode(privateId),
|
||||||
t.stringLiteral(name),
|
|
||||||
value,
|
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) {
|
function buildClassPropertySpec(ref, path, state) {
|
||||||
@ -308,50 +295,32 @@ export default declare((api, options) => {
|
|||||||
state,
|
state,
|
||||||
);
|
);
|
||||||
|
|
||||||
const staticNodesToAdd = [keyDecl, buildInit()];
|
return [keyDecl, buildInit()];
|
||||||
return [staticNodesToAdd];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildClassStaticPrivatePropertySpec(
|
function buildClassStaticPrivatePropertySpec(ref, path, state) {
|
||||||
ref,
|
const { parentPath, scope } = path;
|
||||||
path,
|
const { name } = path.node.key.id;
|
||||||
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,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const privateId = scope.generateUidIdentifier(name);
|
||||||
memberExpressionToFunctions(parentPath, privateNameVisitor, {
|
memberExpressionToFunctions(parentPath, privateNameVisitor, {
|
||||||
name,
|
name,
|
||||||
privateClassId,
|
privateId,
|
||||||
classRef: ref,
|
classRef: ref,
|
||||||
file: state,
|
file: state,
|
||||||
...staticPrivatePropertyHandlerSpec,
|
...staticPrivatePropertyHandlerSpec,
|
||||||
});
|
});
|
||||||
|
|
||||||
staticNodesToAdd.push(
|
return [
|
||||||
t.expressionStatement(
|
template.statement.ast`
|
||||||
t.callExpression(state.addHelper("defineProperty"), [
|
var ${privateId} = {
|
||||||
privateClassId,
|
// configurable is always false for private elements
|
||||||
t.stringLiteral(name),
|
// enumerable is always false for private elements
|
||||||
value || scope.buildUndefinedNode(),
|
writable: true,
|
||||||
]),
|
value: ${path.node.value || scope.buildUndefinedNode()}
|
||||||
),
|
}
|
||||||
);
|
`,
|
||||||
|
];
|
||||||
return [staticNodesToAdd, privateClassId];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildClassProperty = loose
|
const buildClassProperty = loose
|
||||||
@ -461,21 +430,16 @@ export default declare((api, options) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let p = 0;
|
let p = 0;
|
||||||
let privateClassId;
|
|
||||||
for (const prop of props) {
|
for (const prop of props) {
|
||||||
if (prop.node.static) {
|
if (prop.node.static) {
|
||||||
if (prop.isPrivate()) {
|
if (prop.isPrivate()) {
|
||||||
let staticNodesToAdd;
|
staticNodes.push(
|
||||||
[
|
...buildClassStaticPrivateProperty(
|
||||||
staticNodesToAdd,
|
t.cloneNode(ref),
|
||||||
privateClassId,
|
prop,
|
||||||
] = buildClassStaticPrivateProperty(
|
state,
|
||||||
t.cloneNode(ref),
|
),
|
||||||
prop,
|
|
||||||
state,
|
|
||||||
privateClassId,
|
|
||||||
);
|
);
|
||||||
staticNodes.push(...staticNodesToAdd);
|
|
||||||
} else {
|
} else {
|
||||||
staticNodes.push(
|
staticNodes.push(
|
||||||
buildClassProperty(t.cloneNode(ref), prop, state),
|
buildClassProperty(t.cloneNode(ref), prop, state),
|
||||||
|
|||||||
@ -7,7 +7,7 @@ class Foo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static test() {
|
static test() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "foo");
|
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
test() {
|
test() {
|
||||||
@ -16,8 +16,9 @@ class Foo {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _FooStatics = Object.create(null);
|
var _foo = {
|
||||||
|
writable: true,
|
||||||
babelHelpers.defineProperty(_FooStatics, "foo", "foo");
|
value: "foo"
|
||||||
|
};
|
||||||
|
|
||||||
var _bar = new WeakMap();
|
var _bar = new WeakMap();
|
||||||
|
|||||||
@ -1,20 +1,16 @@
|
|||||||
export default (param => {
|
export default (param => {
|
||||||
var _class, _temp;
|
var _class, _temp, _props;
|
||||||
|
|
||||||
return function () {
|
return _temp = _class = class App {
|
||||||
_temp = _class = class App {
|
getParam() {
|
||||||
getParam() {
|
return param;
|
||||||
return param;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
};
|
}, _props = {
|
||||||
|
writable: true,
|
||||||
var _classStatics = Object.create(null);
|
value: {
|
||||||
|
|
||||||
babelHelpers.defineProperty(_classStatics, "props", {
|
|
||||||
prop1: 'prop1',
|
prop1: 'prop1',
|
||||||
prop2: 'prop2'
|
prop2: 'prop2'
|
||||||
});
|
}
|
||||||
return _temp;
|
}, _temp;
|
||||||
}();
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,38 +1,32 @@
|
|||||||
function classFactory() {
|
function classFactory() {
|
||||||
var _class, _temp;
|
var _class, _temp, _foo, _bar;
|
||||||
|
|
||||||
return function () {
|
return _temp = _class = class Foo {
|
||||||
_temp = _class = class Foo {
|
constructor() {
|
||||||
constructor() {
|
_foo.set(this, {
|
||||||
_foo.set(this, {
|
writable: true,
|
||||||
writable: true,
|
value: "foo"
|
||||||
value: "foo"
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
instance() {
|
instance() {
|
||||||
return babelHelpers.classPrivateFieldGet(this, _foo);
|
return babelHelpers.classPrivateFieldGet(this, _foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
static() {
|
static() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, _class, _classStatics, "bar");
|
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, _class, _bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
static instance(inst) {
|
static instance(inst) {
|
||||||
return babelHelpers.classPrivateFieldGet(inst, _foo);
|
return babelHelpers.classPrivateFieldGet(inst, _foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
static static() {
|
static static() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, _class, _classStatics, "bar");
|
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, _class, _bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}, _foo = new WeakMap(), _bar = {
|
||||||
|
writable: true,
|
||||||
var _foo = new WeakMap();
|
value: "bar"
|
||||||
|
}, _temp;
|
||||||
var _classStatics = Object.create(null);
|
|
||||||
|
|
||||||
babelHelpers.defineProperty(_classStatics, "bar", "bar");
|
|
||||||
return _temp;
|
|
||||||
}();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
var _class, _temp;
|
var _class, _temp, _test;
|
||||||
|
|
||||||
call(function () {
|
call((_temp = _class = class {}, _test = {
|
||||||
_temp = _class = class {};
|
writable: true,
|
||||||
|
value: true
|
||||||
var _classStatics = Object.create(null);
|
}, _temp));
|
||||||
|
|
||||||
babelHelpers.defineProperty(_classStatics, "test", true);
|
|
||||||
return _temp;
|
|
||||||
}());
|
|
||||||
export default class _class2 {}
|
export default class _class2 {}
|
||||||
|
var _test2 = {
|
||||||
var _class2Statics = Object.create(null);
|
writable: true,
|
||||||
|
value: true
|
||||||
babelHelpers.defineProperty(_class2Statics, "test", true);
|
};
|
||||||
;
|
;
|
||||||
|
|||||||
@ -1,18 +1,14 @@
|
|||||||
function withContext(ComposedComponent) {
|
function withContext(ComposedComponent) {
|
||||||
var _class, _temp;
|
var _class, _temp, _propTypes;
|
||||||
|
|
||||||
return function () {
|
return _temp = _class = class WithContext extends Component {}, _propTypes = {
|
||||||
_temp = _class = class WithContext extends Component {};
|
writable: true,
|
||||||
|
value: {
|
||||||
var _classStatics = Object.create(null);
|
|
||||||
|
|
||||||
babelHelpers.defineProperty(_classStatics, "propTypes", {
|
|
||||||
context: PropTypes.shape({
|
context: PropTypes.shape({
|
||||||
addCss: PropTypes.func,
|
addCss: PropTypes.func,
|
||||||
setTitle: PropTypes.func,
|
setTitle: PropTypes.func,
|
||||||
setMeta: PropTypes.func
|
setMeta: PropTypes.func
|
||||||
})
|
})
|
||||||
});
|
}
|
||||||
return _temp;
|
}, _temp;
|
||||||
}();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,14 +10,15 @@ function () {
|
|||||||
babelHelpers.createClass(Foo, [{
|
babelHelpers.createClass(Foo, [{
|
||||||
key: "test",
|
key: "test",
|
||||||
value: function test(x) {
|
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;
|
return Foo;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
var _FooStatics = Object.create(null);
|
var _foo = {
|
||||||
|
writable: true,
|
||||||
babelHelpers.defineProperty(_FooStatics, "foo", function (x) {
|
value: function (x) {
|
||||||
return x;
|
return x;
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
export class MyClass {}
|
export class MyClass {}
|
||||||
|
var _property = {
|
||||||
var _MyClassStatics = Object.create(null);
|
writable: true,
|
||||||
|
value: value
|
||||||
babelHelpers.defineProperty(_MyClassStatics, "property", value);
|
};
|
||||||
export default class MyClass2 {}
|
export default class MyClass2 {}
|
||||||
|
var _property2 = {
|
||||||
var _MyClass2Statics = Object.create(null);
|
writable: true,
|
||||||
|
value: value
|
||||||
babelHelpers.defineProperty(_MyClass2Statics, "property", value);
|
};
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
var _class, _temp;
|
var _class, _temp, _num;
|
||||||
|
|
||||||
var Foo = function () {
|
var Foo = (_temp = _class = class Foo {}, _num = {
|
||||||
_temp = _class = class Foo {};
|
writable: true,
|
||||||
|
value: 0
|
||||||
var _classStatics = Object.create(null);
|
}, _temp);
|
||||||
|
|
||||||
babelHelpers.defineProperty(_classStatics, "num", 0);
|
|
||||||
return _temp;
|
|
||||||
}();
|
|
||||||
|
|||||||
@ -1,35 +1,37 @@
|
|||||||
class Base {
|
class Base {
|
||||||
static getThis() {
|
static getThis() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(this, Base, _BaseStatics, "foo");
|
return babelHelpers.classStaticPrivateFieldSpecGet(this, Base, _foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateThis(val) {
|
static updateThis(val) {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecSet(this, Base, _BaseStatics, "foo", val);
|
return babelHelpers.classStaticPrivateFieldSpecSet(this, Base, _foo, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getClass() {
|
static getClass() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(Base, Base, _BaseStatics, "foo");
|
return babelHelpers.classStaticPrivateFieldSpecGet(Base, Base, _foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateClass(val) {
|
static updateClass(val) {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecSet(Base, Base, _BaseStatics, "foo", val);
|
return babelHelpers.classStaticPrivateFieldSpecSet(Base, Base, _foo, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _BaseStatics = Object.create(null);
|
var _foo = {
|
||||||
|
writable: true,
|
||||||
babelHelpers.defineProperty(_BaseStatics, "foo", 1);
|
value: 1
|
||||||
|
};
|
||||||
|
|
||||||
class Sub1 extends Base {
|
class Sub1 extends Base {
|
||||||
static update(val) {
|
static update(val) {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecSet(this, Sub1, _Sub1Statics, "foo", val);
|
return babelHelpers.classStaticPrivateFieldSpecSet(this, Sub1, _foo2, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _Sub1Statics = Object.create(null);
|
var _foo2 = {
|
||||||
|
writable: true,
|
||||||
babelHelpers.defineProperty(_Sub1Statics, "foo", 2);
|
value: 2
|
||||||
|
};
|
||||||
|
|
||||||
class Sub2 extends Base {}
|
class Sub2 extends Base {}
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
class Foo {
|
class Foo {
|
||||||
static test() {
|
static test() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "bar");
|
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
test() {
|
test() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "bar");
|
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _FooStatics = Object.create(null);
|
var _bar = {
|
||||||
|
writable: true,
|
||||||
babelHelpers.defineProperty(_FooStatics, "bar", void 0);
|
value: void 0
|
||||||
|
};
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
class Foo {
|
class Foo {
|
||||||
static test() {
|
static test() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "bar");
|
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
test() {
|
test() {
|
||||||
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _FooStatics, "bar");
|
return babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _FooStatics = Object.create(null);
|
var _bar = {
|
||||||
|
writable: true,
|
||||||
babelHelpers.defineProperty(_FooStatics, "bar", "foo");
|
value: "foo"
|
||||||
|
};
|
||||||
expect("bar" in Foo).toBe(false);
|
expect("bar" in Foo).toBe(false);
|
||||||
expect(Foo.test()).toBe("foo");
|
expect(Foo.test()).toBe("foo");
|
||||||
expect(Foo.test()).toBe("foo");
|
expect(Foo.test()).toBe("foo");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user