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 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
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 20 matching lines...) Expand all Loading... | |
31 | 31 |
32 import 'code_generator.dart'; | 32 import 'code_generator.dart'; |
33 import 'js_field_storage.dart'; | 33 import 'js_field_storage.dart'; |
34 import 'js_interop.dart'; | 34 import 'js_interop.dart'; |
35 import 'js_names.dart' as JS; | 35 import 'js_names.dart' as JS; |
36 import 'js_metalet.dart' as JS; | 36 import 'js_metalet.dart' as JS; |
37 import 'js_module_item_order.dart'; | 37 import 'js_module_item_order.dart'; |
38 import 'js_names.dart'; | 38 import 'js_names.dart'; |
39 import 'js_printer.dart' show writeJsLibrary; | 39 import 'js_printer.dart' show writeJsLibrary; |
40 import 'side_effect_analysis.dart'; | 40 import 'side_effect_analysis.dart'; |
41 import 'package:collection/equality.dart'; | |
42 | 41 |
43 // Various dynamic helpers we call. | 42 // Various dynamic helpers we call. |
44 // If renaming these, make sure to check other places like the | 43 // If renaming these, make sure to check other places like the |
45 // _runtime.js file and comments. | 44 // _runtime.js file and comments. |
46 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 45 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can |
47 // import and generate calls to, rather than dart_runtime.js | 46 // import and generate calls to, rather than dart_runtime.js |
48 const DPUT = 'dput'; | 47 const DPUT = 'dput'; |
49 const DLOAD = 'dload'; | 48 const DLOAD = 'dload'; |
50 const DINDEX = 'dindex'; | 49 const DINDEX = 'dindex'; |
51 const DSETINDEX = 'dsetindex'; | 50 const DSETINDEX = 'dsetindex'; |
52 const DCALL = 'dcall'; | 51 const DCALL = 'dcall'; |
53 const DSEND = 'dsend'; | 52 const DSEND = 'dsend'; |
54 | 53 |
55 const ListEquality _listEquality = const ListEquality(); | |
56 | |
57 class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { | 54 class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
58 final AbstractCompiler compiler; | 55 final AbstractCompiler compiler; |
59 final CodegenOptions options; | 56 final CodegenOptions options; |
60 final TypeRules rules; | 57 final TypeRules rules; |
61 final LibraryElement currentLibrary; | 58 final LibraryElement currentLibrary; |
62 | 59 |
63 /// The global extension type table. | 60 /// The global extension type table. |
64 final HashSet<ClassElement> _extensionTypes; | 61 final HashSet<ClassElement> _extensionTypes; |
65 | 62 |
66 /// Information that is precomputed for this library, indicates which fields | 63 /// Information that is precomputed for this library, indicates which fields |
(...skipping 20 matching lines...) Expand all Loading... | |
87 final _privateNames = new HashMap<String, JS.TemporaryId>(); | 84 final _privateNames = new HashMap<String, JS.TemporaryId>(); |
88 final _moduleItems = <JS.Statement>[]; | 85 final _moduleItems = <JS.Statement>[]; |
89 final _temps = new HashMap<Element, JS.TemporaryId>(); | 86 final _temps = new HashMap<Element, JS.TemporaryId>(); |
90 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); | 87 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); |
91 | 88 |
92 /// The name for the library's exports inside itself. | 89 /// The name for the library's exports inside itself. |
93 /// `exports` was chosen as the most similar to ES module patterns. | 90 /// `exports` was chosen as the most similar to ES module patterns. |
94 final _dartxVar = new JS.Identifier('dartx'); | 91 final _dartxVar = new JS.Identifier('dartx'); |
95 final _exportsVar = new JS.TemporaryId('exports'); | 92 final _exportsVar = new JS.TemporaryId('exports'); |
96 final _runtimeLibVar = new JS.Identifier('dart'); | 93 final _runtimeLibVar = new JS.Identifier('dart'); |
94 final _utilsLibVar = new JS.TemporaryId('utils'); | |
95 final _classesLibVar = new JS.TemporaryId('classes'); | |
96 final _rttiLibVar = new JS.TemporaryId('rtti'); | |
97 final _namedArgTemp = new JS.TemporaryId('opts'); | 97 final _namedArgTemp = new JS.TemporaryId('opts'); |
98 | 98 |
99 final TypeProvider _types; | 99 final TypeProvider _types; |
100 | 100 |
101 ConstFieldVisitor _constField; | 101 ConstFieldVisitor _constField; |
102 | 102 |
103 ModuleItemLoadOrder _loader; | 103 ModuleItemLoadOrder _loader; |
104 | 104 |
105 /// _interceptors.JSArray<E>, used for List literals. | 105 /// _interceptors.JSArray<E>, used for List literals. |
106 ClassElement _jsArray; | 106 ClassElement _jsArray; |
107 | 107 |
108 /// The default value of the module object. See [visitLibraryDirective]. | 108 /// The default value of the module object. See [visitLibraryDirective]. |
109 String _jsModuleValue; | 109 String _jsModuleValue; |
110 | 110 |
111 bool _isDartUtils; | 111 bool _isDartRuntime; |
112 | 112 |
113 Map<String, DartType> _objectMembers; | 113 Map<String, DartType> _objectMembers; |
114 | 114 |
115 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, | 115 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, |
116 this._extensionTypes, this._fieldsNeedingStorage) | 116 this._extensionTypes, this._fieldsNeedingStorage) |
117 : compiler = compiler, | 117 : compiler = compiler, |
118 options = compiler.options.codegenOptions, | 118 options = compiler.options.codegenOptions, |
119 _types = compiler.context.typeProvider { | 119 _types = compiler.context.typeProvider { |
120 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 120 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
121 | 121 |
122 var context = compiler.context; | 122 var context = compiler.context; |
123 var src = context.sourceFactory.forUri('dart:_interceptors'); | 123 var src = context.sourceFactory.forUri('dart:_interceptors'); |
124 var interceptors = context.computeLibraryElement(src); | 124 var interceptors = context.computeLibraryElement(src); |
125 _jsArray = interceptors.getType('JSArray'); | 125 _jsArray = interceptors.getType('JSArray'); |
126 _isDartUtils = currentLibrary.source.uri.toString() == 'dart:_utils'; | 126 |
127 _isDartRuntime = _runtimeLibUris.contains(_getLibUri(currentLibrary)); | |
127 | 128 |
128 _objectMembers = getObjectMemberMap(types); | 129 _objectMembers = getObjectMemberMap(types); |
129 } | 130 } |
131 String _getLibUri(LibraryElement lib) => lib.source.uri.toString(); | |
132 | |
133 static final _runtimeLibUris = new Set<String>.from([ | |
Jennifer Messerly
2016/01/07 20:18:04
ideally we wouldn't need to hard code these, or ge
ochafik
2016/01/13 13:13:20
Dropped this but kept _isDartRuntime (testing on d
| |
134 'dart:_utils', | |
135 'dart:_runtime', | |
136 'dart:_operations', | |
137 'dart:_errors', | |
138 'dart:_classes', | |
139 'dart:_generators', | |
140 'dart:_operations', | |
141 'dart:_types', | |
142 'dart:_rtti' | |
143 ]); | |
130 | 144 |
131 TypeProvider get types => rules.provider; | 145 TypeProvider get types => rules.provider; |
132 | 146 |
133 JS.Program emitLibrary(LibraryUnit library) { | 147 JS.Program emitLibrary(LibraryUnit library) { |
134 // Modify the AST to make coercions explicit. | 148 // Modify the AST to make coercions explicit. |
135 new CoercionReifier(library, rules).reify(); | 149 new CoercionReifier(library, rules).reify(); |
136 | 150 |
137 // Build the public namespace for this library. This allows us to do | 151 // Build the public namespace for this library. This allows us to do |
138 // constant time lookups (contrast with `Element.getChild(name)`). | 152 // constant time lookups (contrast with `Element.getChild(name)`). |
139 if (currentLibrary.publicNamespace == null) { | 153 if (currentLibrary.publicNamespace == null) { |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
189 // TODO(jmesserly): make these immutable in JS? | 203 // TODO(jmesserly): make these immutable in JS? |
190 for (var name in _exports) { | 204 for (var name in _exports) { |
191 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name])); | 205 _moduleItems.add(js.statement('#.# = #;', [_exportsVar, name, name])); |
192 } | 206 } |
193 | 207 |
194 var jsPath = compiler.getModuleName(currentLibrary.source.uri); | 208 var jsPath = compiler.getModuleName(currentLibrary.source.uri); |
195 | 209 |
196 // TODO(jmesserly): it would be great to run the renamer on the body, | 210 // TODO(jmesserly): it would be great to run the renamer on the body, |
197 // then figure out if we really need each of these parameters. | 211 // then figure out if we really need each of these parameters. |
198 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 | 212 // See ES6 modules: https://github.com/dart-lang/dev_compiler/issues/34 |
199 var params = [_exportsVar, _runtimeLibVar]; | 213 var params = [_exportsVar]; |
200 var processImport = | 214 var lazyParams = []; |
201 (LibraryElement library, JS.TemporaryId temp, List list) { | |
202 params.add(temp); | |
203 list.add(js.string(compiler.getModuleName(library.source.uri), "'")); | |
204 }; | |
205 | 215 |
206 var needsDartRuntime = !_isDartUtils; | 216 var libUri = _getLibUri(currentLibrary); |
207 | 217 |
208 var imports = <JS.Expression>[]; | 218 var imports = <JS.Expression>[]; |
219 var lazyImports = <JS.Expression>[]; | |
209 var moduleStatements = <JS.Statement>[]; | 220 var moduleStatements = <JS.Statement>[]; |
210 if (needsDartRuntime) { | 221 |
211 imports.add(js.string('dart/_runtime')); | 222 addImport(String name, JS.Expression libVar, {bool eager: true}) { |
223 (eager ? imports : lazyImports).add(js.string(name, "'")); | |
224 (eager ? params : lazyParams).add(libVar); | |
225 } | |
226 | |
227 if (!_isDartRuntime) { | |
228 addImport('dart/_runtime', _runtimeLibVar); | |
Jennifer Messerly
2016/01/07 20:18:04
can we just import this library explicitly?
/
ochafik
2016/01/13 13:13:20
That's mainly for the "mere mortal" files
| |
212 | 229 |
213 var dartxImport = | 230 var dartxImport = |
214 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); | 231 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); |
215 moduleStatements.add(dartxImport); | 232 moduleStatements.add(dartxImport); |
233 } else { | |
234 if (libUri == 'dart:_generators') { | |
235 addImport('dart/_classes', _classesLibVar); | |
Jennifer Messerly
2016/01/07 20:18:04
same here, can we import this explicitly?
ochafik
2016/01/13 13:13:20
Dropped now that all runtime is in dart:_runtime
| |
236 } | |
216 } | 237 } |
217 moduleStatements.addAll(_moduleItems); | 238 moduleStatements.addAll(_moduleItems); |
218 | 239 |
219 _imports.forEach((library, temp) { | 240 bool shouldImportEagerly(lib) { |
220 if (_loader.libraryIsLoaded(library)) { | 241 var otherLibUri = _getLibUri(lib); |
221 processImport(library, temp, imports); | 242 |
222 } | 243 // utils.dart contains very low-level utils and doesn't import anyone. |
244 if (otherLibUri == 'dart:_utils') return true; | |
Jennifer Messerly
2016/01/07 20:18:05
can we just add these cases to the existing coreli
ochafik
2016/01/13 13:13:20
Ack
| |
245 // Types in types.dart extend rtti.LazyTagged in a way that isn't | |
246 // recognized by the loader: we help it out here. | |
Jennifer Messerly
2016/01/07 20:18:04
can you explain the problem with this? I'm not fol
ochafik
2016/01/13 13:13:20
Dropped special logic
| |
247 if (libUri == 'dart:_types' && otherLibUri == 'dart:_rtti') return true; | |
248 if (libUri == 'dart:_runtime') return otherLibUri != 'dart:_js_helper'; | |
249 // runtime.dart needs to import everything but utils.dart lazily. | |
250 if (_isDartRuntime) return false; | |
251 | |
252 return _loader.libraryIsLoaded(lib); | |
253 } | |
254 | |
255 var importsEagerness = new Map<LibraryElement, bool>.fromIterable( | |
256 _imports.keys, | |
257 value: shouldImportEagerly); | |
258 | |
259 _imports.forEach((LibraryElement lib, JS.TemporaryId temp) { | |
260 bool eager = importsEagerness[lib]; | |
261 addImport(compiler.getModuleName(lib.source.uri), temp, eager: eager); | |
223 }); | 262 }); |
224 | 263 |
225 var lazyImports = <JS.Expression>[]; | 264 params.addAll(lazyParams); |
226 _imports.forEach((library, temp) { | |
227 if (!_loader.libraryIsLoaded(library)) { | |
228 processImport(library, temp, lazyImports); | |
229 } | |
230 }); | |
231 | 265 |
232 var module = | 266 var module = |
233 js.call("function(#) { 'use strict'; #; }", [params, moduleStatements]); | 267 js.call("function(#) { 'use strict'; #; }", [params, moduleStatements]); |
234 | 268 |
235 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ | 269 var moduleDef = js.statement("dart_library.library(#, #, #, #, #)", [ |
236 js.string(jsPath, "'"), | 270 js.string(jsPath, "'"), |
237 _jsModuleValue ?? new JS.LiteralNull(), | 271 _jsModuleValue ?? new JS.LiteralNull(), |
238 js.commentExpression( | 272 js.commentExpression( |
239 "Imports", new JS.ArrayInitializer(imports, multiline: true)), | 273 "Imports", new JS.ArrayInitializer(imports, multiline: true)), |
240 js.commentExpression("Lazy imports", | 274 js.commentExpression("Lazy imports", |
(...skipping 16 matching lines...) Expand all Loading... | |
257 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); | 291 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); |
258 | 292 |
259 var code = _visit(node); | 293 var code = _visit(node); |
260 if (code != null) _moduleItems.add(code); | 294 if (code != null) _moduleItems.add(code); |
261 } | 295 } |
262 | 296 |
263 @override | 297 @override |
264 void visitLibraryDirective(LibraryDirective node) { | 298 void visitLibraryDirective(LibraryDirective node) { |
265 assert(_jsModuleValue == null); | 299 assert(_jsModuleValue == null); |
266 | 300 |
267 var jsName = findAnnotation(node.element, isJSAnnotation); | 301 _jsModuleValue = _getJsName(node.element); |
268 _jsModuleValue = | 302 } |
269 getConstantField(jsName, 'name', types.stringType)?.toStringValue(); | 303 |
304 String _getJsName(Element e) { | |
Jennifer Messerly
2016/01/07 20:18:04
I'm a little confused about this function. It coul
ochafik
2016/01/13 13:13:20
Spun this out in https://codereview.chromium.org/1
| |
305 var jsName = findAnnotation(e, isJSAnnotation); | |
306 return getConstantField(jsName, 'name', types.stringType)?.toStringValue(); | |
270 } | 307 } |
271 | 308 |
272 @override | 309 @override |
273 void visitImportDirective(ImportDirective node) { | 310 void visitImportDirective(ImportDirective node) { |
274 // Nothing to do yet, but we'll want to convert this to an ES6 import once | 311 // Nothing to do yet, but we'll want to convert this to an ES6 import once |
275 // we have support for modules. | 312 // we have support for modules. |
276 } | 313 } |
277 | 314 |
278 @override void visitPartDirective(PartDirective node) {} | 315 @override void visitPartDirective(PartDirective node) {} |
279 @override void visitPartOfDirective(PartOfDirective node) {} | 316 @override void visitPartOfDirective(PartOfDirective node) {} |
280 | 317 |
281 @override | 318 @override |
282 void visitExportDirective(ExportDirective node) { | 319 void visitExportDirective(ExportDirective node) { |
283 var exportName = _libraryName(node.uriElement); | 320 var exportName = _libraryName(node.uriElement); |
284 | 321 |
285 var currentLibNames = currentLibrary.publicNamespace.definedNames; | 322 var currentLibNames = currentLibrary.publicNamespace.definedNames; |
286 | 323 |
287 var args = [_exportsVar, exportName]; | 324 var args = [_exportsVar, exportName]; |
288 if (node.combinators.isNotEmpty) { | 325 if (node.combinators.isNotEmpty) { |
289 var shownNames = <JS.Expression>[]; | 326 var shownNames = <JS.Expression>[]; |
290 var hiddenNames = <JS.Expression>[]; | 327 var hiddenNames = <JS.Expression>[]; |
291 | 328 |
292 var show = node.combinators.firstWhere((c) => c is ShowCombinator, | 329 var show = node.combinators.firstWhere((c) => c is ShowCombinator, |
293 orElse: () => null) as ShowCombinator; | 330 orElse: () => null) as ShowCombinator; |
294 var hide = node.combinators.firstWhere((c) => c is HideCombinator, | 331 var hide = node.combinators.firstWhere((c) => c is HideCombinator, |
295 orElse: () => null) as HideCombinator; | 332 orElse: () => null) as HideCombinator; |
296 if (show != null) { | 333 if (show != null) { |
334 // If the import has a single shown name + a JsName(jsName) annotation, | |
335 // then we use that jsName as the exported name. | |
336 var singleJsName = _getJsName(node.element); | |
337 if (singleJsName != null && show.shownNames.length != 1) { | |
338 throw new StateError('Cannot set js name on more than one export'); | |
Jennifer Messerly
2016/01/07 20:18:04
Can this error be triggered by user code?
If so,
ochafik
2016/01/13 13:13:20
Dropped that (no need to re-export anymore with th
| |
339 } | |
297 shownNames.addAll(show.shownNames | 340 shownNames.addAll(show.shownNames |
298 .map((i) => i.name) | 341 .map((i) => singleJsName ?? i.name) |
299 .where((s) => !currentLibNames.containsKey(s)) | 342 .where((s) => !currentLibNames.containsKey(s)) |
300 .map((s) => js.string(s, "'"))); | 343 .map((s) => js.string(s, "'"))); |
301 } | 344 } |
302 if (hide != null) { | 345 if (hide != null) { |
303 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); | 346 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); |
304 } | 347 } |
305 args.add(new JS.ArrayInitializer(shownNames)); | 348 args.add(new JS.ArrayInitializer(shownNames)); |
306 args.add(new JS.ArrayInitializer(hiddenNames)); | 349 args.add(new JS.ArrayInitializer(hiddenNames)); |
307 } | 350 } |
308 _moduleItems.add(js.statement('dart.export_(#);', [args])); | 351 |
352 // When we compile _runtime.js, we need to source export_ from _utils.js: | |
353 _moduleItems.add(js.statement('#(#);', [_dartExport, args])); | |
309 } | 354 } |
310 | 355 |
311 JS.Identifier _initSymbol(JS.Identifier id) { | 356 JS.Identifier _initSymbol(JS.Identifier id) { |
312 var s = | 357 var s = |
313 js.statement('const # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | 358 js.statement('const # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); |
314 _moduleItems.add(s); | 359 _moduleItems.add(s); |
315 return id; | 360 return id; |
316 } | 361 } |
317 | 362 |
318 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | 363 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, |
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
572 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | 617 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); |
573 } | 618 } |
574 return body; | 619 return body; |
575 } | 620 } |
576 | 621 |
577 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { | 622 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { |
578 var name = type.name; | 623 var name = type.name; |
579 var genericName = '$name\$'; | 624 var genericName = '$name\$'; |
580 var typeParams = type.typeParameters.map((p) => p.name); | 625 var typeParams = type.typeParameters.map((p) => p.name); |
581 if (isPublic(name)) _exports.add(genericName); | 626 if (isPublic(name)) _exports.add(genericName); |
582 return js.statement('const # = dart.generic(function(#) { #; return #; });', | 627 |
583 [genericName, typeParams, body, name]); | 628 return js.statement('const # = #(function(#) { #; return #; });', |
629 [genericName, _dartGeneric, typeParams, body, name]); | |
584 } | 630 } |
585 | 631 |
632 get _dartGeneric => | |
633 js.call('#.generic', [_isDartRuntime ? _classesLibVar : _runtimeLibVar]); | |
Jennifer Messerly
2016/01/07 20:18:05
I made this comment elsewhere, but I don't think w
ochafik
2016/01/13 13:13:20
Parts FTW!
| |
634 | |
635 get _dartExport => | |
636 js.call('#.export', [_isDartRuntime ? _utilsLibVar : _runtimeLibVar]); | |
637 | |
638 get _dartFn => js.call('#.fn', _isDartRuntime ? _rttiLibVar : _runtimeLibVar); | |
639 | |
640 get _dartSetSignature => js.call( | |
641 '#.setSignature', [_isDartRuntime ? _classesLibVar : _runtimeLibVar]); | |
642 | |
586 final _hasDeferredSupertype = new HashSet<ClassElement>(); | 643 final _hasDeferredSupertype = new HashSet<ClassElement>(); |
587 | 644 |
588 bool _deferIfNeeded(DartType type, ClassElement current) { | 645 bool _deferIfNeeded(DartType type, ClassElement current) { |
589 if (type is ParameterizedType) { | 646 if (type is ParameterizedType) { |
590 var typeArguments = type.typeArguments; | 647 var typeArguments = type.typeArguments; |
591 for (var typeArg in typeArguments) { | 648 for (var typeArg in typeArguments) { |
592 var typeElement = typeArg.element; | 649 var typeElement = typeArg.element; |
593 // FIXME(vsm): This does not track mutual recursive dependences. | 650 // FIXME(vsm): This does not track mutual recursive dependences. |
594 if (current == typeElement || _deferIfNeeded(typeArg, current)) { | 651 if (current == typeElement || _deferIfNeeded(typeArg, current)) { |
595 return true; | 652 return true; |
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
825 if (!tStatics.isEmpty) { | 882 if (!tStatics.isEmpty) { |
826 assert(!sNames.isEmpty); | 883 assert(!sNames.isEmpty); |
827 var aNames = new JS.Property( | 884 var aNames = new JS.Property( |
828 _propertyName('names'), new JS.ArrayInitializer(sNames)); | 885 _propertyName('names'), new JS.ArrayInitializer(sNames)); |
829 sigFields.add(build('statics', tStatics)); | 886 sigFields.add(build('statics', tStatics)); |
830 sigFields.add(aNames); | 887 sigFields.add(aNames); |
831 } | 888 } |
832 if (!sigFields.isEmpty || extensions.isNotEmpty) { | 889 if (!sigFields.isEmpty || extensions.isNotEmpty) { |
833 var sig = new JS.ObjectInitializer(sigFields); | 890 var sig = new JS.ObjectInitializer(sigFields); |
834 var classExpr = new JS.Identifier(name); | 891 var classExpr = new JS.Identifier(name); |
835 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig])); | 892 body.add(js.statement('#(#, #);', [_dartSetSignature, classExpr, sig])); |
836 } | 893 } |
837 } | 894 } |
838 | 895 |
839 // If a concrete class implements one of our extensions, we might need to | 896 // If a concrete class implements one of our extensions, we might need to |
840 // add forwarders. | 897 // add forwarders. |
841 if (extensions.isNotEmpty) { | 898 if (extensions.isNotEmpty) { |
842 var methodNames = <JS.Expression>[]; | 899 var methodNames = <JS.Expression>[]; |
843 for (var e in extensions) { | 900 for (var e in extensions) { |
844 methodNames.add(_elementMemberName(e)); | 901 methodNames.add(_elementMemberName(e)); |
845 } | 902 } |
(...skipping 452 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1298 | 1355 |
1299 if (node.isGetter || node.isSetter) { | 1356 if (node.isGetter || node.isSetter) { |
1300 // Add these later so we can use getter/setter syntax. | 1357 // Add these later so we can use getter/setter syntax. |
1301 _properties.add(node); | 1358 _properties.add(node); |
1302 return null; | 1359 return null; |
1303 } | 1360 } |
1304 | 1361 |
1305 var body = <JS.Statement>[]; | 1362 var body = <JS.Statement>[]; |
1306 _flushLibraryProperties(body); | 1363 _flushLibraryProperties(body); |
1307 | 1364 |
1308 var name = node.name.name; | 1365 var name = _getJsName(node.element) ?? node.name.name; |
1309 | 1366 |
1310 var fn = _visit(node.functionExpression); | 1367 var fn = _visit(node.functionExpression); |
1311 bool needsTagging = true; | 1368 // Don't tag functions defined in the low-level runtime. |
1369 bool needsTagging = !_isDartRuntime; | |
1312 | 1370 |
1313 if (currentLibrary.source.isInSystemLibrary && | 1371 if (currentLibrary.source.isInSystemLibrary && |
1314 _isInlineJSFunction(node.functionExpression)) { | 1372 _isInlineJSFunction(node.functionExpression)) { |
1315 fn = _simplifyPassThroughArrowFunCallBody(fn); | 1373 fn = _simplifyPassThroughArrowFunCallBody(fn); |
1316 needsTagging = !_isDartUtils; | |
1317 } | 1374 } |
1318 | 1375 |
1319 var id = new JS.Identifier(name); | 1376 var id = new JS.Identifier(name); |
1320 body.add(annotate(new JS.FunctionDeclaration(id, fn), node.element)); | 1377 body.add(annotate(new JS.FunctionDeclaration(id, fn), node.element)); |
1321 if (needsTagging) { | 1378 if (needsTagging) { |
1322 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) | 1379 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) |
1323 .toStatement()); | 1380 .toStatement()); |
1324 } | 1381 } |
1325 | 1382 |
1326 if (isPublic(name)) _addExport(name); | 1383 if (isPublic(name)) _addExport(name); |
(...skipping 11 matching lines...) Expand all Loading... | |
1338 return _isJSInvocation(stat.expression); | 1395 return _isJSInvocation(stat.expression); |
1339 } | 1396 } |
1340 } | 1397 } |
1341 } | 1398 } |
1342 return false; | 1399 return false; |
1343 } | 1400 } |
1344 | 1401 |
1345 bool _isJSInvocation(Expression expr) => | 1402 bool _isJSInvocation(Expression expr) => |
1346 expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); | 1403 expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); |
1347 | 1404 |
1348 // Simplify `(args) => ((x, y) => { ... })(x, y)` to `(args) => { ... }`. | 1405 // Simplify `(args) => (() => { ... })()` to `(args) => { ... }`. |
1349 // Note: we don't check if the top-level args match the ones passed through | 1406 // Note: this allows silently passing args through to the body, which only |
Jennifer Messerly
2016/01/07 20:18:05
I don't think you need the "Note" anymore?
ochafik
2016/01/13 13:13:20
Done.
| |
1350 // the arrow function, which allows silently passing args through to the | 1407 // works if we don't do weird renamings of Dart params. |
1351 // body (which only works if we don't do weird renamings of Dart params). | |
1352 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) { | 1408 JS.Fun _simplifyPassThroughArrowFunCallBody(JS.Fun fn) { |
1353 String getIdent(JS.Node node) => node is JS.Identifier ? node.name : null; | |
1354 List<String> getIdents(List params) => | |
1355 params.map(getIdent).toList(growable: false); | |
1356 | |
1357 if (fn.body is JS.Block && fn.body.statements.length == 1) { | 1409 if (fn.body is JS.Block && fn.body.statements.length == 1) { |
1358 var stat = fn.body.statements.single; | 1410 var stat = fn.body.statements.single; |
1359 if (stat is JS.Return && stat.value is JS.Call) { | 1411 if (stat is JS.Return && stat.value is JS.Call) { |
1360 JS.Call call = stat.value; | 1412 JS.Call call = stat.value; |
1361 if (call.target is JS.ArrowFun) { | 1413 if (call.target is JS.ArrowFun && call.arguments.isEmpty) { |
1362 var passedArgs = getIdents(call.arguments); | |
1363 JS.ArrowFun innerFun = call.target; | 1414 JS.ArrowFun innerFun = call.target; |
1364 if (_listEquality.equals(getIdents(innerFun.params), passedArgs)) { | 1415 if (innerFun.params.isEmpty) { |
1365 return new JS.Fun(fn.params, innerFun.body); | 1416 return new JS.Fun(fn.params, innerFun.body); |
1366 } | 1417 } |
1367 } | 1418 } |
1368 } | 1419 } |
1369 } | 1420 } |
1370 return fn; | 1421 return fn; |
1371 } | 1422 } |
1372 | 1423 |
1373 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { | 1424 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
1374 var name = node.name.name; | 1425 var name = node.name.name; |
(...skipping 27 matching lines...) Expand all Loading... | |
1402 JS.Expression _emitFunctionTagged(JS.Expression clos, DartType type, | 1453 JS.Expression _emitFunctionTagged(JS.Expression clos, DartType type, |
1403 {topLevel: false}) { | 1454 {topLevel: false}) { |
1404 var name = type.name; | 1455 var name = type.name; |
1405 var lazy = topLevel && !_typeIsLoaded(type); | 1456 var lazy = topLevel && !_typeIsLoaded(type); |
1406 | 1457 |
1407 if (type is FunctionType && (name == '' || name == null)) { | 1458 if (type is FunctionType && (name == '' || name == null)) { |
1408 if (type.returnType.isDynamic && | 1459 if (type.returnType.isDynamic && |
1409 type.optionalParameterTypes.isEmpty && | 1460 type.optionalParameterTypes.isEmpty && |
1410 type.namedParameterTypes.isEmpty && | 1461 type.namedParameterTypes.isEmpty && |
1411 type.normalParameterTypes.every((t) => t.isDynamic)) { | 1462 type.normalParameterTypes.every((t) => t.isDynamic)) { |
1412 return js.call('dart.fn(#)', [clos]); | 1463 return js.call('#(#)', [_dartFn, clos]); |
1413 } | 1464 } |
1414 if (lazy) { | 1465 if (lazy) { |
1415 return js.call('dart.fn(#, () => #)', [clos, _emitFunctionRTTI(type)]); | 1466 return js.call( |
1467 '#(#, () => #)', [_dartFn, clos, _emitFunctionRTTI(type)]); | |
1416 } | 1468 } |
1417 return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]); | 1469 return js.call('#(#, #)', [_dartFn, clos, _emitFunctionTypeParts(type)]); |
1418 } | 1470 } |
1419 throw 'Function has non function type: $type'; | 1471 throw 'Function has non function type: $type'; |
1420 } | 1472 } |
1421 | 1473 |
1422 @override | 1474 @override |
1423 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1475 JS.Expression visitFunctionExpression(FunctionExpression node) { |
1424 var params = _visit(node.parameters) as List<JS.Parameter>; | 1476 var params = _visit(node.parameters) as List<JS.Parameter>; |
1425 if (params == null) params = <JS.Parameter>[]; | 1477 if (params == null) params = <JS.Parameter>[]; |
1426 | 1478 |
1427 var parent = node.parent; | 1479 var parent = node.parent; |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1562 'Unimplemented unknown name', new JS.Identifier(node.name)); | 1614 'Unimplemented unknown name', new JS.Identifier(node.name)); |
1563 } | 1615 } |
1564 | 1616 |
1565 // Get the original declaring element. If we had a property accessor, this | 1617 // Get the original declaring element. If we had a property accessor, this |
1566 // indirects back to a (possibly synthetic) field. | 1618 // indirects back to a (possibly synthetic) field. |
1567 var element = accessor; | 1619 var element = accessor; |
1568 if (accessor is PropertyAccessorElement) element = accessor.variable; | 1620 if (accessor is PropertyAccessorElement) element = accessor.variable; |
1569 | 1621 |
1570 _loader.declareBeforeUse(element); | 1622 _loader.declareBeforeUse(element); |
1571 | 1623 |
1572 var name = element.name; | 1624 var name = _getJsName(element) ?? element.name; |
Jennifer Messerly
2016/01/07 20:18:04
This is related to my other comment on _getJsName,
ochafik
2016/01/13 13:13:20
Moved the exported name resolution logic to down t
| |
1573 | 1625 |
1574 // type literal | 1626 // type literal |
1575 if (element is ClassElement || | 1627 if (element is ClassElement || |
1576 element is DynamicElementImpl || | 1628 element is DynamicElementImpl || |
1577 element is FunctionTypeAliasElement) { | 1629 element is FunctionTypeAliasElement) { |
1578 return _emitTypeName( | 1630 return _emitTypeName( |
1579 fillDynamicTypeArgs((element as dynamic).type, types)); | 1631 fillDynamicTypeArgs((element as dynamic).type, types)); |
1580 } | 1632 } |
1581 | 1633 |
1582 // library member | 1634 // library member |
1583 if (element.enclosingElement is CompilationUnitElement) { | 1635 if (element.enclosingElement is CompilationUnitElement) { |
1584 return _maybeQualifiedName(element); | 1636 return _maybeQualifiedName(element, name); |
1585 } | 1637 } |
1586 | 1638 |
1587 // Unqualified class member. This could mean implicit-this, or implicit | 1639 // Unqualified class member. This could mean implicit-this, or implicit |
1588 // call to a static from the same class. | 1640 // call to a static from the same class. |
1589 if (element is ClassMemberElement && element is! ConstructorElement) { | 1641 if (element is ClassMemberElement && element is! ConstructorElement) { |
1590 bool isStatic = element.isStatic; | 1642 bool isStatic = element.isStatic; |
1591 var type = element.enclosingElement.type; | 1643 var type = element.enclosingElement.type; |
1592 var member = _emitMemberName(name, isStatic: isStatic, type: type); | 1644 var member = _emitMemberName(name, isStatic: isStatic, type: type); |
1593 | 1645 |
1594 // For static methods, we add the raw type name, without generics or | 1646 // For static methods, we add the raw type name, without generics or |
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1877 var stmts = _visitList(node.block.statements) as List<JS.Statement>; | 1929 var stmts = _visitList(node.block.statements) as List<JS.Statement>; |
1878 if (initArgs != null) stmts.insert(0, initArgs); | 1930 if (initArgs != null) stmts.insert(0, initArgs); |
1879 return new JS.Block(stmts); | 1931 return new JS.Block(stmts); |
1880 } | 1932 } |
1881 | 1933 |
1882 @override | 1934 @override |
1883 JS.Block visitBlock(Block node) => | 1935 JS.Block visitBlock(Block node) => |
1884 new JS.Block(_visitList(node.statements) as List<JS.Statement>, | 1936 new JS.Block(_visitList(node.statements) as List<JS.Statement>, |
1885 isScope: true); | 1937 isScope: true); |
1886 | 1938 |
1939 /// Return the type constructor `_foolib.Bar$` given `Bar` from lib `_foolib`. | |
1940 /// | |
1941 /// This implements / expands the `genericTypeConstructor` intrinsic defined | |
1942 /// in `foreign_helper.dart`. | |
1943 JS.Expression _emitGenericTypeConstructor(Expression typeExpression) { | |
Jennifer Messerly
2016/01/07 20:18:04
I don't understand why this is needed. Given a typ
ochafik
2016/01/13 13:13:20
This implements the new intrinsic used in the foll
| |
1944 var ref = _visit(typeExpression); | |
1945 if (ref is JS.PropertyAccess) { | |
1946 var name = (ref.selector as JS.LiteralString).valueWithoutQuotes; | |
1947 return new JS.PropertyAccess( | |
1948 ref.receiver, new JS.LiteralString("'$name\$'")); | |
1949 } else if (ref is JS.MaybeQualifiedId) { | |
1950 var name = (ref.name as JS.Identifier).name; | |
1951 return new JS.PropertyAccess(ref.qualifier, new JS.Identifier('$name\$')); | |
1952 } else { | |
1953 throw new ArgumentError('Invalid type ref: $ref (${ref?.runtimeType})'); | |
1954 } | |
1955 } | |
1956 | |
1887 @override | 1957 @override |
1888 visitMethodInvocation(MethodInvocation node) { | 1958 visitMethodInvocation(MethodInvocation node) { |
1889 if (node.operator != null && node.operator.lexeme == '?.') { | 1959 if (node.operator != null && node.operator.lexeme == '?.') { |
1890 return _emitNullSafe(node); | 1960 return _emitNullSafe(node); |
1891 } | 1961 } |
1962 if (isGenericTypeConstructorInvocation(node)) { | |
1963 return _emitGenericTypeConstructor(node.argumentList.arguments.single); | |
Jennifer Messerly
2016/01/07 20:18:04
I don't understand why this case is needed. It see
ochafik
2016/01/13 13:13:20
Sorry for the lacking doc, this is a new intrinsic
| |
1964 } | |
1892 | 1965 |
1893 var target = _getTarget(node); | 1966 var target = _getTarget(node); |
1894 var result = _emitForeignJS(node); | 1967 var result = _emitForeignJS(node); |
1895 if (result != null) return result; | 1968 if (result != null) return result; |
1896 | 1969 |
1897 String code; | 1970 String code; |
1898 if (target == null || isLibraryPrefix(target)) { | 1971 if (target == null || isLibraryPrefix(target)) { |
1899 if (DynamicInvoke.get(node.methodName)) { | 1972 if (DynamicInvoke.get(node.methodName)) { |
1900 code = 'dart.$DCALL(#, #)'; | 1973 code = 'dart.$DCALL(#, #)'; |
1901 } else { | 1974 } else { |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2207 } else { | 2280 } else { |
2208 jsInit = _visitInitializer(field); | 2281 jsInit = _visitInitializer(field); |
2209 eagerInit = false; | 2282 eagerInit = false; |
2210 } | 2283 } |
2211 | 2284 |
2212 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 2285 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile |
2213 // runtime helpers. | 2286 // runtime helpers. |
2214 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | 2287 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); |
2215 if (isJSTopLevel) eagerInit = true; | 2288 if (isJSTopLevel) eagerInit = true; |
2216 | 2289 |
2217 var fieldName = field.name.name; | 2290 var fieldName = _getJsName(element) ?? field.name.name; |
2291 | |
2218 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || | 2292 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || |
2219 isJSTopLevel) { | 2293 isJSTopLevel) { |
2220 // constant fields don't change, so we can generate them as `let` | 2294 // constant fields don't change, so we can generate them as `let` |
2221 // but add them to the module's exports. However, make sure we generate | 2295 // but add them to the module's exports. However, make sure we generate |
2222 // anything they depend on first. | 2296 // anything they depend on first. |
2223 | 2297 |
2224 if (isPublic(fieldName)) _addExport(fieldName); | 2298 if (isPublic(fieldName)) _addExport(fieldName); |
2225 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; | 2299 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; |
2226 return annotateVariable( | 2300 return annotateVariable( |
2227 js.statement( | 2301 js.statement( |
(...skipping 615 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2843 } | 2917 } |
2844 | 2918 |
2845 String code; | 2919 String code; |
2846 if (member != null && member is MethodElement && !isStatic) { | 2920 if (member != null && member is MethodElement && !isStatic) { |
2847 // Tear-off methods: explicitly bind it. | 2921 // Tear-off methods: explicitly bind it. |
2848 if (target is SuperExpression) { | 2922 if (target is SuperExpression) { |
2849 return js.call('dart.bind(this, #, #.#)', [name, _visit(target), name]); | 2923 return js.call('dart.bind(this, #, #.#)', [name, _visit(target), name]); |
2850 } else if (_requiresStaticDispatch(target, memberId.name)) { | 2924 } else if (_requiresStaticDispatch(target, memberId.name)) { |
2851 var type = member.type; | 2925 var type = member.type; |
2852 var clos = js.call('dart.#.bind(#)', [name, _visit(target)]); | 2926 var clos = js.call('dart.#.bind(#)', [name, _visit(target)]); |
2853 return js.call('dart.fn(#, #)', [clos, _emitFunctionTypeParts(type)]); | 2927 return js.call( |
2928 '#(#, #)', [_dartFn, clos, _emitFunctionTypeParts(type)]); | |
2854 } | 2929 } |
2855 code = 'dart.bind(#, #)'; | 2930 code = 'dart.bind(#, #)'; |
2856 } else if (_requiresStaticDispatch(target, memberId.name)) { | 2931 } else if (_requiresStaticDispatch(target, memberId.name)) { |
2857 return js.call('dart.#(#)', [name, _visit(target)]); | 2932 return js.call('dart.#(#)', [name, _visit(target)]); |
2858 } else { | 2933 } else { |
2859 code = '#.#'; | 2934 code = '#.#'; |
2860 } | 2935 } |
2861 | 2936 |
2862 return js.call(code, [_visit(target), name]); | 2937 return js.call(code, [_visit(target), name]); |
2863 } | 2938 } |
(...skipping 548 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3412 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 3487 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
3413 | 3488 |
3414 FunctionBody _functionBody(node) => | 3489 FunctionBody _functionBody(node) => |
3415 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 3490 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
3416 | 3491 |
3417 /// Choose a canonical name from the library element. | 3492 /// Choose a canonical name from the library element. |
3418 /// This never uses the library's name (the identifier in the `library` | 3493 /// This never uses the library's name (the identifier in the `library` |
3419 /// declaration) as it doesn't have any meaningful rules enforced. | 3494 /// declaration) as it doesn't have any meaningful rules enforced. |
3420 JS.Identifier _libraryName(LibraryElement library) { | 3495 JS.Identifier _libraryName(LibraryElement library) { |
3421 if (library == currentLibrary) return _exportsVar; | 3496 if (library == currentLibrary) return _exportsVar; |
3422 return _imports.putIfAbsent( | 3497 return _imports.putIfAbsent(library, () { |
3423 library, () => new JS.TemporaryId(jsLibraryName(library))); | 3498 var name = library.name; |
3499 if (name == 'dart._utils') return _utilsLibVar; | |
Jennifer Messerly
2016/01/07 20:18:04
I don't think these special cases could be needed.
ochafik
2016/01/13 13:13:20
Done.
| |
3500 else if (name == 'dart._classes') return _classesLibVar; | |
Jennifer Messerly
2016/01/07 20:18:04
also, relying on the library name is super risky.
ochafik
2016/01/13 13:13:20
Acknowledged.
| |
3501 else if (name == 'dart._rtti') return _rttiLibVar; | |
3502 else return new JS.TemporaryId(jsLibraryName(library)); | |
3503 }); | |
3424 } | 3504 } |
3425 | 3505 |
3426 DartType getStaticType(Expression e) => rules.getStaticType(e); | 3506 DartType getStaticType(Expression e) => rules.getStaticType(e); |
3427 | 3507 |
3428 @override | 3508 @override |
3429 String getQualifiedName(TypeDefiningElement type) { | 3509 String getQualifiedName(TypeDefiningElement type) { |
3430 JS.TemporaryId id = _imports[type.library]; | 3510 JS.TemporaryId id = _imports[type.library]; |
3431 return id == null ? type.name : '${id.name}.${type.name}'; | 3511 return id == null ? type.name : '${id.name}.${type.name}'; |
3432 } | 3512 } |
3433 | 3513 |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3524 | 3604 |
3525 /// A special kind of element created by the compiler, signifying a temporary | 3605 /// A special kind of element created by the compiler, signifying a temporary |
3526 /// variable. These objects use instance equality, and should be shared | 3606 /// variable. These objects use instance equality, and should be shared |
3527 /// everywhere in the tree where they are treated as the same variable. | 3607 /// everywhere in the tree where they are treated as the same variable. |
3528 class TemporaryVariableElement extends LocalVariableElementImpl { | 3608 class TemporaryVariableElement extends LocalVariableElementImpl { |
3529 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3609 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3530 | 3610 |
3531 int get hashCode => identityHashCode(this); | 3611 int get hashCode => identityHashCode(this); |
3532 bool operator ==(Object other) => identical(this, other); | 3612 bool operator ==(Object other) => identical(this, other); |
3533 } | 3613 } |
OLD | NEW |