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

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

Issue 1099333002: canonicalize const (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 8 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 | « lib/runtime/dart_runtime.js ('k') | test/codegen/expect/DeltaBlue.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 library dev_compiler.src.codegen.js_codegen; 5 library dev_compiler.src.codegen.js_codegen;
6 6
7 import 'dart:collection' show HashSet, HashMap; 7 import 'dart:collection' show HashSet, HashMap;
8 8
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 /// The variable for the current catch clause 66 /// The variable for the current catch clause
67 SimpleIdentifier _catchParameter; 67 SimpleIdentifier _catchParameter;
68 68
69 ConstantEvaluator _constEvaluator; 69 ConstantEvaluator _constEvaluator;
70 70
71 final _exports = new Set<String>(); 71 final _exports = new Set<String>();
72 final _lazyFields = <VariableDeclaration>[]; 72 final _lazyFields = <VariableDeclaration>[];
73 final _properties = <FunctionDeclaration>[]; 73 final _properties = <FunctionDeclaration>[];
74 final _privateNames = new HashMap<String, JSTemporary>(); 74 final _privateNames = new HashMap<String, JSTemporary>();
75 final _extensionMethodNames = new HashSet<String>(); 75 final _extensionMethodNames = new HashSet<String>();
76 final _pendingSymbols = <JS.Identifier>[]; 76 final _pendingStatements = <JS.Statement>[];
77 final _temps = new HashMap<Element, JSTemporary>(); 77 final _temps = new HashMap<Element, JSTemporary>();
78 78
79 /// The name for the library's exports inside itself. 79 /// The name for the library's exports inside itself.
80 /// This much be a constant because we interpolate it into template strings, 80 /// This much be a constant because we interpolate it into template strings,
81 /// and otherwise it would break caching for them. 81 /// and otherwise it would break caching for them.
82 /// `exports` was chosen as the most similar to ES module patterns. 82 /// `exports` was chosen as the most similar to ES module patterns.
83 final JSTemporary _exportsVar = new JSTemporary('exports'); 83 final JSTemporary _exportsVar = new JSTemporary('exports');
84 final JSTemporary _namedArgTemp = new JSTemporary('opts'); 84 final JSTemporary _namedArgTemp = new JSTemporary('opts');
85 85
86 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or 86 /// Classes we have not emitted yet. Values can be [ClassDeclaration] or
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [ 149 js.statement("(function(#) { 'use strict'; #; })(# || (# = #));", [
150 _exportsVar, 150 _exportsVar,
151 body, 151 body,
152 name, 152 name,
153 name, 153 name,
154 defaultValue 154 defaultValue
155 ]) 155 ])
156 ]); 156 ]);
157 } 157 }
158 158
159 JS.Statement _initSymbol(JS.Identifier id) => 159 JS.Identifier _initSymbol(JS.Identifier id) {
160 js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); 160 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]);
161 _pendingStatements.add(s);
162 return id;
163 }
161 164
162 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, 165 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core,
163 // until we have better name tracking. 166 // until we have better name tracking.
164 String get _SYMBOL { 167 String get _SYMBOL {
165 var name = currentLibrary.name; 168 var name = currentLibrary.name;
166 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; 169 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol';
167 return 'Symbol'; 170 return 'Symbol';
168 } 171 }
169 172
170 @override 173 @override
171 JS.Statement visitCompilationUnit(CompilationUnit node) { 174 JS.Statement visitCompilationUnit(CompilationUnit node) {
172 var source = node.element.source; 175 var source = node.element.source;
173 176
174 _constEvaluator = new ConstantEvaluator(source, types); 177 _constEvaluator = new ConstantEvaluator(source, types);
175 178
176 // TODO(jmesserly): scriptTag, directives. 179 // TODO(jmesserly): scriptTag, directives.
177 var body = <JS.Statement>[]; 180 var body = <JS.Statement>[];
178 for (var child in node.declarations) { 181 for (var child in node.declarations) {
179 // Attempt to group adjacent fields/properties. 182 // Attempt to group adjacent fields/properties.
180 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body); 183 if (child is! TopLevelVariableDeclaration) _flushLazyFields(body);
181 if (child is! FunctionDeclaration) _flushLibraryProperties(body); 184 if (child is! FunctionDeclaration) _flushLibraryProperties(body);
182 185
183 var code = _visit(child); 186 var code = _visit(child);
184 if (code != null) { 187 if (code != null) {
185 if (_pendingSymbols.isNotEmpty) { 188 if (_pendingStatements.isNotEmpty) {
186 body.addAll(_pendingSymbols.map(_initSymbol)); 189 body.addAll(_pendingStatements);
187 _pendingSymbols.clear(); 190 _pendingStatements.clear();
188 } 191 }
189 body.add(code); 192 body.add(code);
190 } 193 }
191 } 194 }
192 195
193 // Flush any unwritten fields/properties. 196 // Flush any unwritten fields/properties.
194 _flushLazyFields(body); 197 _flushLazyFields(body);
195 _flushLibraryProperties(body); 198 _flushLibraryProperties(body);
196 199
197 assert(_pendingSymbols.isEmpty); 200 assert(_pendingStatements.isEmpty);
198 return _statement(body); 201 return _statement(body);
199 } 202 }
200 203
201 bool isPublic(String name) => !name.startsWith('_'); 204 bool isPublic(String name) => !name.startsWith('_');
202 205
203 /// Conversions that we don't handle end up here. 206 /// Conversions that we don't handle end up here.
204 @override 207 @override
205 visitConversion(Conversion node) { 208 visitConversion(Conversion node) {
206 throw 'Unlowered conversion ${node.runtimeType}: $node'; 209 throw 'Unlowered conversion ${node.runtimeType}: $node';
207 } 210 }
(...skipping 1263 matching lines...) Expand 10 before | Expand all | Expand 10 after
1471 visitConstructorName(ConstructorName node) { 1474 visitConstructorName(ConstructorName node) {
1472 var typeName = _visit(node.type); 1475 var typeName = _visit(node.type);
1473 if (node.name != null) { 1476 if (node.name != null) {
1474 return js.call('#.#', [typeName, _emitMemberName(node.name.name)]); 1477 return js.call('#.#', [typeName, _emitMemberName(node.name.name)]);
1475 } 1478 }
1476 return typeName; 1479 return typeName;
1477 } 1480 }
1478 1481
1479 @override 1482 @override
1480 visitInstanceCreationExpression(InstanceCreationExpression node) { 1483 visitInstanceCreationExpression(InstanceCreationExpression node) {
1481 return js.call( 1484 var newExpr = js.call(
1482 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]); 1485 'new #(#)', [_visit(node.constructorName), _visit(node.argumentList)]);
1486 if (node.isConst) return _const(node, newExpr);
1487 return newExpr;
1483 } 1488 }
1484 1489
1485 /// True if this type is built-in to JS, and we use the values unwrapped. 1490 /// True if this type is built-in to JS, and we use the values unwrapped.
1486 /// For these types we generate a calling convention via static 1491 /// For these types we generate a calling convention via static
1487 /// "extension methods". This allows types to be extended without adding 1492 /// "extension methods". This allows types to be extended without adding
1488 /// extensions directly on the prototype. 1493 /// extensions directly on the prototype.
1489 bool _isJSBuiltinType(DartType t) => 1494 bool _isJSBuiltinType(DartType t) =>
1490 typeIsPrimitiveInJS(t) || rules.isStringType(t); 1495 typeIsPrimitiveInJS(t) || rules.isStringType(t);
1491 1496
1492 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) || 1497 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) ||
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
1633 // LocalVariableElementImpl, so we could repurpose to mean "temp". 1638 // LocalVariableElementImpl, so we could repurpose to mean "temp".
1634 // * add a new property to LocalVariableElementImpl. 1639 // * add a new property to LocalVariableElementImpl.
1635 // * create a new subtype of LocalVariableElementImpl to mark a temp. 1640 // * create a new subtype of LocalVariableElementImpl to mark a temp.
1636 var id = 1641 var id =
1637 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); 1642 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1));
1638 id.staticElement = new LocalVariableElementImpl.forNode(id); 1643 id.staticElement = new LocalVariableElementImpl.forNode(id);
1639 id.staticType = type; 1644 id.staticType = type;
1640 return id; 1645 return id;
1641 } 1646 }
1642 1647
1648 JS.Expression _const(Expression node, JS.Expression expr, [String nameHint]) {
1649 var value = js.call('dart.const(#)', expr);
1650
1651 // If we're inside a method or function, capture the value into a
1652 // global temporary, so we don't do the expensive canonicalization step.
1653 var ancestor = node.getAncestor((n) => n is FunctionBody ||
1654 (n is FieldDeclaration && n.staticKeyword == null));
1655 if (ancestor == null) return value;
1656
1657 if (nameHint == null) {
1658 nameHint = 'const_' + getStaticType(node).name;
1659 }
1660
1661 // TODO(jmesserly): enable this once we fix
1662 // https://github.com/dart-lang/dev_compiler/issues/131
1663 /*var temp = new JSTemporary(nameHint);
1664 _pendingStatements.add(js.statement('let # = #;', [temp, value]));
1665 return temp;*/
1666 assert(nameHint != null); // so it's not marked unused
1667 return value;
1668 }
1669
1643 bool _isTemporary(Element node) => node.nameOffset == -1; 1670 bool _isTemporary(Element node) => node.nameOffset == -1;
1644 1671
1645 /// Returns a new expression, which can be be used safely *once* on the 1672 /// Returns a new expression, which can be be used safely *once* on the
1646 /// left hand side, and *once* on the right side of an assignment. 1673 /// left hand side, and *once* on the right side of an assignment.
1647 /// For example: `expr1[expr2] += y` can be compiled as 1674 /// For example: `expr1[expr2] += y` can be compiled as
1648 /// `expr1[expr2] = expr1[expr2] + y`. 1675 /// `expr1[expr2] = expr1[expr2] + y`.
1649 /// 1676 ///
1650 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated 1677 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated
1651 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`. 1678 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`.
1652 /// 1679 ///
(...skipping 439 matching lines...) Expand 10 before | Expand all | Expand 10 after
2092 @override 2119 @override
2093 visitDoubleLiteral(DoubleLiteral node) => js.number(node.value); 2120 visitDoubleLiteral(DoubleLiteral node) => js.number(node.value);
2094 2121
2095 @override 2122 @override
2096 visitNullLiteral(NullLiteral node) => new JS.LiteralNull(); 2123 visitNullLiteral(NullLiteral node) => new JS.LiteralNull();
2097 2124
2098 @override 2125 @override
2099 visitSymbolLiteral(SymbolLiteral node) { 2126 visitSymbolLiteral(SymbolLiteral node) {
2100 // TODO(vsm): When we canonicalize, we need to treat private symbols 2127 // TODO(vsm): When we canonicalize, we need to treat private symbols
2101 // correctly. 2128 // correctly.
2102 // TODO(vsm): Make this core.Symbol instead. 2129 var name = js.string(node.components.join('.'), "'");
2103 var name = js.escapedString(node.components.join('.')); 2130 var nameHint = 'symbol_' + node.components.join('_');
2104 var symbol = js.call('new _internal.Symbol(#)', name); 2131 return _const(node, js.call('new core.Symbol(#)', name), nameHint);
2105 return js.commentExpression('Unimplemented const', symbol);
2106 } 2132 }
2107 2133
2108 @override 2134 @override
2109 visitListLiteral(ListLiteral node) { 2135 visitListLiteral(ListLiteral node) {
2110 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements)); 2136 JS.Expression list = new JS.ArrayInitializer(_visitList(node.elements));
2111 2137
2112 ParameterizedType type = node.staticType; 2138 ParameterizedType type = node.staticType;
2113 if (type.typeArguments.any((a) => a != types.dynamicType)) { 2139 if (type.typeArguments.any((a) => a != types.dynamicType)) {
2114 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]); 2140 list = js.call('dart.setType(#, #)', [list, _emitTypeName(type)]);
2115 } 2141 }
2116 if (node.constKeyword != null) { 2142 if (node.constKeyword != null) return _const(node, list);
2117 list = js.commentExpression('Unimplemented const', list);
2118 }
2119 return list; 2143 return list;
2120 } 2144 }
2121 2145
2122 @override 2146 @override
2123 visitMapLiteral(MapLiteral node) { 2147 visitMapLiteral(MapLiteral node) {
2124 // TODO(jmesserly): we can likely make these faster. 2148 // TODO(jmesserly): we can likely make these faster.
2125 var entries = node.entries; 2149 var entries = node.entries;
2126 var mapArguments = null; 2150 var mapArguments = null;
2127 if (entries.isEmpty) { 2151 if (entries.isEmpty) {
2128 mapArguments = []; 2152 mapArguments = [];
2129 } else if (entries.every((e) => e.key is StringLiteral)) { 2153 } else if (entries.every((e) => e.key is StringLiteral)) {
2130 // Use JS object literal notation if possible, otherwise use an array. 2154 // Use JS object literal notation if possible, otherwise use an array.
2131 // We could do this any time all keys are non-nullable String type. 2155 // We could do this any time all keys are non-nullable String type.
2132 // For now, support StringLiteral as the common non-nullable String case. 2156 // For now, support StringLiteral as the common non-nullable String case.
2133 var props = []; 2157 var props = [];
2134 for (var e in entries) { 2158 for (var e in entries) {
2135 props.add(new JS.Property(_visit(e.key), _visit(e.value))); 2159 props.add(new JS.Property(_visit(e.key), _visit(e.value)));
2136 } 2160 }
2137 mapArguments = new JS.ObjectInitializer(props); 2161 mapArguments = new JS.ObjectInitializer(props);
2138 } else { 2162 } else {
2139 var values = []; 2163 var values = [];
2140 for (var e in entries) { 2164 for (var e in entries) {
2141 values.add(_visit(e.key)); 2165 values.add(_visit(e.key));
2142 values.add(_visit(e.value)); 2166 values.add(_visit(e.value));
2143 } 2167 }
2144 mapArguments = new JS.ArrayInitializer(values); 2168 mapArguments = new JS.ArrayInitializer(values);
2145 } 2169 }
2146 // TODO(jmesserly): add generic types args. 2170 // TODO(jmesserly): add generic types args.
2147 var map = js.call('dart.map(#)', [mapArguments]); 2171 var map = js.call('dart.map(#)', [mapArguments]);
2148 if (node.constKeyword != null) { 2172 if (node.constKeyword != null) return _const(node, map);
2149 map = js.commentExpression('Unimplemented const', map);
2150 }
2151 return map; 2173 return map;
2152 } 2174 }
2153 2175
2154 @override 2176 @override
2155 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) => 2177 JS.LiteralString visitSimpleStringLiteral(SimpleStringLiteral node) =>
2156 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"'); 2178 js.escapedString(node.value, node.isSingleQuoted ? "'" : '"');
2157 2179
2158 @override 2180 @override
2159 JS.Expression visitAdjacentStrings(AdjacentStrings node) => 2181 JS.Expression visitAdjacentStrings(AdjacentStrings node) =>
2160 _visitListToBinary(node.strings, '+'); 2182 _visitListToBinary(node.strings, '+');
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
2279 /// 2301 ///
2280 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed 2302 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed
2281 /// for this transformation to happen, otherwise binary minus is assumed. 2303 /// for this transformation to happen, otherwise binary minus is assumed.
2282 /// 2304 ///
2283 /// Equality is a bit special, it is generated via the Dart `equals` runtime 2305 /// Equality is a bit special, it is generated via the Dart `equals` runtime
2284 /// helper, that checks for null. The user defined method is called '=='. 2306 /// helper, that checks for null. The user defined method is called '=='.
2285 /// 2307 ///
2286 JS.Expression _emitMemberName(String name, 2308 JS.Expression _emitMemberName(String name,
2287 {DartType type, bool unary: false, bool isStatic: false}) { 2309 {DartType type, bool unary: false, bool isStatic: false}) {
2288 if (name.startsWith('_')) { 2310 if (name.startsWith('_')) {
2289 return _privateNames.putIfAbsent(name, () { 2311 return _privateNames.putIfAbsent(
2290 var t = new JSTemporary(name); 2312 name, () => _initSymbol(new JSTemporary(name)));
2291 _pendingSymbols.add(t);
2292 return t;
2293 });
2294 } 2313 }
2295 2314
2296 // Check for extension method: 2315 // Check for extension method:
2297 var extLibrary = _findExtensionLibrary(name, type); 2316 var extLibrary = _findExtensionLibrary(name, type);
2298 2317
2299 if (name == '[]') { 2318 if (name == '[]') {
2300 name = 'get'; 2319 name = 'get';
2301 } else if (name == '[]=') { 2320 } else if (name == '[]=') {
2302 name = 'set'; 2321 name = 'set';
2303 } else if (name == '-' && unary) { 2322 } else if (name == '-' && unary) {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
2338 return extLibrary; 2357 return extLibrary;
2339 } 2358 }
2340 2359
2341 JS.Expression _extensionMethodName(String name, LibraryElement library) { 2360 JS.Expression _extensionMethodName(String name, LibraryElement library) {
2342 var extName = '\$$name'; 2361 var extName = '\$$name';
2343 if (library == currentLibrary) { 2362 if (library == currentLibrary) {
2344 // TODO(jacobr): need to do a better job ensuring that extension method 2363 // TODO(jacobr): need to do a better job ensuring that extension method
2345 // name symbols do not conflict with other symbols before we can let 2364 // name symbols do not conflict with other symbols before we can let
2346 // user defined libraries define extension methods. 2365 // user defined libraries define extension methods.
2347 if (_extensionMethodNames.add(extName)) { 2366 if (_extensionMethodNames.add(extName)) {
2348 _pendingSymbols.add(new JS.Identifier(extName)); 2367 _initSymbol(new JS.Identifier(extName));
2349 _addExport(extName); 2368 _addExport(extName);
2350 } 2369 }
2351 return new JS.Identifier(extName); 2370 return new JS.Identifier(extName);
2352 } 2371 }
2353 return js.call('#.#', [_libraryName(library), _propertyName(extName)]); 2372 return js.call('#.#', [_libraryName(library), _propertyName(extName)]);
2354 } 2373 }
2355 2374
2356 bool _externalOrNative(node) => 2375 bool _externalOrNative(node) =>
2357 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; 2376 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody;
2358 2377
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
2441 // TODO(jmesserly): validate the library. See issue #135. 2460 // TODO(jmesserly): validate the library. See issue #135.
2442 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; 2461 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
2443 2462
2444 bool _isJsPeerInterface(DartObjectImpl value) => 2463 bool _isJsPeerInterface(DartObjectImpl value) =>
2445 value.type.name == 'JsPeerInterface'; 2464 value.type.name == 'JsPeerInterface';
2446 2465
2447 // TODO(jacobr): we would like to do something like the following 2466 // TODO(jacobr): we would like to do something like the following
2448 // but we don't have summary support yet. 2467 // but we don't have summary support yet.
2449 // bool _supportJsExtensionMethod(AnnotatedNode node) => 2468 // bool _supportJsExtensionMethod(AnnotatedNode node) =>
2450 // _getAnnotation(node, "SupportJsExtensionMethod") != null; 2469 // _getAnnotation(node, "SupportJsExtensionMethod") != null;
OLDNEW
« no previous file with comments | « lib/runtime/dart_runtime.js ('k') | test/codegen/expect/DeltaBlue.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698