Index: lib/src/codegen/js_codegen.dart |
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
index e365f91060c3acf128389091de8651711455e9ed..cc3c0a558c3819eb9255299ccc46610bc1f260e4 100644 |
--- a/lib/src/codegen/js_codegen.dart |
+++ b/lib/src/codegen/js_codegen.dart |
@@ -1469,8 +1469,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
_flushLibraryProperties(body); |
var name = node.name.name; |
- |
- var fn = _visit(node.functionExpression); |
+ var fn = _emitFunction(node.functionExpression); |
if (currentLibrary.source.isInSystemLibrary && |
_isInlineJSFunction(node.functionExpression)) { |
@@ -1531,7 +1530,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
var name = node.name.name; |
return annotate( |
- new JS.Method(_propertyName(name), _visit(node.functionExpression), |
+ new JS.Method( |
+ _propertyName(name), _emitFunction(node.functionExpression), |
isGetter: node.isGetter, isSetter: node.isSetter), |
node, |
node.element); |
@@ -1559,7 +1559,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
return _loader.isLoaded(type.element); |
} |
- JS.Expression _emitFunctionTagged(JS.Expression clos, DartType type, |
+ JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, |
{topLevel: false}) { |
var name = type.name; |
var lazy = topLevel && !_typeIsLoaded(type); |
@@ -1569,64 +1569,76 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
type.optionalParameterTypes.isEmpty && |
type.namedParameterTypes.isEmpty && |
type.normalParameterTypes.every((t) => t.isDynamic)) { |
- return js.call('dart.fn(#)', [clos]); |
+ return js.call('dart.fn(#)', [fn]); |
} |
if (lazy) { |
- return js.call('dart.fn(#, () => #)', [clos, _emitFunctionRTTI(type)]); |
+ return js.call('dart.fn(#, () => #)', [fn, _emitFunctionRTTI(type)]); |
} |
- return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]); |
+ return js.call('dart.fn(#, #)', [fn, _emitFunctionTypeParts(type)]); |
} |
throw 'Function has non function type: $type'; |
} |
+ /// Emits an arrow FunctionExpression node. |
+ /// |
+ /// This should be used for all places in Dart's AST where FunctionExpression |
+ /// appears and the function is actually in an Expression context. These |
+ /// correspond to arrow functions in Dart. |
+ /// |
+ /// Contrast with [_emitFunction]. |
@override |
JS.Expression visitFunctionExpression(FunctionExpression node) { |
- var params = visitFormalParameterList(node.parameters); |
+ assert(node.parent is! FunctionDeclaration && |
+ node.parent is! MethodDeclaration); |
+ return _emitFunctionTagged(_emitArrowFunction(node), getStaticType(node), |
+ topLevel: _executesAtTopLevel(node)); |
+ } |
- var body = node.body; |
- var parent = node.parent; |
- var inStmt = parent.parent is FunctionDeclarationStatement; |
- if (parent is FunctionDeclaration) { |
- return _emitFunctionBody(node.element, node.parameters, body); |
- } |
+ JS.ArrowFun _emitArrowFunction(FunctionExpression node) { |
+ List<JS.Parameter> params; |
+ var body = node.body; |
JS.Node jsBody; |
if (body.isGenerator || body.isAsynchronous) { |
+ params = visitFormalParameterList(node.parameters, destructure: false); |
jsBody = _emitGeneratorFunctionBody(node.element, node.parameters, body); |
- } else if (body is ExpressionFunctionBody) { |
- jsBody = _visit(body.expression); |
- } else { |
- jsBody = _visit(body); |
- } |
- |
- var type = node.element.type; |
- var typeFormals = _emitTypeFormals(type.typeFormals); |
- var returnType = emitTypeRef(type.returnType); |
- |
- JS.FunctionExpression fn; |
- if (node.parameters.parameters |
- .every((p) => p.kind != ParameterKind.NAMED)) { |
- fn = new JS.ArrowFun(params, jsBody, |
- typeParams: typeFormals, returnType: returnType); |
} else { |
// 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 hasNamedParams = |
+ node.parameters.parameters.any((p) => p.kind == ParameterKind.NAMED); |
+ |
+ params = visitFormalParameterList(node.parameters, |
+ destructure: !hasNamedParams); |
- if (jsBody is JS.Expression) { |
- jsBody = js.statement("{ return #; }", [jsBody]); |
+ if (body is ExpressionFunctionBody) { |
+ // An arrow function can use the expression directly. |
+ jsBody = _visit(body.expression); |
+ } else { |
+ jsBody = _visit(body); |
} |
- fn = new JS.Fun(params, jsBody, |
- typeParams: typeFormals, returnType: returnType); |
} |
- if (!inStmt) { |
- var type = getStaticType(node); |
- return _emitFunctionTagged(fn, type, topLevel: _executesAtTopLevel(node)); |
- } |
- return fn; |
+ var type = node.element.type; |
+ var fn = new JS.ArrowFun(params, jsBody, |
+ typeParams: _emitTypeFormals(type.typeFormals), |
+ returnType: emitTypeRef(type.returnType)); |
+ return annotate(fn, node); |
+ } |
+ |
+ /// Emits a non-arrow FunctionExpression node. |
+ /// |
+ /// This should be used for all places in Dart's AST where FunctionExpression |
+ /// appears but the function is not actually in an Expression context, such |
+ /// as methods, properties, and top-level functions. |
+ /// |
+ /// Contrast with [visitFunctionExpression]. |
+ JS.Fun _emitFunction(FunctionExpression node) { |
+ var fn = _emitFunctionBody(node.element, node.parameters, node.body); |
+ return annotate(fn, node); |
} |
JS.Fun _emitFunctionBody(ExecutableElement element, |
@@ -1717,7 +1729,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
return js.comment('Unimplemented function get/set statement: $node'); |
} |
- var fn = _visit(func.functionExpression); |
+ var fn = _emitFunction(func.functionExpression); |
var name = new JS.Identifier(func.name.name); |
JS.Statement declareFn; |
@@ -3499,7 +3511,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
// TODO(jmesserly): this will need to be a generic method, if we ever want to |
// self-host strong mode. |
vsm
2016/02/25 00:48:04
Probably don't need the TODO anymore. :-)
Jennifer Messerly
2016/02/25 00:53:45
Yup! Have this fixed in a follow up CL I'm working
|
- List/*<T>*/ _visitList/*<T>*/(Iterable<AstNode> nodes) { |
+ List /*<T>*/ _visitList /*<T>*/ (Iterable<AstNode> nodes) { |
if (nodes == null) return null; |
var result = /*<T>*/ []; |
for (var node in nodes) result.add(_visit(node)); |