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 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
6 | 6 |
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
8 import 'package:analyzer/dart/ast/token.dart'; | 8 import 'package:analyzer/dart/ast/token.dart'; |
9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/constant.dart'; | 10 import 'package:analyzer/src/generated/constant.dart'; |
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
423 ctors.add(member); | 423 ctors.add(member); |
424 } else if (member is FieldDeclaration) { | 424 } else if (member is FieldDeclaration) { |
425 (member.isStatic ? staticFields : fields).add(member); | 425 (member.isStatic ? staticFields : fields).add(member); |
426 } else if (member is MethodDeclaration) { | 426 } else if (member is MethodDeclaration) { |
427 methods.add(member); | 427 methods.add(member); |
428 } | 428 } |
429 } | 429 } |
430 | 430 |
431 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), | 431 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
432 _classHeritage(classElem), _emitClassMethods(node, ctors, fields), | 432 _classHeritage(classElem), _emitClassMethods(node, ctors, fields), |
433 typeParams: _emitTypeParams(classElem).toList(), | 433 typeParams: _emitTypeFormals(classElem.typeParameters), |
434 fields: | 434 fields: |
435 _emitFieldDeclarations(classElem, fields, staticFields).toList()); | 435 _emitFieldDeclarations(classElem, fields, staticFields).toList()); |
436 | 436 |
437 String jsPeerName; | 437 String jsPeerName; |
438 var jsPeer = findAnnotation(classElem, isJsPeerInterface); | 438 var jsPeer = findAnnotation(classElem, isJsPeerInterface); |
439 // Only look at "Native" annotations on registered extension types. | 439 // Only look at "Native" annotations on registered extension types. |
440 // E.g., we're current ignoring the ones in dart:html. | 440 // E.g., we're current ignoring the ones in dart:html. |
441 if (jsPeer == null && _extensionTypes.contains(classElem)) { | 441 if (jsPeer == null && _extensionTypes.contains(classElem)) { |
442 jsPeer = findAnnotation(classElem, isNativeAnnotation); | 442 jsPeer = findAnnotation(classElem, isNativeAnnotation); |
443 } | 443 } |
(...skipping 22 matching lines...) Expand all Loading... |
466 // want to support construction of instances with generic types other | 466 // want to support construction of instances with generic types other |
467 // than dynamic. See issue #154 for Array and List<E> related bug. | 467 // than dynamic. See issue #154 for Array and List<E> related bug. |
468 var copyMembers = js.statement( | 468 var copyMembers = js.statement( |
469 'dart.registerExtension(dart.global.#, #);', | 469 'dart.registerExtension(dart.global.#, #);', |
470 [_propertyName(jsPeerName), classElem.name]); | 470 [_propertyName(jsPeerName), classElem.name]); |
471 return _statement([result, copyMembers]); | 471 return _statement([result, copyMembers]); |
472 } | 472 } |
473 return result; | 473 return result; |
474 } | 474 } |
475 | 475 |
476 Iterable<JS.Identifier> _emitTypeParams(TypeParameterizedElement e) sync* { | 476 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { |
477 if (!options.closure) return; | 477 if (!options.closure) return null; |
478 for (var typeParam in e.typeParameters) { | 478 return typeFormals |
479 yield new JS.Identifier(typeParam.name); | 479 .map((t) => new JS.Identifier(t.name)) |
480 } | 480 .toList(growable: false); |
481 } | 481 } |
482 | 482 |
483 /// Emit field declarations for TypeScript & Closure's ES6_TYPED | 483 /// Emit field declarations for TypeScript & Closure's ES6_TYPED |
484 /// (e.g. `class Foo { i: string; }`) | 484 /// (e.g. `class Foo { i: string; }`) |
485 Iterable<JS.VariableDeclarationList> _emitFieldDeclarations( | 485 Iterable<JS.VariableDeclarationList> _emitFieldDeclarations( |
486 ClassElement classElem, | 486 ClassElement classElem, |
487 List<FieldDeclaration> fields, | 487 List<FieldDeclaration> fields, |
488 List<FieldDeclaration> staticFields) sync* { | 488 List<FieldDeclaration> staticFields) sync* { |
489 if (!options.closure) return; | 489 if (!options.closure) return; |
490 | 490 |
(...skipping 559 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1050 var returnType = emitTypeRef(node.element.enclosingElement.type); | 1050 var returnType = emitTypeRef(node.element.enclosingElement.type); |
1051 | 1051 |
1052 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | 1052 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
1053 var redirect = node.redirectedConstructor; | 1053 var redirect = node.redirectedConstructor; |
1054 if (redirect != null) { | 1054 if (redirect != null) { |
1055 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; | 1055 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; |
1056 // Pass along all arguments verbatim, and let the callee handle them. | 1056 // Pass along all arguments verbatim, and let the callee handle them. |
1057 // TODO(jmesserly): we'll need something different once we have | 1057 // TODO(jmesserly): we'll need something different once we have |
1058 // rest/spread support, but this should work for now. | 1058 // rest/spread support, but this should work for now. |
1059 var params = | 1059 var params = |
1060 _emitFormalParameterList(node.parameters, allowDestructuring: false); | 1060 visitFormalParameterList(node.parameters, destructure: false); |
1061 | 1061 |
1062 var fun = new JS.Fun( | 1062 var fun = new JS.Fun( |
1063 params, | 1063 params, |
1064 js.statement( | 1064 js.statement( |
1065 '{ return $newKeyword #(#); }', [_visit(redirect), params]), | 1065 '{ return $newKeyword #(#); }', [_visit(redirect), params]), |
1066 returnType: returnType); | 1066 returnType: returnType); |
1067 return annotate( | 1067 return annotate( |
1068 new JS.Method(name, fun, isStatic: true), node, node.element); | 1068 new JS.Method(name, fun, isStatic: true), node, node.element); |
1069 } | 1069 } |
1070 | 1070 |
1071 // For const constructors we need to ensure default values are | 1071 // For const constructors we need to ensure default values are |
1072 // available for use by top-level constant initializers. | 1072 // available for use by top-level constant initializers. |
1073 ClassDeclaration cls = node.parent; | 1073 ClassDeclaration cls = node.parent; |
1074 if (node.constKeyword != null) _loader.startTopLevel(cls.element); | 1074 if (node.constKeyword != null) _loader.startTopLevel(cls.element); |
1075 var params = _emitFormalParameterList(node.parameters); | 1075 var params = visitFormalParameterList(node.parameters); |
1076 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); | 1076 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); |
1077 | 1077 |
1078 // Factory constructors are essentially static methods. | 1078 // Factory constructors are essentially static methods. |
1079 if (node.factoryKeyword != null) { | 1079 if (node.factoryKeyword != null) { |
1080 var body = <JS.Statement>[]; | 1080 var body = <JS.Statement>[]; |
1081 var init = _emitArgumentInitializers(node, constructor: true); | 1081 var init = _emitArgumentInitializers(node, constructor: true); |
1082 if (init != null) body.add(init); | 1082 if (init != null) body.add(init); |
1083 body.add(_visit(node.body)); | 1083 body.add(_visit(node.body)); |
1084 var fun = new JS.Fun(params, new JS.Block(body), returnType: returnType); | 1084 var fun = new JS.Fun(params, new JS.Block(body), returnType: returnType); |
1085 return annotate( | 1085 return annotate( |
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1371 t is ParameterizedType && t.typeArguments.any(_hasUnsoundTypeParameter); | 1371 t is ParameterizedType && t.typeArguments.any(_hasUnsoundTypeParameter); |
1372 | 1372 |
1373 JS.Expression _defaultParamValue(FormalParameter param) { | 1373 JS.Expression _defaultParamValue(FormalParameter param) { |
1374 if (param is DefaultFormalParameter && param.defaultValue != null) { | 1374 if (param is DefaultFormalParameter && param.defaultValue != null) { |
1375 return _visit(param.defaultValue); | 1375 return _visit(param.defaultValue); |
1376 } else { | 1376 } else { |
1377 return new JS.LiteralNull(); | 1377 return new JS.LiteralNull(); |
1378 } | 1378 } |
1379 } | 1379 } |
1380 | 1380 |
1381 JS.Fun _emitNativeFunctionBody(List<JS.Parameter> params, | 1381 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { |
1382 List<JS.Expression> paramRefs, MethodDeclaration node) { | |
1383 if (node.isStatic) { | 1382 if (node.isStatic) { |
1384 // TODO(vsm): Do we need to handle this case? | 1383 // TODO(vsm): Do we need to handle this case? |
1385 return null; | 1384 return null; |
1386 } | 1385 } |
1387 | 1386 |
| 1387 var params = visitFormalParameterList(node.parameters, destructure: false); |
1388 String name = node.name.name; | 1388 String name = node.name.name; |
1389 var annotation = findAnnotation(node.element, isJsName); | 1389 var annotation = findAnnotation(node.element, isJsName); |
1390 if (annotation != null) { | 1390 if (annotation != null) { |
1391 name = getConstantField(annotation, 'name', types.stringType) | 1391 name = getConstantField(annotation, 'name', types.stringType) |
1392 ?.toStringValue(); | 1392 ?.toStringValue(); |
1393 } | 1393 } |
1394 if (node.isGetter) { | 1394 if (node.isGetter) { |
1395 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); | 1395 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); |
1396 } else if (node.isSetter) { | 1396 } else if (node.isSetter) { |
1397 return new JS.Fun( | 1397 return new JS.Fun( |
1398 params, js.statement('{ this.# = #; }', [name, paramRefs.last])); | 1398 params, js.statement('{ this.# = #; }', [name, params.last])); |
1399 } else { | 1399 } else { |
1400 return new JS.Fun( | 1400 return new JS.Fun( |
1401 params, js.statement('{ return this.#(#); }', [name, paramRefs])); | 1401 params, js.statement('{ return this.#(#); }', [name, params])); |
1402 } | 1402 } |
1403 } | 1403 } |
1404 | 1404 |
1405 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1405 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
1406 if (node.isAbstract) { | 1406 if (node.isAbstract) { |
1407 return null; | 1407 return null; |
1408 } | 1408 } |
1409 | 1409 |
1410 var params = _visit(node.parameters) as List<JS.Parameter>; | |
1411 if (params == null) params = <JS.Parameter>[]; | |
1412 var paramRefs = _emitParameterReferences(node.parameters); | |
1413 | |
1414 JS.Fun fn; | 1410 JS.Fun fn; |
1415 if (_externalOrNative(node)) { | 1411 if (_externalOrNative(node)) { |
1416 fn = _emitNativeFunctionBody(params, paramRefs, node); | 1412 fn = _emitNativeFunctionBody(node); |
1417 // TODO(vsm): Remove if / when we handle the static case above. | 1413 // TODO(vsm): Remove if / when we handle the static case above. |
1418 if (fn == null) return null; | 1414 if (fn == null) return null; |
1419 } else { | 1415 } else { |
1420 var typeParams = _emitTypeParams(node.element).toList(); | 1416 fn = _emitFunctionBody(node.element, node.parameters, node.body); |
1421 var returnType = emitTypeRef(node.element.returnType); | |
1422 fn = _emitFunctionBody( | |
1423 params, paramRefs, node.body, typeParams, returnType); | |
1424 } | |
1425 | 1417 |
1426 if (node.operatorKeyword != null && | 1418 if (node.operatorKeyword != null && |
1427 node.name.name == '[]=' && | 1419 node.name.name == '[]=' && |
1428 params.isNotEmpty) { | 1420 fn.params.isNotEmpty) { |
1429 // []= methods need to return the value. We could also address this at | 1421 // []= methods need to return the value. We could also address this at |
1430 // call sites, but it's cleaner to instead transform the operator method. | 1422 // call sites, but it's cleaner to instead transform the operator method
. |
1431 var returnValue = new JS.Return(params.last); | 1423 fn = _alwaysReturnLastParameter(fn); |
1432 var body = fn.body; | |
1433 if (JS.Return.foundIn(fn)) { | |
1434 // If a return is inside body, transform `(params) { body }` to | |
1435 // `(params) { (() => { body })(); return value; }`. | |
1436 // TODO(jmesserly): we could instead generate the return differently, | |
1437 // and avoid the immediately invoked function. | |
1438 body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement(); | |
1439 } | 1424 } |
1440 // Rewrite the function to include the return. | |
1441 fn = new JS.Fun(fn.params, new JS.Block([body, returnValue]), | |
1442 typeParams: fn.typeParams, | |
1443 returnType: fn.returnType)..sourceInformation = fn.sourceInformation; | |
1444 } | 1425 } |
1445 | 1426 |
1446 return annotate( | 1427 return annotate( |
1447 new JS.Method(_elementMemberName(node.element), fn, | 1428 new JS.Method(_elementMemberName(node.element), fn, |
1448 isGetter: node.isGetter, | 1429 isGetter: node.isGetter, |
1449 isSetter: node.isSetter, | 1430 isSetter: node.isSetter, |
1450 isStatic: node.isStatic), | 1431 isStatic: node.isStatic), |
1451 node, | 1432 node, |
1452 node.element); | 1433 node.element); |
1453 } | 1434 } |
1454 | 1435 |
| 1436 /// Transform the function so the last parameter is always returned. |
| 1437 /// |
| 1438 /// This is useful for indexed set methods, which otherwise would not have |
| 1439 /// the right return value in JS. |
| 1440 JS.Fun _alwaysReturnLastParameter(JS.Fun fn) { |
| 1441 var body = fn.body; |
| 1442 if (JS.Return.foundIn(fn)) { |
| 1443 // If a return is inside body, transform `(params) { body }` to |
| 1444 // `(params) { (() => { body })(); return value; }`. |
| 1445 // TODO(jmesserly): we could instead generate the return differently, |
| 1446 // and avoid the immediately invoked function. |
| 1447 body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement(); |
| 1448 } |
| 1449 // Rewrite the function to include the return. |
| 1450 return new JS.Fun( |
| 1451 fn.params, new JS.Block([body, new JS.Return(fn.params.last)]), |
| 1452 typeParams: fn.typeParams, |
| 1453 returnType: fn.returnType)..sourceInformation = fn.sourceInformation; |
| 1454 } |
| 1455 |
1455 @override | 1456 @override |
1456 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1457 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
1457 assert(node.parent is CompilationUnit); | 1458 assert(node.parent is CompilationUnit); |
1458 | 1459 |
1459 if (_externalOrNative(node)) return null; | 1460 if (_externalOrNative(node)) return null; |
1460 | 1461 |
1461 if (node.isGetter || node.isSetter) { | 1462 if (node.isGetter || node.isSetter) { |
1462 // Add these later so we can use getter/setter syntax. | 1463 // Add these later so we can use getter/setter syntax. |
1463 _properties.add(node); | 1464 _properties.add(node); |
1464 return null; | 1465 return null; |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1573 if (lazy) { | 1574 if (lazy) { |
1574 return js.call('dart.fn(#, () => #)', [clos, _emitFunctionRTTI(type)]); | 1575 return js.call('dart.fn(#, () => #)', [clos, _emitFunctionRTTI(type)]); |
1575 } | 1576 } |
1576 return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]); | 1577 return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]); |
1577 } | 1578 } |
1578 throw 'Function has non function type: $type'; | 1579 throw 'Function has non function type: $type'; |
1579 } | 1580 } |
1580 | 1581 |
1581 @override | 1582 @override |
1582 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1583 JS.Expression visitFunctionExpression(FunctionExpression node) { |
1583 var params = _visit(node.parameters) as List<JS.Parameter>; | 1584 var params = visitFormalParameterList(node.parameters); |
1584 if (params == null) params = <JS.Parameter>[]; | |
1585 | 1585 |
| 1586 var body = node.body; |
1586 var parent = node.parent; | 1587 var parent = node.parent; |
1587 var inStmt = parent.parent is FunctionDeclarationStatement; | 1588 var inStmt = parent.parent is FunctionDeclarationStatement; |
1588 var typeParams = _emitTypeParams(node.element).toList(); | |
1589 var returnType = emitTypeRef(node.element.returnType); | |
1590 if (parent is FunctionDeclaration) { | 1589 if (parent is FunctionDeclaration) { |
1591 var paramRefs = _emitParameterReferences(node.parameters); | 1590 return _emitFunctionBody(node.element, node.parameters, body); |
1592 return _emitFunctionBody( | 1591 } |
1593 params, paramRefs, node.body, typeParams, returnType); | 1592 |
| 1593 JS.Node jsBody; |
| 1594 if (body.isGenerator || body.isAsynchronous) { |
| 1595 jsBody = _emitGeneratorFunctionBody(node.element, node.parameters, body); |
| 1596 } else if (body is ExpressionFunctionBody) { |
| 1597 jsBody = _visit(body.expression); |
| 1598 } else { |
| 1599 jsBody = _visit(body); |
| 1600 } |
| 1601 |
| 1602 var type = node.element.type; |
| 1603 var typeFormals = _emitTypeFormals(type.typeFormals); |
| 1604 var returnType = emitTypeRef(type.returnType); |
| 1605 |
| 1606 JS.FunctionExpression fn; |
| 1607 if (node.parameters.parameters |
| 1608 .every((p) => p.kind != ParameterKind.NAMED)) { |
| 1609 fn = new JS.ArrowFun(params, jsBody, |
| 1610 typeParams: typeFormals, returnType: returnType); |
1594 } else { | 1611 } else { |
1595 // Chrome Canary does not accept default values with destructuring in | 1612 // Chrome Canary does not accept default values with destructuring in |
1596 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them | 1613 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them |
1597 // with regular functions (e.g. `function({a} = {}) { return 1 }`). | 1614 // with regular functions (e.g. `function({a} = {}) { return 1 }`). |
1598 // Note that Firefox accepts both syntaxes just fine. | 1615 // Note that Firefox accepts both syntaxes just fine. |
1599 // TODO(ochafik): Simplify this code when Chrome Canary catches up. | 1616 // TODO(ochafik): Simplify this code when Chrome Canary catches up. |
1600 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam); | |
1601 | 1617 |
1602 JS.Node jsBody; | 1618 if (jsBody is JS.Expression) { |
1603 var body = node.body; | |
1604 if (body.isGenerator || body.isAsynchronous) { | |
1605 var paramRefs = _emitParameterReferences(node.parameters); | |
1606 jsBody = | |
1607 _emitGeneratorFunctionBody(params, paramRefs, body, returnType); | |
1608 } else if (body is ExpressionFunctionBody) { | |
1609 jsBody = _visit(body.expression); | |
1610 } else { | |
1611 jsBody = _visit(body); | |
1612 } | |
1613 if (jsBody is JS.Expression && !canUseArrowFun) { | |
1614 jsBody = js.statement("{ return #; }", [jsBody]); | 1619 jsBody = js.statement("{ return #; }", [jsBody]); |
1615 } | 1620 } |
1616 var clos = canUseArrowFun | 1621 fn = new JS.Fun(params, jsBody, |
1617 ? new JS.ArrowFun(params, jsBody, | 1622 typeParams: typeFormals, returnType: returnType); |
1618 typeParams: typeParams, returnType: returnType) | |
1619 : new JS.Fun(params, jsBody, | |
1620 typeParams: typeParams, returnType: returnType); | |
1621 if (!inStmt) { | |
1622 var type = getStaticType(node); | |
1623 return _emitFunctionTagged(clos, type, | |
1624 topLevel: _executesAtTopLevel(node)); | |
1625 } | |
1626 return clos; | |
1627 } | 1623 } |
| 1624 |
| 1625 if (!inStmt) { |
| 1626 var type = getStaticType(node); |
| 1627 return _emitFunctionTagged(fn, type, topLevel: _executesAtTopLevel(node)); |
| 1628 } |
| 1629 return fn; |
1628 } | 1630 } |
1629 | 1631 |
1630 JS.Fun _emitFunctionBody( | 1632 JS.Fun _emitFunctionBody(ExecutableElement element, |
1631 List<JS.Parameter> params, | 1633 FormalParameterList parameters, FunctionBody body) { |
1632 List<JS.Expression> paramRefs, | 1634 var returnType = emitTypeRef(element.returnType); |
1633 FunctionBody body, | 1635 |
1634 List<JS.Identifier> typeParams, | |
1635 JS.TypeRef returnType) { | |
1636 // sync*, async, async* | 1636 // sync*, async, async* |
1637 if (body.isAsynchronous || body.isGenerator) { | 1637 if (element.isAsynchronous || element.isGenerator) { |
1638 // TODO(ochafik): Refine params: we don't need default values in the | |
1639 // nested function, so we'd need to generate a custom, simpler params | |
1640 // list here. | |
1641 return new JS.Fun( | 1638 return new JS.Fun( |
1642 params, | 1639 visitFormalParameterList(parameters, destructure: false), |
1643 js.statement('{ return #; }', [ | 1640 js.statement('{ return #; }', |
1644 _emitGeneratorFunctionBody(params, paramRefs, body, returnType) | 1641 [_emitGeneratorFunctionBody(element, parameters, body)]), |
1645 ]), | |
1646 returnType: returnType); | 1642 returnType: returnType); |
1647 } | 1643 } |
1648 // normal function (sync) | 1644 // normal function (sync) |
1649 return new JS.Fun(params, _visit(body), | 1645 return new JS.Fun(visitFormalParameterList(parameters), _visit(body), |
1650 typeParams: typeParams, returnType: returnType); | 1646 typeParams: _emitTypeFormals(element.typeParameters), |
| 1647 returnType: returnType); |
1651 } | 1648 } |
1652 | 1649 |
1653 JS.Expression _emitGeneratorFunctionBody(List<JS.Parameter> params, | 1650 JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, |
1654 List<JS.Expression> paramRefs, FunctionBody body, JS.TypeRef returnType) { | 1651 FormalParameterList parameters, FunctionBody body) { |
1655 var kind = body.isSynchronous ? 'sync' : 'async'; | 1652 var kind = element.isSynchronous ? 'sync' : 'async'; |
1656 if (body.isGenerator) kind += 'Star'; | 1653 if (element.isGenerator) kind += 'Star'; |
1657 | 1654 |
1658 // Transforms `sync*` `async` and `async*` function bodies | 1655 // Transforms `sync*` `async` and `async*` function bodies |
1659 // using ES6 generators. | 1656 // using ES6 generators. |
1660 // | 1657 // |
1661 // `sync*` wraps a generator in a Dart Iterable<T>: | 1658 // `sync*` wraps a generator in a Dart Iterable<T>: |
1662 // | 1659 // |
1663 // function name(<args>) { | 1660 // function name(<args>) { |
1664 // return dart.syncStar(function*(<args>) { | 1661 // return dart.syncStar(function*(<args>) { |
1665 // <body> | 1662 // <body> |
1666 // }, T, <args>).bind(this); | 1663 // }, T, <args>).bind(this); |
(...skipping 13 matching lines...) Expand all Loading... |
1680 // In the body of a `sync*` and `async`, `yield`/`await` are both generated | 1677 // In the body of a `sync*` and `async`, `yield`/`await` are both generated |
1681 // simply as `yield`. | 1678 // simply as `yield`. |
1682 // | 1679 // |
1683 // `async*` uses the `dart.asyncStar` helper, and also has an extra `stream` | 1680 // `async*` uses the `dart.asyncStar` helper, and also has an extra `stream` |
1684 // argument to the generator, which is used for passing values to the | 1681 // argument to the generator, which is used for passing values to the |
1685 // _AsyncStarStreamController implementation type. | 1682 // _AsyncStarStreamController implementation type. |
1686 // `yield` is specially generated inside `async*`, see visitYieldStatement. | 1683 // `yield` is specially generated inside `async*`, see visitYieldStatement. |
1687 // `await` is generated as `yield`. | 1684 // `await` is generated as `yield`. |
1688 // runtime/_generators.js has an example of what the code is generated as. | 1685 // runtime/_generators.js has an example of what the code is generated as. |
1689 var savedController = _asyncStarController; | 1686 var savedController = _asyncStarController; |
1690 List jsParams; | 1687 List jsParams = visitFormalParameterList(parameters); |
1691 if (kind == 'asyncStar') { | 1688 if (kind == 'asyncStar') { |
1692 _asyncStarController = new JS.TemporaryId('stream'); | 1689 _asyncStarController = new JS.TemporaryId('stream'); |
1693 jsParams = [_asyncStarController]..addAll(params); | 1690 jsParams.insert(0, _asyncStarController); |
1694 } else { | 1691 } else { |
1695 _asyncStarController = null; | 1692 _asyncStarController = null; |
1696 jsParams = params; | |
1697 } | 1693 } |
1698 JS.Expression gen = new JS.Fun(jsParams, _visit(body), | 1694 // Visit the body with our async* controller set. |
1699 isGenerator: true, returnType: returnType); | 1695 var jsBody = _visit(body); |
| 1696 _asyncStarController = savedController; |
| 1697 |
| 1698 DartType returnType = _getExpectedReturnType(element); |
| 1699 JS.Expression gen = new JS.Fun(jsParams, jsBody, |
| 1700 isGenerator: true, returnType: emitTypeRef(returnType)); |
1700 if (JS.This.foundIn(gen)) { | 1701 if (JS.This.foundIn(gen)) { |
1701 gen = js.call('#.bind(this)', gen); | 1702 gen = js.call('#.bind(this)', gen); |
1702 } | 1703 } |
1703 _asyncStarController = savedController; | |
1704 | 1704 |
1705 var T = _emitTypeName(_getExpectedReturnType(body)); | 1705 var T = _emitTypeName(returnType); |
1706 return js.call('dart.#(#)', [ | 1706 return js.call('dart.#(#)', [ |
1707 kind, | 1707 kind, |
1708 [gen, T]..addAll(paramRefs) | 1708 [gen, T]..addAll(visitFormalParameterList(parameters, destructure: false)) |
1709 ]); | 1709 ]); |
1710 } | 1710 } |
1711 | 1711 |
1712 @override | 1712 @override |
1713 JS.Statement visitFunctionDeclarationStatement( | 1713 JS.Statement visitFunctionDeclarationStatement( |
1714 FunctionDeclarationStatement node) { | 1714 FunctionDeclarationStatement node) { |
1715 var func = node.functionDeclaration; | 1715 var func = node.functionDeclaration; |
1716 if (func.isGetter || func.isSetter) { | 1716 if (func.isGetter || func.isSetter) { |
1717 return js.comment('Unimplemented function get/set statement: $node'); | 1717 return js.comment('Unimplemented function get/set statement: $node'); |
1718 } | 1718 } |
(...skipping 484 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2203 return args; | 2203 return args; |
2204 } | 2204 } |
2205 | 2205 |
2206 @override | 2206 @override |
2207 JS.Property visitNamedExpression(NamedExpression node) { | 2207 JS.Property visitNamedExpression(NamedExpression node) { |
2208 assert(node.parent is ArgumentList); | 2208 assert(node.parent is ArgumentList); |
2209 return new JS.Property( | 2209 return new JS.Property( |
2210 _propertyName(node.name.label.name), _visit(node.expression)); | 2210 _propertyName(node.name.label.name), _visit(node.expression)); |
2211 } | 2211 } |
2212 | 2212 |
2213 bool _isNamedParam(FormalParameter param) => | 2213 @override |
2214 param.kind == ParameterKind.NAMED; | 2214 List<JS.Parameter> visitFormalParameterList(FormalParameterList node, |
| 2215 {bool destructure: true}) { |
| 2216 if (node == null) return []; |
2215 | 2217 |
2216 @override | 2218 destructure = destructure && options.destructureNamedParams; |
2217 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => | |
2218 _emitFormalParameterList(node); | |
2219 | 2219 |
2220 // TODO(ochafik): Decouple Parameter from Identifier. | |
2221 List<JS.Expression> _emitParameterReferences(FormalParameterList node) => | |
2222 node == null | |
2223 ? <JS.Expression>[] | |
2224 : _emitFormalParameterList(node, allowDestructuring: false) | |
2225 .map((JS.Parameter p) { | |
2226 if (p is JS.RestParameter) return new JS.Spread(p.parameter); | |
2227 return p as JS.Identifier; | |
2228 }).toList(); | |
2229 | |
2230 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, | |
2231 {bool allowDestructuring: true}) { | |
2232 var result = <JS.Parameter>[]; | 2220 var result = <JS.Parameter>[]; |
2233 | |
2234 var namedVars = <JS.DestructuredVariable>[]; | 2221 var namedVars = <JS.DestructuredVariable>[]; |
2235 var destructure = allowDestructuring && options.destructureNamedParams; | |
2236 var hasNamedArgsConflictingWithObjectProperties = false; | 2222 var hasNamedArgsConflictingWithObjectProperties = false; |
2237 var needsOpts = false; | 2223 var needsOpts = false; |
2238 | 2224 |
2239 for (FormalParameter param in node.parameters) { | 2225 for (FormalParameter param in node.parameters) { |
2240 if (param.kind == ParameterKind.NAMED) { | 2226 if (param.kind == ParameterKind.NAMED) { |
2241 if (destructure) { | 2227 if (destructure) { |
2242 if (_jsObjectProperties.contains(param.identifier.name)) { | 2228 if (_jsObjectProperties.contains(param.identifier.name)) { |
2243 hasNamedArgsConflictingWithObjectProperties = true; | 2229 hasNamedArgsConflictingWithObjectProperties = true; |
2244 } | 2230 } |
2245 JS.Expression name; | 2231 JS.Expression name; |
(...skipping 1430 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3676 MethodElement element = _types.objectType.element.getMethod(name); | 3662 MethodElement element = _types.objectType.element.getMethod(name); |
3677 return (element != null && !element.isStatic); | 3663 return (element != null && !element.isStatic); |
3678 } | 3664 } |
3679 | 3665 |
3680 bool _isObjectProperty(String name) { | 3666 bool _isObjectProperty(String name) { |
3681 return _isObjectGetter(name) || _isObjectMethod(name); | 3667 return _isObjectGetter(name) || _isObjectMethod(name); |
3682 } | 3668 } |
3683 | 3669 |
3684 // TODO(leafp): Various analyzer pieces computed similar things. | 3670 // TODO(leafp): Various analyzer pieces computed similar things. |
3685 // Share this logic somewhere? | 3671 // Share this logic somewhere? |
3686 DartType _getExpectedReturnType(FunctionBody body) { | 3672 DartType _getExpectedReturnType(ExecutableElement element) { |
3687 FunctionType functionType; | 3673 FunctionType functionType = element.type; |
3688 var parent = body.parent; | |
3689 if (parent is Declaration) { | |
3690 functionType = (parent.element as dynamic)?.type; | |
3691 } else { | |
3692 assert(parent is FunctionExpression); | |
3693 functionType = parent.staticType; | |
3694 } | |
3695 if (functionType == null) { | 3674 if (functionType == null) { |
3696 return DynamicTypeImpl.instance; | 3675 return DynamicTypeImpl.instance; |
3697 } | 3676 } |
3698 var type = functionType.returnType; | 3677 var type = functionType.returnType; |
3699 | 3678 |
3700 InterfaceType expectedType = null; | 3679 InterfaceType expectedType = null; |
3701 if (body.isAsynchronous) { | 3680 if (element.isAsynchronous) { |
3702 if (body.isGenerator) { | 3681 if (element.isGenerator) { |
3703 // Stream<T> -> T | 3682 // Stream<T> -> T |
3704 expectedType = _types.streamType; | 3683 expectedType = _types.streamType; |
3705 } else { | 3684 } else { |
3706 // Future<T> -> T | 3685 // Future<T> -> T |
3707 // TODO(vsm): Revisit with issue #228. | 3686 // TODO(vsm): Revisit with issue #228. |
3708 expectedType = _types.futureType; | 3687 expectedType = _types.futureType; |
3709 } | 3688 } |
3710 } else { | 3689 } else { |
3711 if (body.isGenerator) { | 3690 if (element.isGenerator) { |
3712 // Iterable<T> -> T | 3691 // Iterable<T> -> T |
3713 expectedType = _types.iterableType; | 3692 expectedType = _types.iterableType; |
3714 } else { | 3693 } else { |
3715 // T -> T | 3694 // T -> T |
3716 return type; | 3695 return type; |
3717 } | 3696 } |
3718 } | 3697 } |
3719 if (type.isDynamic) { | 3698 if (type.isDynamic) { |
3720 return type; | 3699 return type; |
3721 } else if (type is InterfaceType && type.element == expectedType.element) { | 3700 } else if (type is InterfaceType && type.element == expectedType.element) { |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3823 | 3802 |
3824 /// A special kind of element created by the compiler, signifying a temporary | 3803 /// A special kind of element created by the compiler, signifying a temporary |
3825 /// variable. These objects use instance equality, and should be shared | 3804 /// variable. These objects use instance equality, and should be shared |
3826 /// everywhere in the tree where they are treated as the same variable. | 3805 /// everywhere in the tree where they are treated as the same variable. |
3827 class TemporaryVariableElement extends LocalVariableElementImpl { | 3806 class TemporaryVariableElement extends LocalVariableElementImpl { |
3828 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3807 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3829 | 3808 |
3830 int get hashCode => identityHashCode(this); | 3809 int get hashCode => identityHashCode(this); |
3831 bool operator ==(Object other) => identical(this, other); | 3810 bool operator ==(Object other) => identical(this, other); |
3832 } | 3811 } |
OLD | NEW |