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

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/code_generator.dart

Issue 2929503002: refactor _emitFunctionBody slightly to simplify it (Closed)
Patch Set: fix Created 3 years, 6 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 | no next file » | 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 2
3 // for details. All rights reserved. Use of this source code is governed by a 3 // for details. All rights reserved. Use of this source code is governed by a
4 // BSD-style license that can be found in the LICENSE file. 4 // BSD-style license that can be found in the LICENSE file.
5 5
6 import 'dart:collection' show HashMap, HashSet; 6 import 'dart:collection' show HashMap, HashSet;
7 import 'dart:math' show min, max; 7 import 'dart:math' show min, max;
8 8
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
10 import 'package:analyzer/dart/ast/ast.dart'; 10 import 'package:analyzer/dart/ast/ast.dart';
(...skipping 1584 matching lines...) Expand 10 before | Expand all | Expand 10 after
1595 _runtimeModule, 1595 _runtimeModule,
1596 _declareMemberName(method, useDisplayName: true), 1596 _declareMemberName(method, useDisplayName: true),
1597 positionalArgs, 1597 positionalArgs,
1598 new JS.ObjectInitializer(invocationProps) 1598 new JS.ObjectInitializer(invocationProps)
1599 ]); 1599 ]);
1600 1600
1601 if (!method.returnType.isDynamic) { 1601 if (!method.returnType.isDynamic) {
1602 fnBody = js.call('#._check(#)', [_emitType(method.returnType), fnBody]); 1602 fnBody = js.call('#._check(#)', [_emitType(method.returnType), fnBody]);
1603 } 1603 }
1604 1604
1605 var fn = new JS.Fun(fnArgs, js.statement('{ return #; }', [fnBody]), 1605 var fn = _makeGenericFunction(new JS.Fun(
1606 typeParams: _emitTypeFormals(method.type.typeFormals)); 1606 fnArgs, js.statement('{ return #; }', [fnBody]),
1607 typeParams: _emitTypeFormals(method.type.typeFormals)));
1607 1608
1608 // TODO(jmesserly): generic type arguments will get dropped. 1609 // TODO(jmesserly): generic type arguments will get dropped.
1609 // We have a similar issue with `dgsend` helpers. 1610 // We have a similar issue with `dgsend` helpers.
1610 return new JS.Method( 1611 return new JS.Method(
1611 _declareMemberName(method, 1612 _declareMemberName(method,
1612 useExtension: _extensionTypes.isNativeClass(type.element)), 1613 useExtension: _extensionTypes.isNativeClass(type.element)),
1613 _makeGenericFunction(fn), 1614 fn,
1614 isGetter: method is PropertyAccessorElement && method.isGetter, 1615 isGetter: method is PropertyAccessorElement && method.isGetter,
1615 isSetter: method is PropertyAccessorElement && method.isSetter, 1616 isSetter: method is PropertyAccessorElement && method.isSetter,
1616 isStatic: false); 1617 isStatic: false);
1617 } 1618 }
1618 1619
1619 /// This is called whenever a derived class needs to introduce a new field, 1620 /// This is called whenever a derived class needs to introduce a new field,
1620 /// shadowing a field or getter/setter pair on its parent. 1621 /// shadowing a field or getter/setter pair on its parent.
1621 /// 1622 ///
1622 /// This is important because otherwise, trying to read or write the field 1623 /// This is important because otherwise, trying to read or write the field
1623 /// would end up calling the getter or setter, and one of those might not even 1624 /// would end up calling the getter or setter, and one of those might not even
(...skipping 811 matching lines...) Expand 10 before | Expand all | Expand 10 after
2435 2436
2436 JS.Fun fn; 2437 JS.Fun fn;
2437 if (_externalOrNative(node)) { 2438 if (_externalOrNative(node)) {
2438 if (node.isStatic) { 2439 if (node.isStatic) {
2439 // TODO(vsm): Do we need to handle this case? 2440 // TODO(vsm): Do we need to handle this case?
2440 return null; 2441 return null;
2441 } 2442 }
2442 fn = _emitNativeFunctionBody(node); 2443 fn = _emitNativeFunctionBody(node);
2443 } else { 2444 } else {
2444 fn = _emitFunctionBody(node.element, node.parameters, node.body); 2445 fn = _emitFunctionBody(node.element, node.parameters, node.body);
2445
2446 if (node.operatorKeyword != null &&
2447 node.name.name == '[]=' &&
2448 fn.params.isNotEmpty) {
2449 // []= methods need to return the value. We could also address this at
2450 // call sites, but it's cleaner to instead transform the operator method .
2451 fn = _alwaysReturnLastParameter(fn);
2452 }
2453
2454 fn = _makeGenericFunction(fn);
2455 } 2446 }
2456 2447
2457 return annotate( 2448 return annotate(
2458 new JS.Method(_declareMemberName(node.element), fn, 2449 new JS.Method(_declareMemberName(node.element), fn,
2459 isGetter: node.isGetter, 2450 isGetter: node.isGetter,
2460 isSetter: node.isSetter, 2451 isSetter: node.isSetter,
2461 isStatic: node.isStatic), 2452 isStatic: node.isStatic),
2462 node, 2453 node,
2463 node.element); 2454 node.element);
2464 } 2455 }
2465 2456
2466 /// Transform the function so the last parameter is always returned. 2457 /// Transform the function so the last parameter is always returned.
2467 /// 2458 ///
2468 /// This is useful for indexed set methods, which otherwise would not have 2459 /// This is useful for indexed set methods, which otherwise would not have
2469 /// the right return value in JS. 2460 /// the right return value in JS.
2470 JS.Fun _alwaysReturnLastParameter(JS.Fun fn) { 2461 JS.Node _alwaysReturnLastParameter(JS.Node body, JS.Parameter lastParam) {
2471 var body = fn.body; 2462 if (JS.Return.foundIn(body)) {
2472 if (JS.Return.foundIn(fn)) {
2473 // If a return is inside body, transform `(params) { body }` to 2463 // If a return is inside body, transform `(params) { body }` to
2474 // `(params) { (() => { body })(); return value; }`. 2464 // `(params) { (() => { body })(); return value; }`.
2475 // TODO(jmesserly): we could instead generate the return differently, 2465 // TODO(jmesserly): we could instead generate the return differently,
2476 // and avoid the immediately invoked function. 2466 // and avoid the immediately invoked function.
2477 body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement(); 2467 body = new JS.Call(new JS.ArrowFun([], body), []).toStatement();
2478 } 2468 }
2479 // Rewrite the function to include the return. 2469 return new JS.Block([body, new JS.Return(lastParam)]);
2480 return new JS.Fun(
2481 fn.params, new JS.Block([body, new JS.Return(fn.params.last)]),
2482 typeParams: fn.typeParams, returnType: fn.returnType)
2483 ..sourceInformation = fn.sourceInformation;
2484 } 2470 }
2485 2471
2486 @override 2472 @override
2487 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { 2473 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) {
2488 assert(node.parent is CompilationUnit); 2474 assert(node.parent is CompilationUnit);
2489 2475
2490 if (_externalOrNative(node)) return null; 2476 if (_externalOrNative(node)) return null;
2491 2477
2492 if (node.isGetter || node.isSetter) { 2478 if (node.isGetter || node.isSetter) {
2493 PropertyAccessorElement element = node.element; 2479 PropertyAccessorElement element = node.element;
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
2622 /// Contrast with [_emitFunction]. 2608 /// Contrast with [_emitFunction].
2623 @override 2609 @override
2624 JS.Expression visitFunctionExpression(FunctionExpression node) { 2610 JS.Expression visitFunctionExpression(FunctionExpression node) {
2625 assert(node.parent is! FunctionDeclaration && 2611 assert(node.parent is! FunctionDeclaration &&
2626 node.parent is! MethodDeclaration); 2612 node.parent is! MethodDeclaration);
2627 return _emitFunctionTagged(_emitArrowFunction(node), getStaticType(node), 2613 return _emitFunctionTagged(_emitArrowFunction(node), getStaticType(node),
2628 topLevel: _executesAtTopLevel(node)); 2614 topLevel: _executesAtTopLevel(node));
2629 } 2615 }
2630 2616
2631 JS.ArrowFun _emitArrowFunction(FunctionExpression node) { 2617 JS.ArrowFun _emitArrowFunction(FunctionExpression node) {
2632 JS.Fun f = _emitFunctionBody(node.element, node.parameters, node.body); 2618 JS.Fun fn = _emitFunctionBody(node.element, node.parameters, node.body);
2619
2620 return annotate(_toArrowFunction(fn), node);
2621 }
2622
2623 JS.Fun _makeGenericFunction(JS.Fun fn) {
2624 if (fn.typeParams == null || fn.typeParams.isEmpty) return fn;
2625
2626 return new JS.Fun(
2627 fn.typeParams,
2628 new JS.Block([
2629 // Convert the function to an => function, to ensure `this` binding.
2630 new JS.Return(_toArrowFunction(fn))
2631 ]));
2632 }
2633
2634 JS.ArrowFun _toArrowFunction(JS.Fun f) {
2633 JS.Node body = f.body; 2635 JS.Node body = f.body;
2634 2636
2635 // Simplify `=> { return e; }` to `=> e` 2637 // Simplify `=> { return e; }` to `=> e`
2636 if (body is JS.Block) { 2638 if (body is JS.Block) {
2637 JS.Block block = body; 2639 JS.Block block = body;
2638 if (block.statements.length == 1) { 2640 if (block.statements.length == 1) {
2639 JS.Statement s = block.statements[0]; 2641 JS.Statement s = block.statements[0];
2640 if (s is JS.Return && s.value != null) body = s.value; 2642 if (s is JS.Return && s.value != null) body = s.value;
2641 } 2643 }
2642 } 2644 }
2643 2645
2644 // Convert `function(...) { ... }` to `(...) => ...` 2646 // Convert `function(...) { ... }` to `(...) => ...`
2645 // This is for readability, but it also ensures correct `this` binding. 2647 // This is for readability, but it also ensures correct `this` binding.
2646 var fn = new JS.ArrowFun(f.params, body, 2648 return new JS.ArrowFun(f.params, body,
2647 typeParams: f.typeParams, returnType: f.returnType); 2649 typeParams: f.typeParams, returnType: f.returnType)
2648 2650 ..sourceInformation = f.sourceInformation;
2649 return annotate(_makeGenericArrowFun(fn), node);
2650 }
2651
2652 JS.ArrowFun _makeGenericArrowFun(JS.ArrowFun fn) {
2653 if (fn.typeParams == null || fn.typeParams.isEmpty) return fn;
2654 return new JS.ArrowFun(fn.typeParams, fn);
2655 }
2656
2657 JS.Fun _makeGenericFunction(JS.Fun fn) {
2658 if (fn.typeParams == null || fn.typeParams.isEmpty) return fn;
2659
2660 // TODO(jmesserly): we could make these default to `dynamic`.
2661 return new JS.Fun(
2662 fn.typeParams,
2663 new JS.Block([
2664 // Convert the function to an => function, to ensure `this` binding.
2665 new JS.Return(new JS.ArrowFun(fn.params, fn.body,
2666 typeParams: fn.typeParams, returnType: fn.returnType))
2667 ]));
2668 } 2651 }
2669 2652
2670 /// Emits a non-arrow FunctionExpression node. 2653 /// Emits a non-arrow FunctionExpression node.
2671 /// 2654 ///
2672 /// This should be used for all places in Dart's AST where FunctionExpression 2655 /// This should be used for all places in Dart's AST where FunctionExpression
2673 /// appears but the function is not actually in an Expression context, such 2656 /// appears but the function is not actually in an Expression context, such
2674 /// as methods, properties, and top-level functions. 2657 /// as methods, properties, and top-level functions.
2675 /// 2658 ///
2676 /// Contrast with [visitFunctionExpression]. 2659 /// Contrast with [visitFunctionExpression].
2677 JS.Fun _emitFunction(FunctionExpression node) { 2660 JS.Fun _emitFunction(FunctionExpression node) {
2678 var fn = _emitFunctionBody(node.element, node.parameters, node.body); 2661 return annotate(
2679 return annotate(_makeGenericFunction(fn), node); 2662 _emitFunctionBody(node.element, node.parameters, node.body), node);
2680 } 2663 }
2681 2664
2682 JS.Fun _emitFunctionBody(ExecutableElement element, 2665 JS.Fun _emitFunctionBody(ExecutableElement element,
2683 FormalParameterList parameters, FunctionBody body) { 2666 FormalParameterList parameters, FunctionBody body) {
2684 FunctionType type = element.type; 2667 FunctionType type = element.type;
2685 2668
2686 // normal function (sync), vs (sync*, async, async*) 2669 // normal function (sync), vs (sync*, async, async*)
2687 var stdFn = !(element.isAsynchronous || element.isGenerator); 2670 var stdFn = !(element.isAsynchronous || element.isGenerator);
2688 var formals = _emitFormalParameterList(parameters, destructure: stdFn); 2671 var formals = _emitFormalParameterList(parameters, destructure: stdFn);
2689 var code = (stdFn) 2672 var code = (stdFn)
2690 ? _visit(body) 2673 ? _visit(body)
2691 : new JS.Block( 2674 : new JS.Block(
2692 [_emitGeneratorFunctionBody(element, parameters, body).toReturn()]); 2675 [_emitGeneratorFunctionBody(element, parameters, body).toReturn()]);
2693 var typeFormals = _emitTypeFormals(type.typeFormals); 2676 var typeFormals = _emitTypeFormals(type.typeFormals);
2694 var returnType = emitTypeRef(type.returnType); 2677 var returnType = emitTypeRef(type.returnType);
2695 if (type.typeFormals.isNotEmpty) { 2678 if (type.typeFormals.isNotEmpty) {
2696 code = new JS.Block(<JS.Statement>[ 2679 code = new JS.Block(<JS.Statement>[
2697 new JS.Block(_typeTable.discharge(type.typeFormals)), 2680 new JS.Block(_typeTable.discharge(type.typeFormals)),
2698 code 2681 code
2699 ]); 2682 ]);
2700 } 2683 }
2701 return new JS.Fun(formals, code, 2684
2702 typeParams: typeFormals, returnType: returnType); 2685 if (element.isOperator && element.name == '[]=' && formals.isNotEmpty) {
2686 // []= methods need to return the value. We could also address this at
2687 // call sites, but it's cleaner to instead transform the operator method.
2688 code = _alwaysReturnLastParameter(code, formals.last);
2689 }
2690
2691 return _makeGenericFunction(new JS.Fun(formals, code,
2692 typeParams: typeFormals, returnType: returnType));
2703 } 2693 }
2704 2694
2705 JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, 2695 JS.Expression _emitGeneratorFunctionBody(ExecutableElement element,
2706 FormalParameterList parameters, FunctionBody body) { 2696 FormalParameterList parameters, FunctionBody body) {
2707 var kind = element.isSynchronous ? 'sync' : 'async'; 2697 var kind = element.isSynchronous ? 'sync' : 'async';
2708 if (element.isGenerator) kind += 'Star'; 2698 if (element.isGenerator) kind += 'Star';
2709 2699
2710 // Transforms `sync*` `async` and `async*` function bodies 2700 // Transforms `sync*` `async` and `async*` function bodies
2711 // using ES6 generators. 2701 // using ES6 generators.
2712 // 2702 //
(...skipping 3278 matching lines...) Expand 10 before | Expand all | Expand 10 after
5991 if (targetIdentifier.staticElement is! PrefixElement) return false; 5981 if (targetIdentifier.staticElement is! PrefixElement) return false;
5992 var prefix = targetIdentifier.staticElement as PrefixElement; 5982 var prefix = targetIdentifier.staticElement as PrefixElement;
5993 5983
5994 // The library the prefix is referring to must come from a deferred import. 5984 // The library the prefix is referring to must come from a deferred import.
5995 var containingLibrary = resolutionMap 5985 var containingLibrary = resolutionMap
5996 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) 5986 .elementDeclaredByCompilationUnit(target.root as CompilationUnit)
5997 .library; 5987 .library;
5998 var imports = containingLibrary.getImportsWithPrefix(prefix); 5988 var imports = containingLibrary.getImportsWithPrefix(prefix);
5999 return imports.length == 1 && imports[0].isDeferred; 5989 return imports.length == 1 && imports[0].isDeferred;
6000 } 5990 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698