Skip to content

Commit

Permalink
Implement the "decorator metadata" proposal (#15895)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Sep 25, 2023
1 parent 3744545 commit 672b881
Show file tree
Hide file tree
Showing 26 changed files with 188 additions and 71 deletions.
2 changes: 1 addition & 1 deletion packages/babel-helpers/src/helpers-generated.ts
Expand Up @@ -39,7 +39,7 @@ export default Object.freeze({
),
applyDecs2305: helper(
"7.21.0",
'import checkInRHS from"checkInRHS";function createAddInitializerMethod(e,t){return function(r){assertNotFinished(t,"addInitializer"),assertCallable(r,"An initializer"),e.push(r)}}function assertInstanceIfPrivate(e,t){if(!e(t))throw new TypeError("Attempted to access private element on non-instance")}function memberDec(e,t,r,n,a,i,s,o,c,l){var u;switch(i){case 1:u="accessor";break;case 2:u="method";break;case 3:u="getter";break;case 4:u="setter";break;default:u="field"}var f,d,p={kind:u,name:o?"#"+r:r,static:s,private:o},h={v:!1};if(0!==i&&(p.addInitializer=createAddInitializerMethod(a,h)),o||0!==i&&2!==i)if(2===i)f=function(e){return assertInstanceIfPrivate(l,e),n.value};else{var v=0===i||1===i;(v||3===i)&&(f=o?function(e){return assertInstanceIfPrivate(l,e),n.get.call(e)}:function(e){return n.get.call(e)}),(v||4===i)&&(d=o?function(e,t){assertInstanceIfPrivate(l,e),n.set.call(e,t)}:function(e,t){n.set.call(e,t)})}else f=function(e){return e[r]},0===i&&(d=function(e,t){e[r]=t});var y=o?l.bind():function(e){return r in e};p.access=f&&d?{get:f,set:d,has:y}:f?{get:f,has:y}:{set:d,has:y};try{return e.call(t,c,p)}finally{h.v=!0}}function assertNotFinished(e,t){if(e.v)throw new Error("attempted to call "+t+" after decoration was finished")}function assertCallable(e,t){if("function"!=typeof e)throw new TypeError(t+" must be a function")}function assertValidReturnValue(e,t){var r=typeof t;if(1===e){if("object"!==r||null===t)throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0");void 0!==t.get&&assertCallable(t.get,"accessor.get"),void 0!==t.set&&assertCallable(t.set,"accessor.set"),void 0!==t.init&&assertCallable(t.init,"accessor.init")}else if("function"!==r){var n;throw n=0===e?"field":5===e?"class":"method",new TypeError(n+" decorators must return a function or void 0")}}function curryThis1(e){return function(){return e(this)}}function curryThis2(e){return function(t){e(this,t)}}function applyMemberDec(e,t,r,n,a,i,s,o,c,l){var u,f,d,p,h,v,y=r[0];n||Array.isArray(y)||(y=[y]),o?u=0===i||1===i?{get:curryThis1(r[3]),set:curryThis2(r[4])}:3===i?{get:r[3]}:4===i?{set:r[3]}:{value:r[3]}:0!==i&&(u=Object.getOwnPropertyDescriptor(t,a)),1===i?d={get:u.get,set:u.set}:2===i?d=u.value:3===i?d=u.get:4===i&&(d=u.set);for(var g=n?2:1,m=y.length-1;m>=0;m-=g){var b;if(void 0!==(p=memberDec(y[m],n?y[m-1]:void 0,a,u,c,i,s,o,d,l)))assertValidReturnValue(i,p),0===i?b=p:1===i?(b=p.init,h=p.get||d.get,v=p.set||d.set,d={get:h,set:v}):d=p,void 0!==b&&(void 0===f?f=b:"function"==typeof f?f=[f,b]:f.push(b))}if(0===i||1===i){if(void 0===f)f=function(e,t){return t};else if("function"!=typeof f){var I=f;f=function(e,t){for(var r=t,n=I.length-1;n>=0;n--)r=I[n].call(e,r);return r}}else{var w=f;f=function(e,t){return w.call(e,t)}}e.push(f)}0!==i&&(1===i?(u.get=d.get,u.set=d.set):2===i?u.value=d:3===i?u.get=d:4===i&&(u.set=d),o?1===i?(e.push((function(e,t){return d.get.call(e,t)})),e.push((function(e,t){return d.set.call(e,t)}))):2===i?e.push(d):e.push((function(e,t){return d.call(e,t)})):Object.defineProperty(t,a,u))}function applyMemberDecs(e,t,r){for(var n,a,i,s=[],o=new Map,c=new Map,l=0;l<t.length;l++){var u=t[l];if(Array.isArray(u)){var f,d,p=u[1],h=u[2],v=u.length>3,y=16&p,g=!!(8&p),m=r;if(p&=7,g?(f=e,0!==p&&(d=a=a||[]),v&&!i&&(i=function(t){return checkInRHS(t)===e}),m=i):(f=e.prototype,0!==p&&(d=n=n||[])),0!==p&&!v){var b=g?c:o,I=b.get(h)||0;if(!0===I||3===I&&4!==p||4===I&&3!==p)throw new Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: "+h);b.set(h,!(!I&&p>2)||p)}applyMemberDec(s,f,u,y,h,p,g,v,d,m)}}return pushInitializers(s,n),pushInitializers(s,a),s}function pushInitializers(e,t){t&&e.push((function(e){for(var r=0;r<t.length;r++)t[r].call(e);return e}))}function applyClassDecs(e,t,r){if(t.length){for(var n=[],a=e,i=e.name,s=r?2:1,o=t.length-1;o>=0;o-=s){var c={v:!1};try{var l=t[o].call(r?t[o-1]:void 0,a,{kind:"class",name:i,addInitializer:createAddInitializerMethod(n,c)})}finally{c.v=!0}void 0!==l&&(assertValidReturnValue(5,l),a=l)}return[a,function(){for(var e=0;e<n.length;e++)n[e].call(a)}]}}export default function applyDecs2305(e,t,r,n,a){return{e:applyMemberDecs(e,t,a),get c(){return applyClassDecs(e,r,n)}}}',
'import checkInRHS from"checkInRHS";function createAddInitializerMethod(e,t){return function(r){assertNotFinished(t,"addInitializer"),assertCallable(r,"An initializer"),e.push(r)}}function assertInstanceIfPrivate(e,t){if(!e(t))throw new TypeError("Attempted to access private element on non-instance")}function memberDec(e,t,r,a,n,i,s,o,c,l,u){var f;switch(i){case 1:f="accessor";break;case 2:f="method";break;case 3:f="getter";break;case 4:f="setter";break;default:f="field"}var d,p,h={kind:f,name:o?"#"+r:r,static:s,private:o,metadata:u},v={v:!1};if(0!==i&&(h.addInitializer=createAddInitializerMethod(n,v)),o||0!==i&&2!==i)if(2===i)d=function(e){return assertInstanceIfPrivate(l,e),a.value};else{var y=0===i||1===i;(y||3===i)&&(d=o?function(e){return assertInstanceIfPrivate(l,e),a.get.call(e)}:function(e){return a.get.call(e)}),(y||4===i)&&(p=o?function(e,t){assertInstanceIfPrivate(l,e),a.set.call(e,t)}:function(e,t){a.set.call(e,t)})}else d=function(e){return e[r]},0===i&&(p=function(e,t){e[r]=t});var m=o?l.bind():function(e){return r in e};h.access=d&&p?{get:d,set:p,has:m}:d?{get:d,has:m}:{set:p,has:m};try{return e.call(t,c,h)}finally{v.v=!0}}function assertNotFinished(e,t){if(e.v)throw new Error("attempted to call "+t+" after decoration was finished")}function assertCallable(e,t){if("function"!=typeof e)throw new TypeError(t+" must be a function")}function assertValidReturnValue(e,t){var r=typeof t;if(1===e){if("object"!==r||null===t)throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0");void 0!==t.get&&assertCallable(t.get,"accessor.get"),void 0!==t.set&&assertCallable(t.set,"accessor.set"),void 0!==t.init&&assertCallable(t.init,"accessor.init")}else if("function"!==r){var a;throw a=0===e?"field":5===e?"class":"method",new TypeError(a+" decorators must return a function or void 0")}}function curryThis1(e){return function(){return e(this)}}function curryThis2(e){return function(t){e(this,t)}}function applyMemberDec(e,t,r,a,n,i,s,o,c,l,u){var f,d,p,h,v,y,m=r[0];a||Array.isArray(m)||(m=[m]),o?f=0===i||1===i?{get:curryThis1(r[3]),set:curryThis2(r[4])}:3===i?{get:r[3]}:4===i?{set:r[3]}:{value:r[3]}:0!==i&&(f=Object.getOwnPropertyDescriptor(t,n)),1===i?p={get:f.get,set:f.set}:2===i?p=f.value:3===i?p=f.get:4===i&&(p=f.set);for(var g=a?2:1,b=m.length-1;b>=0;b-=g){var I;if(void 0!==(h=memberDec(m[b],a?m[b-1]:void 0,n,f,c,i,s,o,p,l,u)))assertValidReturnValue(i,h),0===i?I=h:1===i?(I=h.init,v=h.get||p.get,y=h.set||p.set,p={get:v,set:y}):p=h,void 0!==I&&(void 0===d?d=I:"function"==typeof d?d=[d,I]:d.push(I))}if(0===i||1===i){if(void 0===d)d=function(e,t){return t};else if("function"!=typeof d){var w=d;d=function(e,t){for(var r=t,a=w.length-1;a>=0;a--)r=w[a].call(e,r);return r}}else{var M=d;d=function(e,t){return M.call(e,t)}}e.push(d)}0!==i&&(1===i?(f.get=p.get,f.set=p.set):2===i?f.value=p:3===i?f.get=p:4===i&&(f.set=p),o?1===i?(e.push((function(e,t){return p.get.call(e,t)})),e.push((function(e,t){return p.set.call(e,t)}))):2===i?e.push(p):e.push((function(e,t){return p.call(e,t)})):Object.defineProperty(t,n,f))}function applyMemberDecs(e,t,r,a){for(var n,i,s,o=[],c=new Map,l=new Map,u=0;u<t.length;u++){var f=t[u];if(Array.isArray(f)){var d,p,h=f[1],v=f[2],y=f.length>3,m=16&h,g=!!(8&h),b=r;if(h&=7,g?(d=e,0!==h&&(p=i=i||[]),y&&!s&&(s=function(t){return checkInRHS(t)===e}),b=s):(d=e.prototype,0!==h&&(p=n=n||[])),0!==h&&!y){var I=g?l:c,w=I.get(v)||0;if(!0===w||3===w&&4!==h||4===w&&3!==h)throw new Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: "+v);I.set(v,!(!w&&h>2)||h)}applyMemberDec(o,d,f,m,v,h,g,y,p,b,a)}}return pushInitializers(o,n),pushInitializers(o,i),o}function pushInitializers(e,t){t&&e.push((function(e){for(var r=0;r<t.length;r++)t[r].call(e);return e}))}function applyClassDecs(e,t,r,a){if(t.length){for(var n=[],i=e,s=e.name,o=r?2:1,c=t.length-1;c>=0;c-=o){var l={v:!1};try{var u=t[c].call(r?t[c-1]:void 0,i,{kind:"class",name:s,addInitializer:createAddInitializerMethod(n,l),metadata:a})}finally{l.v=!0}void 0!==u&&(assertValidReturnValue(5,u),i=u)}return[defineMetadata(i,a),function(){for(var e=0;e<n.length;e++)n[e].call(i)}]}}function defineMetadata(e,t){return Object.defineProperty(e,Symbol.metadata||Symbol.for("Symbol.metadata"),{configurable:!0,enumerable:!0,value:t})}export default function applyDecs2305(e,t,r,a,n,i){if(arguments.length>=6)var s=i[Symbol.metadata||Symbol.for("Symbol.metadata")];var o=Object.create(void 0===s?null:s),c=applyMemberDecs(e,t,n,o);return r.length||defineMetadata(e,o),{e:c,get c(){return applyClassDecs(e,r,a,o)}}}',
),
asyncGeneratorDelegate: helper(
"7.0.0-beta.0",
Expand Down
49 changes: 39 additions & 10 deletions packages/babel-helpers/src/helpers/applyDecs2305.js
Expand Up @@ -45,7 +45,8 @@ function memberDec(
isStatic,
isPrivate,
value,
hasPrivateBrand
hasPrivateBrand,
metadata
) {
var kindStr;

Expand All @@ -71,6 +72,7 @@ function memberDec(
name: isPrivate ? "#" + name : name,
static: isStatic,
private: isPrivate,
metadata: metadata,
};

var decoratorFinishedRef = { v: false };
Expand Down Expand Up @@ -211,7 +213,8 @@ function applyMemberDec(
isStatic,
isPrivate,
initializers,
hasPrivateBrand
hasPrivateBrand,
metadata
) {
var decs = decInfo[0];

Expand Down Expand Up @@ -276,7 +279,8 @@ function applyMemberDec(
isStatic,
isPrivate,
value,
hasPrivateBrand
hasPrivateBrand,
metadata
);

if (newValue !== void 0) {
Expand Down Expand Up @@ -369,7 +373,7 @@ function applyMemberDec(
}
}

function applyMemberDecs(Class, decInfos, instanceBrand) {
function applyMemberDecs(Class, decInfos, instanceBrand, metadata) {
var ret = [];
var protoInitializers;
var staticInitializers;
Expand Down Expand Up @@ -451,7 +455,8 @@ function applyMemberDecs(Class, decInfos, instanceBrand) {
isStatic,
isPrivate,
initializers,
hasPrivateBrand
hasPrivateBrand,
metadata
);
}

Expand All @@ -471,7 +476,7 @@ function pushInitializers(ret, initializers) {
}
}

function applyClassDecs(targetClass, classDecs, decoratorsHaveThis) {
function applyClassDecs(targetClass, classDecs, decoratorsHaveThis, metadata) {
if (classDecs.length) {
var initializers = [];
var newClass = targetClass;
Expand All @@ -493,6 +498,7 @@ function applyClassDecs(targetClass, classDecs, decoratorsHaveThis) {
initializers,
decoratorFinishedRef
),
metadata,
}
);
} finally {
Expand All @@ -506,7 +512,7 @@ function applyClassDecs(targetClass, classDecs, decoratorsHaveThis) {
}

return [
newClass,
defineMetadata(newClass, metadata),
function () {
for (var i = 0; i < initializers.length; i++) {
initializers[i].call(newClass);
Expand All @@ -518,6 +524,14 @@ function applyClassDecs(targetClass, classDecs, decoratorsHaveThis) {
// so we don't have to return an empty array here.
}

function defineMetadata(Class, metadata) {
return Object.defineProperty(
Class,
Symbol.metadata || Symbol.for("Symbol.metadata"),
{ configurable: true, enumerable: true, value: metadata }
);
}

/**
Basic usage:
Expand Down Expand Up @@ -669,13 +683,28 @@ export default function applyDecs2305(
memberDecs,
classDecs,
classDecsHaveThis,
instanceBrand
instanceBrand,
parentClass
) {
if (arguments.length >= 6) {
var parentMetadata =
parentClass[Symbol.metadata || Symbol.for("Symbol.metadata")];
}
var metadata = Object.create(
parentMetadata === void 0 ? null : parentMetadata
);
var e = applyMemberDecs(targetClass, memberDecs, instanceBrand, metadata);
if (!classDecs.length) defineMetadata(targetClass, metadata);
return {
e: applyMemberDecs(targetClass, memberDecs, instanceBrand),
e: e,
// Lazily apply class decorations so that member init locals can be properly bound.
get c() {
return applyClassDecs(targetClass, classDecs, classDecsHaveThis);
return applyClassDecs(
targetClass,
classDecs,
classDecsHaveThis,
metadata
);
},
};
}
Expand Up @@ -1069,6 +1069,15 @@ function transformClass(
);
}

let { superClass } = originalClass;
if (superClass && (process.env.BABEL_8_BREAKING || version === "2023-05")) {
const id = path.scope.maybeGenerateMemoised(superClass);
if (id) {
originalClass.superClass = t.assignmentExpression("=", id, superClass);
superClass = id;
}
}

originalClass.body.body.unshift(
t.staticBlock(
[
Expand All @@ -1080,6 +1089,7 @@ function transformClass(
t.arrayExpression(classDecorations),
t.numericLiteral(classDecorationsFlag),
needsInstancePrivateBrandCheck ? lastInstancePrivateName : null,
t.cloneNode(superClass),
state,
version,
),
Expand Down Expand Up @@ -1111,6 +1121,7 @@ function createLocalsAssignment(
classDecorations: t.ArrayExpression,
classDecorationsFlag: t.NumericLiteral,
maybePrivateBranName: t.PrivateName | null,
superClass: null | t.Expression,
state: PluginPass,
version: DecoratorVersionKind,
) {
Expand All @@ -1136,7 +1147,11 @@ function createLocalsAssignment(
}

if (process.env.BABEL_8_BREAKING || version === "2023-05") {
if (maybePrivateBranName || classDecorationsFlag.value !== 0) {
if (
maybePrivateBranName ||
superClass ||
classDecorationsFlag.value !== 0
) {
args.push(classDecorationsFlag);
}
if (maybePrivateBranName) {
Expand All @@ -1145,7 +1160,10 @@ function createLocalsAssignment(
_ => ${t.cloneNode(maybePrivateBranName)} in _
` as t.ArrowFunctionExpression,
);
} else if (superClass) {
args.push(t.unaryExpression("void", t.numericLiteral(0)));
}
if (superClass) args.push(superClass);
rhs = t.callExpression(state.addHelper("applyDecs2305"), args);
} else if (version === "2023-01") {
if (maybePrivateBranName) {
Expand Down
@@ -1,9 +1,9 @@
var _initClass;
var _initClass, _Bar;
const dec = () => {};
let _Foo;
class Foo extends Bar {
class Foo extends (_Bar = Bar) {
static {
[_Foo, _initClass] = babelHelpers.applyDecs2305(this, [], [dec]).c;
[_Foo, _initClass] = babelHelpers.applyDecs2305(this, [], [dec], 0, void 0, _Bar).c;
}
constructor() {
let foo = super();
Expand Down
@@ -1,10 +1,10 @@
var _call_x, _initProto;
var _call_x, _initProto, _Bar;
const dec = () => {};
class Foo extends Bar {
class Foo extends (_Bar = Bar) {
static {
[_call_x, _initProto] = babelHelpers.applyDecs2305(this, [[dec, 3, "x", function () {
return Bar.prototype.foo.call(this);
}]], [], 0, _ => #x in _).e;
}]], [], 0, _ => #x in _, _Bar).e;
}
constructor(...args) {
super(...args);
Expand Down
@@ -1,10 +1,10 @@
var _call_x, _initProto;
var _call_x, _initProto, _Bar;
const dec = () => {};
class Foo extends Bar {
class Foo extends (_Bar = Bar) {
static {
[_call_x, _initProto] = babelHelpers.applyDecs2305(this, [[dec, 2, "x", function () {
return Bar.prototype.foo.call(this);
}]], [], 0, _ => #x in _).e;
}]], [], 0, _ => #x in _, _Bar).e;
}
constructor(...args) {
super(...args);
Expand Down
@@ -1,4 +1,4 @@
var _initClass, _A, _temp, _initClass2, _C, _temp2, _initClass3, _D, _temp3, _initClass4, _decorated_class, _temp4, _class5, _initClass5, _G, _temp5, _initClass6, _decorated_class2, _temp6, _class7, _initClass7, _H, _temp7, _initClass8, _K, _temp8;
var _initClass, _A, _temp, _initClass2, _C, _temp2, _initClass3, _D, _temp3, _initClass4, _decorated_class, _temp4, _class5, _initClass5, _G, _temp5, _initClass6, _decorated_class2, _temp6, _class7, _initClass7, _H, _I, _temp7, _initClass8, _K, _L, _temp8;
const dec = () => {};
const A = (new (_temp = class extends babelHelpers.identity {
constructor() {
Expand Down Expand Up @@ -50,18 +50,18 @@ const H = (new (_temp7 = class extends babelHelpers.identity {
super(_H), (() => {})(), _initClass7();
}
}, (_class8 => {
class H extends I {}
class H extends (_I = I) {}
_class8 = H;
[_H, _initClass7] = babelHelpers.applyDecs2305(_class8, [], [dec]).c;
[_H, _initClass7] = babelHelpers.applyDecs2305(_class8, [], [dec], 0, void 0, _I).c;
})(), _temp7)(), _H);
const J = (new (_temp8 = class extends babelHelpers.identity {
constructor() {
super(_K), (() => {})(), _initClass8();
}
}, (_class9 => {
class K extends L {}
class K extends (_L = L) {}
_class9 = K;
[_K, _initClass8] = babelHelpers.applyDecs2305(_class9, [], [dec]).c;
[_K, _initClass8] = babelHelpers.applyDecs2305(_class9, [], [dec], 0, void 0, _L).c;
})(), _temp8)(), _K);
function classFactory() {
var _initClass9, _decorated_class3, _temp9, _class11;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -1,4 +1,4 @@
var _initClass, _class, _initClass2, _class2;
var _initClass, _class, _initClass2, _Bar2, _class2;
const dec1 = () => {};
const dec2 = () => {};
let _Bar;
Expand All @@ -7,7 +7,7 @@ _class = Bar;
[_Bar, _initClass] = babelHelpers.applyDecs2305(_class, [], [dec1]).c;
_initClass();
let _Foo;
class Foo extends _Bar {}
class Foo extends (_Bar2 = _Bar) {}
_class2 = Foo;
[_Foo, _initClass2] = babelHelpers.applyDecs2305(_class2, [], [dec2]).c;
[_Foo, _initClass2] = babelHelpers.applyDecs2305(_class2, [], [dec2], 0, void 0, _Bar2).c;
_initClass2();

0 comments on commit 672b881

Please sign in to comment.