Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(902)

Unified Diff: lib/facade_converter.ts

Issue 2225953002: Strip more unused features. (Closed) Base URL: git@github.com:dart-lang/js_facade_gen.git@master
Patch Set: Fix types Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/expression.ts ('k') | lib/literal.ts » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/facade_converter.ts
diff --git a/lib/facade_converter.ts b/lib/facade_converter.ts
index 76346c4e9c4df4e5ee823d0c03bee7a18fc9791f..c43dda92e5216def334104a373d7d09e45d13ab0 100644
--- a/lib/facade_converter.ts
+++ b/lib/facade_converter.ts
@@ -1,40 +1,197 @@
-import * as base from './base';
import * as ts from 'typescript';
+
+import * as base from './base';
+import {Set} from './base';
+import {DART_LIBRARIES_FOR_BROWSER_TYPES, TS_TO_DART_TYPENAMES} from './dart_libraries_for_browser_types';
import {Transpiler} from './main';
+import {MergedType} from './merge';
type CallHandler = (c: ts.CallExpression, context: ts.Expression) => void;
type PropertyHandler = (c: ts.PropertyAccessExpression) => void;
-type Set = {
- [s: string]: boolean
-};
const FACADE_DEBUG = false;
-
const FACADE_NODE_MODULES_PREFIX = /^(\.\.\/)*node_modules\//;
-function merge(...args: {[key: string]: any}[]): {[key: string]: any} {
- let returnObject: {[key: string]: any} = {};
- for (let arg of args) {
- for (let key of Object.getOwnPropertyNames(arg)) {
- returnObject[key] = arg[key];
+// These constants must be kept in sync with package:func/func.dart which
+// provides a cannonical set of typedefs defining commonly used function types
+// to simplify specifying function types in Dart.
+const MAX_DART_FUNC_ACTION_PARAMETERS = 4;
+const MAX_DART_FUNC_ACTION_PARAMETERS_OPTIONAL = 1;
+
+/**
+ * Prefix to add to a variable name that leaves the JS name referenced
+ * unchanged.
+ */
+const DART_RESERVED_NAME_PREFIX = 'JS$';
+
+export function fixupIdentifierName(text: string): string {
+ return (FacadeConverter.DART_RESERVED_WORDS.indexOf(text) !== -1 ||
+ FacadeConverter.DART_OTHER_KEYWORDS.indexOf(text) !== -1 || text[0] === '_') ?
+ DART_RESERVED_NAME_PREFIX + text :
+ text;
+}
+
+function numOptionalParameters(parameters: ts.NodeArray<ts.ParameterDeclaration>): number {
+ for (let i = 0; i < parameters.length; ++i) {
+ if (parameters[i].questionToken) return parameters.length - i;
+ }
+ return 0;
+}
+
+function hasVarArgs(parameters: ts.NodeArray<ts.ParameterDeclaration>): boolean {
+ for (let i = 0; i < parameters.length; ++i) {
+ if (parameters[i].dotDotDotToken) return true;
+ }
+ return false;
+}
+
+/**
+ * Generates the JavaScript expression required to reference the node
+ * from the global context. InterfaceDeclarations do not technically correspond
+ * to actual JavaScript objects but we still generate a reference path for them
+ * so that we have a guaranteed unique name.
+ *
+ * Example JS path:
+ * module m1 {
+ * module m2 {
+ * class foo { }
+ * }
+ * }
+ * Path: m1.m2.foo
+ */
+function fullJsPath(node: base.NamedDeclaration): string {
+ let parts: Array<string> = [base.ident(node.name)];
+ let p: ts.Node = node.parent;
+ while (p != null) {
+ let kind = p.kind;
+ if (kind === ts.SyntaxKind.ModuleDeclaration || kind === ts.SyntaxKind.InterfaceDeclaration ||
+ kind === ts.SyntaxKind.ClassDeclaration) {
+ parts.unshift(base.ident((<base.NamedDeclaration>p).name));
}
+ p = p.parent;
+ }
+ return parts.join('.');
+}
+
+class DartNameRecord {
+ name: string;
+ constructor(private node: ts.Node, name: string, private library: DartLibrary) {
+ this.name = name;
}
- return returnObject;
}
+export class DartLibrary {
+ constructor(private fileName: string) { this.usedNames = {}; }
+
+ /**
+ * @returns {boolean} whether the name was added.
+ */
+ addName(name: string): boolean {
+ if (Object.prototype.hasOwnProperty.call(this.usedNames, name)) {
+ return false;
+ }
+ this.usedNames[name] = true;
+ return true;
+ }
+
+ private usedNames: Set;
+}
+
+export class NameRewriter {
+ private dartTypes: ts.Map<DartNameRecord> = {};
+ // TODO(jacobr): use libraries to track what library imports need to be
+ // emitted. Complex cases such as ts.SyntaxKind.TypeReference
+ // where emitting the type could require emitting imports to libraries not
+ // specified in the imports listed in TypeScript. Additionally, d.ts files
+ // can contain multiple modules and for readability we may want an option
+ // to emit each of those modules as a separate Dart library. That would also
+ // require tracking what external libraries are referenced.
+ private libraries: ts.Map<DartLibrary> = {};
+
+ private computeName(node: base.NamedDeclaration): DartNameRecord {
+ let fullPath = fullJsPath(node);
+ if (Object.prototype.hasOwnProperty.call(this.dartTypes, fullPath)) {
+ return this.dartTypes[fullPath];
+ }
+ let sourceFile = <ts.SourceFile>base.getAncestor(node, ts.SyntaxKind.SourceFile);
+ let fileName = sourceFile.fileName;
+ let library: DartLibrary;
+ if (Object.prototype.hasOwnProperty.call(this.libraries, fileName)) {
+ library = this.libraries[fileName];
+ } else {
+ library = new DartLibrary(fileName);
+ this.libraries[fileName] = library;
+ }
+ let parts = fullPath.split('.');
+ for (let i = parts.length - 1; i >= 0; i--) {
+ // Find a unique name by including more of the module hierarchy in the
+ // name. This is an arbitrary but hopefully unsurprising scheme to
+ // generate unique names. There may be classes or members with conflicting
+ // names due to a single d.ts file containing multiple modules.
+ // TODO(jacobr): we should suppress this behavior outside of JS Interop
+ // mode and instead generate a compile error if there are conflicting
+ // names.
+ let candidateName = fixupIdentifierName(parts.slice(i).join('_'));
+ if (library.addName(candidateName)) {
+ // Able to add name to library.
+ let ret = new DartNameRecord(node, candidateName, library);
+ this.dartTypes[fullPath] = ret;
+ return ret;
+ }
+ }
+
+ // Usually the module name prefixes should be sufficient to disambiguate
+ // names but sometimes we need to add a numeric prefix as well to
+ // disambiguate. We could alternately append the full module prefix as well
+ // to make the name choice completely unsurprising albeit even uglier.
+ // This case should be very rarely hit.
+ let i = 2;
+ while (true) {
+ let candidateName = parts[parts.length - 1] + i;
+ if (library.addName(candidateName)) {
+ // Able to add name to library.
+ let ret = new DartNameRecord(node, candidateName, library);
+ this.dartTypes[fullPath] = ret;
+ return ret;
+ }
+ i++;
+ }
+ }
+
+ lookupName(node: base.NamedDeclaration, context: ts.Node) { return this.computeName(node).name; }
+}
export class FacadeConverter extends base.TranspilerBase {
private tc: ts.TypeChecker;
- private candidateProperties: {[propertyName: string]: boolean} = {};
+ // For the Dart keyword list see
+ // https://www.dartlang.org/docs/dart-up-and-running/ch02.html#keywords
+ static DART_RESERVED_WORDS =
+ ('assert break case catch class const continue default do else enum extends false final ' +
+ 'finally for if in is new null rethrow return super switch this throw true try let void ' +
+ 'while with')
+ .split(/ /);
+
+ // These are the built-in and limited keywords.
+ static DART_OTHER_KEYWORDS =
+ ('abstract as async await deferred dynamic export external factory get implements import ' +
+ 'library operator part set static sync typedef yield call')
+ .split(/ /);
+
private candidateTypes: {[typeName: string]: boolean} = {};
private typingsRootRegex: RegExp;
private genericMethodDeclDepth = 0;
- constructor(transpiler: Transpiler, typingsRoot = '') {
+ constructor(
+ transpiler: Transpiler, typingsRoot = '', private nameRewriter: NameRewriter,
+ private useHtml: boolean) {
super(transpiler);
- this.extractPropertyNames(this.callHandlers, this.candidateProperties);
- this.extractPropertyNames(this.propertyHandlers, this.candidateProperties);
- this.extractPropertyNames(this.TS_TO_DART_TYPENAMES, this.candidateTypes);
+ if (useHtml) {
+ this.extractPropertyNames(TS_TO_DART_TYPENAMES, this.candidateTypes);
+ Object.keys(DART_LIBRARIES_FOR_BROWSER_TYPES)
+ .forEach((propName) => this.candidateTypes[propName] = true);
+ } else {
+ this.extractPropertyNames(TS_TO_DART_TYPENAMES, this.candidateTypes);
+ }
this.typingsRootRegex = new RegExp('^' + typingsRoot.replace('.', '\\.'));
}
@@ -42,6 +199,9 @@ export class FacadeConverter extends base.TranspilerBase {
private extractPropertyNames(m: ts.Map<ts.Map<any>>, candidates: {[k: string]: boolean}) {
for (let fileName of Object.keys(m)) {
const file = m[fileName];
+ if (file === undefined) {
+ return;
+ }
Object.keys(file)
.map((propName) => propName.substring(propName.lastIndexOf('.') + 1))
.forEach((propName) => candidates[propName] = true);
@@ -50,63 +210,6 @@ export class FacadeConverter extends base.TranspilerBase {
setTypeChecker(tc: ts.TypeChecker) { this.tc = tc; }
- maybeHandleCall(c: ts.CallExpression): boolean {
- if (!this.tc) return false;
- let {context, symbol} = this.getCallInformation(c);
- if (!symbol) {
- // getCallInformation returns a symbol if we understand this call.
- return false;
- }
- let handler = this.getHandler(c, symbol, this.callHandlers);
- return handler && !handler(c, context);
- }
-
- handlePropertyAccess(pa: ts.PropertyAccessExpression): boolean {
- if (!this.tc) return;
- let ident = pa.name.text;
- if (!this.candidateProperties.hasOwnProperty(ident)) return false;
- let symbol = this.tc.getSymbolAtLocation(pa.name);
- if (!symbol) {
- this.reportMissingType(pa, ident);
- return false;
- }
-
- let handler = this.getHandler(pa, symbol, this.propertyHandlers);
- return handler && !handler(pa);
- }
-
- /**
- * Searches for type references that require extra imports and emits the imports as necessary.
- */
- emitExtraImports(sourceFile: ts.SourceFile) {
- let libraries = <ts.Map<string>>{
- 'XMLHttpRequest': 'dart:html',
- 'KeyboardEvent': 'dart:html',
- 'Uint8Array': 'dart:typed_arrays',
- 'ArrayBuffer': 'dart:typed_arrays',
- 'Promise': 'dart:async',
- };
- let emitted: Set = {};
- this.emitImports(sourceFile, libraries, emitted, sourceFile);
- }
-
- private emitImports(
- n: ts.Node, libraries: ts.Map<string>, emitted: Set, sourceFile: ts.SourceFile): void {
- if (n.kind === ts.SyntaxKind.TypeReference) {
- let type = base.ident((<ts.TypeReferenceNode>n).typeName);
- if (libraries.hasOwnProperty(type)) {
- let toEmit = libraries[type];
- if (!emitted[toEmit]) {
- this.emit(`import "${toEmit}";`);
- emitted[toEmit] = true;
- }
- }
- }
-
- n.getChildren(sourceFile)
- .forEach((child: ts.Node) => this.emitImports(child, libraries, emitted, sourceFile));
- }
-
pushTypeParameterNames(n: ts.FunctionLikeDeclaration) {
if (!n.typeParameters) return;
this.genericMethodDeclDepth++;
@@ -137,11 +240,12 @@ export class FacadeConverter extends base.TranspilerBase {
}
/**
- * The Dart Development Compiler (DDC) has a syntax extension that uses comments to emulate
- * generic methods in Dart. ts2dart has to hack around this and keep track of which type names
- * in the current scope are actually DDC type parameters and need to be emitted in comments.
+ * The Dart analyzer has a syntax extension that uses comments to emulate
+ * generic methods in Dart. We work around this and keep track of which type
+ * names in the current scope need to be emitted in comments.
*
- * TODO(martinprobst): Remove this once the DDC hack has made it into Dart proper.
+ * TODO(jacobr): Remove this once all Dart implementations support generic
+ * methods.
*/
private isGenericMethodTypeParameterName(name: ts.EntityName): boolean {
// Avoid checking this unless needed.
@@ -158,6 +262,169 @@ export class FacadeConverter extends base.TranspilerBase {
return symbol.declarations.some(d => d.parent.kind === ts.SyntaxKind.FunctionDeclaration);
}
+ generateTypeList(types: ts.TypeNode[], insideComment: boolean, seperator = ','): string {
+ let that = this;
+ return types.map((type) => { return that.generateDartTypeName(type, insideComment); })
+ .join(seperator);
+ }
+
+ maybeGenerateTypeArguments(
+ n: {typeArguments?: ts.NodeArray<ts.TypeNode>}, insideComment: boolean): string {
+ if (!n.typeArguments) return '';
+ return '<' + this.generateTypeList(n.typeArguments, insideComment) + '>';
+ }
+
+ generateDartTypeName(node: ts.TypeNode, insideComment: boolean): string {
+ let name: string;
+ let comment: string;
+ if (!node) {
+ return 'dynamic';
+ }
+ switch (node.kind) {
+ case ts.SyntaxKind.TypeQuery:
+ let query = <ts.TypeQueryNode>node;
+ name = 'dynamic';
+ name += '/* Dart does not support TypeQuery: typeof ' + base.ident(query.exprName) + ' */';
+ break;
+ case ts.SyntaxKind.LastTypeNode:
+ let type = (node as ts.ParenthesizedTypeNode).type;
+ if (!type) {
+ // This case occurs for String literal types
+ comment = node.getText();
+ // TODO(jacobr): find a better way to detect string literal types.
+ name = comment[0] === '"' ? 'String' : 'dynamic';
+ break;
+ }
+ return this.generateDartTypeName(type, insideComment);
+ case ts.SyntaxKind.TypePredicate:
+ return this.generateDartTypeName((node as ts.TypePredicateNode).type, insideComment);
+ case ts.SyntaxKind.TupleType:
+ let tuple = <ts.TupleTypeNode>node;
+ name = 'List<';
+ let mergedType = new MergedType(this);
+ tuple.elementTypes.forEach((t) => mergedType.merge(t));
+ name += this.generateDartTypeName(mergedType.toTypeNode(), insideComment);
+ name += '>';
+ comment = 'Tuple<' + this.generateTypeList(tuple.elementTypes, insideComment) + '>';
+ break;
+ case ts.SyntaxKind.UnionType:
+ let union = <ts.UnionTypeNode>node;
+ // TODO(jacobr): this isn't fundamentally JS Interop specific but we
+ // choose to be more aggressive at finding a useful value for the
+ // union when in JS Interop mode while otherwise we expect that union
+ // types will not be used extensively.
+ let simpleType = this.toSimpleDartType(union.types);
+ if (simpleType) {
+ name = this.generateDartTypeName(simpleType, insideComment);
+ } else {
+ name = 'dynamic';
+ }
+ let types = union.types;
+ comment = this.generateTypeList(types, true, '|');
+ break;
+ case ts.SyntaxKind.TypePredicate:
+ return this.generateDartTypeName((node as ts.TypePredicateNode).type, insideComment);
+ case ts.SyntaxKind.TypeReference:
+ let typeRef = <ts.TypeReferenceNode>node;
+ name = this.generateDartName(typeRef.typeName, insideComment) +
+ this.maybeGenerateTypeArguments(typeRef, insideComment);
+ break;
+ case ts.SyntaxKind.TypeLiteral:
+ let members = (<ts.TypeLiteralNode>node).members;
+ if (members.length === 1 && members[0].kind === ts.SyntaxKind.IndexSignature) {
+ let indexSig = <ts.IndexSignatureDeclaration>(members[0]);
+ if (indexSig.parameters.length > 1) {
+ this.reportError(indexSig, 'Expected an index signature to have a single parameter');
+ }
+ // Unfortunately for JS interop, we cannot treat JS Objects as Dart
+ // Map objects. We could treat them as JSMap<indexSig.type>
+ // if we define a base JSMap type that is Map like but not actually
+ // a map.
+ name = 'dynamic';
+ comment = 'JSMap of <' + this.generateDartTypeName(indexSig.parameters[0].type, true) +
+ ',' + this.generateDartTypeName(indexSig.type, true) + '>';
+ } else {
+ name = 'dynamic';
+ comment = node.getText();
+ }
+ break;
+ case ts.SyntaxKind.FunctionType:
+ let callSignature = <ts.FunctionOrConstructorTypeNode>node;
+ let parameters = callSignature.parameters;
+
+ // Use a function signature from package:func where possible.
+ let numOptional = numOptionalParameters(parameters);
+ let isVoid = callSignature.type && callSignature.type.kind === ts.SyntaxKind.VoidKeyword;
+ if (parameters.length <= MAX_DART_FUNC_ACTION_PARAMETERS &&
+ numOptional <= MAX_DART_FUNC_ACTION_PARAMETERS_OPTIONAL && !hasVarArgs(parameters)) {
+ this.emitImport('package:func/func.dart');
+ let typeDefName = (isVoid) ? 'VoidFunc' : 'Func';
+ typeDefName += parameters.length;
+ if (numOptional > 0) {
+ typeDefName += 'Opt' + numOptional;
+ }
+ name = typeDefName;
+ let numArgs = parameters.length + (isVoid ? 0 : 1);
+ if (numArgs > 0) {
+ name += '<';
+ }
+ let isFirst = true;
+ for (let i = 0; i < parameters.length; ++i) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ name += ', ';
+ }
+ name += this.generateDartTypeName(parameters[i].type, insideComment);
+ }
+ if (!isVoid) {
+ if (!isFirst) {
+ name += ', ';
+ }
+ name += this.generateDartTypeName(callSignature.type, insideComment);
+ }
+ if (numArgs > 0) {
+ name += '>';
+ }
+ } else {
+ name = 'Function';
+ if (node.getSourceFile()) {
+ comment = node.getText();
+ }
+ }
+ break;
+ case ts.SyntaxKind.ArrayType:
+ name = 'List' +
+ '<' + this.generateDartTypeName((<ts.ArrayTypeNode>node).elementType, insideComment) +
+ '>';
+ break;
+ case ts.SyntaxKind.NumberKeyword:
+ name = 'num';
+ break;
+ case ts.SyntaxKind.StringLiteral:
+ case ts.SyntaxKind.StringKeyword:
+ name = 'String';
+ break;
+ case ts.SyntaxKind.VoidKeyword:
+ name = 'void';
+ break;
+ case ts.SyntaxKind.BooleanKeyword:
+ name = 'bool';
+ break;
+ case ts.SyntaxKind.AnyKeyword:
+ name = 'dynamic';
+ break;
+ default:
+ this.reportError(node, 'Unexpected TypeNode kind');
+ }
+ if (name == null) {
+ name = 'XXX NULLNAME';
+ }
+
+ name = name.trim();
+ return base.formatType(name, comment, insideComment);
+ }
+
visitTypeName(typeName: ts.EntityName) {
if (typeName.kind !== ts.SyntaxKind.Identifier) {
this.visit(typeName);
@@ -167,104 +434,292 @@ export class FacadeConverter extends base.TranspilerBase {
if (this.isGenericMethodTypeParameterName(typeName)) {
// DDC generic methods hack - all names that are type parameters to generic methods have to be
// emitted in comments.
- this.emit('dynamic/*=');
- this.emit(ident);
- this.emit('*/');
+ this.emitType('dynamic', ident);
return;
}
- if (this.candidateTypes.hasOwnProperty(ident) && this.tc) {
- let symbol = this.tc.getSymbolAtLocation(typeName);
- if (!symbol) {
- this.reportMissingType(typeName, ident);
- return;
- }
- let fileAndName = this.getFileAndName(typeName, symbol);
- if (fileAndName) {
- let fileSubs = this.TS_TO_DART_TYPENAMES[fileAndName.fileName];
- if (fileSubs && fileSubs.hasOwnProperty(fileAndName.qname)) {
- this.emit(fileSubs[fileAndName.qname]);
- return;
- }
+ let custom = this.lookupCustomDartTypeName(<ts.Identifier>typeName, this.insideCodeComment);
+ if (custom) {
+ if (custom.comment) {
+ this.emitType(custom.name, custom.comment);
+ } else {
+ this.emit(custom.name);
}
+ } else {
+ this.visit(typeName);
}
- this.emit(ident);
}
- shouldEmitNew(c: ts.CallExpression): boolean {
- if (!this.tc) return true;
-
- let ci = this.getCallInformation(c);
- let symbol = ci.symbol;
- // getCallInformation returns a symbol if we understand this call.
- if (!symbol) return true;
+ getSymbolDeclaration(symbol: ts.Symbol, n?: ts.Node): ts.Declaration {
+ if (!symbol) return null;
+ let decl = symbol.valueDeclaration;
+ if (!decl) {
+ // In the case of a pure declaration with no assignment, there is no value declared.
+ // Just grab the first declaration, hoping it is declared once.
+ if (!symbol.declarations || symbol.declarations.length === 0) {
+ this.reportError(n, 'no declarations for symbol ' + symbol.name);
+ return;
+ }
+ decl = symbol.declarations[0];
+ }
+ return decl;
+ }
- let loc = this.getFileAndName(c, symbol);
- if (!loc) return true;
- let {fileName, qname} = loc;
- let fileSubs = this.callHandlerReplaceNew[fileName];
- if (!fileSubs) return true;
- return !fileSubs[qname];
+ generateDartName(identifier: ts.EntityName, insideComment: boolean): string {
+ let ret = this.lookupCustomDartTypeName(identifier, insideComment);
+ if (ret) return base.formatType(ret.name, ret.comment, insideComment);
+ // TODO(jacobr): handle library import prefixes better. This generally works
+ // but is somewhat fragile.
+ return base.ident(identifier);
}
- private getCallInformation(c: ts.CallExpression): {context?: ts.Expression, symbol?: ts.Symbol} {
+ /**
+ * Returns null if declaration cannot be found or is not valid in Dart.
+ */
+ getDeclaration(identifier: ts.EntityName): ts.Declaration {
let symbol: ts.Symbol;
- let context: ts.Expression;
- let ident: string;
- let expr = c.expression;
+ if (!this.tc) return null;
+
+ symbol = this.tc.getSymbolAtLocation(identifier);
+ let declaration = this.getSymbolDeclaration(symbol, identifier);
+ if (symbol && symbol.flags & ts.SymbolFlags.TypeParameter) {
+ let kind = declaration.parent.kind;
+ // Only kinds of TypeParameters supported by Dart.
+ if (kind !== ts.SyntaxKind.ClassDeclaration && kind !== ts.SyntaxKind.InterfaceDeclaration) {
+ return null;
+ }
+ }
+ return declaration;
+ }
- if (expr.kind === ts.SyntaxKind.Identifier) {
- // Function call.
- ident = base.ident(expr);
- if (!this.candidateProperties.hasOwnProperty(ident)) return {};
- symbol = this.tc.getSymbolAtLocation(expr);
- if (FACADE_DEBUG) console.error('s:', symbol);
+ /**
+ * Returns a custom Dart type name or null if the type isn't a custom Dart
+ * type.
+ */
+ lookupCustomDartTypeName(identifier: ts.EntityName, insideComment: boolean):
+ {name?: string, comment?: string, keep?: boolean} {
+ let ident = base.ident(identifier);
+ let symbol: ts.Symbol;
+ if (!this.tc) return null;
+
+ symbol = this.tc.getSymbolAtLocation(identifier);
+ let declaration = this.getSymbolDeclaration(symbol, identifier);
+ if (symbol && symbol.flags & ts.SymbolFlags.TypeParameter) {
+ let kind = declaration.parent.kind;
+ // Only kinds of TypeParameters supported by Dart.
+ if (kind !== ts.SyntaxKind.ClassDeclaration && kind !== ts.SyntaxKind.InterfaceDeclaration) {
+ return {name: 'dynamic', comment: ident};
+ }
+ }
+ if (this.candidateTypes.hasOwnProperty(ident)) {
if (!symbol) {
- this.reportMissingType(c, ident);
- return {};
+ return null;
}
- context = null;
- } else if (expr.kind === ts.SyntaxKind.PropertyAccessExpression) {
- // Method call.
- let pa = <ts.PropertyAccessExpression>expr;
- ident = base.ident(pa.name);
- if (!this.candidateProperties.hasOwnProperty(ident)) return {};
+ let fileAndName = this.getFileAndName(identifier, symbol);
- symbol = this.tc.getSymbolAtLocation(pa);
- if (FACADE_DEBUG) console.error('s:', symbol);
+ if (fileAndName) {
+ let fileSubs = TS_TO_DART_TYPENAMES[fileAndName.fileName];
+ if (fileSubs) {
+ let dartBrowserType = DART_LIBRARIES_FOR_BROWSER_TYPES.hasOwnProperty(fileAndName.qname);
+ if (dartBrowserType) {
+ this.emitImport(DART_LIBRARIES_FOR_BROWSER_TYPES[fileAndName.qname]);
+ }
+ if (fileSubs.hasOwnProperty(fileAndName.qname)) {
+ return {name: fileSubs[fileAndName.qname]};
+ }
+ if (dartBrowserType) {
+ // Not a rename but has a dart core libraries definition.
+ return {name: fileAndName.qname};
+ }
+ }
+ }
+ }
+ if (symbol) {
+ if (symbol.flags & ts.SymbolFlags.Enum) {
+ // We can't treat JavaScript enums as Dart enums in this case.
+ return {name: 'num', comment: ident};
+ }
+ // TODO(jacobr): we could choose to only support type alais declarations
+ // for JS interop but it seems handling type alaises is generally helpful
+ // without much risk of generating confusing Dart code.
+ if (declaration.kind === ts.SyntaxKind.TypeAliasDeclaration) {
+ let alias = <ts.TypeAliasDeclaration>declaration;
+ if (alias.typeParameters) {
+ // We can handle this case but currently do not.
+ this.reportError(declaration, 'Type parameters for type alaises are not supported');
+ }
+ return {name: this.generateDartTypeName(alias.type, insideComment)};
+ }
- // Error will be reported by PropertyAccess handling below.
- if (!symbol) return {};
+ let kind = declaration.kind;
+ if (kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.InterfaceDeclaration ||
+ kind === ts.SyntaxKind.VariableDeclaration ||
+ kind === ts.SyntaxKind.PropertyDeclaration ||
+ kind === ts.SyntaxKind.FunctionDeclaration) {
+ let name = this.nameRewriter.lookupName(<base.NamedDeclaration>declaration, identifier);
+ if (kind === ts.SyntaxKind.InterfaceDeclaration &&
+ base.isFunctionTypedefLikeInterface(<ts.InterfaceDeclaration>declaration) &&
+ base.getAncestor(identifier, ts.SyntaxKind.HeritageClause)) {
+ // TODO(jacobr): we need to specify a specific call method for this
+ // case if we want to get the most from Dart type checking.
+ return {name: 'Function', comment: name};
+ }
+ return {name: name, keep: true};
+ }
+ }
+ return null;
+ }
- context = pa.expression;
+ // TODO(jacobr): performance of this method could easily be optimized.
+ /**
+ * This method works around the lack of Dart support for union types
+ * generating a valid Dart type that satisfies all the types passed in.
+ */
+ toSimpleDartType(types: Array<ts.TypeNode>) {
+ // We use MergeType to ensure that we have already deduped types that are
+ // equivalent even if they aren't obviously identical.
+ // MergedType will also follow typed aliases, etc which allows us to avoid
+ // including that logic here as well.
+ let mergedType = new MergedType(this);
+ types.forEach((type) => { mergedType.merge(type); });
+ let merged = mergedType.toTypeNode();
+ if (merged.kind === ts.SyntaxKind.UnionType) {
+ // For union types find a Dart type that satisfies all the types.
+ types = (<ts.UnionTypeNode>merged).types;
+ /**
+ * Generate a common base type for an array of types.
+ * The implemented is currently incomplete often returning null when there
+ * might really be a valid common base type.
+ */
+ let common: ts.TypeNode = types[0];
+ for (let i = 1; i < types.length && common != null; ++i) {
+ let type = types[i];
+ if (common !== type) {
+ if (base.isCallableType(common, this.tc) && base.isCallableType(type, this.tc)) {
+ // Fall back to a generic Function type if both types are Function.
+ let fn = <ts.FunctionOrConstructorTypeNode>ts.createNode(ts.SyntaxKind.FunctionType);
+ fn.parameters = <ts.NodeArray<ts.ParameterDeclaration>>[];
+ let parameter = <ts.ParameterDeclaration>ts.createNode(ts.SyntaxKind.Parameter);
+ parameter.dotDotDotToken = ts.createNode(ts.SyntaxKind.DotDotDotToken);
+ let name = <ts.Identifier>ts.createNode(ts.SyntaxKind.Identifier);
+ name.text = 'args';
+ fn.parameters.push(parameter);
+ common = fn;
+ } else {
+ switch (type.kind) {
+ case ts.SyntaxKind.ArrayType:
+ if (common.kind !== ts.SyntaxKind.ArrayType) {
+ return null;
+ }
+ let array = <ts.ArrayTypeNode>ts.createNode(ts.SyntaxKind.ArrayType);
+ array.elementType = this.toSimpleDartType([
+ (common as ts.ArrayTypeNode).elementType, (type as ts.ArrayTypeNode).elementType
+ ]);
+ common = array;
+ break;
+ // case ts.SyntaxKind
+ case ts.SyntaxKind.TypeReference:
+ if (common.kind !== ts.SyntaxKind.TypeReference) {
+ return null;
+ }
+ common = this.commonSupertype(common, type);
+ break;
+
+ default:
+ return null;
+ }
+ }
+ }
+ }
+ return common;
}
- return {context, symbol};
+ return merged;
+ }
+
+ toTypeNode(type: ts.Type): ts.TypeNode {
+ if (!type) return null;
+ let symbol = type.getSymbol();
+ if (!symbol) return null;
+
+ let referenceType = <ts.TypeReferenceNode>ts.createNode(ts.SyntaxKind.TypeReference);
+ // TODO(jacobr): property need to prefix the name better.
+ referenceType.typeName = this.createEntityName(symbol);
+ referenceType.typeName.parent = referenceType;
+ return referenceType;
}
- private getHandler<T>(n: ts.Node, symbol: ts.Symbol, m: ts.Map<ts.Map<T>>): T {
- let loc = this.getFileAndName(n, symbol);
- if (!loc) return null;
- let {fileName, qname} = loc;
- let fileSubs = m[fileName];
- if (!fileSubs) return null;
- return fileSubs[qname];
+ createEntityName(symbol: ts.Symbol): ts.EntityName {
+ let parts = this.tc.getFullyQualifiedName(symbol).split('.');
+ let identifier = <ts.Identifier>ts.createNode(ts.SyntaxKind.Identifier);
+ identifier.text = parts[parts.length - 1];
+ // TODO(jacobr): do we need to include all parts in the entity name?
+ return identifier;
+ }
+
+ safeGetBaseTypes(type: ts.InterfaceType): ts.ObjectType[] {
+ // For an unknown, calling TypeChecker.getBaseTypes on an interface
+ // that is a typedef like interface causes the typescript compiler to stack
+ // overflow. Not sure if this is a bug in the typescript compiler or I am
+ // missing something obvious.
+ let declaration = base.getDeclaration(type) as ts.InterfaceDeclaration;
+ if (base.isFunctionTypedefLikeInterface(declaration)) {
+ return [];
+ }
+ return this.tc.getBaseTypes(type);
+ }
+
+ // TODO(jacobr): all of these subtype checks are fragile and are likely a
+ // mistake. We would be better off handling subtype relationships in Dart
+ // where we could reuse an existing Dart type system.
+ checkTypeSubtypeOf(source: ts.Type, target: ts.Type) {
+ if (source === target) return true;
+ if (!(source.flags & ts.TypeFlags.Interface)) return false;
+ let baseTypes = this.safeGetBaseTypes(source as ts.InterfaceType);
+ for (let i = 0; i < baseTypes.length; ++i) {
+ if (baseTypes[i] === target) return true;
+ }
+ return false;
+ }
+
+ commonSupertype(nodeA: ts.TypeNode, nodeB: ts.TypeNode): ts.TypeNode {
+ if (nodeA == null || nodeB == null) return null;
+ return this.toTypeNode(this.getCommonSupertype(
+ this.tc.getTypeAtLocation(nodeA), this.tc.getTypeAtLocation(nodeB)));
+ }
+
+ getCommonSupertype(a: ts.Type, b: ts.Type): ts.Type {
+ if (a === b) return a;
+ // This logic was probably a mistake. It adds a lot of complexity and we can
+ // do better performing these calculations in the Dart analyzer based
+ // directly on the union types specified in comments.
+ return null;
+ /*
+ if (!(a.flags & ts.TypeFlags.Interface) || !(b.flags & ts.TypeFlags.Interface)) {
+ return null;
+ }
+
+ let bestCommonSuperType: ts.Type = null;
+ let candidatesA = this.safeGetBaseTypes(a as ts.InterfaceType);
+ candidatesA.push(a);
+
+ for (let i = 0; i < candidatesA.length; ++i) {
+ let type = candidatesA[i];
+ if (this.checkTypeSubtypeOf(b, type)) {
+ if (!bestCommonSuperType || this.checkTypeSubtypeOf(bestCommonSuperType, type)) {
+ bestCommonSuperType = type;
+ }
+ }
+ }
+ return bestCommonSuperType;
+ */
}
private getFileAndName(n: ts.Node, originalSymbol: ts.Symbol): {fileName: string, qname: string} {
let symbol = originalSymbol;
while (symbol.flags & ts.SymbolFlags.Alias) symbol = this.tc.getAliasedSymbol(symbol);
- let decl = symbol.valueDeclaration;
- if (!decl) {
- // In the case of a pure declaration with no assignment, there is no value declared.
- // Just grab the first declaration, hoping it is declared once.
- if (!symbol.declarations || symbol.declarations.length === 0) {
- this.reportError(n, 'no declarations for symbol ' + originalSymbol.name);
- return null;
- }
- decl = symbol.declarations[0];
- }
+ let decl = this.getSymbolDeclaration(symbol, n);
const fileName = decl.getSourceFile().fileName;
const canonicalFileName = this.getRelativeFileName(fileName)
@@ -281,446 +736,4 @@ export class FacadeConverter extends base.TranspilerBase {
if (FACADE_DEBUG) console.error('fn:', fileName, 'cfn:', canonicalFileName, 'qn:', qname);
return {fileName: canonicalFileName, qname};
}
-
- private isNamedType(node: ts.Node, fileName: string, qname: string): boolean {
- let symbol = this.tc.getTypeAtLocation(node).getSymbol();
- if (!symbol) return false;
- let actual = this.getFileAndName(node, symbol);
- if (fileName === 'lib' && !(actual.fileName === 'lib' || actual.fileName === 'lib.es6')) {
- return false;
- } else {
- if (fileName !== actual.fileName) return false;
- }
- return qname === actual.qname;
- }
-
- private reportMissingType(n: ts.Node, ident: string) {
- this.reportError(
- n, `Untyped property access to "${ident}" which could be ` + `a special ts2dart builtin. ` +
- `Please add type declarations to disambiguate.`);
- }
-
- isInsideConstExpr(node: ts.Node): boolean {
- return this.isConstCall(
- <ts.CallExpression>this.getAncestor(node, ts.SyntaxKind.CallExpression));
- }
-
- isConstCall(node: ts.Expression): boolean {
- return node && node.kind === ts.SyntaxKind.CallExpression &&
- base.ident((<ts.CallExpression>node).expression) === 'CONST_EXPR';
- }
-
- private emitMethodCall(name: string, args?: ts.Expression[]) {
- this.emit('.');
- this.emitCall(name, args);
- }
-
- private emitCall(name: string, args?: ts.Expression[]) {
- this.emit(name);
- this.emit('(');
- if (args) this.visitList(args);
- this.emit(')');
- }
-
- private stdlibTypeReplacements: ts.Map<string> = {
- 'Date': 'DateTime',
- 'Array': 'List',
- 'XMLHttpRequest': 'HttpRequest',
- 'Uint8Array': 'Uint8List',
- 'ArrayBuffer': 'ByteBuffer',
- 'Promise': 'Future',
-
- // Dart has two different incompatible DOM APIs
- // https://github.com/angular/angular/issues/2770
- 'Node': 'dynamic',
- 'Text': 'dynamic',
- 'Element': 'dynamic',
- 'Event': 'dynamic',
- 'HTMLElement': 'dynamic',
- 'HTMLAnchorElement': 'dynamic',
- 'HTMLStyleElement': 'dynamic',
- 'HTMLInputElement': 'dynamic',
- 'HTMLDocument': 'dynamic',
- 'History': 'dynamic',
- 'Location': 'dynamic',
- };
-
- private TS_TO_DART_TYPENAMES: ts.Map<ts.Map<string>> = {
- 'lib': this.stdlibTypeReplacements,
- 'lib.es6': this.stdlibTypeReplacements,
- 'angular2/src/facade/lang': {'Date': 'DateTime'},
-
- 'rxjs/Observable': {'Observable': 'Stream'},
- 'es6-promise/es6-promise': {'Promise': 'Future'},
- 'es6-shim/es6-shim': {'Promise': 'Future'},
- };
-
- private es6Promises: ts.Map<CallHandler> = {
- 'Promise.catch': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emit('.catchError(');
- this.visitList(c.arguments);
- this.emit(')');
- },
- 'Promise.then': (c: ts.CallExpression, context: ts.Expression) => {
- // then() in Dart doesn't support 2 arguments.
- this.visit(context);
- this.emit('.then(');
- this.visit(c.arguments[0]);
- this.emit(')');
- if (c.arguments.length > 1) {
- this.emit('.catchError(');
- this.visit(c.arguments[1]);
- this.emit(')');
- }
- },
- 'Promise': (c: ts.CallExpression, context: ts.Expression) => {
- if (c.kind !== ts.SyntaxKind.NewExpression) return true;
- this.assert(c, c.arguments.length === 1, 'Promise construction must take 2 arguments.');
- this.assert(
- c, c.arguments[0].kind === ts.SyntaxKind.ArrowFunction ||
- c.arguments[0].kind === ts.SyntaxKind.FunctionExpression,
- 'Promise argument must be a function expression (or arrow function).');
- let callback: ts.FunctionLikeDeclaration;
- if (c.arguments[0].kind === ts.SyntaxKind.ArrowFunction) {
- callback = <ts.FunctionLikeDeclaration>(<ts.ArrowFunction>c.arguments[0]);
- } else if (c.arguments[0].kind === ts.SyntaxKind.FunctionExpression) {
- callback = <ts.FunctionLikeDeclaration>(<ts.FunctionExpression>c.arguments[0]);
- }
- this.assert(
- c, callback.parameters.length > 0 && callback.parameters.length < 3,
- 'Promise executor must take 1 or 2 arguments (resolve and reject).');
-
- const completerVarName = this.uniqueId('completer');
- this.assert(
- c, callback.parameters[0].name.kind === ts.SyntaxKind.Identifier,
- 'First argument of the Promise executor is not a straight parameter.');
- let resolveParameterIdent = <ts.Identifier>(callback.parameters[0].name);
-
- this.emit('(() {'); // Create a new scope.
- this.emit(`Completer ${completerVarName} = new Completer();`);
- this.emit('var');
- this.emit(resolveParameterIdent.text);
- this.emit(`= ${completerVarName}.complete;`);
-
- if (callback.parameters.length === 2) {
- this.assert(
- c, callback.parameters[1].name.kind === ts.SyntaxKind.Identifier,
- 'First argument of the Promise executor is not a straight parameter.');
- let rejectParameterIdent = <ts.Identifier>(callback.parameters[1].name);
- this.emit('var');
- this.emit(rejectParameterIdent.text);
- this.emit(`= ${completerVarName}.completeError;`);
- }
- this.emit('(()');
- this.visit(callback.body);
- this.emit(')();');
- this.emit(`return ${completerVarName}.future;`);
- this.emit('})()');
- },
- };
-
- private stdlibHandlers: ts.Map<CallHandler> = merge(this.es6Promises, {
- 'Array.push': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emitMethodCall('add', c.arguments);
- },
- 'Array.pop': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emitMethodCall('removeLast');
- },
- 'Array.shift': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emit('. removeAt ( 0 )');
- },
- 'Array.unshift': (c: ts.CallExpression, context: ts.Expression) => {
- this.emit('(');
- this.visit(context);
- if (c.arguments.length === 1) {
- this.emit('.. insert ( 0,');
- this.visit(c.arguments[0]);
- this.emit(') ) . length');
- } else {
- this.emit('.. insertAll ( 0, [');
- this.visitList(c.arguments);
- this.emit(']) ) . length');
- }
- },
- 'Array.map': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emitMethodCall('map', c.arguments);
- this.emitMethodCall('toList');
- },
- 'Array.filter': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emitMethodCall('where', c.arguments);
- this.emitMethodCall('toList');
- },
- 'Array.some': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emitMethodCall('any', c.arguments);
- },
- 'Array.slice': (c: ts.CallExpression, context: ts.Expression) => {
- this.emitCall('ListWrapper.slice', [context, ...c.arguments]);
- },
- 'Array.splice': (c: ts.CallExpression, context: ts.Expression) => {
- this.emitCall('ListWrapper.splice', [context, ...c.arguments]);
- },
- 'Array.concat': (c: ts.CallExpression, context: ts.Expression) => {
- this.emit('( new List . from (');
- this.visit(context);
- this.emit(')');
- c.arguments.forEach(arg => {
- if (!this.isNamedType(arg, 'lib', 'Array')) {
- this.reportError(arg, 'Array.concat only takes Array arguments');
- }
- this.emit('.. addAll (');
- this.visit(arg);
- this.emit(')');
- });
- this.emit(')');
- },
- 'Array.join': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- if (c.arguments.length) {
- this.emitMethodCall('join', c.arguments);
- } else {
- this.emit('. join ( "," )');
- }
- },
- 'Array.reduce': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
-
- if (c.arguments.length >= 2) {
- this.emitMethodCall('fold', [c.arguments[1], c.arguments[0]]);
- } else {
- this.emit('. fold ( null ,');
- this.visit(c.arguments[0]);
- this.emit(')');
- }
- },
- 'ArrayConstructor.isArray': (c: ts.CallExpression, context: ts.Expression) => {
- this.emit('( (');
- this.visitList(c.arguments); // Should only be 1.
- this.emit(')');
- this.emit('is List');
- this.emit(')');
- },
- 'Console.log': (c: ts.CallExpression, context: ts.Expression) => {
- this.emit('print(');
- if (c.arguments.length === 1) {
- this.visit(c.arguments[0]);
- } else {
- this.emit('[');
- this.visitList(c.arguments);
- this.emit('].join(" ")');
- }
- this.emit(')');
- },
- 'RegExp.exec': (c: ts.CallExpression, context: ts.Expression) => {
- if (context.kind !== ts.SyntaxKind.RegularExpressionLiteral) {
- // Fail if the exec call isn't made directly on a regexp literal.
- // Multiple exec calls on the same global regexp have side effects
- // (each return the next match), which we can't reproduce with a simple
- // Dart RegExp (users should switch to some facade / wrapper instead).
- this.reportError(
- c, 'exec is only supported on regexp literals, ' +
- 'to avoid side-effect of multiple calls on global regexps.');
- }
- if (c.parent.kind === ts.SyntaxKind.ElementAccessExpression) {
- // The result of the exec call is used for immediate indexed access:
- // this use-case can be accommodated by RegExp.firstMatch, which returns
- // a Match instance with operator[] which returns groups (special index
- // 0 returns the full text of the match).
- this.visit(context);
- this.emitMethodCall('firstMatch', c.arguments);
- } else {
- // In the general case, we want to return a List. To transform a Match
- // into a List of its groups, we alias it in a local closure that we
- // call with the Match value. We are then able to use the group method
- // to generate a List large enough to hold groupCount groups + the
- // full text of the match at special group index 0.
- this.emit('((match) => new List.generate(1 + match.groupCount, match.group))(');
- this.visit(context);
- this.emitMethodCall('firstMatch', c.arguments);
- this.emit(')');
- }
- },
- 'RegExp.test': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emitMethodCall('hasMatch', c.arguments);
- },
- 'String.substr': (c: ts.CallExpression, context: ts.Expression) => {
- this.reportError(
- c, 'substr is unsupported, use substring (but beware of the different semantics!)');
- this.visit(context);
- this.emitMethodCall('substr', c.arguments);
- },
- });
-
- private es6Collections: ts.Map<CallHandler> = {
- 'Map.set': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emit('[');
- this.visit(c.arguments[0]);
- this.emit(']');
- this.emit('=');
- this.visit(c.arguments[1]);
- },
- 'Map.get': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emit('[');
- this.visit(c.arguments[0]);
- this.emit(']');
- },
- 'Map.has': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emitMethodCall('containsKey', c.arguments);
- },
- 'Map.delete': (c: ts.CallExpression, context: ts.Expression) => {
- // JS Map.delete(k) returns whether k was present in the map,
- // convert to:
- // (Map.containsKey(k) && (Map.remove(k) !== null || true))
- // (Map.remove(k) !== null || true) is required to always returns true
- // when Map.containsKey(k)
- this.emit('(');
- this.visit(context);
- this.emitMethodCall('containsKey', c.arguments);
- this.emit('&& (');
- this.visit(context);
- this.emitMethodCall('remove', c.arguments);
- this.emit('!= null || true ) )');
- },
- 'Map.forEach': (c: ts.CallExpression, context: ts.Expression) => {
- let cb: any;
- let params: any;
-
- switch (c.arguments[0].kind) {
- case ts.SyntaxKind.FunctionExpression:
- cb = <ts.FunctionExpression>(c.arguments[0]);
- params = cb.parameters;
- if (params.length !== 2) {
- this.reportError(c, 'Map.forEach callback requires exactly two arguments');
- return;
- }
- this.visit(context);
- this.emit('. forEach ( (');
- this.visit(params[1]);
- this.emit(',');
- this.visit(params[0]);
- this.emit(')');
- this.visit(cb.body);
- this.emit(')');
- break;
-
- case ts.SyntaxKind.ArrowFunction:
- cb = <ts.ArrowFunction>(c.arguments[0]);
- params = cb.parameters;
- if (params.length !== 2) {
- this.reportError(c, 'Map.forEach callback requires exactly two arguments');
- return;
- }
- this.visit(context);
- this.emit('. forEach ( (');
- this.visit(params[1]);
- this.emit(',');
- this.visit(params[0]);
- this.emit(')');
- if (cb.body.kind !== ts.SyntaxKind.Block) {
- this.emit('=>');
- }
- this.visit(cb.body);
- this.emit(')');
- break;
-
- default:
- this.visit(context);
- this.emit('. forEach ( ( k , v ) => (');
- this.visit(c.arguments[0]);
- this.emit(') ( v , k ) )');
- break;
- }
- },
- 'Array.find': (c: ts.CallExpression, context: ts.Expression) => {
- this.visit(context);
- this.emit('. firstWhere (');
- this.visit(c.arguments[0]);
- this.emit(', orElse : ( ) => null )');
- },
- };
-
- private callHandlerReplaceNew: ts.Map<ts.Map<boolean>> = {
- 'es6-promise/es6-promise': {'Promise': true},
- 'es6-shim/es6-shim': {'Promise': true},
- };
-
- private callHandlers: ts.Map<ts.Map<CallHandler>> = {
- 'lib': this.stdlibHandlers,
- 'lib.es6': this.stdlibHandlers,
- 'es6-promise/es6-promise': this.es6Promises,
- 'es6-shim/es6-shim': merge(this.es6Promises, this.es6Collections),
- 'es6-collections/es6-collections': this.es6Collections,
- 'angular2/manual_typings/globals': this.es6Collections,
- 'angular2/src/facade/collection': {
- 'Map': (c: ts.CallExpression, context: ts.Expression): boolean => {
- // The actual Map constructor is special cased for const calls.
- if (!this.isInsideConstExpr(c)) return true;
- if (c.arguments.length) {
- this.reportError(c, 'Arguments on a Map constructor in a const are unsupported');
- }
- if (c.typeArguments) {
- this.emit('<');
- this.visitList(c.typeArguments);
- this.emit('>');
- }
- this.emit('{ }');
- return false;
- },
- },
- 'angular2/src/core/di/forward_ref': {
- 'forwardRef': (c: ts.CallExpression, context: ts.Expression) => {
- // The special function forwardRef translates to an unwrapped value in Dart.
- const callback = <ts.FunctionExpression>c.arguments[0];
- if (callback.kind !== ts.SyntaxKind.ArrowFunction) {
- this.reportError(c, 'forwardRef takes only arrow functions');
- return;
- }
- this.visit(callback.body);
- },
- },
- 'angular2/src/facade/lang': {
- 'CONST_EXPR': (c: ts.CallExpression, context: ts.Expression) => {
- // `const` keyword is emitted in the array literal handling, as it needs to be transitive.
- this.visitList(c.arguments);
- },
- 'normalizeBlank': (c: ts.CallExpression, context: ts.Expression) => {
- // normalizeBlank is a noop in Dart, so erase it.
- this.visitList(c.arguments);
- },
- },
- };
-
- private es6CollectionsProp: ts.Map<PropertyHandler> = {
- 'Map.size': (p: ts.PropertyAccessExpression) => {
- this.visit(p.expression);
- this.emit('.');
- this.emit('length');
- },
- };
- private es6PromisesProp: ts.Map<PropertyHandler> = {
- 'resolve': (p: ts.PropertyAccessExpression) => {
- this.visit(p.expression);
- this.emit('.value');
- },
- 'reject': (p: ts.PropertyAccessExpression) => {
- this.visit(p.expression);
- this.emit('.error');
- },
- };
-
- private propertyHandlers: ts.Map<ts.Map<PropertyHandler>> = {
- 'es6-shim/es6-shim': this.es6CollectionsProp,
- 'es6-collections/es6-collections': this.es6CollectionsProp,
- 'es6-promise/es6-promise': this.es6PromisesProp,
- };
}
« no previous file with comments | « lib/expression.ts ('k') | lib/literal.ts » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698