From 5c81253121701244f059b34cf632b0e475b4ddaa Mon Sep 17 00:00:00 2001 From: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> Date: Thu, 4 Jan 2024 02:50:58 +0800 Subject: [PATCH] fix: Class decorator correctly passes return value (#16199) * fix * review --- .../babel-helpers/src/helpers-generated.ts | 4 +-- .../src/helpers/applyDecs2305.ts | 34 +++++++++++-------- .../decorator-access-modified-classes/exec.js | 23 +++++++++++++ .../decorator-access-modified-classes/exec.js | 23 +++++++++++++ .../options.json | 0 5 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes--to-es2015/decorator-access-modified-classes/exec.js create mode 100644 packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes/decorator-access-modified-classes/exec.js rename packages/babel-plugin-proposal-decorators/test/fixtures/{2023-05-classes--to-es2015/ctx => 2023-05-classes/decorator-access-modified-classes}/options.json (100%) diff --git a/packages/babel-helpers/src/helpers-generated.ts b/packages/babel-helpers/src/helpers-generated.ts index 9e0a0fd34e7d..30aac461668c 100644 --- a/packages/babel-helpers/src/helpers-generated.ts +++ b/packages/babel-helpers/src/helpers-generated.ts @@ -43,10 +43,10 @@ export default Object.freeze({ "7.21.0", 'import checkInRHS from"checkInRHS";import setFunctionName from"setFunctionName";import toPropertyKey from"toPropertyKey";function applyDecs2301Factory(){function createAddInitializerMethod(e,t){return function(r){!function(e,t){if(e.v)throw new Error("attempted to call addInitializer after decoration was finished")}(t),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){var u;switch(a){case 1:u="accessor";break;case 2:u="method";break;case 3:u="getter";break;case 4:u="setter";break;default:u="field"}var l,f,p={kind:u,name:s?"#"+t:toPropertyKey(t),static:i,private:s},d={v:!1};if(0!==a&&(p.addInitializer=createAddInitializerMethod(n,d)),s||0!==a&&2!==a)if(2===a)l=function(e){return assertInstanceIfPrivate(c,e),r.value};else{var h=0===a||1===a;(h||3===a)&&(l=s?function(e){return assertInstanceIfPrivate(c,e),r.get.call(e)}:function(e){return r.get.call(e)}),(h||4===a)&&(f=s?function(e,t){assertInstanceIfPrivate(c,e),r.set.call(e,t)}:function(e,t){r.set.call(e,t)})}else l=function(e){return e[t]},0===a&&(f=function(e,r){e[t]=r});var v=s?c.bind():function(e){return t in e};p.access=l&&f?{get:l,set:f,has:v}:l?{get:l,has:v}:{set:f,has:v};try{return e(o,p)}finally{d.v=!0}}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)throw new TypeError((0===e?"field":10===e?"class":"method")+" decorators must return a function or void 0")}function curryThis2(e){return function(t){e(this,t)}}function applyMemberDec(e,t,r,n,a,i,s,o,c){var u,l,f,p,d,h,v,y,g=r[0];if(s?(0===a||1===a?(u={get:(d=r[3],function(){return d(this)}),set:curryThis2(r[4])},f="get"):3===a?(u={get:r[3]},f="get"):4===a?(u={set:r[3]},f="set"):u={value:r[3]},0!==a&&(1===a&&setFunctionName(u.set,"#"+n,"set"),setFunctionName(u[f||"value"],"#"+n,f))):0!==a&&(u=Object.getOwnPropertyDescriptor(t,n)),1===a?p={get:u.get,set:u.set}:2===a?p=u.value:3===a?p=u.get:4===a&&(p=u.set),"function"==typeof g)void 0!==(h=memberDec(g,n,u,o,a,i,s,p,c))&&(assertValidReturnValue(a,h),0===a?l=h:1===a?(l=h.init,v=h.get||p.get,y=h.set||p.set,p={get:v,set:y}):p=h);else for(var m=g.length-1;m>=0;m--){var b;void 0!==(h=memberDec(g[m],n,u,o,a,i,s,p,c))&&(assertValidReturnValue(a,h),0===a?b=h:1===a?(b=h.init,v=h.get||p.get,y=h.set||p.set,p={get:v,set:y}):p=h,void 0!==b&&(void 0===l?l=b:"function"==typeof l?l=[l,b]:l.push(b)))}if(0===a||1===a){if(void 0===l)l=function(e,t){return t};else if("function"!=typeof l){var I=l;l=function(e,t){for(var r=t,n=0;n3,y=d>=5,g=r;if(y?(f=e,0!=(d-=5)&&(p=a=a||[]),v&&!i&&(i=function(t){return checkInRHS(t)===e}),g=i):(f=e.prototype,0!==d&&(p=n=n||[])),0!==d&&!v){var m=y?c:o,b=m.get(h)||0;if(!0===b||3===b&&4!==d||4===b&&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);!b&&d>2?m.set(h,d):m.set(h,!0)}applyMemberDec(s,f,l,h,d,y,v,p,g)}}return pushInitializers(s,n),pushInitializers(s,a),s}function pushInitializers(e,t){t&&e.push((function(e){for(var r=0;r0){for(var r=[],n=e,a=e.name,i=t.length-1;i>=0;i--){var s={v:!1};try{var o=t[i](n,{kind:"class",name:a,addInitializer:createAddInitializerMethod(r,s)})}finally{s.v=!0}void 0!==o&&(assertValidReturnValue(10,o),n=o)}return[n,function(){for(var e=0;e=0;j-=r?2:1){var D=g[j],E=r?g[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(w)v=D.call(E,e,O);else{var k,F;O.static=l,O.private=f,f||!p&&2!==o?2===o?k=function(e){return m(e),S.value}:((o<2||3===o)&&(k=i(S,"get",f&&m)),(o<2||4===o)&&(F=i(S,"set",f&&m))):(k=function(e){return e[n]},p&&(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),v=D.call(E,d?{get:S.get,set:S.set}:S[P],O),d){if("object"==typeof v&&v)(y=s(v.get,"accessor.get"))&&(S.get=y),(y=s(v.set,"accessor.set"))&&(S.set=y),(y=s(v.init,"accessor.init"))&&A.push(y);else if(void 0!==v)throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0")}else s(v,(p?"field":"method")+" decorators","return")&&(p?A.push(v):S[P]=v)}}finally{I.v=!0}}return(p||d)&&u.push((function(e,t){for(var r=A.length-1;r>=0;r--)t=A[r].call(e,t);return t})),p||w||(f?d?u.push(i(S,"get"),i(S,"set")):u.push(2===o?S[P]:i.call.bind(S[P])):Object.defineProperty(e,n,S)),v}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;f3,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(s(applyDec(e,[r],n,e.name,5,f,t),"class decorators","return")||e,f),c.bind(null,t,e)]}}}', + '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=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||!p&&2!==o?2===o?k=function(e){return m(e),w.value}:((o<2||3===o)&&(k=i(w,"get",f&&m)),(o<2||4===o)&&(F=i(w,"set",f&&m))):(k=function(e){return e[n]},p&&(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;f3,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: 544, gzip size: 300 asyncGeneratorDelegate: helper( diff --git a/packages/babel-helpers/src/helpers/applyDecs2305.ts b/packages/babel-helpers/src/helpers/applyDecs2305.ts index 25b8a2b352c3..d7d1074e1716 100644 --- a/packages/babel-helpers/src/helpers/applyDecs2305.ts +++ b/packages/babel-helpers/src/helpers/applyDecs2305.ts @@ -306,7 +306,7 @@ export default /* @no-mangle */ function applyDecs2305( } } - var newValue; + var newValue = Class; for (var i = decs.length - 1; i >= 0; i -= decoratorsHaveThis ? 2 : 1) { var dec = (decs as Function[])[i], @@ -336,7 +336,15 @@ export default /* @no-mangle */ function applyDecs2305( try { if (isClass) { - newValue = dec.call(decThis, Class, ctx); + if ( + (_ = assertCallable( + dec.call(decThis, newValue, ctx), + "class decorators", + "return", + )) + ) { + newValue = _; + } } else { ctx.static = isStatic; ctx.private = isPrivate; @@ -563,19 +571,15 @@ export default /* @no-mangle */ function applyDecs2305( return ( classDecs.length && [ defineMetadata( - assertCallable( - applyDec( - targetClass, - [classDecs], - classDecsHaveThis, - targetClass.name, - PROP_KIND.CLASS, - metadata, - initializers, - ), - "class decorators", - "return", - ) || targetClass, + applyDec( + targetClass, + [classDecs], + classDecsHaveThis, + targetClass.name, + PROP_KIND.CLASS, + metadata, + initializers, + ), metadata, ), runInitializers.bind(null, initializers, targetClass), diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes--to-es2015/decorator-access-modified-classes/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes--to-es2015/decorator-access-modified-classes/exec.js new file mode 100644 index 000000000000..2bfa992f5a3a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes--to-es2015/decorator-access-modified-classes/exec.js @@ -0,0 +1,23 @@ +function decorateA(target) { + return class extends target { + a() { + return "a"; + } + }; +} + +function decorateB(target) { + return class extends target { + b() { + return "b"; + } + }; +} + +@decorateB +@decorateA +class Target {} + +const target = new Target(); +expect(target.a()).toBe("a"); +expect(target.b()).toBe("b"); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes/decorator-access-modified-classes/exec.js b/packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes/decorator-access-modified-classes/exec.js new file mode 100644 index 000000000000..2bfa992f5a3a --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes/decorator-access-modified-classes/exec.js @@ -0,0 +1,23 @@ +function decorateA(target) { + return class extends target { + a() { + return "a"; + } + }; +} + +function decorateB(target) { + return class extends target { + b() { + return "b"; + } + }; +} + +@decorateB +@decorateA +class Target {} + +const target = new Target(); +expect(target.a()).toBe("a"); +expect(target.b()).toBe("b"); diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes--to-es2015/ctx/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes/decorator-access-modified-classes/options.json similarity index 100% rename from packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes--to-es2015/ctx/options.json rename to packages/babel-plugin-proposal-decorators/test/fixtures/2023-05-classes/decorator-access-modified-classes/options.json