Moves type serialization to the emitter.

This commit is contained in:
Ron Buckton
2015-06-30 19:02:35 -07:00
parent 5208bded90
commit 9eab88570f
4 changed files with 259 additions and 277 deletions
+19 -214
View File
@@ -853,7 +853,7 @@ namespace ts {
}
// Resolves a qualified name and any involved aliases
function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags): Symbol {
function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean): Symbol {
if (nodeIsMissing(name)) {
return undefined;
}
@@ -862,7 +862,7 @@ namespace ts {
if (name.kind === SyntaxKind.Identifier) {
let message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
symbol = resolveName(name, (<Identifier>name).text, meaning, message, <Identifier>name);
symbol = resolveName(name, (<Identifier>name).text, meaning, !ignoreErrors ? message : undefined, <Identifier>name);
if (!symbol) {
return undefined;
}
@@ -871,13 +871,15 @@ namespace ts {
let left = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).left : (<PropertyAccessExpression>name).expression;
let right = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).right : (<PropertyAccessExpression>name).name;
let namespace = resolveEntityName(left, SymbolFlags.Namespace);
let namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors);
if (!namespace || namespace === unknownSymbol || nodeIsMissing(right)) {
return undefined;
}
symbol = getSymbol(getExportsOfSymbol(namespace), right.text, meaning);
if (!symbol) {
error(right, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString(right));
if (!ignoreErrors) {
error(right, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString(right));
}
return undefined;
}
}
@@ -2450,10 +2452,6 @@ namespace ts {
return links.type;
}
function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode {
return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type;
}
function getAnnotatedAccessorType(accessor: AccessorDeclaration): Type {
if (accessor) {
if (accessor.kind === SyntaxKind.GetAccessor) {
@@ -10353,13 +10351,10 @@ namespace ts {
// as if it were an expression so that we can emit the type in a value position when we
// serialize the type metadata.
if (node && node.kind === SyntaxKind.TypeReference) {
let type = getTypeFromTypeNode(node);
let shouldCheckIfUnknownType = type === unknownType && compilerOptions.isolatedModules;
if (!type || (!shouldCheckIfUnknownType && type.flags & (TypeFlags.Intrinsic | TypeFlags.NumberLike | TypeFlags.StringLike))) {
return;
}
if (shouldCheckIfUnknownType || type.symbol.valueDeclaration) {
checkExpression((<TypeReferenceNode>node).typeName);
let root = getFirstIdentifier((<TypeReferenceNode>node).typeName);
let rootSymbol = resolveName(root, root.text, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
if (rootSymbol && rootSymbol.flags & SymbolFlags.Alias && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) {
markAliasSymbolAsReferenced(rootSymbol);
}
}
}
@@ -12122,7 +12117,7 @@ namespace ts {
Debug.assert(node.kind === SyntaxKind.Identifier);
return <Identifier>node;
}
function checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean {
let moduleName = getExternalModuleName(node);
if (!nodeIsMissing(moduleName) && moduleName.kind !== SyntaxKind.StringLiteral) {
@@ -13212,203 +13207,15 @@ namespace ts {
return undefined;
}
/** Serializes an EntityName (with substitutions) to an appropriate JS constructor value. Used by the __metadata decorator. */
function serializeEntityName(node: EntityName, fallbackPath?: string[]): string {
if (node.kind === SyntaxKind.Identifier) {
// TODO(ron.buckton): The getExpressionNameSubstitution function has been removed, but calling it
// here has no effect anyway as an identifier in a type name is not an expression.
// var substitution = getExpressionNameSubstitution(<Identifier>node, getGeneratedNameForNode);
// var text = substitution || (<Identifier>node).text;
var text = (<Identifier>node).text;
if (fallbackPath) {
fallbackPath.push(text);
}
else {
return text;
}
}
else {
var left = serializeEntityName((<QualifiedName>node).left, fallbackPath);
var right = serializeEntityName((<QualifiedName>node).right, fallbackPath);
if (!fallbackPath) {
return left + "." + right;
}
}
}
/** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */
function serializeTypeReferenceNode(node: TypeReferenceNode): string | string[] {
// serialization of a TypeReferenceNode uses the following rules:
//
// * The serialized type of a TypeReference that is `void` is "void 0".
// * The serialized type of a TypeReference that is a `boolean` is "Boolean".
// * The serialized type of a TypeReference that is an enum or `number` is "Number".
// * The serialized type of a TypeReference that is a string literal or `string` is "String".
// * The serialized type of a TypeReference that is a tuple is "Array".
// * The serialized type of a TypeReference that is a `symbol` is "Symbol".
// * The serialized type of a TypeReference with a value declaration is its entity name.
// * The serialized type of a TypeReference with a call or construct signature is "Function".
// * The serialized type of any other type is "Object".
let type = getTypeFromTypeNode(node);
if (type.flags & TypeFlags.Void) {
return "void 0";
}
else if (type.flags & TypeFlags.Boolean) {
return "Boolean";
}
else if (type.flags & TypeFlags.NumberLike) {
return "Number";
}
else if (type.flags & TypeFlags.StringLike) {
return "String";
}
else if (type.flags & TypeFlags.Tuple) {
return "Array";
}
else if (type.flags & TypeFlags.ESSymbol) {
return "Symbol";
}
else if (type === unknownType) {
var fallbackPath: string[] = [];
serializeEntityName(node.typeName, fallbackPath);
return fallbackPath;
}
else if (type.symbol && type.symbol.valueDeclaration) {
return serializeEntityName(node.typeName);
}
else if (typeHasCallOrConstructSignatures(type)) {
return "Function";
}
return "Object";
}
/** Serializes a TypeNode to an appropriate JS constructor value. Used by the __metadata decorator. */
function serializeTypeNode(node: TypeNode | LiteralExpression): string | string[] {
// serialization of a TypeNode uses the following rules:
//
// * The serialized type of `void` is "void 0" (undefined).
// * The serialized type of a parenthesized type is the serialized type of its nested type.
// * The serialized type of a Function or Constructor type is "Function".
// * The serialized type of an Array or Tuple type is "Array".
// * The serialized type of `boolean` is "Boolean".
// * The serialized type of `string` or a string-literal type is "String".
// * The serialized type of a type reference is handled by `serializeTypeReferenceNode`.
// * The serialized type of any other type node is "Object".
if (node) {
switch (node.kind) {
case SyntaxKind.VoidKeyword:
return "void 0";
case SyntaxKind.ParenthesizedType:
return serializeTypeNode((<ParenthesizedTypeNode>node).type);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
return "Function";
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
return "Array";
case SyntaxKind.BooleanKeyword:
return "Boolean";
case SyntaxKind.StringKeyword:
case SyntaxKind.StringLiteral:
return "String";
case SyntaxKind.NumberKeyword:
return "Number";
case SyntaxKind.TypeReference:
return serializeTypeReferenceNode(<TypeReferenceNode>node);
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeLiteral:
case SyntaxKind.UnionType:
case SyntaxKind.AnyKeyword:
break;
default:
Debug.fail("Cannot serialize unexpected type node.");
break;
}
}
return "Object";
}
/** Serializes the type of a declaration to an appropriate JS constructor value. Used by the __metadata decorator for a class member. */
function serializeTypeOfNode(node: Node): string | string[] {
// serialization of the type of a declaration uses the following rules:
//
// * The serialized type of a ClassDeclaration is "Function"
// * The serialized type of a ParameterDeclaration is the serialized type of its type annotation.
// * The serialized type of a PropertyDeclaration is the serialized type of its type annotation.
// * The serialized type of an AccessorDeclaration is the serialized type of the return type annotation of its getter or parameter type annotation of its setter.
// * The serialized type of any other FunctionLikeDeclaration is "Function".
// * The serialized type of any other node is "void 0".
//
// For rules on serializing type annotations, see `serializeTypeNode`.
switch (node.kind) {
case SyntaxKind.ClassDeclaration: return "Function";
case SyntaxKind.PropertyDeclaration: return serializeTypeNode((<PropertyDeclaration>node).type);
case SyntaxKind.Parameter: return serializeTypeNode((<ParameterDeclaration>node).type);
case SyntaxKind.GetAccessor: return serializeTypeNode((<AccessorDeclaration>node).type);
case SyntaxKind.SetAccessor: return serializeTypeNode(getSetAccessorTypeAnnotationNode(<AccessorDeclaration>node));
}
if (isFunctionLike(node)) {
return "Function";
}
return "void 0";
}
/** Serializes the parameter types of a function or the constructor of a class. Used by the __metadata decorator for a method or set accessor. */
function serializeParameterTypesOfNode(node: Node): (string | string[])[] {
// serialization of parameter types uses the following rules:
//
// * If the declaration is a class, the parameters of the first constructor with a body are used.
// * If the declaration is function-like and has a body, the parameters of the function are used.
//
// For the rules on serializing the type of each parameter declaration, see `serializeTypeOfDeclaration`.
if (node) {
var valueDeclaration: FunctionLikeDeclaration;
if (node.kind === SyntaxKind.ClassDeclaration) {
valueDeclaration = getFirstConstructorWithBody(<ClassDeclaration>node);
}
else if (isFunctionLike(node) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
valueDeclaration = <FunctionLikeDeclaration>node;
}
if (valueDeclaration) {
var result: (string | string[])[];
var parameters = valueDeclaration.parameters;
var parameterCount = parameters.length;
if (parameterCount > 0) {
result = new Array<string>(parameterCount);
for (var i = 0; i < parameterCount; i++) {
if (parameters[i].dotDotDotToken) {
var parameterType = parameters[i].type;
if (parameterType.kind === SyntaxKind.ArrayType) {
parameterType = (<ArrayTypeNode>parameterType).elementType;
}
else if (parameterType.kind === SyntaxKind.TypeReference && (<TypeReferenceNode>parameterType).typeArguments && (<TypeReferenceNode>parameterType).typeArguments.length === 1) {
parameterType = (<TypeReferenceNode>parameterType).typeArguments[0];
}
else {
parameterType = undefined;
}
result[i] = serializeTypeNode(parameterType);
}
else {
result[i] = serializeTypeOfNode(parameters[i]);
}
}
return result;
}
}
function isTypeReferenceWithValueDeclaration(node: TypeReferenceNode): boolean {
let symbol = resolveEntityName(node.typeName, SymbolFlags.Value, /*ignoreErrors*/ true);
if (symbol.valueDeclaration) {
let type = getTypeOfSymbol(symbol);
return typeHasCallOrConstructSignatures(type);
}
return emptyArray;
}
/** Serializes the return type of function. Used by the __metadata decorator for a method. */
function serializeReturnTypeOfNode(node: Node): string | string[] {
if (node && isFunctionLike(node)) {
return serializeTypeNode((<FunctionLikeDeclaration>node).type);
}
return "void 0";
return false;
}
function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) {
@@ -13508,9 +13315,7 @@ namespace ts {
collectLinkedAliases,
getBlockScopedVariableId,
getReferencedValueDeclaration,
serializeTypeOfNode,
serializeParameterTypesOfNode,
serializeReturnTypeOfNode,
isTypeReferenceWithValueDeclaration,
};
}
+235 -60
View File
@@ -2025,13 +2025,52 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
emit(node.name);
decreaseIndentIf(indentedBeforeDot, indentedAfterDot);
}
function emitQualifiedName(node: QualifiedName) {
emit(node.left);
write(".");
emit(node.right);
}
function emitQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean) {
if (node.left.kind === SyntaxKind.Identifier) {
emitEntityNameAsExpression(node.left, useFallback);
}
else if (useFallback) {
let temp = createAndRecordTempVariable(TempFlags.Auto);
write("(");
emitNodeWithoutSourceMap(temp);
write(" = ");
emitEntityNameAsExpression(node.left, /*useFallback*/ true);
write(") && ");
emitNodeWithoutSourceMap(temp);
}
else {
emitEntityNameAsExpression(node.left, /*useFallback*/ false);
}
write(".");
emitNodeWithoutSourceMap(node.right);
}
function emitEntityNameAsExpression(node: EntityName, useFallback: boolean) {
switch (node.kind) {
case SyntaxKind.Identifier:
if (useFallback) {
write("typeof ");
emitExpressionIdentifier(<Identifier>node);
write(" !== 'undefined' && ");
}
emitExpressionIdentifier(<Identifier>node);
break;
case SyntaxKind.QualifiedName:
emitQualifiedNameAsExpression(<QualifiedName>node, useFallback);
break;
}
}
function emitIndexedAccess(node: ElementAccessExpression) {
if (tryEmitConstantValue(node)) {
return;
@@ -4569,83 +4608,219 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
}
return false;
}
/** Serializes the type of a declaration to an appropriate JS constructor value. Used by the __metadata decorator for a class member. */
function emitSerializedTypeOfNode(node: Node) {
// serialization of the type of a declaration uses the following rules:
//
// * The serialized type of a ClassDeclaration is "Function"
// * The serialized type of a ParameterDeclaration is the serialized type of its type annotation.
// * The serialized type of a PropertyDeclaration is the serialized type of its type annotation.
// * The serialized type of an AccessorDeclaration is the serialized type of the return type annotation of its getter or parameter type annotation of its setter.
// * The serialized type of any other FunctionLikeDeclaration is "Function".
// * The serialized type of any other node is "void 0".
//
// For rules on serializing type annotations, see `serializeTypeNode`.
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
write("Function");
return;
case SyntaxKind.PropertyDeclaration:
emitSerializedTypeNode((<PropertyDeclaration>node).type);
return;
case SyntaxKind.Parameter:
emitSerializedTypeNode((<ParameterDeclaration>node).type);
return;
case SyntaxKind.GetAccessor:
emitSerializedTypeNode((<AccessorDeclaration>node).type);
return;
case SyntaxKind.SetAccessor:
emitSerializedTypeNode(getSetAccessorTypeAnnotationNode(<AccessorDeclaration>node));
return;
}
if (isFunctionLike(node)) {
write("Function");
return;
}
write("void 0");
}
function emitSerializedTypeNode(node: TypeNode) {
switch (node.kind) {
case SyntaxKind.VoidKeyword:
write("void 0");
return;
case SyntaxKind.ParenthesizedType:
emitSerializedTypeNode((<ParenthesizedTypeNode>node).type);
return;
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
write("Function");
return;
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
write("Array");
return;
case SyntaxKind.BooleanKeyword:
write("Boolean");
return;
case SyntaxKind.StringKeyword:
case SyntaxKind.StringLiteral:
write("String");
return;
case SyntaxKind.NumberKeyword:
write("Number");
return;
case SyntaxKind.SymbolKeyword:
write("Symbol");
return;
case SyntaxKind.TypeReference:
emitSerializedTypeReferenceNode(<TypeReferenceNode>node);
return;
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeLiteral:
case SyntaxKind.UnionType:
case SyntaxKind.AnyKeyword:
break;
default:
Debug.fail("Cannot serialize unexpected type node.");
break;
}
write("Object");
}
/** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */
function emitSerializedTypeReferenceNode(node: TypeReferenceNode) {
let typeName = node.typeName;
if (resolver.isTypeReferenceWithValueDeclaration(node)) {
emitEntityNameAsExpression(typeName, /*useFallback*/ false);
}
else {
write("(");
emitEntityNameAsExpression(typeName, /*useFallback*/ true);
write(") || Object");
}
}
/** Serializes the parameter types of a function or the constructor of a class. Used by the __metadata decorator for a method or set accessor. */
function emitSerializedParameterTypesOfNode(node: Node) {
// serialization of parameter types uses the following rules:
//
// * If the declaration is a class, the parameters of the first constructor with a body are used.
// * If the declaration is function-like and has a body, the parameters of the function are used.
//
// For the rules on serializing the type of each parameter declaration, see `serializeTypeOfDeclaration`.
if (node) {
var valueDeclaration: FunctionLikeDeclaration;
if (node.kind === SyntaxKind.ClassDeclaration) {
valueDeclaration = getFirstConstructorWithBody(<ClassDeclaration>node);
}
else if (isFunctionLike(node) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
valueDeclaration = <FunctionLikeDeclaration>node;
}
if (valueDeclaration) {
var parameters = valueDeclaration.parameters;
var parameterCount = parameters.length;
if (parameterCount > 0) {
for (var i = 0; i < parameterCount; i++) {
if (i > 0) {
write(", ");
}
if (parameters[i].dotDotDotToken) {
var parameterType = parameters[i].type;
if (parameterType.kind === SyntaxKind.ArrayType) {
parameterType = (<ArrayTypeNode>parameterType).elementType;
}
else if (parameterType.kind === SyntaxKind.TypeReference && (<TypeReferenceNode>parameterType).typeArguments && (<TypeReferenceNode>parameterType).typeArguments.length === 1) {
parameterType = (<TypeReferenceNode>parameterType).typeArguments[0];
}
else {
parameterType = undefined;
}
emitSerializedTypeNode(parameterType);
}
else {
emitSerializedTypeOfNode(parameters[i]);
}
}
}
}
}
}
/** Serializes the return type of function. Used by the __metadata decorator for a method. */
function emitSerializedReturnTypeOfNode(node: Node): string | string[] {
if (node && isFunctionLike(node)) {
emitSerializedTypeNode((<FunctionLikeDeclaration>node).type);
return;
}
write("void 0");
}
function emitSerializedTypeMetadata(node: Declaration, writeComma: boolean): number {
// This method emits the serialized type metadata for a decorator target.
// The caller should have already tested whether the node has decorators.
let argumentsWritten = 0;
if (compilerOptions.emitDecoratorMetadata) {
if (shouldEmitTypeMetadata(node)) {
var serializedType = resolver.serializeTypeOfNode(node);
if (serializedType) {
if (writeComma) {
write(", ");
}
writeLine();
write("__metadata('design:type', ");
emitSerializedType(node, serializedType);
write(")");
argumentsWritten++;
if (writeComma) {
write(", ");
}
writeLine();
write("__metadata('design:type', ");
emitSerializedTypeOfNode(node);
write(")");
argumentsWritten++;
}
if (shouldEmitParamTypesMetadata(node)) {
var serializedTypes = resolver.serializeParameterTypesOfNode(node);
if (serializedTypes) {
if (writeComma || argumentsWritten) {
write(", ");
}
writeLine();
write("__metadata('design:paramtypes', [");
for (var i = 0; i < serializedTypes.length; ++i) {
if (i > 0) {
write(", ");
}
emitSerializedType(node, serializedTypes[i]);
}
write("])");
argumentsWritten++;
if (writeComma || argumentsWritten) {
write(", ");
}
writeLine();
write("__metadata('design:paramtypes', [");
emitSerializedParameterTypesOfNode(node);
write("])");
argumentsWritten++;
}
if (shouldEmitReturnTypeMetadata(node)) {
var serializedType = resolver.serializeReturnTypeOfNode(node);
if (serializedType) {
if (writeComma || argumentsWritten) {
write(", ");
}
writeLine();
write("__metadata('design:returntype', ");
emitSerializedType(node, serializedType);
write(")");
argumentsWritten++;
if (writeComma || argumentsWritten) {
write(", ");
}
writeLine();
write("__metadata('design:returntype', ");
emitSerializedReturnTypeOfNode(node);
write(")");
argumentsWritten++;
}
}
return argumentsWritten;
}
function serializeTypeNameSegment(location: Node, path: string[], index: number): string {
switch (index) {
case 0:
return `typeof ${path[index]} !== 'undefined' && ${path[index]}`;
case 1:
return `${serializeTypeNameSegment(location, path, index - 1) }.${path[index]}`;
default:
let temp = createAndRecordTempVariable(TempFlags.Auto).text;
return `(${temp} = ${serializeTypeNameSegment(location, path, index - 1) }) && ${temp}.${path[index]}`;
}
}
function emitSerializedType(location: Node, name: string | string[]): void {
if (typeof name === "string") {
write(name);
return;
}
else {
Debug.assert(name.length > 0, "Invalid serialized type name");
write(`(${serializeTypeNameSegment(location, name, name.length - 1) }) || Object`);
}
}
function emitInterfaceDeclaration(node: InterfaceDeclaration) {
emitOnlyPinnedOrTripleSlashComments(node);
}
+1 -3
View File
@@ -1525,9 +1525,7 @@ namespace ts {
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
getBlockScopedVariableId(node: Identifier): number;
getReferencedValueDeclaration(reference: Identifier): Declaration;
serializeTypeOfNode(node: Node): string | string[];
serializeParameterTypesOfNode(node: Node): (string | string[])[];
serializeReturnTypeOfNode(node: Node): string | string[];
isTypeReferenceWithValueDeclaration(node: TypeReferenceNode): boolean;
}
export const enum SymbolFlags {
+4
View File
@@ -1684,6 +1684,10 @@ namespace ts {
});
}
export function getSetAccessorTypeAnnotationNode(accessor: AccessorDeclaration): TypeNode {
return accessor && accessor.parameters.length > 0 && accessor.parameters[0].type;
}
export function shouldEmitToOwnFile(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean {
if (!isDeclarationFile(sourceFile)) {
if ((isExternalModule(sourceFile) || !compilerOptions.out)) {