Skip to content

Commit

Permalink
More aggressively inline decorators in the static block (#16294)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Feb 26, 2024
1 parent c5ab4f5 commit 774e467
Show file tree
Hide file tree
Showing 130 changed files with 446 additions and 300 deletions.
1 change: 0 additions & 1 deletion babel.config.js
Expand Up @@ -543,7 +543,6 @@ function pluginToggleBooleanFlag({ types: t }, { name, value }) {
if (left.value === false) return res.replace(right.replacement);
if (right.value === false) return res.replace(left.replacement);
if (left.unrelated && right.unrelated) return res.unrelated();
console.log(left, right);
return res.replace(
t.logicalExpression("||", left.replacement, right.replacement)
);
Expand Down
Expand Up @@ -751,6 +751,42 @@ function createPrivateBrandCheckClosure(brandName: t.PrivateName) {
);
}

// Check if the expression does not reference function-specific
// context or the given identifier name.
// `true` means "maybe" and `false` means "no".
function usesFunctionContext(expression: t.Node) {
try {
t.traverseFast(expression, node => {
if (
t.isThisExpression(node) ||
t.isSuper(node) ||
t.isIdentifier(node, { name: "arguments" }) ||
(t.isMetaProperty(node) && node.meta.name !== "import")
) {
// TODO: Add early return support to t.traverseFast
throw null;
}
});
return false;
} catch {
return true;
}
}

function usesPrivateField(expression: t.Node) {
try {
t.traverseFast(expression, node => {
if (t.isPrivateName(node)) {
// TODO: Add early return support to t.traverseFast
throw null;
}
});
return false;
} catch {
return true;
}
}

function checkPrivateMethodUpdateError(
path: NodePath<t.Class>,
decoratedPrivateMethods: Set<string>,
Expand Down Expand Up @@ -810,6 +846,8 @@ function transformClass(

const classDecorators = path.node.decorators;
let hasElementDecorators = false;
let hasComputedKeysSideEffects = false;
let elemDecsUseFnContext = false;

const generateClassPrivateUid = createLazyPrivateUidGeneratorForClass(path);

Expand Down Expand Up @@ -868,6 +906,8 @@ function transformClass(
break;
}
hasElementDecorators = true;
elemDecsUseFnContext ||=
element.node.decorators.some(usesFunctionContext);
} else if (element.node.type === "ClassAccessorProperty") {
// @ts-expect-error todo: propertyVisitor.ClassAccessorProperty should be callable. Improve typings.
propertyVisitor.ClassAccessorProperty(
Expand Down Expand Up @@ -896,6 +936,10 @@ function transformClass(
isStatic,
);
}

if ("computed" in element.node && element.node.computed) {
hasComputedKeysSideEffects ||= !scopeParent.isStatic(element.node.key);
}
}

if (!classDecorators && !hasElementDecorators) {
Expand All @@ -922,14 +966,16 @@ function transformClass(
// Memoise the this value `a.b` of decorator member expressions `@a.b.dec`,
type HandleDecoratorExpressionsResult = {
// whether the whole decorator list requires memoisation
needMemoise: boolean;
hasSideEffects: boolean;
usesFnContext: boolean;
// the this value of each decorator if applicable
decoratorsThis: (t.Expression | undefined)[];
};
function handleDecoratorExpressions(
expressions: t.Expression[],
): HandleDecoratorExpressionsResult {
let needMemoise = false;
let hasSideEffects = false;
let usesFnContext = false;
const decoratorsThis: (t.Expression | null)[] = [];
for (const expression of expressions) {
let object;
Expand All @@ -954,11 +1000,18 @@ function transformClass(
}
}
decoratorsThis.push(object);
needMemoise ||= !scopeParent.isStatic(expression);
hasSideEffects ||= !scopeParent.isStatic(expression);
usesFnContext ||= usesFunctionContext(expression);
}
return { needMemoise, decoratorsThis };
return { hasSideEffects, usesFnContext, decoratorsThis };
}

const willExtractSomeElemDecs =
hasComputedKeysSideEffects ||
(process.env.BABEL_8_BREAKING
? elemDecsUseFnContext
: elemDecsUseFnContext || version !== "2023-11");

let needsDeclaraionForClassBinding = false;
let classDecorationsFlag = 0;
let classDecorations: t.Expression[] = [];
Expand All @@ -971,7 +1024,8 @@ function transformClass(
path.node.decorators = null;

const decoratorExpressions = classDecorators.map(el => el.expression);
const { needMemoise, decoratorsThis } =
const classDecsUsePrivateName = decoratorExpressions.some(usesPrivateField);
const { hasSideEffects, decoratorsThis } =
handleDecoratorExpressions(decoratorExpressions);

const { haveThis, decs } = generateDecorationList(
Expand All @@ -982,7 +1036,10 @@ function transformClass(
classDecorationsFlag = haveThis ? 1 : 0;
classDecorations = decs;

if (needMemoise) {
if (
(hasSideEffects && willExtractSomeElemDecs) ||
classDecsUsePrivateName
) {
classDecorationsId = memoiseExpression(
t.arrayExpression(classDecorations),
"classDecs",
Expand Down Expand Up @@ -1042,7 +1099,7 @@ function transformClass(

if (hasDecorators) {
const decoratorExpressions = decorators.map(d => d.expression);
const { needMemoise, decoratorsThis } =
const { hasSideEffects, usesFnContext, decoratorsThis } =
handleDecoratorExpressions(decoratorExpressions);
const { decs, haveThis } = generateDecorationList(
decoratorExpressions,
Expand All @@ -1051,7 +1108,7 @@ function transformClass(
);
decoratorsHaveThis = haveThis;
decoratorsArray = decs.length === 1 ? decs[0] : t.arrayExpression(decs);
if (needMemoise) {
if (usesFnContext || (hasSideEffects && willExtractSomeElemDecs)) {
decoratorsArray = memoiseExpression(decoratorsArray, name + "Decs");
}
}
Expand Down Expand Up @@ -1311,7 +1368,9 @@ function transformClass(
toSortedDecoratorInfo(elementDecoratorInfo);

const elementDecorations = generateDecorationExprs(
sortedElementDecoratorInfo,
process.env.BABEL_8_BREAKING || version === "2023-11"
? elementDecoratorInfo
: sortedElementDecoratorInfo,
version,
);

Expand Down Expand Up @@ -1500,11 +1559,14 @@ function createLocalsAssignment(
setClassName
? createSetFunctionNameCall(state, setClassName)
: t.thisExpression(),
elementDecorations,
classDecorations,
elementDecorations,
];

if (!process.env.BABEL_8_BREAKING) {
if (version !== "2023-11") {
args.splice(1, 2, elementDecorations, classDecorations);
}
if (
version === "2021-12" ||
(version === "2022-03" && !state.availableHelper("applyDecs2203R"))
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-helpers/src/helpers-generated.ts
Expand Up @@ -48,10 +48,10 @@ export default Object.freeze({
"7.21.0",
'import checkInRHS from"checkInRHS";import setFunctionName from"setFunctionName";import toPropertyKey from"toPropertyKey";export default function applyDecs2305(e,t,r,n,o,a){function i(e,t,r){return function(n,o){return r&&r(n),e[t].call(n,o)}}function c(e,t){for(var r=0;r<e.length;r++)e[r].call(t);return t}function s(e,t,r,n){if("function"!=typeof e&&(n||void 0!==e))throw new TypeError(t+" must "+(r||"be")+" a function"+(n?"":" or undefined"));return e}function applyDec(e,t,r,n,o,a,c,u,l,f,p,d,h){function m(e){if(!h(e))throw new TypeError("Attempted to access private element on non-instance")}var y,v=t[0],g=t[3],b=!u;if(!b){r||Array.isArray(v)||(v=[v]);var w={},S=[],A=3===o?"get":4===o||d?"set":"value";f?(p||d?w={get:setFunctionName((function(){return g(this)}),n,"get"),set:function(e){t[4](this,e)}}:w[A]=g,p||setFunctionName(w[A],n,2===o?"":A)):p||(w=Object.getOwnPropertyDescriptor(e,n))}for(var P=e,j=v.length-1;j>=0;j-=r?2:1){var D=v[j],E=r?v[j-1]:void 0,I={},O={kind:["field","accessor","method","getter","setter","class"][o],name:n,metadata:a,addInitializer:function(e,t){if(e.v)throw new Error("attempted to call addInitializer after decoration was finished");s(t,"An initializer","be",!0),c.push(t)}.bind(null,I)};try{if(b)(y=s(D.call(E,P,O),"class decorators","return"))&&(P=y);else{var k,F;O.static=l,O.private=f,f?2===o?k=function(e){return m(e),w.value}:(o<4&&(k=i(w,"get",m)),3!==o&&(F=i(w,"set",m))):(k=function(e){return e[n]},(o<2||4===o)&&(F=function(e,t){e[n]=t}));var N=O.access={has:f?h.bind():function(e){return n in e}};if(k&&(N.get=k),F&&(N.set=F),P=D.call(E,d?{get:w.get,set:w.set}:w[A],O),d){if("object"==typeof P&&P)(y=s(P.get,"accessor.get"))&&(w.get=y),(y=s(P.set,"accessor.set"))&&(w.set=y),(y=s(P.init,"accessor.init"))&&S.push(y);else if(void 0!==P)throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0")}else s(P,(p?"field":"method")+" decorators","return")&&(p?S.push(P):w[A]=P)}}finally{I.v=!0}}return(p||d)&&u.push((function(e,t){for(var r=S.length-1;r>=0;r--)t=S[r].call(e,t);return t})),p||b||(f?d?u.push(i(w,"get"),i(w,"set")):u.push(2===o?w[A]:i.call.bind(w[A])):Object.defineProperty(e,n,w)),P}function u(e,t){return Object.defineProperty(e,Symbol.metadata||Symbol.for("Symbol.metadata"),{configurable:!0,enumerable:!0,value:t})}if(arguments.length>=6)var l=a[Symbol.metadata||Symbol.for("Symbol.metadata")];var f=Object.create(null==l?null:l),p=function(e,t,r,n){var o,a,i=[],s=function(t){return checkInRHS(t)===e},u=new Map;function l(e){e&&i.push(c.bind(null,e))}for(var f=0;f<t.length;f++){var p=t[f];if(Array.isArray(p)){var d=p[1],h=p[2],m=p.length>3,y=16&d,v=!!(8&d),g=0==(d&=7),b=h+"/"+v;if(!g&&!m){var w=u.get(b);if(!0===w||3===w&&4!==d||4===w&&3!==d)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);u.set(b,!(d>2)||d)}applyDec(v?e:e.prototype,p,y,m?"#"+h:toPropertyKey(h),d,n,v?a=a||[]:o=o||[],i,v,m,g,1===d,v&&m?s:r)}}return l(o),l(a),i}(e,t,o,f);return r.length||u(e,f),{e:p,get c(){var t=[];return r.length&&[u(applyDec(e,[r],n,e.name,5,f,t),f),c.bind(null,t,e)]}}}',
),
// size: 2917, gzip size: 1487
// size: 2951, gzip size: 1503
applyDecs2311: helper(
"7.23.0",
'import checkInRHS from"checkInRHS";import setFunctionName from"setFunctionName";import toPropertyKey from"toPropertyKey";export default function applyDecs2311(e,t,n,r,o,i){var a,c,u=Symbol.metadata||Symbol.for("Symbol.metadata"),s=Object.defineProperty,f=Object.create,l=[f(null),f(null)],p=n.length;function d(t,n,r){return function(o,i){n&&(i=o,o=e);for(var a=0;a<t.length;a++)i=t[a].apply(o,r?[i]:[]);return r?i:o}}function m(e,t,n,r){if("function"!=typeof e&&(r||void 0!==e))throw new TypeError(t+" must "+(n||"be")+" a function"+(r?"":" or undefined"));return e}function applyDec(e,t,n,r,o,i,u,f,p,h,y,v){function g(e){if(!v(e))throw new TypeError("Attempted to access private element on non-instance")}var b=[].concat(t[0]),w=t[3],D=!u,S=3===o,j=4===o,E=2===o;function I(t,n,r){return function(o,i){return n&&(i=o,o=e),r&&r(o),P[t].call(o,i)}}if(!D){var P={},k=[],F=S?"get":j||y?"set":"value";if(p?(h||y?P={get:setFunctionName((function(){return w(this)}),r,"get"),set:function(e){t[4](this,e)}}:P[F]=w,h||setFunctionName(P[F],r,E?"":F)):h||(P=Object.getOwnPropertyDescriptor(e,r)),!h&&!p){if((c=l[+f][r])&&7!=(c^o))throw new Error("Decorating two elements with the same name ("+P[F].name+") is not supported yet");l[+f][r]=o<3?1:o}}for(var N=e,O=b.length-1;O>=0;O-=n?2:1){var z=b[O],H=n?b[O-1]:void 0,K={},R={kind:["field","accessor","method","getter","setter","class"][o],name:r,metadata:a,addInitializer:function(e,t){if(e.v)throw new Error("attempted to call addInitializer after decoration was finished");m(t,"An initializer","be",!0),i.push(t)}.bind(null,K)};if(D)c=z.call(H,N,R),K.v=1,m(c,"class decorators","return")&&(N=c);else if(R.static=f,R.private=p,c=R.access={has:p?v.bind():function(e){return r in e}},j||(c.get=p?E?function(e){return g(e),P.value}:I("get",0,g):function(e){return e[r]}),E||S||(c.set=p?I("set",0,g):function(e,t){e[r]=t}),N=z.call(H,y?{get:P.get,set:P.set}:P[F],R),K.v=1,y){if("object"==typeof N&&N)(c=m(N.get,"accessor.get"))&&(P.get=c),(c=m(N.set,"accessor.set"))&&(P.set=c),(c=m(N.init,"accessor.init"))&&k.unshift(c);else if(void 0!==N)throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined")}else m(N,(h?"field":"method")+" decorators","return")&&(h?k.unshift(N):P[F]=N)}return o<2&&u.push(d(k,f,1),d(i,f,0)),h||D||(p?y?u.splice(-1,0,I("get",f),I("set",f)):u.push(E?P[F]:m.call.bind(P[F])):s(e,r,P)),N}function h(e){return s(e,u,{configurable:!0,enumerable:!0,value:a})}return void 0!==i&&(a=i[u]),a=f(null==a?null:a),c=function(){var n,r,i=[];function a(e){e&&i.push(d(e))}for(var c=0;c<t.length;c++){var u=t[c],s=u[1],f=u[2],l=!!u[3],p=16&s,m=!!(8&s),h=0==(s&=7),y=1===s;applyDec(m?e:e.prototype,u,p,l?"#"+f:toPropertyKey(f),s,h||y?[]:m?r=r||[]:n=n||[],i,m,l,h,y,m&&l?function(t){return checkInRHS(t)===e}:o)}return a(n),a(r),i}(),p||h(e),{e:c,get c(){var t=[];return p&&[h(e=applyDec(e,[n],r,e.name,5,t)),d(t,1)]}}}',
'import checkInRHS from"checkInRHS";import setFunctionName from"setFunctionName";import toPropertyKey from"toPropertyKey";export default function applyDecs2311(e,t,n,r,o,i){var a,c,u,s,f,l,p,d=Symbol.metadata||Symbol.for("Symbol.metadata"),m=Object.defineProperty,h=Object.create,y=[h(null),h(null)],v=t.length;function g(t,n,r){return function(o,i){n&&(i=o,o=e);for(var a=0;a<t.length;a++)i=t[a].apply(o,r?[i]:[]);return r?i:o}}function b(e,t,n,r){if("function"!=typeof e&&(r||void 0!==e))throw new TypeError(t+" must "+(n||"be")+" a function"+(r?"":" or undefined"));return e}function applyDec(e,t,n,r,o,i,u,s,f,l,p){function d(e){if(!p(e))throw new TypeError("Attempted to access private element on non-instance")}var h=[].concat(t[0]),v=t[3],w=!u,D=1===o,S=3===o,j=4===o,E=2===o;function I(t,n,r){return function(o,i){return n&&(i=o,o=e),r&&r(o),P[t].call(o,i)}}if(!w){var P={},k=[],F=S?"get":j||D?"set":"value";if(f?(l||D?P={get:setFunctionName((function(){return v(this)}),r,"get"),set:function(e){t[4](this,e)}}:P[F]=v,l||setFunctionName(P[F],r,E?"":F)):l||(P=Object.getOwnPropertyDescriptor(e,r)),!l&&!f){if((c=y[+s][r])&&7!=(c^o))throw new Error("Decorating two elements with the same name ("+P[F].name+") is not supported yet");y[+s][r]=o<3?1:o}}for(var N=e,O=h.length-1;O>=0;O-=n?2:1){var z=h[O],H=n?h[O-1]:void 0,K={},R={kind:["field","accessor","method","getter","setter","class"][o],name:r,metadata:a,addInitializer:function(e,t){if(e.v)throw new Error("attempted to call addInitializer after decoration was finished");b(t,"An initializer","be",!0),i.push(t)}.bind(null,K)};if(w)c=z.call(H,N,R),K.v=1,b(c,"class decorators","return")&&(N=c);else if(R.static=s,R.private=f,c=R.access={has:f?p.bind():function(e){return r in e}},j||(c.get=f?E?function(e){return d(e),P.value}:I("get",0,d):function(e){return e[r]}),E||S||(c.set=f?I("set",0,d):function(e,t){e[r]=t}),N=z.call(H,D?{get:P.get,set:P.set}:P[F],R),K.v=1,D){if("object"==typeof N&&N)(c=b(N.get,"accessor.get"))&&(P.get=c),(c=b(N.set,"accessor.set"))&&(P.set=c),(c=b(N.init,"accessor.init"))&&k.unshift(c);else if(void 0!==N)throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined")}else b(N,(l?"field":"method")+" decorators","return")&&(l?k.unshift(N):P[F]=N)}return o<2&&u.push(g(k,s,1),g(i,s,0)),l||w||(f?D?u.splice(-1,0,I("get",s),I("set",s)):u.push(E?P[F]:b.call.bind(P[F])):m(e,r,P)),N}function w(e){return m(e,d,{configurable:!0,enumerable:!0,value:a})}return void 0!==i&&(a=i[d]),a=h(null==a?null:a),f=[],l=function(e){e&&f.push(g(e))},p=function(t,r){for(var i=0;i<n.length;i++){var a=n[i],c=a[1],l=7&c;if((8&c)==t&&!l==r){var p=a[2],d=!!a[3],m=16&c;applyDec(t?e:e.prototype,a,m,d?"#"+p:toPropertyKey(p),l,l<2?[]:t?s=s||[]:u=u||[],f,!!t,d,r,t&&d?function(t){return checkInRHS(t)===e}:o)}}},p(8,0),p(0,0),p(8,1),p(0,1),l(u),l(s),c=f,v||w(e),{e:c,get c(){var n=[];return v&&[w(e=applyDec(e,[t],r,e.name,5,n)),g(n,1)]}}}',
),
// size: 187, gzip size: 167
assertClassBrand: helper(
Expand Down

0 comments on commit 774e467

Please sign in to comment.