Index: lib/src/codegen/js_codegen.dart |
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
index 5de52be15ed167191e55a7c85fd916fe5383f9e2..cd4b5c9b812a71288b821e696a00ae6110152b5a 100644 |
--- a/lib/src/codegen/js_codegen.dart |
+++ b/lib/src/codegen/js_codegen.dart |
@@ -15,7 +15,6 @@ import 'package:analyzer/src/dart/ast/token.dart' |
show StringToken, Token, TokenType; |
import 'package:analyzer/src/generated/type_system.dart' |
show StrongTypeSystemImpl; |
-import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; |
import 'ast_builder.dart' show AstBuilder; |
import 'reify_coercions.dart' show CoercionReifier, Tuple2; |
@@ -106,6 +105,9 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
/// _interceptors.JSArray<E>, used for List literals. |
ClassElement _jsArray; |
+ /// The current function body being compiled. |
+ FunctionBody _currentFunction; |
+ |
/// The default value of the module object. See [visitLibraryDirective]. |
String _jsModuleValue; |
@@ -131,13 +133,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
// Modify the AST to make coercions explicit. |
new CoercionReifier(library, rules).reify(); |
- // Build the public namespace for this library. This allows us to do |
- // constant time lookups (contrast with `Element.getChild(name)`). |
- if (currentLibrary.publicNamespace == null) { |
- (currentLibrary as LibraryElementImpl).publicNamespace = |
- new PublicNamespaceBuilder().build(currentLibrary); |
- } |
- |
library.library.directives.forEach(_visit); |
var units = library.partsThenLibrary; |
@@ -1110,7 +1105,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
return result === void 0 ? this : result; |
}''') as JS.Block; |
} else { |
+ var savedFunction = _currentFunction; |
+ _currentFunction = node.body; |
body = _emitConstructorBody(node, fields); |
+ _currentFunction = savedFunction; |
} |
// We generate constructors as initializer methods in the class; |
@@ -1590,39 +1588,24 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
} |
JS.ArrowFun _emitArrowFunction(FunctionExpression node) { |
- List<JS.Parameter> params; |
- |
- var body = node.body; |
- var type = node.element.type; |
+ JS.Fun f = _emitFunctionBody(node.element, node.parameters, node.body); |
+ var body = f.body; |
- JS.Node jsBody; |
- if (body.isGenerator || body.isAsynchronous) { |
- params = visitFormalParameterList(node.parameters, destructure: false); |
- jsBody = _emitGeneratorFunctionBody(node.element, node.parameters, body); |
- } else { |
- params = visitFormalParameterList(node.parameters); |
- |
- // Chrome Canary does not accept default values with destructuring in |
- // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them |
- // with regular functions (e.g. `function({a} = {}) { return 1 }`). |
- // Note that Firefox accepts both syntaxes just fine. |
- // TODO(ochafik): Simplify this code when Chrome Canary catches up. |
- bool destructureNamed = params.any((p) => |
- p is JS.DestructuredVariable && |
- p.structure is JS.ObjectBindingPattern); |
- |
- if (body is ExpressionFunctionBody && !destructureNamed) { |
- // An arrow function can use the expression directly. |
- jsBody = _visit(body.expression); |
- } else { |
- jsBody = _visit(body); |
+ // Simplify `=> { return e; }` to `=> e` |
+ if (body is JS.Block) { |
+ JS.Block block = body; |
+ if (block.statements.length == 1) { |
+ JS.Statement s = block.statements[0]; |
+ if (s is JS.Return) body = s.value; |
} |
} |
- var fn = new JS.ArrowFun(params, jsBody, |
- typeParams: _emitTypeFormals(type.typeFormals), |
- returnType: emitTypeRef(type.returnType)); |
- return annotate(fn, node); |
+ // Convert `function(...) { ... }` to `(...) => ...` |
+ // This is for readability, but it also ensures correct `this` binding. |
+ return annotate( |
+ new JS.ArrowFun(f.params, body, |
+ typeParams: f.typeParams, returnType: f.returnType), |
+ node); |
} |
/// Emits a non-arrow FunctionExpression node. |
@@ -2074,8 +2057,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
@override |
JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { |
+ var savedFunction = _currentFunction; |
+ _currentFunction = node; |
var initArgs = _emitArgumentInitializers(node.parent); |
var ret = new JS.Return(_visit(node.expression)); |
+ _currentFunction = savedFunction; |
return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); |
} |
@@ -2084,9 +2070,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
@override |
JS.Block visitBlockFunctionBody(BlockFunctionBody node) { |
+ var savedFunction = _currentFunction; |
+ _currentFunction = node; |
var initArgs = _emitArgumentInitializers(node.parent); |
var stmts = _visitList(node.block.statements) as List<JS.Statement>; |
if (initArgs != null) stmts.insert(0, initArgs); |
+ _currentFunction = savedFunction; |
return new JS.Block(stmts); |
} |
@@ -2821,7 +2810,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
Map<String, JS.Expression> scope, String name, Expression expr, |
{Expression context}) { |
// No need to do anything for stateless expressions. |
- if (isStateless(expr, context)) return expr; |
+ if (isStateless(_currentFunction, expr, context)) return expr; |
var t = _createTemporary('#$name', getStaticType(expr)); |
scope[name] = _visit(expr); |
@@ -3407,7 +3396,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
_visitList(node.elements) as List<JS.Expression>); |
ParameterizedType type = node.staticType; |
var elementType = type.typeArguments.single; |
- if (elementType != types.dynamicType) { |
+ // TODO(jmesserly): analyzer will usually infer `List<Object>` because |
+ // that is the least upper bound of the element types. So we rarely |
+ // generate a plain `List<dynamic>` anymore. |
+ if (!elementType.isDynamic) { |
// dart.list helper internally depends on _interceptors.JSArray. |
_loader.declareBeforeUse(_jsArray); |
list = js.call('dart.list(#, #)', [list, _emitTypeName(elementType)]); |