Index: pkg/dev_compiler/lib/src/compiler/code_generator.dart |
diff --git a/pkg/dev_compiler/lib/src/compiler/code_generator.dart b/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
index 9b1c72fe7718123745657f34ee42af5a0433cf74..1c52f9c770cd9d61ed448bf30f6e003daef6c072 100644 |
--- a/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
+++ b/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
@@ -2398,15 +2398,16 @@ class CodeGenerator extends Object |
/// |
/// This is useful for indexed set methods, which otherwise would not have |
/// the right return value in JS. |
- JS.Node _alwaysReturnLastParameter(JS.Node body, JS.Parameter lastParam) { |
+ JS.Block _alwaysReturnLastParameter(JS.Block body, JS.Parameter lastParam) { |
+ JS.Statement blockBody = body; |
if (JS.Return.foundIn(body)) { |
// If a return is inside body, transform `(params) { body }` to |
// `(params) { (() => { body })(); return value; }`. |
// TODO(jmesserly): we could instead generate the return differently, |
// and avoid the immediately invoked function. |
- body = new JS.Call(new JS.ArrowFun([], body), []).toStatement(); |
+ blockBody = new JS.Call(new JS.ArrowFun([], body), []).toStatement(); |
} |
- return new JS.Block([body, new JS.Return(lastParam)]); |
+ return new JS.Block([blockBody, new JS.Return(lastParam)]); |
} |
@override |
@@ -2609,17 +2610,15 @@ class CodeGenerator extends Object |
// normal function (sync), vs (sync*, async, async*) |
var stdFn = !(element.isAsynchronous || element.isGenerator); |
var formals = _emitFormalParameterList(parameters, destructure: stdFn); |
- var code = (stdFn) |
+ JS.Block code = stdFn |
? _visit(body) |
: new JS.Block( |
[_emitGeneratorFunctionBody(element, parameters, body).toReturn()]); |
var typeFormals = _emitTypeFormals(type.typeFormals); |
var returnType = emitTypeRef(type.returnType); |
if (type.typeFormals.isNotEmpty) { |
- code = new JS.Block(<JS.Statement>[ |
- new JS.Block(_typeTable.discharge(type.typeFormals)), |
- code |
- ]); |
+ code = new JS.Block( |
+ [new JS.Block(_typeTable.discharge(type.typeFormals)), code]); |
} |
if (element.isOperator && element.name == '[]=' && formals.isNotEmpty) { |
@@ -2628,6 +2627,18 @@ class CodeGenerator extends Object |
code = _alwaysReturnLastParameter(code, formals.last); |
} |
+ if (body is BlockFunctionBody) { |
+ var params = element.parameters.map((e) => e.name).toSet(); |
+ bool shadowsParam = body.block.statements.any((s) => |
+ s is VariableDeclarationStatement && |
+ s.variables.variables.any((v) => params.contains(v.name.name))); |
+ if (shadowsParam) { |
+ code = new JS.Block([ |
+ new JS.Block([code], isScope: true) |
+ ]); |
+ } |
+ } |
+ |
return _makeGenericFunction(new JS.Fun(formals, code, |
typeParams: typeFormals, returnType: returnType)); |
} |
@@ -3358,7 +3369,7 @@ class CodeGenerator extends Object |
var savedFunction = _currentFunction; |
_currentFunction = node; |
var initArgs = _emitArgumentInitializers(node.parent); |
- var stmts = _visitList(node.block.statements) as List<JS.Statement>; |
+ var stmts = _visitList<JS.Statement>(node.block.statements); |
if (initArgs != null) stmts.insert(0, initArgs); |
_currentFunction = savedFunction; |
return new JS.Block(stmts); |
@@ -3366,8 +3377,7 @@ class CodeGenerator extends Object |
@override |
JS.Block visitBlock(Block node) => |
- new JS.Block(_visitList(node.statements) as List<JS.Statement>, |
- isScope: true); |
+ new JS.Block(_visitList(node.statements), isScope: true); |
@override |
visitMethodInvocation(MethodInvocation node) { |
@@ -3879,8 +3889,7 @@ class CodeGenerator extends Object |
@override |
visitVariableDeclarationList(VariableDeclarationList node) { |
- return new JS.VariableDeclarationList( |
- 'let', _visitList(node.variables) as List<JS.VariableInitialization>); |
+ return new JS.VariableDeclarationList('let', _visitList(node.variables)); |
} |
@override |
@@ -4631,7 +4640,7 @@ class CodeGenerator extends Object |
var vars = <JS.MetaLetVariable, JS.Expression>{}; |
_cascadeTarget = _bindValue(vars, '_', node.target, context: node); |
- var sections = _visitList(node.cascadeSections) as List<JS.Expression>; |
+ var sections = _visitList<JS.Expression>(node.cascadeSections); |
sections.add(_visit(_cascadeTarget)); |
var result = new JS.MetaLet(vars, sections, statelessResult: true); |
_cascadeTarget = savedCascadeTemp; |
@@ -5097,8 +5106,7 @@ class CodeGenerator extends Object |
} |
} |
- body.add( |
- new JS.Block(_visitList(node.body.statements) as List<JS.Statement>)); |
+ body.add(new JS.Block(_visitList(node.body.statements))); |
_catchParameter = savedCatch; |
return _statement(body); |
} |
@@ -5106,7 +5114,7 @@ class CodeGenerator extends Object |
@override |
JS.Case visitSwitchCase(SwitchCase node) { |
var expr = _visit(node.expression); |
- var body = _visitList(node.statements) as List<JS.Statement>; |
+ var body = _visitList<JS.Statement>(node.statements); |
if (node.labels.isNotEmpty) { |
body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); |
} |
@@ -5116,7 +5124,7 @@ class CodeGenerator extends Object |
@override |
JS.Default visitSwitchDefault(SwitchDefault node) { |
- var body = _visitList(node.statements) as List<JS.Statement>; |
+ var body = _visitList<JS.Statement>(node.statements); |
if (node.labels.isNotEmpty) { |
body.insert(0, js.comment('Unimplemented case labels: ${node.labels}')); |
} |
@@ -5125,9 +5133,8 @@ class CodeGenerator extends Object |
} |
@override |
- JS.Switch visitSwitchStatement(SwitchStatement node) => new JS.Switch( |
- _visit(node.expression), |
- _visitList(node.members) as List<JS.SwitchClause>); |
+ JS.Switch visitSwitchStatement(SwitchStatement node) => |
+ new JS.Switch(_visit(node.expression), _visitList(node.members)); |
@override |
JS.Statement visitLabeledStatement(LabeledStatement node) { |
@@ -5173,8 +5180,7 @@ class CodeGenerator extends Object |
visitListLiteral(ListLiteral node) { |
var isConst = node.constKeyword != null; |
JS.Expression emitList() { |
- JS.Expression list = new JS.ArrayInitializer( |
- _visitList(node.elements) as List<JS.Expression>); |
+ JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); |
ParameterizedType type = node.staticType; |
var elementType = type.typeArguments.single; |
// TODO(jmesserly): analyzer will usually infer `List<Object>` because |
@@ -5280,23 +5286,18 @@ class CodeGenerator extends Object |
T _visit<T extends JS.Node>(AstNode node) { |
if (node == null) return null; |
- var result = node.accept(this); |
+ var result = node.accept(this) as T; |
return result != null ? annotate(result, node) : null; |
} |
- // TODO(jmesserly): we should make sure this only returns JS AST nodes. |
- List/*<R>*/ _visitList/*<T extends AstNode, R>*/(Iterable/*<T>*/ nodes) { |
- if (nodes == null) return null; |
- var result = /*<R>*/ []; |
- for (var node in nodes) result.add(_visit(node) as dynamic/*=R*/); |
- return result; |
+ List<R> _visitList<R extends JS.Node>(Iterable<AstNode> nodes) { |
+ return nodes?.map<R>(_visit)?.toList(); |
} |
/// Visits a list of expressions, creating a comma expression if needed in JS. |
JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
if (nodes == null || nodes.isEmpty) return null; |
- return new JS.Expression.binary( |
- _visitList(nodes) as List<JS.Expression>, operator); |
+ return new JS.Expression.binary(_visitList(nodes), operator); |
} |
/// Generates an expression for a boolean conversion context (if, while, &&, |
@@ -5520,12 +5521,10 @@ class CodeGenerator extends Object |
() => new JS.TemporaryId(jsLibraryName(_libraryRoot, library))); |
} |
- JS.Node/*=T*/ annotate/*<T extends JS.Node>*/( |
- JS.Node/*=T*/ node, AstNode original, |
- [Element element]) { |
+ T annotate<T extends JS.Node>(T node, AstNode original, [Element element]) { |
if (options.closure && element != null) { |
- node = node.withClosureAnnotation(closureAnnotationFor( |
- node, original, element, namedArgumentTemp.name)) as dynamic/*=T*/; |
+ node.closureAnnotation = |
+ closureAnnotationFor(node, original, element, namedArgumentTemp.name); |
} |
return node..sourceInformation = original; |
} |