Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1729933002: simplify generation of functions (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/codegen/expect/destructuring.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | test/codegen/expect/destructuring.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698