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 |