| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library js_codegen; | 5 library js_codegen; |
| 6 | 6 |
| 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
| (...skipping 1311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1322 | 1322 |
| 1323 JS.Expression _defaultParamValue(FormalParameter param) { | 1323 JS.Expression _defaultParamValue(FormalParameter param) { |
| 1324 if (param is DefaultFormalParameter && param.defaultValue != null) { | 1324 if (param is DefaultFormalParameter && param.defaultValue != null) { |
| 1325 return _visit(param.defaultValue); | 1325 return _visit(param.defaultValue); |
| 1326 } else { | 1326 } else { |
| 1327 return new JS.LiteralNull(); | 1327 return new JS.LiteralNull(); |
| 1328 } | 1328 } |
| 1329 } | 1329 } |
| 1330 | 1330 |
| 1331 JS.Fun _emitNativeFunctionBody( | 1331 JS.Fun _emitNativeFunctionBody( |
| 1332 List<JS.Parameter> params, MethodDeclaration node) { | 1332 List<JS.Parameter> params, List<JS.Expression> paramRefs, |
| 1333 MethodDeclaration node) { |
| 1333 if (node.isStatic) { | 1334 if (node.isStatic) { |
| 1334 // TODO(vsm): Do we need to handle this case? | 1335 // TODO(vsm): Do we need to handle this case? |
| 1335 return null; | 1336 return null; |
| 1336 } | 1337 } |
| 1337 | 1338 |
| 1338 String name = node.name.name; | 1339 String name = node.name.name; |
| 1339 var annotation = findAnnotation(node.element, isJsName); | 1340 var annotation = findAnnotation(node.element, isJsName); |
| 1340 if (annotation != null) { | 1341 if (annotation != null) { |
| 1341 name = getConstantField(annotation, 'name', types.stringType) | 1342 name = getConstantField(annotation, 'name', types.stringType) |
| 1342 ?.toStringValue(); | 1343 ?.toStringValue(); |
| 1343 } | 1344 } |
| 1344 if (node.isGetter) { | 1345 if (node.isGetter) { |
| 1345 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); | 1346 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); |
| 1346 } else if (node.isSetter) { | 1347 } else if (node.isSetter) { |
| 1347 return new JS.Fun( | 1348 return new JS.Fun( |
| 1348 params, js.statement('{ this.# = #; }', [name, params.last])); | 1349 params, js.statement('{ this.# = #; }', [name, paramRefs.last])); |
| 1349 } else { | 1350 } else { |
| 1350 return new JS.Fun( | 1351 return new JS.Fun( |
| 1351 params, js.statement('{ return this.#(#); }', [name, params])); | 1352 params, js.statement('{ return this.#(#); }', [name, paramRefs])); |
| 1352 } | 1353 } |
| 1353 } | 1354 } |
| 1354 | 1355 |
| 1355 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1356 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
| 1356 if (node.isAbstract) { | 1357 if (node.isAbstract) { |
| 1357 return null; | 1358 return null; |
| 1358 } | 1359 } |
| 1359 | 1360 |
| 1360 var params = _visit(node.parameters) as List<JS.Parameter>; | 1361 var params = _visit(node.parameters) as List<JS.Parameter>; |
| 1361 if (params == null) params = <JS.Parameter>[]; | 1362 if (params == null) params = <JS.Parameter>[]; |
| 1363 var paramRefs = _emitParameterReferences(node.parameters); |
| 1362 | 1364 |
| 1363 JS.Fun fn; | 1365 JS.Fun fn; |
| 1364 if (_externalOrNative(node)) { | 1366 if (_externalOrNative(node)) { |
| 1365 fn = _emitNativeFunctionBody(params, node); | 1367 fn = _emitNativeFunctionBody(params, paramRefs, node); |
| 1366 // TODO(vsm): Remove if / when we handle the static case above. | 1368 // TODO(vsm): Remove if / when we handle the static case above. |
| 1367 if (fn == null) return null; | 1369 if (fn == null) return null; |
| 1368 } else { | 1370 } else { |
| 1369 var typeParams = _emitTypeParams(node.element).toList(); | 1371 var typeParams = _emitTypeParams(node.element).toList(); |
| 1370 var returnType = emitTypeRef(node.element.returnType); | 1372 var returnType = emitTypeRef(node.element.returnType); |
| 1371 fn = _emitFunctionBody(params, node.body, typeParams, returnType); | 1373 fn = _emitFunctionBody( |
| 1374 params, paramRefs, node.body, typeParams, returnType); |
| 1372 } | 1375 } |
| 1373 | 1376 |
| 1374 if (node.operatorKeyword != null && | 1377 if (node.operatorKeyword != null && |
| 1375 node.name.name == '[]=' && | 1378 node.name.name == '[]=' && |
| 1376 params.isNotEmpty) { | 1379 params.isNotEmpty) { |
| 1377 // []= methods need to return the value. We could also address this at | 1380 // []= methods need to return the value. We could also address this at |
| 1378 // call sites, but it's cleaner to instead transform the operator method. | 1381 // call sites, but it's cleaner to instead transform the operator method. |
| 1379 var returnValue = new JS.Return(params.last); | 1382 var returnValue = new JS.Return(params.last); |
| 1380 var body = fn.body; | 1383 var body = fn.body; |
| 1381 if (JS.Return.foundIn(fn)) { | 1384 if (JS.Return.foundIn(fn)) { |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1540 @override | 1543 @override |
| 1541 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1544 JS.Expression visitFunctionExpression(FunctionExpression node) { |
| 1542 var params = _visit(node.parameters) as List<JS.Parameter>; | 1545 var params = _visit(node.parameters) as List<JS.Parameter>; |
| 1543 if (params == null) params = <JS.Parameter>[]; | 1546 if (params == null) params = <JS.Parameter>[]; |
| 1544 | 1547 |
| 1545 var parent = node.parent; | 1548 var parent = node.parent; |
| 1546 var inStmt = parent.parent is FunctionDeclarationStatement; | 1549 var inStmt = parent.parent is FunctionDeclarationStatement; |
| 1547 var typeParams = _emitTypeParams(node.element).toList(); | 1550 var typeParams = _emitTypeParams(node.element).toList(); |
| 1548 var returnType = emitTypeRef(node.element.returnType); | 1551 var returnType = emitTypeRef(node.element.returnType); |
| 1549 if (parent is FunctionDeclaration) { | 1552 if (parent is FunctionDeclaration) { |
| 1550 return _emitFunctionBody(params, node.body, typeParams, returnType); | 1553 var paramRefs = _emitParameterReferences(node.parameters); |
| 1554 return _emitFunctionBody( |
| 1555 params, paramRefs, node.body, typeParams, returnType); |
| 1551 } else { | 1556 } else { |
| 1552 // Chrome Canary does not accept default values with destructuring in | 1557 // Chrome Canary does not accept default values with destructuring in |
| 1553 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them | 1558 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them |
| 1554 // with regular functions (e.g. `function({a} = {}) { return 1 }`). | 1559 // with regular functions (e.g. `function({a} = {}) { return 1 }`). |
| 1555 // Note that Firefox accepts both syntaxes just fine. | 1560 // Note that Firefox accepts both syntaxes just fine. |
| 1556 // TODO(ochafik): Simplify this code when Chrome Canary catches up. | 1561 // TODO(ochafik): Simplify this code when Chrome Canary catches up. |
| 1557 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam); | 1562 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam); |
| 1558 | 1563 |
| 1559 JS.Node jsBody; | 1564 JS.Node jsBody; |
| 1560 var body = node.body; | 1565 var body = node.body; |
| 1561 if (body.isGenerator || body.isAsynchronous) { | 1566 if (body.isGenerator || body.isAsynchronous) { |
| 1562 jsBody = _emitGeneratorFunctionBody(params, body, returnType); | 1567 var paramRefs = _emitParameterReferences(node.parameters); |
| 1568 jsBody = _emitGeneratorFunctionBody( |
| 1569 params, paramRefs, body, returnType); |
| 1563 } else if (body is ExpressionFunctionBody) { | 1570 } else if (body is ExpressionFunctionBody) { |
| 1564 jsBody = _visit(body.expression); | 1571 jsBody = _visit(body.expression); |
| 1565 } else { | 1572 } else { |
| 1566 jsBody = _visit(body); | 1573 jsBody = _visit(body); |
| 1567 } | 1574 } |
| 1568 if (jsBody is JS.Expression && !canUseArrowFun) { | 1575 if (jsBody is JS.Expression && !canUseArrowFun) { |
| 1569 jsBody = js.statement("{ return #; }", [jsBody]); | 1576 jsBody = js.statement("{ return #; }", [jsBody]); |
| 1570 } | 1577 } |
| 1571 var clos = canUseArrowFun | 1578 var clos = canUseArrowFun |
| 1572 ? new JS.ArrowFun(params, jsBody, | 1579 ? new JS.ArrowFun(params, jsBody, |
| 1573 typeParams: typeParams, returnType: returnType) | 1580 typeParams: typeParams, returnType: returnType) |
| 1574 : new JS.Fun(params, jsBody, | 1581 : new JS.Fun(params, jsBody, |
| 1575 typeParams: typeParams, returnType: returnType); | 1582 typeParams: typeParams, returnType: returnType); |
| 1576 if (!inStmt) { | 1583 if (!inStmt) { |
| 1577 var type = getStaticType(node); | 1584 var type = getStaticType(node); |
| 1578 return _emitFunctionTagged(clos, type, | 1585 return _emitFunctionTagged(clos, type, |
| 1579 topLevel: _executesAtTopLevel(node)); | 1586 topLevel: _executesAtTopLevel(node)); |
| 1580 } | 1587 } |
| 1581 return clos; | 1588 return clos; |
| 1582 } | 1589 } |
| 1583 } | 1590 } |
| 1584 | 1591 |
| 1585 JS.Fun _emitFunctionBody(List<JS.Parameter> params, FunctionBody body, | 1592 JS.Fun _emitFunctionBody(List<JS.Parameter> params, |
| 1593 List<JS.Expression> paramRefs, FunctionBody body, |
| 1586 List<JS.Identifier> typeParams, JS.TypeRef returnType) { | 1594 List<JS.Identifier> typeParams, JS.TypeRef returnType) { |
| 1587 // sync*, async, async* | 1595 // sync*, async, async* |
| 1588 if (body.isAsynchronous || body.isGenerator) { | 1596 if (body.isAsynchronous || body.isGenerator) { |
| 1597 // TODO(ochafik): Refine params: we don't need default values in the |
| 1598 // nested function, so we'd need to generate a custom, simpler params |
| 1599 // list here. |
| 1589 return new JS.Fun( | 1600 return new JS.Fun( |
| 1590 params, | 1601 params, |
| 1591 js.statement('{ return #; }', | 1602 js.statement('{ return #; }', [ |
| 1592 [_emitGeneratorFunctionBody(params, body, returnType)]), | 1603 _emitGeneratorFunctionBody(params, paramRefs, body, returnType) |
| 1604 ]), |
| 1593 returnType: returnType); | 1605 returnType: returnType); |
| 1594 } | 1606 } |
| 1595 // normal function (sync) | 1607 // normal function (sync) |
| 1596 return new JS.Fun(params, _visit(body), | 1608 return new JS.Fun(params, _visit(body), |
| 1597 typeParams: typeParams, returnType: returnType); | 1609 typeParams: typeParams, returnType: returnType); |
| 1598 } | 1610 } |
| 1599 | 1611 |
| 1600 JS.Expression _emitGeneratorFunctionBody( | 1612 JS.Expression _emitGeneratorFunctionBody( |
| 1601 List<JS.Parameter> params, FunctionBody body, JS.TypeRef returnType) { | 1613 List<JS.Parameter> params, List<JS.Expression> paramRefs, |
| 1614 FunctionBody body, JS.TypeRef returnType) { |
| 1602 var kind = body.isSynchronous ? 'sync' : 'async'; | 1615 var kind = body.isSynchronous ? 'sync' : 'async'; |
| 1603 if (body.isGenerator) kind += 'Star'; | 1616 if (body.isGenerator) kind += 'Star'; |
| 1604 | 1617 |
| 1605 // Transforms `sync*` `async` and `async*` function bodies | 1618 // Transforms `sync*` `async` and `async*` function bodies |
| 1606 // using ES6 generators. | 1619 // using ES6 generators. |
| 1607 // | 1620 // |
| 1608 // `sync*` wraps a generator in a Dart Iterable<T>: | 1621 // `sync*` wraps a generator in a Dart Iterable<T>: |
| 1609 // | 1622 // |
| 1610 // function name(<args>) { | 1623 // function name(<args>) { |
| 1611 // return dart.syncStar(function*(<args>) { | 1624 // return dart.syncStar(function*(<args>) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1645 JS.Expression gen = new JS.Fun(jsParams, _visit(body), | 1658 JS.Expression gen = new JS.Fun(jsParams, _visit(body), |
| 1646 isGenerator: true, returnType: returnType); | 1659 isGenerator: true, returnType: returnType); |
| 1647 if (JS.This.foundIn(gen)) { | 1660 if (JS.This.foundIn(gen)) { |
| 1648 gen = js.call('#.bind(this)', gen); | 1661 gen = js.call('#.bind(this)', gen); |
| 1649 } | 1662 } |
| 1650 _asyncStarController = savedController; | 1663 _asyncStarController = savedController; |
| 1651 | 1664 |
| 1652 var T = _emitTypeName(_getExpectedReturnType(body)); | 1665 var T = _emitTypeName(_getExpectedReturnType(body)); |
| 1653 return js.call('dart.#(#)', [ | 1666 return js.call('dart.#(#)', [ |
| 1654 kind, | 1667 kind, |
| 1655 [gen, T]..addAll(params) | 1668 [gen, T]..addAll(paramRefs) |
| 1656 ]); | 1669 ]); |
| 1657 } | 1670 } |
| 1658 | 1671 |
| 1659 @override | 1672 @override |
| 1660 JS.Statement visitFunctionDeclarationStatement( | 1673 JS.Statement visitFunctionDeclarationStatement( |
| 1661 FunctionDeclarationStatement node) { | 1674 FunctionDeclarationStatement node) { |
| 1662 var func = node.functionDeclaration; | 1675 var func = node.functionDeclaration; |
| 1663 if (func.isGetter || func.isSetter) { | 1676 if (func.isGetter || func.isSetter) { |
| 1664 return js.comment('Unimplemented function get/set statement: $node'); | 1677 return js.comment('Unimplemented function get/set statement: $node'); |
| 1665 } | 1678 } |
| (...skipping 491 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2157 _propertyName(node.name.label.name), _visit(node.expression)); | 2170 _propertyName(node.name.label.name), _visit(node.expression)); |
| 2158 } | 2171 } |
| 2159 | 2172 |
| 2160 bool _isNamedParam(FormalParameter param) => | 2173 bool _isNamedParam(FormalParameter param) => |
| 2161 param.kind == ParameterKind.NAMED; | 2174 param.kind == ParameterKind.NAMED; |
| 2162 | 2175 |
| 2163 @override | 2176 @override |
| 2164 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => | 2177 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => |
| 2165 _emitFormalParameterList(node); | 2178 _emitFormalParameterList(node); |
| 2166 | 2179 |
| 2180 // TODO(ochafik): Decouple Parameter from Identifier. |
| 2181 List<JS.Expression> _emitParameterReferences(FormalParameterList node) => |
| 2182 node == null |
| 2183 ? <JS.Expression>[] |
| 2184 : _emitFormalParameterList(node, allowDestructuring: false) |
| 2185 .map((JS.Parameter p) { |
| 2186 if (p is JS.RestParameter) return new JS.Spread(p.parameter); |
| 2187 return p as JS.Identifier; |
| 2188 }) |
| 2189 .toList(); |
| 2190 |
| 2167 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, | 2191 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, |
| 2168 {bool allowDestructuring: true}) { | 2192 {bool allowDestructuring: true}) { |
| 2169 var result = <JS.Parameter>[]; | 2193 var result = <JS.Parameter>[]; |
| 2170 | 2194 |
| 2171 var namedVars = <JS.DestructuredVariable>[]; | 2195 var namedVars = <JS.DestructuredVariable>[]; |
| 2172 var destructure = allowDestructuring && options.destructureNamedParams; | 2196 var destructure = allowDestructuring && options.destructureNamedParams; |
| 2173 var hasNamedArgsConflictingWithObjectProperties = false; | 2197 var hasNamedArgsConflictingWithObjectProperties = false; |
| 2174 var needsOpts = false; | 2198 var needsOpts = false; |
| 2175 | 2199 |
| 2176 for (FormalParameter param in node.parameters) { | 2200 for (FormalParameter param in node.parameters) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 2191 namedVars.add(new JS.DestructuredVariable( | 2215 namedVars.add(new JS.DestructuredVariable( |
| 2192 name: name, | 2216 name: name, |
| 2193 structure: structure, | 2217 structure: structure, |
| 2194 defaultValue: _defaultParamValue(param))); | 2218 defaultValue: _defaultParamValue(param))); |
| 2195 } else { | 2219 } else { |
| 2196 needsOpts = true; | 2220 needsOpts = true; |
| 2197 } | 2221 } |
| 2198 } else { | 2222 } else { |
| 2199 var jsParam = _visit(param); | 2223 var jsParam = _visit(param); |
| 2200 result.add( | 2224 result.add( |
| 2201 param is DefaultFormalParameter && options.destructureNamedParams | 2225 param is DefaultFormalParameter && destructure |
| 2202 ? new JS.DestructuredVariable( | 2226 ? new JS.DestructuredVariable( |
| 2203 name: jsParam, defaultValue: _defaultParamValue(param)) | 2227 name: jsParam, defaultValue: _defaultParamValue(param)) |
| 2204 : jsParam); | 2228 : jsParam); |
| 2205 } | 2229 } |
| 2206 } | 2230 } |
| 2207 | 2231 |
| 2208 if (needsOpts) { | 2232 if (needsOpts) { |
| 2209 result.add(_namedArgTemp); | 2233 result.add(_namedArgTemp); |
| 2210 } else if (namedVars.isNotEmpty) { | 2234 } else if (namedVars.isNotEmpty) { |
| 2211 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so | 2235 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so |
| (...skipping 1543 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3755 | 3779 |
| 3756 /// A special kind of element created by the compiler, signifying a temporary | 3780 /// A special kind of element created by the compiler, signifying a temporary |
| 3757 /// variable. These objects use instance equality, and should be shared | 3781 /// variable. These objects use instance equality, and should be shared |
| 3758 /// everywhere in the tree where they are treated as the same variable. | 3782 /// everywhere in the tree where they are treated as the same variable. |
| 3759 class TemporaryVariableElement extends LocalVariableElementImpl { | 3783 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 3760 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3784 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 3761 | 3785 |
| 3762 int get hashCode => identityHashCode(this); | 3786 int get hashCode => identityHashCode(this); |
| 3763 bool operator ==(Object other) => identical(this, other); | 3787 bool operator ==(Object other) => identical(this, other); |
| 3764 } | 3788 } |
| OLD | NEW |