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'; |
11 import 'package:analyzer/src/generated/element.dart'; | 11 import 'package:analyzer/src/generated/element.dart'; |
12 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; | 12 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
14 import 'package:analyzer/src/dart/ast/token.dart' | 14 import 'package:analyzer/src/dart/ast/token.dart' |
15 show StringToken, Token, TokenType; | 15 show StringToken, Token, TokenType; |
16 import 'package:analyzer/src/generated/type_system.dart' | 16 import 'package:analyzer/src/generated/type_system.dart' |
17 show StrongTypeSystemImpl; | 17 show StrongTypeSystemImpl; |
18 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; | |
19 | 18 |
20 import 'ast_builder.dart' show AstBuilder; | 19 import 'ast_builder.dart' show AstBuilder; |
21 import 'reify_coercions.dart' show CoercionReifier, Tuple2; | 20 import 'reify_coercions.dart' show CoercionReifier, Tuple2; |
22 | 21 |
23 import '../js/js_ast.dart' as JS; | 22 import '../js/js_ast.dart' as JS; |
24 import '../js/js_ast.dart' show js; | 23 import '../js/js_ast.dart' show js; |
25 | 24 |
26 import '../closure/closure_annotator.dart' show ClosureAnnotator; | 25 import '../closure/closure_annotator.dart' show ClosureAnnotator; |
27 import '../compiler.dart' | 26 import '../compiler.dart' |
28 show AbstractCompiler, corelibOrder, getCorelibModuleName; | 27 show AbstractCompiler, corelibOrder, getCorelibModuleName; |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 | 98 |
100 final TypeProvider _types; | 99 final TypeProvider _types; |
101 | 100 |
102 ConstFieldVisitor _constField; | 101 ConstFieldVisitor _constField; |
103 | 102 |
104 ModuleItemLoadOrder _loader; | 103 ModuleItemLoadOrder _loader; |
105 | 104 |
106 /// _interceptors.JSArray<E>, used for List literals. | 105 /// _interceptors.JSArray<E>, used for List literals. |
107 ClassElement _jsArray; | 106 ClassElement _jsArray; |
108 | 107 |
| 108 /// The current function body being compiled. |
| 109 FunctionBody _currentFunction; |
| 110 |
109 /// The default value of the module object. See [visitLibraryDirective]. | 111 /// The default value of the module object. See [visitLibraryDirective]. |
110 String _jsModuleValue; | 112 String _jsModuleValue; |
111 | 113 |
112 bool _isDartRuntime; | 114 bool _isDartRuntime; |
113 | 115 |
114 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, | 116 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, |
115 this._extensionTypes, this._fieldsNeedingStorage) | 117 this._extensionTypes, this._fieldsNeedingStorage) |
116 : compiler = compiler, | 118 : compiler = compiler, |
117 options = compiler.options.codegenOptions, | 119 options = compiler.options.codegenOptions, |
118 _types = compiler.context.typeProvider { | 120 _types = compiler.context.typeProvider { |
119 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 121 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
120 | 122 |
121 var context = compiler.context; | 123 var context = compiler.context; |
122 var src = context.sourceFactory.forUri('dart:_interceptors'); | 124 var src = context.sourceFactory.forUri('dart:_interceptors'); |
123 var interceptors = context.computeLibraryElement(src); | 125 var interceptors = context.computeLibraryElement(src); |
124 _jsArray = interceptors.getType('JSArray'); | 126 _jsArray = interceptors.getType('JSArray'); |
125 _isDartRuntime = currentLibrary.source.uri.toString() == 'dart:_runtime'; | 127 _isDartRuntime = currentLibrary.source.uri.toString() == 'dart:_runtime'; |
126 } | 128 } |
127 | 129 |
128 TypeProvider get types => _types; | 130 TypeProvider get types => _types; |
129 | 131 |
130 JS.Program emitLibrary(LibraryUnit library) { | 132 JS.Program emitLibrary(LibraryUnit library) { |
131 // Modify the AST to make coercions explicit. | 133 // Modify the AST to make coercions explicit. |
132 new CoercionReifier(library, rules).reify(); | 134 new CoercionReifier(library, rules).reify(); |
133 | 135 |
134 // Build the public namespace for this library. This allows us to do | |
135 // constant time lookups (contrast with `Element.getChild(name)`). | |
136 if (currentLibrary.publicNamespace == null) { | |
137 (currentLibrary as LibraryElementImpl).publicNamespace = | |
138 new PublicNamespaceBuilder().build(currentLibrary); | |
139 } | |
140 | |
141 library.library.directives.forEach(_visit); | 136 library.library.directives.forEach(_visit); |
142 | 137 |
143 var units = library.partsThenLibrary; | 138 var units = library.partsThenLibrary; |
144 | 139 |
145 // Rather than directly visit declarations, we instead use [_loader] to | 140 // Rather than directly visit declarations, we instead use [_loader] to |
146 // visit them. It has the ability to sort elements on demand, so | 141 // visit them. It has the ability to sort elements on demand, so |
147 // dependencies between top level items are handled with a minimal | 142 // dependencies between top level items are handled with a minimal |
148 // reordering of the user's input code. The loader will call back into | 143 // reordering of the user's input code. The loader will call back into |
149 // this visitor via [_emitModuleItem] when it's ready to visit the item | 144 // this visitor via [_emitModuleItem] when it's ready to visit the item |
150 // for real. | 145 // for real. |
(...skipping 952 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1103 // Mark the parameter as no-rename. | 1098 // Mark the parameter as no-rename. |
1104 body = js.statement('''{ | 1099 body = js.statement('''{ |
1105 // Get the class name for this instance. | 1100 // Get the class name for this instance. |
1106 let name = this.constructor.name; | 1101 let name = this.constructor.name; |
1107 // Call the default constructor. | 1102 // Call the default constructor. |
1108 let result = void 0; | 1103 let result = void 0; |
1109 if (name in this) result = this[name](...arguments); | 1104 if (name in this) result = this[name](...arguments); |
1110 return result === void 0 ? this : result; | 1105 return result === void 0 ? this : result; |
1111 }''') as JS.Block; | 1106 }''') as JS.Block; |
1112 } else { | 1107 } else { |
| 1108 var savedFunction = _currentFunction; |
| 1109 _currentFunction = node.body; |
1113 body = _emitConstructorBody(node, fields); | 1110 body = _emitConstructorBody(node, fields); |
| 1111 _currentFunction = savedFunction; |
1114 } | 1112 } |
1115 | 1113 |
1116 // We generate constructors as initializer methods in the class; | 1114 // We generate constructors as initializer methods in the class; |
1117 // this allows use of `super` for instance methods/properties. | 1115 // this allows use of `super` for instance methods/properties. |
1118 // It also avoids V8 restrictions on `super` in default constructors. | 1116 // It also avoids V8 restrictions on `super` in default constructors. |
1119 return annotate( | 1117 return annotate( |
1120 new JS.Method(name, new JS.Fun(params, body, returnType: returnType)), | 1118 new JS.Method(name, new JS.Fun(params, body, returnType: returnType)), |
1121 node, | 1119 node, |
1122 node.element); | 1120 node.element); |
1123 } | 1121 } |
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1583 /// Contrast with [_emitFunction]. | 1581 /// Contrast with [_emitFunction]. |
1584 @override | 1582 @override |
1585 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1583 JS.Expression visitFunctionExpression(FunctionExpression node) { |
1586 assert(node.parent is! FunctionDeclaration && | 1584 assert(node.parent is! FunctionDeclaration && |
1587 node.parent is! MethodDeclaration); | 1585 node.parent is! MethodDeclaration); |
1588 return _emitFunctionTagged(_emitArrowFunction(node), getStaticType(node), | 1586 return _emitFunctionTagged(_emitArrowFunction(node), getStaticType(node), |
1589 topLevel: _executesAtTopLevel(node)); | 1587 topLevel: _executesAtTopLevel(node)); |
1590 } | 1588 } |
1591 | 1589 |
1592 JS.ArrowFun _emitArrowFunction(FunctionExpression node) { | 1590 JS.ArrowFun _emitArrowFunction(FunctionExpression node) { |
1593 List<JS.Parameter> params; | 1591 JS.Fun f = _emitFunctionBody(node.element, node.parameters, node.body); |
| 1592 var body = f.body; |
1594 | 1593 |
1595 var body = node.body; | 1594 // Simplify `=> { return e; }` to `=> e` |
1596 var type = node.element.type; | 1595 if (body is JS.Block) { |
1597 | 1596 JS.Block block = body; |
1598 JS.Node jsBody; | 1597 if (block.statements.length == 1) { |
1599 if (body.isGenerator || body.isAsynchronous) { | 1598 JS.Statement s = block.statements[0]; |
1600 params = visitFormalParameterList(node.parameters, destructure: false); | 1599 if (s is JS.Return) body = s.value; |
1601 jsBody = _emitGeneratorFunctionBody(node.element, node.parameters, body); | |
1602 } else { | |
1603 params = visitFormalParameterList(node.parameters); | |
1604 | |
1605 // Chrome Canary does not accept default values with destructuring in | |
1606 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them | |
1607 // with regular functions (e.g. `function({a} = {}) { return 1 }`). | |
1608 // Note that Firefox accepts both syntaxes just fine. | |
1609 // TODO(ochafik): Simplify this code when Chrome Canary catches up. | |
1610 bool destructureNamed = params.any((p) => | |
1611 p is JS.DestructuredVariable && | |
1612 p.structure is JS.ObjectBindingPattern); | |
1613 | |
1614 if (body is ExpressionFunctionBody && !destructureNamed) { | |
1615 // An arrow function can use the expression directly. | |
1616 jsBody = _visit(body.expression); | |
1617 } else { | |
1618 jsBody = _visit(body); | |
1619 } | 1600 } |
1620 } | 1601 } |
1621 | 1602 |
1622 var fn = new JS.ArrowFun(params, jsBody, | 1603 // Convert `function(...) { ... }` to `(...) => ...` |
1623 typeParams: _emitTypeFormals(type.typeFormals), | 1604 // This is for readability, but it also ensures correct `this` binding. |
1624 returnType: emitTypeRef(type.returnType)); | 1605 return annotate( |
1625 return annotate(fn, node); | 1606 new JS.ArrowFun(f.params, body, |
| 1607 typeParams: f.typeParams, returnType: f.returnType), |
| 1608 node); |
1626 } | 1609 } |
1627 | 1610 |
1628 /// Emits a non-arrow FunctionExpression node. | 1611 /// Emits a non-arrow FunctionExpression node. |
1629 /// | 1612 /// |
1630 /// This should be used for all places in Dart's AST where FunctionExpression | 1613 /// This should be used for all places in Dart's AST where FunctionExpression |
1631 /// appears but the function is not actually in an Expression context, such | 1614 /// appears but the function is not actually in an Expression context, such |
1632 /// as methods, properties, and top-level functions. | 1615 /// as methods, properties, and top-level functions. |
1633 /// | 1616 /// |
1634 /// Contrast with [visitFunctionExpression]. | 1617 /// Contrast with [visitFunctionExpression]. |
1635 JS.Fun _emitFunction(FunctionExpression node) { | 1618 JS.Fun _emitFunction(FunctionExpression node) { |
(...skipping 431 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2067 // or when one of the expressions is stateless, which seems common. | 2050 // or when one of the expressions is stateless, which seems common. |
2068 var vars = <String, JS.Expression>{}; | 2051 var vars = <String, JS.Expression>{}; |
2069 var left = _bindValue(vars, 'l', node.target); | 2052 var left = _bindValue(vars, 'l', node.target); |
2070 var body = js.call('# == null ? null : #', | 2053 var body = js.call('# == null ? null : #', |
2071 [_visit(left), _emitSet(_stripNullAwareOp(node, left), right)]); | 2054 [_visit(left), _emitSet(_stripNullAwareOp(node, left), right)]); |
2072 return new JS.MetaLet(vars, [body]); | 2055 return new JS.MetaLet(vars, [body]); |
2073 } | 2056 } |
2074 | 2057 |
2075 @override | 2058 @override |
2076 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { | 2059 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 2060 var savedFunction = _currentFunction; |
| 2061 _currentFunction = node; |
2077 var initArgs = _emitArgumentInitializers(node.parent); | 2062 var initArgs = _emitArgumentInitializers(node.parent); |
2078 var ret = new JS.Return(_visit(node.expression)); | 2063 var ret = new JS.Return(_visit(node.expression)); |
| 2064 _currentFunction = savedFunction; |
2079 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); | 2065 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); |
2080 } | 2066 } |
2081 | 2067 |
2082 @override | 2068 @override |
2083 JS.Block visitEmptyFunctionBody(EmptyFunctionBody node) => new JS.Block([]); | 2069 JS.Block visitEmptyFunctionBody(EmptyFunctionBody node) => new JS.Block([]); |
2084 | 2070 |
2085 @override | 2071 @override |
2086 JS.Block visitBlockFunctionBody(BlockFunctionBody node) { | 2072 JS.Block visitBlockFunctionBody(BlockFunctionBody node) { |
| 2073 var savedFunction = _currentFunction; |
| 2074 _currentFunction = node; |
2087 var initArgs = _emitArgumentInitializers(node.parent); | 2075 var initArgs = _emitArgumentInitializers(node.parent); |
2088 var stmts = _visitList(node.block.statements) as List<JS.Statement>; | 2076 var stmts = _visitList(node.block.statements) as List<JS.Statement>; |
2089 if (initArgs != null) stmts.insert(0, initArgs); | 2077 if (initArgs != null) stmts.insert(0, initArgs); |
| 2078 _currentFunction = savedFunction; |
2090 return new JS.Block(stmts); | 2079 return new JS.Block(stmts); |
2091 } | 2080 } |
2092 | 2081 |
2093 @override | 2082 @override |
2094 JS.Block visitBlock(Block node) => | 2083 JS.Block visitBlock(Block node) => |
2095 new JS.Block(_visitList(node.statements) as List<JS.Statement>, | 2084 new JS.Block(_visitList(node.statements) as List<JS.Statement>, |
2096 isScope: true); | 2085 isScope: true); |
2097 | 2086 |
2098 @override | 2087 @override |
2099 visitMethodInvocation(MethodInvocation node) { | 2088 visitMethodInvocation(MethodInvocation node) { |
(...skipping 714 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2814 /// | 2803 /// |
2815 /// If the expression does not end up using `x` more than once, or if those | 2804 /// If the expression does not end up using `x` more than once, or if those |
2816 /// expressions can be treated as stateless (e.g. they are non-mutated | 2805 /// expressions can be treated as stateless (e.g. they are non-mutated |
2817 /// variables), then the resulting code will be simplified automatically. | 2806 /// variables), then the resulting code will be simplified automatically. |
2818 /// | 2807 /// |
2819 /// [scope] will be mutated to contain the new temporary's initialization. | 2808 /// [scope] will be mutated to contain the new temporary's initialization. |
2820 Expression _bindValue( | 2809 Expression _bindValue( |
2821 Map<String, JS.Expression> scope, String name, Expression expr, | 2810 Map<String, JS.Expression> scope, String name, Expression expr, |
2822 {Expression context}) { | 2811 {Expression context}) { |
2823 // No need to do anything for stateless expressions. | 2812 // No need to do anything for stateless expressions. |
2824 if (isStateless(expr, context)) return expr; | 2813 if (isStateless(_currentFunction, expr, context)) return expr; |
2825 | 2814 |
2826 var t = _createTemporary('#$name', getStaticType(expr)); | 2815 var t = _createTemporary('#$name', getStaticType(expr)); |
2827 scope[name] = _visit(expr); | 2816 scope[name] = _visit(expr); |
2828 return t; | 2817 return t; |
2829 } | 2818 } |
2830 | 2819 |
2831 /// Desugars postfix increment. | 2820 /// Desugars postfix increment. |
2832 /// | 2821 /// |
2833 /// In the general case [expr] can be one of [IndexExpression], | 2822 /// In the general case [expr] can be one of [IndexExpression], |
2834 /// [PrefixExpression] or [PropertyAccess] and we need to | 2823 /// [PrefixExpression] or [PropertyAccess] and we need to |
(...skipping 565 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3400 return _emitConst(emitSymbol); | 3389 return _emitConst(emitSymbol); |
3401 } | 3390 } |
3402 | 3391 |
3403 @override | 3392 @override |
3404 visitListLiteral(ListLiteral node) { | 3393 visitListLiteral(ListLiteral node) { |
3405 JS.Expression emitList() { | 3394 JS.Expression emitList() { |
3406 JS.Expression list = new JS.ArrayInitializer( | 3395 JS.Expression list = new JS.ArrayInitializer( |
3407 _visitList(node.elements) as List<JS.Expression>); | 3396 _visitList(node.elements) as List<JS.Expression>); |
3408 ParameterizedType type = node.staticType; | 3397 ParameterizedType type = node.staticType; |
3409 var elementType = type.typeArguments.single; | 3398 var elementType = type.typeArguments.single; |
3410 if (elementType != types.dynamicType) { | 3399 // TODO(jmesserly): analyzer will usually infer `List<Object>` because |
| 3400 // that is the least upper bound of the element types. So we rarely |
| 3401 // generate a plain `List<dynamic>` anymore. |
| 3402 if (!elementType.isDynamic) { |
3411 // dart.list helper internally depends on _interceptors.JSArray. | 3403 // dart.list helper internally depends on _interceptors.JSArray. |
3412 _loader.declareBeforeUse(_jsArray); | 3404 _loader.declareBeforeUse(_jsArray); |
3413 list = js.call('dart.list(#, #)', [list, _emitTypeName(elementType)]); | 3405 list = js.call('dart.list(#, #)', [list, _emitTypeName(elementType)]); |
3414 } | 3406 } |
3415 return list; | 3407 return list; |
3416 } | 3408 } |
3417 if (node.constKeyword != null) return _emitConst(emitList); | 3409 if (node.constKeyword != null) return _emitConst(emitList); |
3418 return emitList(); | 3410 return emitList(); |
3419 } | 3411 } |
3420 | 3412 |
(...skipping 430 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3851 | 3843 |
3852 /// A special kind of element created by the compiler, signifying a temporary | 3844 /// A special kind of element created by the compiler, signifying a temporary |
3853 /// variable. These objects use instance equality, and should be shared | 3845 /// variable. These objects use instance equality, and should be shared |
3854 /// everywhere in the tree where they are treated as the same variable. | 3846 /// everywhere in the tree where they are treated as the same variable. |
3855 class TemporaryVariableElement extends LocalVariableElementImpl { | 3847 class TemporaryVariableElement extends LocalVariableElementImpl { |
3856 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3848 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3857 | 3849 |
3858 int get hashCode => identityHashCode(this); | 3850 int get hashCode => identityHashCode(this); |
3859 bool operator ==(Object other) => identical(this, other); | 3851 bool operator ==(Object other) => identical(this, other); |
3860 } | 3852 } |
OLD | NEW |