Emit for decorators, updated to ES6 classes.

This commit is contained in:
Ron Buckton
2015-03-17 13:41:29 -07:00
parent 39001f5395
commit bd4d7fc9ee
4 changed files with 438 additions and 27 deletions
+24
View File
@@ -11267,6 +11267,29 @@ module ts {
!hasProperty(getGeneratedNamesForSourceFile(getSourceFile(location)), name);
}
function getClassDeclarationVariableId(n: Identifier): number {
Debug.assert(!nodeIsSynthesized(n));
let isClassDeclaration = n.parent.kind === SyntaxKind.ClassDeclaration;
let symbol =
(isClassDeclaration ? getSymbolOfNode(n.parent) : undefined) ||
getNodeLinks(n).resolvedSymbol ||
resolveName(n, n.text, SymbolFlags.Value | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
symbol = getExportSymbolOfValueSymbolIfExported(symbol);
let isClass = symbol && (symbol.flags & SymbolFlags.Class) !== 0;
if (isClass) {
// side-effect of calling this method:
// assign id to symbol if it was not yet set
getSymbolLinks(symbol);
return symbol.id;
}
return undefined;
}
function getBlockScopedVariableId(n: Identifier): number {
Debug.assert(!nodeIsSynthesized(n));
@@ -11309,6 +11332,7 @@ module ts {
getConstantValue,
isUnknownIdentifier,
getBlockScopedVariableId,
getClassDeclarationVariableId,
};
}
+341 -27
View File
@@ -277,20 +277,14 @@ module ts {
}
}
function getFirstConstructorWithBody(node: ClassDeclaration): ConstructorDeclaration {
return forEach(node.members, member => {
if (member.kind === SyntaxKind.Constructor && nodeIsPresent((<ConstructorDeclaration>member).body)) {
return <ConstructorDeclaration>member;
}
});
}
function getAllAccessorDeclarations(declarations: NodeArray<Declaration>, accessor: AccessorDeclaration) {
let firstAccessor: AccessorDeclaration;
let lastAccessor: AccessorDeclaration;
let getAccessor: AccessorDeclaration;
let setAccessor: AccessorDeclaration;
if (hasDynamicName(accessor)) {
firstAccessor = accessor;
lastAccessor = accessor;
if (accessor.kind === SyntaxKind.GetAccessor) {
getAccessor = accessor;
}
@@ -312,6 +306,7 @@ module ts {
firstAccessor = <AccessorDeclaration>member;
}
lastAccessor = <AccessorDeclaration>member;
if (member.kind === SyntaxKind.GetAccessor && !getAccessor) {
getAccessor = <AccessorDeclaration>member;
}
@@ -325,6 +320,7 @@ module ts {
}
return {
firstAccessor,
lastAccessor,
getAccessor,
setAccessor
};
@@ -1582,8 +1578,12 @@ module ts {
let currentScopeNames: Map<string>;
let generatedBlockScopeNames: string[];
let generatedClassNames: string[];
let generatedComputedPropertyNames: string[];
let generatedSetterNames: string[];
let extendsEmitted = false;
let decorateEmitted = false;
let tempCount = 0;
let tempVariables: Identifier[];
let tempParameters: Identifier[];
@@ -2565,6 +2565,25 @@ module ts {
emitLiteral(<LiteralExpression>node);
}
else if (node.kind === SyntaxKind.ComputedPropertyName) {
if (nodeIsDecorated(node.parent)) {
if (!generatedComputedPropertyNames) {
generatedComputedPropertyNames = [];
}
let generatedName = generatedComputedPropertyNames[node.id];
if (generatedName) {
write(generatedName);
return;
}
let generatedVariable = createTempVariable(node);
generatedName = generatedVariable.text;
recordTempDeclaration(generatedVariable);
generatedComputedPropertyNames[node.id] = generatedName;
write(generatedName);
write(" = ");
}
emit((<ComputedPropertyName>node).expression);
}
else {
@@ -2623,26 +2642,29 @@ module ts {
}
}
function getGeneratedNameForIdentifier(node: Identifier): string {
if (nodeIsSynthesized(node) || !generatedBlockScopeNames) {
return undefined;
function getGeneratedNameForIdentifier(node: Identifier, allowGeneratedIdentifiers: boolean): string {
if (!nodeIsSynthesized(node)) {
if (allowGeneratedIdentifiers && generatedBlockScopeNames) {
var variableId = resolver.getBlockScopedVariableId(node)
if (variableId !== undefined) {
return generatedBlockScopeNames[variableId];
}
}
if (languageVersion >= ScriptTarget.ES6 && generatedClassNames) {
var variableId = resolver.getClassDeclarationVariableId(node);
if (variableId !== undefined) {
return generatedClassNames[variableId];
}
}
}
var variableId = resolver.getBlockScopedVariableId(node)
if (variableId === undefined) {
return undefined;
}
return generatedBlockScopeNames[variableId];
return undefined;
}
function emitIdentifier(node: Identifier, allowGeneratedIdentifiers: boolean) {
if (allowGeneratedIdentifiers) {
let generatedName = getGeneratedNameForIdentifier(node);
if (generatedName) {
write(generatedName);
return;
}
let generatedName = getGeneratedNameForIdentifier(node, allowGeneratedIdentifiers);
if (generatedName) {
write(generatedName);
return;
}
if (!node.parent) {
write(node.text);
@@ -3034,7 +3056,7 @@ module ts {
function emitComputedPropertyName(node: ComputedPropertyName) {
write("[");
emit(node.expression);
emitExpressionForPropertyName(node);
write("]");
}
@@ -3065,7 +3087,7 @@ module ts {
if (languageVersion < ScriptTarget.ES6) {
// Emit identifier as an identifier
write(": ");
var generatedName = getGeneratedNameForIdentifier(node.name);
var generatedName = getGeneratedNameForIdentifier(node.name, /*allowGeneratedIdentifiers*/ true);
if (generatedName) {
write(generatedName);
}
@@ -4723,6 +4745,23 @@ module ts {
emitLeadingComments(accessors.setAccessor);
write("set: ");
emitStart(accessors.setAccessor);
if (forEach(accessors.setAccessor.parameters, nodeIsDecorated)) {
if (!generatedSetterNames) {
generatedSetterNames = [];
}
var preferredName: string;
if ((<AccessorDeclaration>member).name.kind === SyntaxKind.Identifier) {
preferredName = "set_" + (<Identifier>(<AccessorDeclaration>member).name).text;
}
let generatedVariable = createTempVariable(node, preferredName);
let generatedName = generatedVariable.text;
recordTempDeclaration(generatedVariable);
generatedSetterNames[accessors.setAccessor.id] = generatedName;
write(generatedName);
write(" = ");
}
write("function ");
emitSignatureAndBody(accessors.setAccessor);
emitEnd(accessors.setAccessor);
@@ -4893,6 +4932,15 @@ module ts {
tempParameters = saveTempParameters;
}
function emitClassDeclaration(node: ClassDeclaration) {
if (languageVersion < ScriptTarget.ES6) {
emitClassDeclarationBelowES6(<ClassDeclaration>node);
}
else {
emitClassDeclarationForES6AndHigher(<ClassDeclaration>node);
}
}
function emitClassDeclarationForES6AndHigher(node: ClassDeclaration) {
if (isES6ModuleMemberDeclaration(node)) {
write("export ");
@@ -4902,16 +4950,56 @@ module ts {
}
}
if (nodeOrChildIsDecorated(node)) {
if ((node.flags & NodeFlags.Default) === 0) {
write("let ");
emitDeclarationName(node);
write(" = ");
}
write("(function() {");
var saveTempCount = tempCount;
var saveTempVariables = tempVariables;
var saveTempParameters = tempParameters;
var saveGeneratedComputedPropertyNames = generatedComputedPropertyNames;
var saveGeneratedSetterNames = generatedSetterNames;
tempCount = 0;
tempVariables = undefined;
tempParameters = undefined;
generatedComputedPropertyNames = undefined;
generatedSetterNames = undefined;
increaseIndent();
writeLine();
if (nodeIsDecorated(node)) {
if (!generatedClassNames) {
generatedClassNames = [];
}
var generatedName = generateUniqueNameForLocation(node, "_" + node.name.text);
var variableId = resolver.getClassDeclarationVariableId(node.name);
write("let ");
write(generatedName);
write(" = ");
}
}
write("class ");
// check if this is an "export default class" as it may not have a name
if (node.name || !(node.flags & NodeFlags.Default)) {
emitDeclarationName(node);
if (nodeIsDecorated(node)) {
generatedClassNames[variableId] = generatedName;
}
}
var baseTypeNode = getClassBaseTypeNode(node);
if (baseTypeNode) {
write(" extends ");
emit(baseTypeNode.typeName);
}
write(" {");
increaseIndent();
scopeEmitStart(node);
@@ -4931,6 +5019,23 @@ module ts {
writeLine();
emitMemberAssignments(node, NodeFlags.Static);
if (nodeOrChildIsDecorated(node)) {
writeLine();
emitDecoratorsOfClass(node);
emitTempDeclarations(/*newLine*/ true);
writeLine();
tempCount = saveTempCount;
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
generatedComputedPropertyNames = saveGeneratedComputedPropertyNames;
generatedSetterNames = saveGeneratedSetterNames;
write("return ");
emitDeclarationName(node);
decreaseIndent();
writeLine();
write("})();");
}
// If this is an exported class, but not on the top level (i.e. on an internal
// module), export it
if (!isES6ModuleMemberDeclaration(node) && (node.flags & NodeFlags.Export)) {
@@ -4953,6 +5058,16 @@ module ts {
write("_super");
}
write(") {");
let saveTempCount = tempCount;
let saveTempVariables = tempVariables;
let saveTempParameters = tempParameters;
let saveGeneratedComputedPropertyNames = generatedComputedPropertyNames;
let saveGeneratedSetterNames = generatedSetterNames;
tempCount = 0;
tempVariables = undefined;
tempParameters = undefined;
generatedComputedPropertyNames = undefined;
generatedSetterNames = undefined;
increaseIndent();
scopeEmitStart(node);
if (baseTypeNode) {
@@ -4968,6 +5083,14 @@ module ts {
emitMemberFunctionsForES5AndLower(node);
emitMemberAssignments(node, NodeFlags.Static);
writeLine();
emitDecoratorsOfClass(node);
emitTempDeclarations(/*newLine*/ true);
writeLine();
tempCount = saveTempCount;
tempVariables = saveTempVariables;
tempParameters = saveTempParameters;
generatedComputedPropertyNames = saveGeneratedComputedPropertyNames;
generatedSetterNames = saveGeneratedSetterNames;
emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => {
write("return ");
emitDeclarationName(node);
@@ -5000,6 +5123,172 @@ module ts {
}
}
function emitTargetOfClassElement(node: ClassDeclaration, member: Node) {
emitDeclarationName(node);
if (!(member.flags & NodeFlags.Static)) {
write(".prototype");
}
}
function getDecoratorsOfMember(node: ClassDeclaration, member: ClassElement) {
switch (member.kind) {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.PropertyDeclaration:
return member.decorators;
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
let accessors = getAllAccessorDeclarations(node.members, <AccessorDeclaration>member);
if (member === accessors.firstAccessor) {
let decorators = accessors.firstAccessor.decorators;
if (member !== accessors.lastAccessor && accessors.lastAccessor.decorators) {
if (decorators) {
return decorators.concat(accessors.lastAccessor.decorators);
}
return accessors.lastAccessor.decorators;
}
return decorators;
}
break;
}
return undefined;
}
function emitExpressionOfDecorator(node: Decorator) {
let expression = node.expression;
emit(expression);
}
function emitDecorateStart(decorators: Decorator[]): void {
write("__decorate([");
let decoratorCount = decorators.length;
for (let i = 0; i < decoratorCount; i++) {
if (i > 0) {
write(", ");
}
let decorator = decorators[i];
emitStart(decorator);
emitExpressionOfDecorator(decorator);
emitEnd(decorator);
}
write(`], `);
}
function emitDecoratorsOfParameter(node: FunctionLikeDeclaration, parameter: ParameterDeclaration, parameterIndex: number) {
let decorators = parameter.decorators;
if (!decorators || decorators.length === 0) {
return;
}
emitStart(node);
emitDecorateStart(decorators);
emitPropertyAccessForMethod(node);
write(", ");
write(String(parameterIndex));
write(");");
emitEnd(node);
writeLine();
}
function emitPropertyAccessForMethod(node: FunctionLikeDeclaration) {
if (node.parent && node.parent.kind === SyntaxKind.ClassDeclaration) {
if (node.kind === SyntaxKind.Constructor) {
emitDeclarationName(<ClassDeclaration>node.parent);
}
else if (node.kind === SyntaxKind.SetAccessor) {
if (generatedSetterNames) {
var generatedName = generatedSetterNames[node.id];
if (generatedName) {
write(generatedName);
return;
}
}
write("Object.getOwnPropertyDescriptor(");
emitTargetOfClassElement(<ClassDeclaration>node.parent, node);
write(", ");
emitExpressionForPropertyName(node.name);
write(").set");
}
else {
emitTargetOfClassElement(<ClassDeclaration>node.parent, node);
emitMemberAccessForPropertyName(node.name);
}
}
}
function emitDecoratorsOfParameters(node: FunctionLikeDeclaration) {
forEach(node.parameters, (parameter, parameterIndex) => emitDecoratorsOfParameter(node, parameter, parameterIndex));
}
function emitDecoratorsOfMembers(node: ClassDeclaration) {
forEach(node.members, member => emitDecoratorsOfMember(node, member));
}
function emitDecoratorsOfMember(node: ClassDeclaration, member: ClassElement) {
switch (member.kind) {
case SyntaxKind.MethodDeclaration:
emitDecoratorsOfParameters(<MethodDeclaration>member);
break;
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
let accessors = getAllAccessorDeclarations(node.members, <AccessorDeclaration>member);
if (member === accessors.firstAccessor && accessors.setAccessor) {
emitDecoratorsOfParameters(accessors.setAccessor);
}
break;
case SyntaxKind.PropertyDeclaration:
break;
default:
return;
}
let name = member.name;
let decorators = getDecoratorsOfMember(node, member);
if (!decorators || decorators.length === 0) {
return;
}
if (languageVersion >= ScriptTarget.ES5) {
emitStart(member);
emitDecorateStart(decorators);
emitTargetOfClassElement(node, member);
write(", ");
emitExpressionForPropertyName(name);
write(");");
emitEnd(member);
writeLine();
}
}
function emitDecoratorsOfConstructor(node: ClassDeclaration) {
let constructor = getFirstConstructorWithBody(node);
if (constructor) {
emitDecoratorsOfParameters(constructor);
}
let decorators = node.decorators;
if (!decorators || decorators.length === 0) {
return;
}
emitStart(node);
emitDeclarationName(node);
write(" = ");
emitDecorateStart(node.decorators);
emitDeclarationName(node);
write(");");
emitEnd(node);
writeLine();
}
function emitDecoratorsOfClass(node: ClassDeclaration) {
emitDecoratorsOfMembers(node);
emitDecoratorsOfConstructor(node);
}
function emitInterfaceDeclaration(node: InterfaceDeclaration) {
emitPinnedOrTripleSlashComments(node);
}
@@ -5654,6 +5943,17 @@ module ts {
return statements.length;
}
function writeHelper(text: string): void {
let lines = text.split(/\r\n|\r|\n/g);
for (let i = 0; i < lines.length; ++i) {
let line = lines[i];
if (line.length) {
writeLine();
write(line);
}
}
}
function emitSourceFileNode(node: SourceFile) {
// Start new file on new line
writeLine();
@@ -5680,6 +5980,20 @@ module ts {
write("};");
extendsEmitted = true;
}
if (!decorateEmitted && resolver.getNodeCheckFlags(node) & NodeCheckFlags.EmitDecorate) {
writeHelper(`
var __decorate = this.__decorate || function (decorators, target, key) {
var kind = key == null ? 0 : typeof key == "number" ? 1 : 2, result = target;
if (kind == 2) result = Object.getOwnPropertyDescriptor(target, typeof key == "symbol" ? key : key = String(key));
for (var i = decorators.length - 1; i >= 0; --i) {
var decorator = decorators[i];
result = (kind == 0 ? decorator(result) : kind == 1 ? decorator(target, key) : decorator(target, key, result)) || result;
}
if (kind == 2 && result) Object.defineProperty(target, key, result);
if (kind == 0) return result;
};`);
decorateEmitted = true;
}
if (isExternalModule(node)) {
if (languageVersion >= ScriptTarget.ES6) {
emitES6Module(node, startIndex);
@@ -5897,7 +6211,7 @@ module ts {
case SyntaxKind.VariableDeclaration:
return emitVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.ClassDeclaration:
return languageVersion < ScriptTarget.ES6 ? emitClassDeclarationBelowES6(<ClassDeclaration>node) : emitClassDeclarationForES6AndHigher(<ClassDeclaration>node);
return emitClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return emitInterfaceDeclaration(<InterfaceDeclaration>node);
case SyntaxKind.EnumDeclaration:
+1
View File
@@ -1233,6 +1233,7 @@ module ts {
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
isUnknownIdentifier(location: Node, name: string): boolean;
getBlockScopedVariableId(node: Identifier): number;
getClassDeclarationVariableId(node: Identifier): number;
}
export const enum SymbolFlags {
+72
View File
@@ -586,6 +586,78 @@ module ts {
return forEach(node.members, getConstructorWithBody);
}
export function nodeOrChildIsDecorated(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
if (node.decorators) {
return true;
}
return forEach((<ClassDeclaration>node).members, nodeOrChildIsDecorated);
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.Parameter:
if (node.decorators) {
return true;
}
return false;
case SyntaxKind.GetAccessor:
if ((<FunctionLikeDeclaration>node).body && node.decorators) {
return true;
}
return false;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.SetAccessor:
if ((<FunctionLikeDeclaration>node).body && node.decorators) {
return true;
}
return forEach((<FunctionLikeDeclaration>node).parameters, nodeOrChildIsDecorated);
}
return false;
}
export function nodeIsDecorated(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
if (node.decorators) {
return true;
}
return false;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.Parameter:
if (node.decorators) {
return true;
}
return false;
case SyntaxKind.GetAccessor:
if ((<FunctionLikeDeclaration>node).body && node.decorators) {
return true;
}
return false;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.SetAccessor:
if ((<FunctionLikeDeclaration>node).body && node.decorators) {
return true;
}
return false;
}
return false;
}
export function isExpression(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisKeyword: