| Index: lib/declaration.ts | 
| diff --git a/lib/declaration.ts b/lib/declaration.ts | 
| index 68bef4461317208b5688f5a1e4bddff8c07e549a..9f0df8eb189fd8d1bf5d49bbcd9739bf0ff4322a 100644 | 
| --- a/lib/declaration.ts | 
| +++ b/lib/declaration.ts | 
| @@ -3,11 +3,15 @@ import * as ts from 'typescript'; | 
| import * as base from './base'; | 
| import {FacadeConverter} from './facade_converter'; | 
| import {Transpiler} from './main'; | 
| -import {MergedParameter, MergedType} from './merge'; | 
| +import {MergedParameter, MergedType, MergedTypeParameters} from './merge'; | 
|  | 
| export function isFunctionLikeProperty( | 
| decl: ts.PropertyDeclaration|ts.ParameterDeclaration, tc: ts.TypeChecker): boolean { | 
| if (!decl.type) return false; | 
| +  if (decl.name.kind !== ts.SyntaxKind.Identifier) { | 
| +    // No need to promote properties | 
| +    return false; | 
| +  } | 
| let name = base.ident(decl.name); | 
| if (name.match(/^on[A-Z]/)) return false; | 
| return base.isFunctionType(decl.type, tc); | 
| @@ -16,7 +20,6 @@ export function isFunctionLikeProperty( | 
| export default class DeclarationTranspiler extends base.TranspilerBase { | 
| private tc: ts.TypeChecker; | 
|  | 
| -  private moduleStack: string[] = []; | 
| private extendsClass: boolean = false; | 
|  | 
| static NUM_FAKE_REST_PARAMETERS = 5; | 
| @@ -24,19 +27,40 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| setTypeChecker(tc: ts.TypeChecker) { this.tc = tc; } | 
| setFacadeConverter(fc: FacadeConverter) { this.fc = fc; } | 
|  | 
| -  getJsPath(node: ts.Node): string { | 
| -    let path = [].concat(this.moduleStack); | 
| +  getJsPath(node: ts.Node, suppressUnneededPaths = true): string { | 
| +    let path: Array<String> = []; | 
| +    let moduleDecl = | 
| +        base.getAncestor(node, ts.SyntaxKind.ModuleDeclaration) as ts.ModuleDeclaration; | 
| +    while (moduleDecl != null) { | 
| +      path.unshift(moduleDecl.name.text); | 
| +      moduleDecl = | 
| +          base.getAncestor( | 
| +                  moduleDecl.parent, ts.SyntaxKind.ModuleDeclaration) as ts.ModuleDeclaration; | 
| +    } | 
| + | 
| let classDecl = base.getEnclosingClass(node); | 
| if (classDecl) { | 
| -      path.push(classDecl.name.text); | 
| +      if (classDecl.kind === ts.SyntaxKind.InterfaceDeclaration) { | 
| +        let interfaceDecl = classDecl as base.ExtendedInterfaceDeclaration; | 
| +        if (interfaceDecl.classLikeVariableDeclaration) { | 
| +          // We upgrade these variable interface declarations to behave more | 
| +          // like class declarations as we have a valid concrete JS class to | 
| +          // an appropriate class object. | 
| +          return this.getJsPath(interfaceDecl.classLikeVariableDeclaration, false); | 
| +        } | 
| +        return ''; | 
| +      } else { | 
| +        path.push(classDecl.name.text); | 
| +      } | 
| } | 
|  | 
| switch (node.kind) { | 
| case ts.SyntaxKind.ModuleDeclaration: | 
| +        path.push((<ts.ModuleDeclaration>node).name.text); | 
| break; | 
| case ts.SyntaxKind.ClassDeclaration: | 
| case ts.SyntaxKind.InterfaceDeclaration: | 
| -        path.push((<base.ClassLike>node).name.text); | 
| +        // Already handled by call to getEnclosingClass. | 
| break; | 
| case ts.SyntaxKind.EnumDeclaration: | 
| path.push((<ts.EnumDeclaration>node).name.text); | 
| @@ -44,6 +68,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| case ts.SyntaxKind.PropertyDeclaration: | 
| case ts.SyntaxKind.VariableDeclaration: | 
| case ts.SyntaxKind.MethodDeclaration: | 
| +      case ts.SyntaxKind.MethodSignature: | 
| case ts.SyntaxKind.FunctionDeclaration: | 
| case ts.SyntaxKind.GetAccessor: | 
| case ts.SyntaxKind.SetAccessor: | 
| @@ -55,8 +80,9 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| default: | 
| throw 'Internal error. Unexpected node kind:' + node.kind; | 
| } | 
| -    if (path.length === 1) { | 
| -      // No need to specify the path if is simply the node name. | 
| +    if (suppressUnneededPaths && path.length === 1) { | 
| +      // No need to specify the path if is simply the node name or the escaped version of the node | 
| +      // name. | 
| return ''; | 
| } | 
| return path.join('.'); | 
| @@ -64,20 +90,10 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
|  | 
| private isAnonymousInterface(node: ts.Node): boolean { | 
| if (node.kind !== ts.SyntaxKind.InterfaceDeclaration) return false; | 
| -    // This is a bit of a hack but for the purposes of Dart codegen, | 
| -    // interfaces with static members or constructors have a known class name | 
| -    // at least for the purposes of resolving static members. | 
| -    // Example case that triggers this case: | 
| -    // interface Foo { | 
| -    //   bar(); | 
| -    // } | 
| -    // declare let Foo: { | 
| -    //   new(): Foo, | 
| -    //   SOME_STATIC : number; | 
| -    // } | 
| -    return (<ts.InterfaceDeclaration>node).members.every((m: ts.Declaration) => { | 
| -      return m.kind !== ts.SyntaxKind.Constructor && !base.isStatic(m); | 
| -    }); | 
| +    let interfaceDecl = node as base.ExtendedInterfaceDeclaration; | 
| +    // If we were able to associate a variable declaration with the interface definition then | 
| +    // the interface isn't actually anonymous. | 
| +    return !interfaceDecl.classLikeVariableDeclaration; | 
| } | 
|  | 
| maybeEmitJsAnnotation(node: ts.Node) { | 
| @@ -101,12 +117,12 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| /** | 
| * Emit fake constructors to placate the Dart Analyzer for JS Interop classes. | 
| */ | 
| -  maybeEmitFakeConstructors(decl: base.ClassLike) { | 
| +  maybeEmitFakeConstructors(decl: ts.Node) { | 
| if (decl.kind === ts.SyntaxKind.ClassDeclaration) { | 
| // Required to avoid spurious dart errors involving base classes without | 
| // default constructors. | 
| this.emit('// @Ignore\n'); | 
| -      this.fc.visitTypeName(decl.name); | 
| +      this.fc.visitTypeName((<ts.ClassDeclaration>decl).name); | 
| this.emit('.fakeConstructor$()'); | 
| if (this.extendsClass) { | 
| // Required to keep the Dart Analyzer happy when a class has subclasses. | 
| @@ -126,7 +142,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| if (name.kind !== ts.SyntaxKind.Identifier) { | 
| throw 'Internal error: unexpected function name kind:' + name.kind; | 
| } | 
| -    let entry = this.fc.lookupCustomDartTypeName(<ts.Identifier>name, this.insideCodeComment); | 
| +    let entry = this.fc.lookupCustomDartTypeName(<ts.Identifier>name); | 
| if (entry) { | 
| this.emit(entry.name); | 
| return; | 
| @@ -167,7 +183,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| boolean { | 
| let type = <ts.InterfaceType>this.tc.getTypeAtLocation(decl); | 
|  | 
| -    let properties = this.tc.getPropertiesOfType(type); | 
| +    let symbols = this.tc.getPropertiesOfType(type); | 
| let baseTypes = this.tc.getBaseTypes(type); | 
| if (this.notSimpleBagOfProperties(type)) return false; | 
| for (let i = 0; i < baseTypes.length; ++i) { | 
| @@ -175,9 +191,19 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| if (this.notSimpleBagOfProperties(baseType)) return false; | 
| } | 
|  | 
| +    let properties: ts.Declaration[] = []; | 
| + | 
| +    for (let i = 0; i < symbols.length; ++i) { | 
| +      let symbol = symbols[i]; | 
| +      let property = symbol.valueDeclaration; | 
| +      properties.push(property); | 
| +    } | 
| +    return this.hasOnlyPropertiesHelper(properties, outProperties); | 
| +  } | 
| + | 
| +  hasOnlyPropertiesHelper(properties: ts.Declaration[], outProperties: ts.Declaration[]): boolean { | 
| for (let i = 0; i < properties.length; ++i) { | 
| -      let symbol = properties[i]; | 
| -      let node = symbol.valueDeclaration; | 
| +      let node = properties[i]; | 
| switch (node.kind) { | 
| case ts.SyntaxKind.PropertyDeclaration: | 
| case ts.SyntaxKind.PropertySignature: | 
| @@ -195,15 +221,19 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| return outProperties.length > 0; | 
| } | 
|  | 
| -  visitClassBody(decl: base.ClassLike) { | 
| +  visitClassBody(decl: base.ClassLike|ts.TypeLiteralNode, name: ts.Identifier) { | 
| let properties: ts.PropertyDeclaration[] = []; | 
| -    let isPropertyBag = decl.kind === ts.SyntaxKind.InterfaceDeclaration && | 
| -        this.hasOnlyProperties(<ts.InterfaceDeclaration>decl, properties); | 
| +    let isPropertyBag = false; | 
| +    if (decl.kind === ts.SyntaxKind.InterfaceDeclaration) { | 
| +      isPropertyBag = this.hasOnlyProperties(<ts.InterfaceDeclaration>decl, properties); | 
| +    } else if (decl.kind === ts.SyntaxKind.TypeLiteral) { | 
| +      isPropertyBag = this.hasOnlyPropertiesHelper(decl.members, properties); | 
| +    } | 
| this.visitMergingOverloads(decl.members); | 
|  | 
| if (isPropertyBag) { | 
| this.emit('external factory'); | 
| -      this.fc.visitTypeName(decl.name); | 
| +      this.fc.visitTypeName(name); | 
| this.emitNoSpace('({'); | 
| for (let i = 0; i < properties.length; i++) { | 
| if (i > 0) this.emitNoSpace(','); | 
| @@ -274,18 +304,28 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| case ts.SyntaxKind.ClassDeclaration: | 
| case ts.SyntaxKind.InterfaceDeclaration: | 
| case ts.SyntaxKind.VariableStatement: | 
| -          orderedGroups.push([node]); | 
| -          return; | 
| case ts.SyntaxKind.GetAccessor: | 
| case ts.SyntaxKind.SetAccessor: | 
| case ts.SyntaxKind.SemicolonClassElement: | 
| case ts.SyntaxKind.ModuleDeclaration: | 
| case ts.SyntaxKind.TypeAliasDeclaration: | 
| case ts.SyntaxKind.ExportAssignment: | 
| +        case ts.SyntaxKind.EnumDeclaration: | 
| +        case ts.SyntaxKind.ImportDeclaration: | 
| +        case ts.SyntaxKind.ExportDeclaration: | 
| +        case ts.SyntaxKind.GlobalModuleExportDeclaration: | 
| +        case ts.SyntaxKind.ImportEqualsDeclaration: | 
| +        case ts.SyntaxKind.EmptyStatement: | 
| +        case ts.SyntaxKind.ExpressionStatement: | 
| +          // Types where we don't need to perform any merging overloads work. | 
| orderedGroups.push([node]); | 
| return; | 
| default: | 
| -          console.log('Warning: unexpected type... overloads: ' + node.kind + ' ' + node.getText()); | 
| +          // This warning is to make sure we aren't quietly missing a type we need to perform | 
| +          // overload chunking and merging on. | 
| +          console.log( | 
| +              'Warning: unexpected type found looking for overloads: ' + node.kind + ' ' + | 
| +              node.getText()); | 
| orderedGroups.push([node]); | 
| return; | 
| } | 
| @@ -344,9 +384,13 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| let mergedType = new MergedType(this.fc); | 
| mergedType.merge(first.type); | 
|  | 
| +      let mergedTypeParams = new MergedTypeParameters(this.fc); | 
| +      mergedTypeParams.merge(first.typeParameters); | 
| + | 
| for (let i = 1; i < group.length; ++i) { | 
| let signature = <ts.SignatureDeclaration>group[i]; | 
| mergedType.merge(signature.type); | 
| +        mergedTypeParams.merge(signature.typeParameters); | 
| let overlap = Math.min(signature.parameters.length, mergedParams.length); | 
| for (let j = 0; j < overlap; ++j) { | 
| mergedParams[j].merge(signature.parameters[j]); | 
| @@ -363,6 +407,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| merged.parameters = <ts.NodeArray<ts.ParameterDeclaration>>mergedParams.map( | 
| (p) => p.toParameterDeclaration()); | 
| merged.type = mergedType.toTypeNode(); | 
| +      merged.typeParameters = mergedTypeParams.toTypeParameters(); | 
|  | 
| this.fc.visit(merged); | 
| }); | 
| @@ -379,12 +424,15 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| switch (node.kind) { | 
| case ts.SyntaxKind.ModuleDeclaration: | 
| let moduleDecl = <ts.ModuleDeclaration>node; | 
| +        if (moduleDecl.name.text.slice(0, 2) === '..') { | 
| +          this.emit( | 
| +              '\n// Library augmentation not allowed by Dart. Ignoring augmentation of ' + | 
| +              moduleDecl.name.text + '\n'); | 
| +          break; | 
| +        } | 
| this.emit('\n// Module ' + moduleDecl.name.text + '\n'); | 
| -        this.moduleStack.push(moduleDecl.name.text); | 
| - | 
| this.visit(moduleDecl.body); | 
| this.emit('\n// End module ' + moduleDecl.name.text + '\n'); | 
| -        this.moduleStack.pop(); | 
| break; | 
| case ts.SyntaxKind.ExportKeyword: | 
| // TODO(jacobr): perhaps add a specific Dart annotation to indicate | 
| @@ -439,7 +487,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| let arrayType = <ts.ArrayTypeNode>type; | 
| paramType = arrayType.elementType; | 
| } else if (type.kind !== ts.SyntaxKind.AnyKeyword) { | 
| -              throw 'Unexpected type for varargs: ' + type.kind; | 
| +              console.log('Warning: falling back to dynamic for varArgs type: ' + type.getText()); | 
| } | 
| } | 
|  | 
| @@ -477,6 +525,10 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| let member = <ts.EnumMember>node; | 
| this.visit(member.name); | 
| } break; | 
| +      case ts.SyntaxKind.SourceFile: | 
| +        let sourceFile = node as ts.SourceFile; | 
| +        this.visitMergingOverloads(sourceFile.statements); | 
| +        break; | 
| case ts.SyntaxKind.ModuleBlock: { | 
| let block = <ts.ModuleBlock>node; | 
| this.visitMergingOverloads(block.statements); | 
| @@ -530,11 +582,27 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| // let exportAssignment = <ts.ExportAssignment>node; | 
| this.emit('/* WARNING: export assignment not yet supported. */\n'); | 
| break; | 
| -      case ts.SyntaxKind.TypeAliasDeclaration: | 
| -        // Dart does not provide special syntax for definning type alais | 
| -        // declarations so we do not emit anything here and resolve alaises | 
| -        // to their original types at each usage site. | 
| -        break; | 
| +      case ts.SyntaxKind.TypeAliasDeclaration: { | 
| +        // Object literal type alias declarations are equivalent to interface declarations. | 
| +        let alias = <ts.TypeAliasDeclaration>node; | 
| +        let type = alias.type; | 
| +        if (type.kind === ts.SyntaxKind.TypeLiteral) { | 
| +          let literal = <ts.TypeLiteralNode>type; | 
| +          this.emit('@anonymous\n@JS()\n'); | 
| +          this.visitClassLikeHelper( | 
| +              'abstract class', literal, alias.name, alias.typeParameters, null); | 
| +        } else if (type.kind === ts.SyntaxKind.FunctionType) { | 
| +          // Function type alias definitions are equivalent to dart typedefs. | 
| +          this.visitFunctionTypedefInterface( | 
| +              base.ident(alias.name), type as ts.FunctionTypeNode, alias.typeParameters); | 
| +        } else { | 
| +          this.enterCodeComment(); | 
| +          this.emit(alias.getText()); | 
| +          this.exitCodeComment(); | 
| +          this.emit('\n'); | 
| +        } | 
| +        // We ignore other type alias declarations as Dart doesn't have a corresponding feature yet. | 
| +      } break; | 
| case ts.SyntaxKind.ClassDeclaration: | 
| case ts.SyntaxKind.InterfaceDeclaration: { | 
| this.extendsClass = false; | 
| @@ -547,7 +615,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| break; | 
| } | 
|  | 
| -        let customName = this.fc.lookupCustomDartTypeName(classDecl.name, this.insideCodeComment); | 
| +        let customName = this.fc.lookupCustomDartTypeName(classDecl.name); | 
| if (customName && !customName.keep) { | 
| this.emit('\n/* Skipping class ' + base.ident(classDecl.name) + '*/\n'); | 
| break; | 
| @@ -586,10 +654,19 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| // Find containing class name. | 
| let classDecl = base.getEnclosingClass(ctorDecl); | 
| if (!classDecl) this.reportError(ctorDecl, 'cannot find outer class node'); | 
| +        let isAnonymous = this.isAnonymousInterface(classDecl); | 
| +        if (isAnonymous) { | 
| +          this.emit('// Constructors on anonymous interfaces are not yet supported.\n'); | 
| +          this.enterCodeComment(); | 
| +        } | 
| this.visitDeclarationMetadata(ctorDecl); | 
| this.fc.visitTypeName(classDecl.name); | 
| this.visitParameters(ctorDecl.parameters); | 
| this.emitNoSpace(';'); | 
| +        if (isAnonymous) { | 
| +          this.exitCodeComment(); | 
| +          this.emit('\n'); | 
| +        } | 
| } break; | 
| case ts.SyntaxKind.PropertyDeclaration: | 
| this.visitProperty(<ts.PropertyDeclaration>node); | 
| @@ -684,11 +761,9 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| this.enterCodeComment(); | 
| } | 
| this.emitNoSpace('<'); | 
| -        // Emit the names literally instead of visiting, otherwise they will be replaced with the | 
| -        // comment hack themselves. | 
| -        // TODO(jacobr): we can use the regular type parameter visiting pattern | 
| -        // now that we properly track whether we are inside a comment. | 
| -        this.emitNoSpace(fn.typeParameters.map(p => base.ident(p.name)).join(', ')); | 
| +        this.enterTypeArguments(); | 
| +        this.visitList(fn.typeParameters); | 
| +        this.exitTypeArguments(); | 
| this.emitNoSpace('>'); | 
| if (!insideComment) { | 
| this.exitCodeComment(); | 
| @@ -738,15 +813,25 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| } | 
|  | 
| private visitClassLike(keyword: string, decl: base.ClassLike) { | 
| +    return this.visitClassLikeHelper( | 
| +        keyword, decl, decl.name, decl.typeParameters, decl.heritageClauses); | 
| +  } | 
| + | 
| +  private visitClassLikeHelper( | 
| +      keyword: string, decl: base.ClassLike|ts.TypeLiteralNode, name: ts.Identifier, | 
| +      typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>, | 
| +      heritageClauses: ts.NodeArray<ts.HeritageClause>) { | 
| this.emit(keyword); | 
| -    this.fc.visitTypeName(decl.name); | 
| -    if (decl.typeParameters) { | 
| +    this.fc.visitTypeName(name); | 
| +    if (typeParameters) { | 
| this.emit('<'); | 
| -      this.visitList(decl.typeParameters); | 
| +      this.enterTypeArguments(); | 
| +      this.visitList(typeParameters); | 
| +      this.exitTypeArguments(); | 
| this.emit('>'); | 
| } | 
|  | 
| -    this.visitEachIfPresent(decl.heritageClauses); | 
| +    this.visitEachIfPresent(heritageClauses); | 
| this.emit('{'); | 
|  | 
| this.maybeEmitFakeConstructors(decl); | 
| @@ -766,7 +851,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| (ctor) => | 
| (<ts.ConstructorDeclaration>ctor).parameters.forEach(synthesizePropertyParam)); | 
|  | 
| -    this.visitClassBody(decl); | 
| +    this.visitClassBody(decl, name); | 
| this.emit('}'); | 
| } | 
|  | 
| @@ -801,7 +886,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| * call signature, by translating to a Dart `typedef`. | 
| */ | 
| private visitFunctionTypedefInterface( | 
| -      name: string, signature: ts.CallSignatureDeclaration, | 
| +      name: string, signature: ts.SignatureDeclaration, | 
| typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>) { | 
| this.emit('typedef'); | 
| if (signature.type) { | 
| @@ -810,7 +895,9 @@ export default class DeclarationTranspiler extends base.TranspilerBase { | 
| this.emit(name); | 
| if (typeParameters) { | 
| this.emitNoSpace('<'); | 
| +      this.enterTypeArguments(); | 
| this.visitList(typeParameters); | 
| +      this.exitTypeArguments(); | 
| this.emitNoSpace('>'); | 
| } | 
| this.visitParameters(signature.parameters); | 
|  |