Skip to content

Commit

Permalink
Update decorators to match latest spec (#14353)
Browse files Browse the repository at this point in the history
Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
  • Loading branch information
pzuraq and nicolo-ribaudo committed Mar 18, 2022
1 parent 338d7f1 commit 7316f93
Show file tree
Hide file tree
Showing 28 changed files with 423 additions and 203 deletions.
2 changes: 1 addition & 1 deletion packages/babel-helpers/src/helpers-generated.ts

Large diffs are not rendered by default.

200 changes: 129 additions & 71 deletions packages/babel-helpers/src/helpers/applyDecs.js
Expand Up @@ -18,12 +18,16 @@
CLASS = 10; // only used in assertValidReturnValue
*/

function createMetadataMethodsForProperty(metadataMap, kind, property) {
function createMetadataMethodsForProperty(
metadataMap,
kind,
property,
decoratorFinishedRef
) {
return {
getMetadata: function (key) {
if (typeof key !== "symbol") {
throw new TypeError("Metadata keys must be symbols, received: " + key);
}
assertNotFinished(decoratorFinishedRef, "getMetadata");
assertMetadataKey(key);

var metadataForKey = metadataMap[key];

Expand All @@ -44,9 +48,8 @@ function createMetadataMethodsForProperty(metadataMap, kind, property) {
}
},
setMetadata: function (key, value) {
if (typeof key !== "symbol") {
throw new TypeError("Metadata keys must be symbols, received: " + key);
}
assertNotFinished(decoratorFinishedRef, "setMetadata");
assertMetadataKey(key);

var metadataForKey = metadataMap[key];

Expand Down Expand Up @@ -120,21 +123,24 @@ function convertMetadataMapToFinal(obj, metadataMap) {
obj[Symbol.metadata || Symbol.for("Symbol.metadata")] = metadataMap;
}

function createAddInitializerMethod(initializers) {
function createAddInitializerMethod(initializers, decoratorFinishedRef) {
return function addInitializer(initializer) {
assertNotFinished(decoratorFinishedRef, "addInitializer");
assertCallable(initializer, "An initializer");
initializers.push(initializer);
};
}

function memberDecCtx(
function memberDec(
dec,
name,
desc,
metadataMap,
initializers,
kind,
isStatic,
isPrivate
isPrivate,
value
) {
var kindStr;

Expand Down Expand Up @@ -162,8 +168,13 @@ function memberDecCtx(
isPrivate: isPrivate,
};

var decoratorFinishedRef = { v: false };

if (kind !== 0 /* FIELD */) {
ctx.addInitializer = createAddInitializerMethod(initializers);
ctx.addInitializer = createAddInitializerMethod(
initializers,
decoratorFinishedRef
);
}

var metadataKind, metadataName;
Expand Down Expand Up @@ -202,10 +213,36 @@ function memberDecCtx(
metadataName = name;
}

return Object.assign(
ctx,
createMetadataMethodsForProperty(metadataMap, metadataKind, metadataName)
);
try {
return dec(
value,
Object.assign(
ctx,
createMetadataMethodsForProperty(
metadataMap,
metadataKind,
metadataName,
decoratorFinishedRef
)
)
);
} finally {
decoratorFinishedRef.v = true;
}
}

function assertNotFinished(decoratorFinishedRef, fnName) {
if (decoratorFinishedRef.v) {
throw new Error(
"attempted to call " + fnName + " after decoration was finished"
);
}
}

function assertMetadataKey(key) {
if (typeof key !== "symbol") {
throw new TypeError("Metadata keys must be symbols, received: " + key);
}
}

function assertCallable(fn, hint) {
Expand All @@ -220,7 +257,7 @@ function assertValidReturnValue(kind, value) {
if (kind === 1 /* ACCESSOR */) {
if (type !== "object" || value === null) {
throw new TypeError(
"accessor decorators must return an object with get, set, or initializer properties or void 0"
"accessor decorators must return an object with get, set, or init properties or void 0"
);
}
if (value.get !== undefined) {
Expand All @@ -229,8 +266,11 @@ function assertValidReturnValue(kind, value) {
if (value.set !== undefined) {
assertCallable(value.set, "accessor.set");
}
if (value.initialize !== undefined) {
assertCallable(value.initialize, "accessor.initialize");
if (value.init !== undefined) {
assertCallable(value.init, "accessor.init");
}
if (value.initializer !== undefined) {
assertCallable(value.initializer, "accessor.initializer");
}
} else if (type !== "function") {
var hint;
Expand All @@ -245,6 +285,18 @@ function assertValidReturnValue(kind, value) {
}
}

function getInit(desc) {
var initializer;
if (
(initializer = desc.init) == null &&
(initializer = desc.initializer) &&
typeof console !== "undefined"
) {
console.warn(".initializer has been renamed to .init as of March 2022");
}
return initializer;
}

function applyMemberDec(
ret,
base,
Expand Down Expand Up @@ -296,29 +348,28 @@ function applyMemberDec(
value = desc.set;
}

var ctx = memberDecCtx(
name,
desc,
metadataMap,
initializers,
kind,
isStatic,
isPrivate
);

var newValue, get, set;

if (typeof decs === "function") {
newValue = decs(value, ctx);
newValue = memberDec(
decs,
name,
desc,
metadataMap,
initializers,
kind,
isStatic,
isPrivate,
value
);

if (newValue !== void 0) {
assertValidReturnValue(kind, newValue);

if (kind === 0 /* FIELD */) {
initializer = newValue;
} else if (kind === 1 /* ACCESSOR */) {
initializer = newValue.initialize;

initializer = getInit(newValue);
get = newValue.get || value.get;
set = newValue.set || value.set;

Expand All @@ -331,7 +382,17 @@ function applyMemberDec(
for (var i = decs.length - 1; i >= 0; i--) {
var dec = decs[i];

newValue = dec(value, ctx);
newValue = memberDec(
dec,
name,
desc,
metadataMap,
initializers,
kind,
isStatic,
isPrivate,
value
);

if (newValue !== void 0) {
assertValidReturnValue(kind, newValue);
Expand All @@ -340,8 +401,7 @@ function applyMemberDec(
if (kind === 0 /* FIELD */) {
newInit = newValue;
} else if (kind === 1 /* ACCESSOR */) {
newInit = newValue.initialize;

newInit = getInit(newValue);
get = newValue.get || value.get;
set = newValue.set || value.set;

Expand Down Expand Up @@ -514,59 +574,57 @@ function applyMemberDecs(

function pushInitializers(ret, initializers) {
if (initializers) {
if (initializers.length > 0) {
// Slice the array, which means that `addInitializer` can no longer add
// additional initializers to the array
initializers = initializers.slice();

ret.push(function (instance) {
for (var i = 0; i < initializers.length; i++) {
initializers[i].call(instance);
}
return instance;
});
} else {
ret.push(function (instance) {
return instance;
});
}
ret.push(function (instance) {
for (var i = 0; i < initializers.length; i++) {
initializers[i].call(instance);
}
return instance;
});
}
}

function applyClassDecs(ret, targetClass, metadataMap, classDecs) {
if (classDecs.length > 0) {
var initializers = [];
var newClass = targetClass;

var name = targetClass.name;
var ctx = Object.assign(
{
kind: "class",
name: name,
addInitializer: createAddInitializerMethod(initializers),
},
createMetadataMethodsForProperty(metadataMap, 0 /* CONSTRUCTOR */, name)
);

for (var i = classDecs.length - 1; i >= 0; i--) {
var nextNewClass = classDecs[i](newClass, ctx);
var decoratorFinishedRef = { v: false };

try {
var ctx = Object.assign(
{
kind: "class",
name: name,
addInitializer: createAddInitializerMethod(
initializers,
decoratorFinishedRef
),
},
createMetadataMethodsForProperty(
metadataMap,
0 /* CONSTRUCTOR */,
name,
decoratorFinishedRef
)
);
var nextNewClass = classDecs[i](newClass, ctx);
} finally {
decoratorFinishedRef.v = true;
}

if (nextNewClass !== undefined) {
assertValidReturnValue(10 /* CLASS */, nextNewClass);
newClass = nextNewClass;
}
}

ret.push(newClass);

if (initializers.length > 0) {
ret.push(function () {
for (var i = 0; i < initializers.length; i++) {
initializers[i].call(newClass);
}
});
} else {
ret.push(function () {});
}
ret.push(newClass, function () {
for (var i = 0; i < initializers.length; i++) {
initializers[i].call(newClass);
}
});
}
}

Expand Down
Expand Up @@ -311,11 +311,28 @@ function isDecoratorInfo(
return "decorators" in info;
}

function filteredOrderedDecoratorInfo(
info: (DecoratorInfo | ComputedPropInfo)[],
): DecoratorInfo[] {
const filtered = info.filter(isDecoratorInfo);

return [
...filtered.filter(
el => el.isStatic && el.kind >= ACCESSOR && el.kind <= SETTER,
),
...filtered.filter(
el => !el.isStatic && el.kind >= ACCESSOR && el.kind <= SETTER,
),
...filtered.filter(el => el.isStatic && el.kind === FIELD),
...filtered.filter(el => !el.isStatic && el.kind === FIELD),
];
}

function generateDecorationExprs(
info: (DecoratorInfo | ComputedPropInfo)[],
): t.ArrayExpression {
return t.arrayExpression(
info.filter(isDecoratorInfo).map(el => {
filteredOrderedDecoratorInfo(info).map(el => {
const decs =
el.decorators.length > 1
? t.arrayExpression(el.decorators)
Expand All @@ -341,19 +358,19 @@ function generateDecorationExprs(
function extractElementLocalAssignments(
decorationInfo: (DecoratorInfo | ComputedPropInfo)[],
) {
const locals: t.Identifier[] = [];
const localIds: t.Identifier[] = [];

for (const el of decorationInfo) {
if ("locals" in el && el.locals) {
if (Array.isArray(el.locals)) {
locals.push(...el.locals);
} else {
locals.push(el.locals);
}
for (const el of filteredOrderedDecoratorInfo(decorationInfo)) {
const { locals } = el;

if (Array.isArray(locals)) {
localIds.push(...locals);
} else if (locals !== undefined) {
localIds.push(locals);
}
}

return locals;
return localIds;
}

function addCallAccessorsFor(
Expand Down
Expand Up @@ -12,7 +12,7 @@ function dec({ get, set }, context) {
set.call(this, v + 1);
},

initialize(v) {
init(v) {
return v ? v : 1;
}
}
Expand Down

0 comments on commit 7316f93

Please sign in to comment.