| Index: lib/src/codegen/js_codegen.dart
 | 
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
 | 
| index 0e589ab808dc327876a0fcdfe9c59c5e081d9f29..3cba7a75f96f8198fa8d1fcfca9b793dd279fe6d 100644
 | 
| --- a/lib/src/codegen/js_codegen.dart
 | 
| +++ b/lib/src/codegen/js_codegen.dart
 | 
| @@ -29,7 +29,9 @@ import 'package:dev_compiler/src/info.dart';
 | 
|  import 'package:dev_compiler/src/options.dart';
 | 
|  import 'package:dev_compiler/src/report.dart';
 | 
|  import 'package:dev_compiler/src/utils.dart';
 | 
| +
 | 
|  import 'code_generator.dart';
 | 
| +import 'js_names.dart';
 | 
|  
 | 
|  bool _isAnnotationType(Annotation m, String name) => m.name.name == name;
 | 
|  
 | 
| @@ -53,7 +55,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|    /// The variable for the target of the current `..` cascade expression.
 | 
|    SimpleIdentifier _cascadeTarget;
 | 
|    /// The variable for the current catch clause
 | 
| -  String _catchParameter;
 | 
| +  SimpleIdentifier _catchParameter;
 | 
|  
 | 
|    ClassDeclaration currentClass;
 | 
|    ConstantEvaluator _constEvaluator;
 | 
| @@ -78,6 +80,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|  
 | 
|    LibraryElement get currentLibrary => libraryInfo.library;
 | 
|  
 | 
| +  /// The name for the library's exports inside itself.
 | 
| +  /// This much be a constant because we interpolate it into template strings,
 | 
| +  /// and otherwise it would break caching for them.
 | 
| +  /// `exports` was chosen as the most similar to ES module patterns.
 | 
| +  final JSTemporary _exportsVar = new JSTemporary('exports');
 | 
| +  final JSTemporary _namedArgTemp = new JSTemporary('opts');
 | 
| +
 | 
|    JS.Program emitLibrary(LibraryUnit library) {
 | 
|      var jsDefaultValue = '{}';
 | 
|      var unit = library.library;
 | 
| @@ -113,7 +122,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|  
 | 
|      // TODO(jmesserly): make these immutable in JS?
 | 
|      for (var name in _exports) {
 | 
| -      body.add(js.statement('$_EXPORTS.# = #;', [name, name]));
 | 
| +      body.add(js.statement('#.# = #;', [_exportsVar, name, name]));
 | 
|      }
 | 
|  
 | 
|      var name = jsLibraryName(libraryInfo.library);
 | 
| @@ -121,13 +130,18 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|      var defaultValue = js.call(jsDefaultValue);
 | 
|      return new JS.Program([
 | 
|        js.statement('var #;', name),
 | 
| -      js.statement("(function($_EXPORTS) { 'use strict'; #; })(# || (# = #));",
 | 
| -          [body, name, name, defaultValue])
 | 
| +      js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [
 | 
| +        _exportsVar,
 | 
| +        body,
 | 
| +        name,
 | 
| +        name,
 | 
| +        defaultValue
 | 
| +      ])
 | 
|      ]);
 | 
|    }
 | 
|  
 | 
| -  JS.Statement _initPrivateSymbol(String name) =>
 | 
| -      js.statement('let # = $_SYMBOL(#);', [name, js.string(name, "'")]);
 | 
| +  JS.Statement _initPrivateSymbol(String name) => js.statement(
 | 
| +      'let # = $_SYMBOL(#);', [new JSTemporary(name), js.string(name, "'")]);
 | 
|  
 | 
|    // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
 | 
|    // until we have better name tracking.
 | 
| @@ -315,7 +329,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|  
 | 
|        var target = genericName;
 | 
|        if (_needQualifiedName(classElem)) {
 | 
| -        target = js.call('#.#', [_EXPORTS, genericName]);
 | 
| +        target = js.call('#.#', [_exportsVar, genericName]);
 | 
|        }
 | 
|        genericInst = js.call('#(#)', [target, dynamicArgs]);
 | 
|      }
 | 
| @@ -329,7 +343,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|          return js.statement(
 | 
|              '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', [
 | 
|            genericDef,
 | 
| -          _EXPORTS,
 | 
| +          _exportsVar,
 | 
|            js.string(name, "'"),
 | 
|            genericName
 | 
|          ]);
 | 
| @@ -337,7 +351,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|  
 | 
|        return js.statement(
 | 
|            'dart.defineLazyClass(#, { get #() { #; return #; } });', [
 | 
| -        _EXPORTS,
 | 
| +        _exportsVar,
 | 
|          _propertyName(name),
 | 
|          body,
 | 
|          name
 | 
| @@ -561,7 +575,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|          }
 | 
|        }
 | 
|      }
 | 
| -    var lazy = _emitLazyFields(name, lazyStatics);
 | 
| +    var lazy = _emitLazyFields(new JS.Identifier(name), lazyStatics);
 | 
|      if (lazy != null) body.add(lazy);
 | 
|      return _statement(body);
 | 
|    }
 | 
| @@ -801,9 +815,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|        var name = param.identifier.name;
 | 
|  
 | 
|        if (param.kind == ParameterKind.NAMED) {
 | 
| -        body.add(js.statement('let # = opt\$ && # in opt\$ ? opt\$.# : #;', [
 | 
| +        body.add(js.statement('let # = # && # in # ? #.# : #;', [
 | 
|            name,
 | 
| +          _namedArgTemp,
 | 
|            js.string(name, "'"),
 | 
| +          _namedArgTemp,
 | 
| +          _namedArgTemp,
 | 
|            name,
 | 
|            _defaultParamValue(param),
 | 
|          ]));
 | 
| @@ -934,8 +951,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|      } else if (variable is ConstFieldElementImpl) {
 | 
|        var className = (variable.enclosingElement as ClassElement).name;
 | 
|        return js.call('#.#', [className, name]);
 | 
| -    } else if (e is ParameterElement && e.isInitializingFormal) {
 | 
| -      name = _fieldParameterName(name);
 | 
| +    } else if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) {
 | 
| +      /// Rename private names so they don't shadow the private field symbol.
 | 
| +      /// The renamer would handle this, but it would prefer to rename the
 | 
| +      /// temporary used for the private symbol. Instead rename the parameter.
 | 
| +      return new JSTemporary('${name.substring(1)}');
 | 
| +    } else if (_isTemporary(e)) {
 | 
| +      return new JSTemporary(e.name);
 | 
|      }
 | 
|      return new JS.Identifier(name);
 | 
|    }
 | 
| @@ -1162,7 +1184,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|      var result = <JS.Identifier>[];
 | 
|      for (FormalParameter param in node.parameters) {
 | 
|        if (param.kind == ParameterKind.NAMED) {
 | 
| -        result.add(new JS.Identifier(r'opt$'));
 | 
| +        result.add(_namedArgTemp);
 | 
|          break;
 | 
|        }
 | 
|        result.add(_visit(param));
 | 
| @@ -1265,13 +1287,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|    }
 | 
|  
 | 
|    void _flushLazyFields(List<JS.Statement> body) {
 | 
| -    var code = _emitLazyFields(_EXPORTS, _lazyFields);
 | 
| +    var code = _emitLazyFields(_exportsVar, _lazyFields);
 | 
|      if (code != null) body.add(code);
 | 
|      _lazyFields.clear();
 | 
|    }
 | 
|  
 | 
|    JS.Statement _emitLazyFields(
 | 
| -      String objExpr, List<VariableDeclaration> fields) {
 | 
| +      JS.Expression objExpr, List<VariableDeclaration> fields) {
 | 
|      if (fields.isEmpty) return null;
 | 
|  
 | 
|      var methods = [];
 | 
| @@ -1294,8 +1316,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|  
 | 
|    void _flushLibraryProperties(List<JS.Statement> body) {
 | 
|      if (_properties.isEmpty) return;
 | 
| -    body.add(js.statement('dart.copyProperties($_EXPORTS, { # });',
 | 
| -        [_properties.map(_emitTopLevelProperty)]));
 | 
| +    body.add(js.statement('dart.copyProperties(#, { # });', [
 | 
| +      _exportsVar,
 | 
| +      _properties.map(_emitTopLevelProperty)
 | 
| +    ]));
 | 
|      _properties.clear();
 | 
|    }
 | 
|  
 | 
| @@ -1466,17 +1490,26 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|  
 | 
|    // TODO(jmesserly, vsm): Refactor this logic.
 | 
|    SimpleIdentifier _createTemporary(String name, DartType type) {
 | 
| +    // We use an invalid source location to signal that this is a temporary.
 | 
| +    // See [_isTemporary].
 | 
| +    // TODO(jmesserly): alternatives are
 | 
| +    // * (ab)use Element.isSynthetic, which isn't currently used for
 | 
| +    //   LocalVariableElementImpl, so we could repurpose to mean "temp".
 | 
| +    // * add a new property to LocalVariableElementImpl.
 | 
| +    // * create a new subtype of LocalVariableElementImpl to mark a temp.
 | 
|      var id =
 | 
| -        new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, 0));
 | 
| +        new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1));
 | 
|      id.staticElement = new LocalVariableElementImpl.forNode(id);
 | 
|      id.staticType = type;
 | 
|      return id;
 | 
|    }
 | 
|  
 | 
| +  bool _isTemporary(Element node) => node.nameOffset == -1;
 | 
| +
 | 
|    JS.Expression _emitPostfixIncrement(Expression expr, Token op) {
 | 
|      var type = rules.getStaticType(expr);
 | 
|      assert(type != null);
 | 
| -    var tmp = _createTemporary('\$tmp', type);
 | 
| +    var tmp = _createTemporary('x', type);
 | 
|  
 | 
|      // Increment and write
 | 
|      var one = AstBuilder.integerLiteral(1);
 | 
| @@ -1588,20 +1621,16 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|        // remains valid in an expression context.
 | 
|        // TODO(jmesserly): need a better way to handle temps.
 | 
|        // TODO(jmesserly): special case for parent is ExpressionStatement?
 | 
| -      _cascadeTarget =
 | 
| -          new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, '_', 0));
 | 
| -      _cascadeTarget.staticElement =
 | 
| -          new LocalVariableElementImpl.forNode(_cascadeTarget);
 | 
| -      _cascadeTarget.staticType = node.target.staticType;
 | 
| +      _cascadeTarget = _createTemporary('_', node.target.staticType);
 | 
|  
 | 
|        var body = _visitList(node.cascadeSections);
 | 
|        if (node.parent is! ExpressionStatement) {
 | 
| -        body.add(js.statement('return #;', _cascadeTarget.name));
 | 
| +        body.add(js.statement('return #;', _visit(_cascadeTarget)));
 | 
|        }
 | 
|  
 | 
|        var bindThis = _maybeBindThis(node.cascadeSections);
 | 
|        result = js.call('((#) => { # })$bindThis(#)', [
 | 
| -        _cascadeTarget.name,
 | 
| +        _visit(_cascadeTarget),
 | 
|          body,
 | 
|          _visit(node.target)
 | 
|        ]);
 | 
| @@ -1638,15 +1667,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|  
 | 
|    @override
 | 
|    visitFormalParameter(FormalParameter node) =>
 | 
| -      new JS.Identifier(node.identifier.name);
 | 
| -
 | 
| -  @override
 | 
| -  visitFieldFormalParameter(FieldFormalParameter node) =>
 | 
| -      new JS.Identifier(_fieldParameterName(node.identifier.name));
 | 
| -
 | 
| -  /// Rename private names so they don't shadow the private field symbol.
 | 
| -  // TODO(jmesserly): replace this ad-hoc rename with a general strategy.
 | 
| -  _fieldParameterName(name) => name.startsWith('_') ? '\$$name' : name;
 | 
| +      visitSimpleIdentifier(node.identifier);
 | 
|  
 | 
|    @override
 | 
|    JS.This visitThisExpression(ThisExpression node) => new JS.This();
 | 
| @@ -1719,9 +1740,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|    @override
 | 
|    visitRethrowExpression(RethrowExpression node) {
 | 
|      if (node.parent is ExpressionStatement) {
 | 
| -      return js.statement('throw #;', _catchParameter);
 | 
| +      return js.statement('throw #;', _visit(_catchParameter));
 | 
|      } else {
 | 
| -      return js.call('dart.throw_(#)', _catchParameter);
 | 
| +      return js.call('dart.throw_(#)', _visit(_catchParameter));
 | 
|      }
 | 
|    }
 | 
|  
 | 
| @@ -1782,21 +1803,34 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|      // TODO(jmesserly): need a better way to get a temporary variable.
 | 
|      // This could incorrectly shadow a user's name.
 | 
|      var savedCatch = _catchParameter;
 | 
| -    _catchParameter = '\$e';
 | 
|  
 | 
|      if (clauses.length == 1) {
 | 
|        // Special case for a single catch.
 | 
| -      var clause = clauses.single;
 | 
| -      if (clause.exceptionParameter != null) {
 | 
| -        _catchParameter = clause.exceptionParameter.name;
 | 
| -      }
 | 
| +      _catchParameter = clauses.single.exceptionParameter;
 | 
| +    } else {
 | 
| +      _catchParameter = _createTemporary('e', rules.provider.dynamicType);
 | 
| +    }
 | 
| +
 | 
| +    JS.Statement catchBody = null;
 | 
| +    for (var clause in clauses.reversed) {
 | 
| +      catchBody = _catchClauseGuard(clause, catchBody);
 | 
|      }
 | 
|  
 | 
| -    var catchVarDecl = new JS.Identifier(_catchParameter);
 | 
| -    var catchBody = new JS.Block(_visitList(clauses));
 | 
| +    var catchVarDecl = _visit(_catchParameter);
 | 
|      _catchParameter = savedCatch;
 | 
| +    return new JS.Catch(catchVarDecl, new JS.Block([catchBody]));
 | 
| +  }
 | 
| +
 | 
| +  JS.Statement _catchClauseGuard(CatchClause clause, JS.Statement otherwise) {
 | 
| +    var then = visitCatchClause(clause);
 | 
| +
 | 
| +    // Discard following clauses, if any, as they are unreachable.
 | 
| +    if (clause.exceptionType == null) return then;
 | 
|  
 | 
| -    return new JS.Catch(catchVarDecl, catchBody);
 | 
| +    return new JS.If(js.call('dart.is(#, #)', [
 | 
| +      _visit(_catchParameter),
 | 
| +      _emitTypeName(clause.exceptionType.type),
 | 
| +    ]), then, otherwise);
 | 
|    }
 | 
|  
 | 
|    JS.Statement _statement(Iterable stmts) {
 | 
| @@ -1807,6 +1841,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|      return new JS.Block(s);
 | 
|    }
 | 
|  
 | 
| +  /// Visits the catch clause body. This skips the exception type guard, if any.
 | 
| +  /// That is handled in [_visitCatch].
 | 
|    @override
 | 
|    JS.Statement visitCatchClause(CatchClause node) {
 | 
|      var body = [];
 | 
| @@ -1814,11 +1850,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|      var savedCatch = _catchParameter;
 | 
|      if (node.catchKeyword != null) {
 | 
|        var name = node.exceptionParameter;
 | 
| -      if (name != null && name.name != _catchParameter) {
 | 
| -        var decl = new JS.Identifier(name.name);
 | 
| -        decl.sourceInformation = name;
 | 
| -        body.add(js.statement('let # = #;', [decl, _catchParameter]));
 | 
| -        _catchParameter = name.name;
 | 
| +      if (name != null && name != _catchParameter) {
 | 
| +        body.add(js.statement(
 | 
| +            'let # = #;', [_visit(name), _visit(_catchParameter)]));
 | 
| +        _catchParameter = name;
 | 
|        }
 | 
|        if (node.stackTraceParameter != null) {
 | 
|          var stackVar = node.stackTraceParameter.name;
 | 
| @@ -1829,15 +1864,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|  
 | 
|      body.add(_visit(node.body));
 | 
|      _catchParameter = savedCatch;
 | 
| -
 | 
| -    if (node.exceptionType != null) {
 | 
| -      return js.statement('if (dart.is(#, #)) #;', [
 | 
| -        _catchParameter,
 | 
| -        _emitTypeName(node.exceptionType.type),
 | 
| -        _statement(body)
 | 
| -      ]);
 | 
| -    }
 | 
| -
 | 
|      return _statement(body);
 | 
|    }
 | 
|  
 | 
| @@ -2071,7 +2097,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|    JS.Expression _jsMemberName(String name, {bool unary: false}) {
 | 
|      if (name.startsWith('_')) {
 | 
|        if (_privateNames.add(name)) _pendingPrivateNames.add(name);
 | 
| -      return new JS.Identifier(name);
 | 
| +      return new JSTemporary(name);
 | 
|      }
 | 
|      if (name == '[]') {
 | 
|        name = 'get';
 | 
| @@ -2092,9 +2118,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|    /// Choose a canonical name from the library element.
 | 
|    /// This never uses the library's name (the identifier in the `library`
 | 
|    /// declaration) as it doesn't have any meaningful rules enforced.
 | 
| -  String _libraryName(LibraryElement library) {
 | 
| -    if (library == libraryInfo.library) return _EXPORTS;
 | 
| -    return jsLibraryName(library);
 | 
| +  JS.Identifier _libraryName(LibraryElement library) {
 | 
| +    if (library == libraryInfo.library) return _exportsVar;
 | 
| +    return new JS.Identifier(jsLibraryName(library));
 | 
|    }
 | 
|  
 | 
|    String _maybeBindThis(node) {
 | 
| @@ -2105,12 +2131,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
 | 
|      return visitor._bindThis ? '.bind(this)' : '';
 | 
|    }
 | 
|  
 | 
| -  /// The name for the library's exports inside itself.
 | 
| -  /// This much be a constant because we interpolate it into template strings,
 | 
| -  /// and otherwise it would break caching for them.
 | 
| -  /// `exports` was chosen as the most similar to ES module patterns.
 | 
| -  static const String _EXPORTS = 'exports';
 | 
| -
 | 
|    static bool _needsImplicitThis(Element e) =>
 | 
|        e is PropertyAccessorElement && !e.variable.isStatic ||
 | 
|            e is ClassMemberElement && !e.isStatic && e is! ConstructorElement;
 | 
| @@ -2218,8 +2238,8 @@ class JSGenerator extends CodeGenerator {
 | 
|  }
 | 
|  
 | 
|  void _writeNode(JS.JavaScriptPrintingContext context, JS.Node node) {
 | 
| -  var opts = new JS.JavaScriptPrintingOptions(avoidKeywordsInIdentifiers: true);
 | 
| -  node.accept(new JS.Printer(opts, context));
 | 
| +  var opts = new JS.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
 | 
| +  node.accept(new JS.Printer(opts, context, localNamer: new JSNamer(node)));
 | 
|  }
 | 
|  
 | 
|  /// This is a debugging helper to print a JS node.
 | 
| 
 |