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 | 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 23 matching lines...) Expand all Loading... | |
34 import 'package:analyzer/src/task/strong/ast_properties.dart' | 34 import 'package:analyzer/src/task/strong/ast_properties.dart' |
35 show isDynamicInvoke, setIsDynamicInvoke, getImplicitAssignmentCast; | 35 show isDynamicInvoke, setIsDynamicInvoke, getImplicitAssignmentCast; |
36 import 'package:path/path.dart' show separator; | 36 import 'package:path/path.dart' show separator; |
37 | 37 |
38 import '../closure/closure_annotator.dart' show ClosureAnnotator; | 38 import '../closure/closure_annotator.dart' show ClosureAnnotator; |
39 import '../js_ast/js_ast.dart' as JS; | 39 import '../js_ast/js_ast.dart' as JS; |
40 import '../js_ast/js_ast.dart' show js; | 40 import '../js_ast/js_ast.dart' show js; |
41 import 'ast_builder.dart' show AstBuilder; | 41 import 'ast_builder.dart' show AstBuilder; |
42 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; | 42 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; |
43 import 'element_helpers.dart'; | 43 import 'element_helpers.dart'; |
44 import 'element_loader.dart' show ElementLoader; | |
45 import 'extension_types.dart' show ExtensionTypeSet; | 44 import 'extension_types.dart' show ExtensionTypeSet; |
46 import 'js_interop.dart'; | 45 import 'js_interop.dart'; |
47 import 'js_metalet.dart' as JS; | 46 import 'js_metalet.dart' as JS; |
48 import 'js_names.dart' as JS; | 47 import 'js_names.dart' as JS; |
49 import 'js_typeref_codegen.dart' show JsTypeRefCodegen; | 48 import 'js_typeref_codegen.dart' show JsTypeRefCodegen; |
50 import 'module_builder.dart' show pathToJSIdentifier; | 49 import 'module_builder.dart' show pathToJSIdentifier; |
51 import 'nullable_type_inference.dart' show NullableTypeInference; | 50 import 'nullable_type_inference.dart' show NullableTypeInference; |
52 import 'property_model.dart'; | 51 import 'property_model.dart'; |
53 import 'reify_coercions.dart' show CoercionReifier; | 52 import 'reify_coercions.dart' show CoercionReifier; |
54 import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless; | 53 import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless; |
(...skipping 10 matching lines...) Expand all Loading... | |
65 /// The set of libraries we are currently compiling, and the temporaries used | 64 /// The set of libraries we are currently compiling, and the temporaries used |
66 /// to refer to them. | 65 /// to refer to them. |
67 /// | 66 /// |
68 /// We sometimes special case codegen for a single library, as it simplifies | 67 /// We sometimes special case codegen for a single library, as it simplifies |
69 /// name scoping requirements. | 68 /// name scoping requirements. |
70 final _libraries = new Map<LibraryElement, JS.Identifier>(); | 69 final _libraries = new Map<LibraryElement, JS.Identifier>(); |
71 | 70 |
72 /// Imported libraries, and the temporaries used to refer to them. | 71 /// Imported libraries, and the temporaries used to refer to them. |
73 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 72 final _imports = new Map<LibraryElement, JS.TemporaryId>(); |
74 | 73 |
74 /// The list of dart:_runtime SDK functions; these are assumed by other code | |
75 /// in the SDK to be generated before anything else. | |
76 final _internalSdkFunctions = <JS.ModuleItem>[]; | |
77 | |
75 /// The list of output module items, in the order they need to be emitted in. | 78 /// The list of output module items, in the order they need to be emitted in. |
76 final _moduleItems = <JS.ModuleItem>[]; | 79 final _moduleItems = <JS.ModuleItem>[]; |
77 | 80 |
78 /// Table of named and possibly hoisted types. | 81 /// Table of named and possibly hoisted types. |
79 TypeTable _typeTable; | 82 TypeTable _typeTable; |
80 | 83 |
81 /// The global extension type table. | 84 /// The global extension type table. |
82 final ExtensionTypeSet _extensionTypes; | 85 final ExtensionTypeSet _extensionTypes; |
83 | 86 |
84 /// The variable for the target of the current `..` cascade expression. | 87 /// The variable for the target of the current `..` cascade expression. |
(...skipping 13 matching lines...) Expand all Loading... | |
98 new HashMap<LibraryElement, HashMap<String, JS.TemporaryId>>(); | 101 new HashMap<LibraryElement, HashMap<String, JS.TemporaryId>>(); |
99 final _initializingFormalTemps = | 102 final _initializingFormalTemps = |
100 new HashMap<ParameterElement, JS.TemporaryId>(); | 103 new HashMap<ParameterElement, JS.TemporaryId>(); |
101 | 104 |
102 JS.Identifier _extensionSymbolsModule; | 105 JS.Identifier _extensionSymbolsModule; |
103 JS.Identifier _runtimeModule; | 106 JS.Identifier _runtimeModule; |
104 final namedArgumentTemp = new JS.TemporaryId('opts'); | 107 final namedArgumentTemp = new JS.TemporaryId('opts'); |
105 | 108 |
106 final _hasDeferredSupertype = new HashSet<ClassElement>(); | 109 final _hasDeferredSupertype = new HashSet<ClassElement>(); |
107 | 110 |
108 final _eagerTopLevelFields = new HashSet<Element>.identity(); | |
109 | |
110 /// The type provider from the current Analysis [context]. | 111 /// The type provider from the current Analysis [context]. |
111 final TypeProvider types; | 112 final TypeProvider types; |
112 | 113 |
113 final LibraryElement dartCoreLibrary; | 114 final LibraryElement dartCoreLibrary; |
114 final LibraryElement dartJSLibrary; | 115 final LibraryElement dartJSLibrary; |
115 | 116 |
116 /// The dart:async `StreamIterator<>` type. | 117 /// The dart:async `StreamIterator<>` type. |
117 final InterfaceType _asyncStreamIterator; | 118 final InterfaceType _asyncStreamIterator; |
118 | 119 |
119 /// The dart:_interceptors JSArray element. | 120 /// The dart:_interceptors JSArray element. |
120 final ClassElement _jsArray; | 121 final ClassElement _jsArray; |
121 | 122 |
122 final ClassElement boolClass; | 123 final ClassElement boolClass; |
123 final ClassElement intClass; | 124 final ClassElement intClass; |
124 final ClassElement interceptorClass; | 125 final ClassElement interceptorClass; |
125 final ClassElement nullClass; | 126 final ClassElement nullClass; |
126 final ClassElement numClass; | 127 final ClassElement numClass; |
127 final ClassElement objectClass; | 128 final ClassElement objectClass; |
128 final ClassElement stringClass; | 129 final ClassElement stringClass; |
129 final ClassElement functionClass; | 130 final ClassElement functionClass; |
130 final ClassElement privateSymbolClass; | 131 final ClassElement privateSymbolClass; |
131 | 132 |
132 ConstFieldVisitor _constants; | 133 ConstFieldVisitor _constants; |
133 | 134 |
134 /// The current function body being compiled. | 135 /// The current function body being compiled. |
135 FunctionBody _currentFunction; | 136 FunctionBody _currentFunction; |
136 | 137 |
137 /// Helper class for emitting elements in the proper order to allow | 138 HashMap<TypeDefiningElement, AstNode> _declarationNodes; |
138 /// JS to load the module. | 139 |
139 ElementLoader _loader; | 140 /// The stack of currently emitting elements, if generating top-level code |
141 /// for them. This is not used when inside method bodies, because order does | |
142 /// not matter for those. | |
143 final _topLevelElements = <TypeDefiningElement>[]; | |
144 | |
145 /// The current element being loaded. | |
146 /// We can use this to determine if we're loading top-level code or not: | |
147 /// | |
148 /// _currentElements.last == _topLevelElements.last | |
149 // | |
150 // TODO(jmesserly): ideally we'd only track types here, in other words, | |
151 // TypeDefiningElement. However we still rely on this for [currentLibrary] so | |
152 // we need something to be pushed always. | |
153 final _currentElements = <Element>[]; | |
154 | |
155 final _deferredProperties = new HashMap<PropertyAccessorElement, JS.Method>(); | |
140 | 156 |
141 BuildUnit _buildUnit; | 157 BuildUnit _buildUnit; |
142 | 158 |
143 String _libraryRoot; | 159 String _libraryRoot; |
144 | 160 |
145 bool _superAllowed = true; | 161 bool _superAllowed = true; |
146 | 162 |
147 List<JS.TemporaryId> _superHelperSymbols = <JS.TemporaryId>[]; | 163 List<JS.TemporaryId> _superHelperSymbols = <JS.TemporaryId>[]; |
148 List<JS.Method> _superHelpers = <JS.Method>[]; | 164 List<JS.Method> _superHelpers = <JS.Method>[]; |
149 | 165 |
(...skipping 25 matching lines...) Expand all Loading... | |
175 intClass = _getLibrary(c, 'dart:core').getType('int'), | 191 intClass = _getLibrary(c, 'dart:core').getType('int'), |
176 numClass = _getLibrary(c, 'dart:core').getType('num'), | 192 numClass = _getLibrary(c, 'dart:core').getType('num'), |
177 nullClass = _getLibrary(c, 'dart:core').getType('Null'), | 193 nullClass = _getLibrary(c, 'dart:core').getType('Null'), |
178 objectClass = _getLibrary(c, 'dart:core').getType('Object'), | 194 objectClass = _getLibrary(c, 'dart:core').getType('Object'), |
179 stringClass = _getLibrary(c, 'dart:core').getType('String'), | 195 stringClass = _getLibrary(c, 'dart:core').getType('String'), |
180 functionClass = _getLibrary(c, 'dart:core').getType('Function'), | 196 functionClass = _getLibrary(c, 'dart:core').getType('Function'), |
181 privateSymbolClass = | 197 privateSymbolClass = |
182 _getLibrary(c, 'dart:_internal').getType('PrivateSymbol'), | 198 _getLibrary(c, 'dart:_internal').getType('PrivateSymbol'), |
183 dartJSLibrary = _getLibrary(c, 'dart:js'); | 199 dartJSLibrary = _getLibrary(c, 'dart:js'); |
184 | 200 |
185 LibraryElement get currentLibrary => _loader.currentElement.library; | 201 Element get currentElement => _currentElements.last; |
202 | |
203 LibraryElement get currentLibrary => currentElement.library; | |
186 | 204 |
187 /// The main entry point to JavaScript code generation. | 205 /// The main entry point to JavaScript code generation. |
188 /// | 206 /// |
189 /// Takes the metadata for the build unit, as well as resolved trees and | 207 /// Takes the metadata for the build unit, as well as resolved trees and |
190 /// errors, and computes the output module code and optionally the source map. | 208 /// errors, and computes the output module code and optionally the source map. |
191 JSModuleFile compile(BuildUnit unit, List<CompilationUnit> compilationUnits, | 209 JSModuleFile compile(BuildUnit unit, List<CompilationUnit> compilationUnits, |
192 List<String> errors) { | 210 List<String> errors) { |
193 _buildUnit = unit; | 211 _buildUnit = unit; |
194 _libraryRoot = _buildUnit.libraryRoot; | 212 _libraryRoot = _buildUnit.libraryRoot; |
195 if (!_libraryRoot.endsWith(separator)) { | 213 if (!_libraryRoot.endsWith(separator)) { |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
278 js.call('const # = Object.create(null)', [libraryTemp]))); | 296 js.call('const # = Object.create(null)', [libraryTemp]))); |
279 | 297 |
280 // dart:_runtime has a magic module that holds extension method symbols. | 298 // dart:_runtime has a magic module that holds extension method symbols. |
281 // TODO(jmesserly): find a cleaner design for this. | 299 // TODO(jmesserly): find a cleaner design for this. |
282 if (isSdkInternalRuntime(library)) { | 300 if (isSdkInternalRuntime(library)) { |
283 items.add(new JS.ExportDeclaration(js | 301 items.add(new JS.ExportDeclaration(js |
284 .call('const # = Object.create(null)', [_extensionSymbolsModule]))); | 302 .call('const # = Object.create(null)', [_extensionSymbolsModule]))); |
285 } | 303 } |
286 } | 304 } |
287 | 305 |
288 // Collect all Element -> Node mappings, in case we need to forward declare | 306 // Collect all class/type Element -> Node mappings |
289 // any nodes. | 307 // in case we need to forward declare any classes. |
290 var nodes = new HashMap<Element, AstNode>.identity(); | 308 _declarationNodes = new HashMap<TypeDefiningElement, AstNode>.identity(); |
291 var sdkBootstrappingFns = new List<FunctionElement>(); | |
292 for (var unit in compilationUnits) { | 309 for (var unit in compilationUnits) { |
293 if (isSdkInternalRuntime( | 310 for (var declaration in unit.declarations) { |
294 resolutionMap.elementDeclaredByCompilationUnit(unit).library)) { | 311 var element = declaration.element; |
295 sdkBootstrappingFns.addAll( | 312 if (element is TypeDefiningElement) { |
296 resolutionMap.elementDeclaredByCompilationUnit(unit).functions); | 313 _declarationNodes[element] = declaration; |
314 } | |
297 } | 315 } |
298 _collectElements(unit, nodes); | |
299 } | 316 } |
300 _loader = new ElementLoader(nodes); | |
301 if (compilationUnits.isNotEmpty) { | 317 if (compilationUnits.isNotEmpty) { |
302 _constants = new ConstFieldVisitor(context, | 318 _constants = new ConstFieldVisitor(context, |
303 dummySource: resolutionMap | 319 dummySource: resolutionMap |
304 .elementDeclaredByCompilationUnit(compilationUnits.first) | 320 .elementDeclaredByCompilationUnit(compilationUnits.first) |
305 .source); | 321 .source); |
306 } | 322 } |
307 | 323 |
308 // Add implicit dart:core dependency so it is first. | 324 // Add implicit dart:core dependency so it is first. |
309 emitLibraryName(dartCoreLibrary); | 325 emitLibraryName(dartCoreLibrary); |
310 | 326 |
311 // Emit SDK bootstrapping functions first, if any. | |
312 sdkBootstrappingFns.forEach(_emitDeclaration); | |
313 | |
314 // Visit each compilation unit and emit its code. | 327 // Visit each compilation unit and emit its code. |
315 // | 328 // |
316 // NOTE: declarations are not necessarily emitted in this order. | 329 // NOTE: declarations are not necessarily emitted in this order. |
317 // Order will be changed as needed so the resulting code can execute. | 330 // Order will be changed as needed so the resulting code can execute. |
318 // This is done by forward declaring items. | 331 // This is done by forward declaring items. |
319 compilationUnits.forEach(_finishDeclarationsInUnit); | 332 compilationUnits.forEach(_emitCompilationUnit); |
333 assert(_deferredProperties.isEmpty); | |
334 | |
335 // Visit directives (for exports) | |
336 compilationUnits.forEach(_emitExportDirectives); | |
320 | 337 |
321 // Declare imports | 338 // Declare imports |
322 _finishImports(items); | 339 _finishImports(items); |
323 | 340 |
324 // Discharge the type table cache variables and | 341 // Discharge the type table cache variables and |
325 // hoisted definitions. | 342 // hoisted definitions. |
326 items.addAll(_typeTable.discharge()); | 343 items.addAll(_typeTable.discharge()); |
344 items.addAll(_internalSdkFunctions); | |
327 | 345 |
328 // Track the module name for each library in the module. | 346 // Track the module name for each library in the module. |
329 // This data is only required for debugging. | 347 // This data is only required for debugging. |
330 _moduleItems.add(js.statement( | 348 _moduleItems.add(js.statement( |
331 '#.trackLibraries(#, #, ${JSModuleFile.sourceMapHoleID});', | 349 '#.trackLibraries(#, #, ${JSModuleFile.sourceMapHoleID});', |
332 [_runtimeModule, js.string(name), _librariesDebuggerObject()])); | 350 [_runtimeModule, js.string(name), _librariesDebuggerObject()])); |
333 | 351 |
334 // Add the module's code (produced by visiting compilation units, above) | 352 // Add the module's code (produced by visiting compilation units, above) |
335 _copyAndFlattenBlocks(items, _moduleItems); | 353 _copyAndFlattenBlocks(items, _moduleItems); |
336 | 354 |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
425 } | 443 } |
426 | 444 |
427 /// Flattens blocks in [items] to a single list. | 445 /// Flattens blocks in [items] to a single list. |
428 /// | 446 /// |
429 /// This will not flatten blocks that are marked as being scopes. | 447 /// This will not flatten blocks that are marked as being scopes. |
430 void _copyAndFlattenBlocks( | 448 void _copyAndFlattenBlocks( |
431 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { | 449 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { |
432 for (var item in items) { | 450 for (var item in items) { |
433 if (item is JS.Block && !item.isScope) { | 451 if (item is JS.Block && !item.isScope) { |
434 _copyAndFlattenBlocks(result, item.statements); | 452 _copyAndFlattenBlocks(result, item.statements); |
435 } else { | 453 } else if (item != null) { |
436 result.add(item); | 454 result.add(item); |
437 } | 455 } |
438 } | 456 } |
439 } | 457 } |
440 | 458 |
441 String _libraryToModule(LibraryElement library) { | 459 String _libraryToModule(LibraryElement library) { |
442 assert(!_libraries.containsKey(library)); | 460 assert(!_libraries.containsKey(library)); |
443 var source = library.source; | 461 var source = library.source; |
444 // TODO(jmesserly): we need to split out HTML. | 462 // TODO(jmesserly): we need to split out HTML. |
445 if (source.uri.scheme == 'dart') { | 463 if (source.uri.scheme == 'dart') { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
478 if (module == coreModuleName) { | 496 if (module == coreModuleName) { |
479 imports.add(new JS.NameSpecifier(_runtimeModule)); | 497 imports.add(new JS.NameSpecifier(_runtimeModule)); |
480 imports.add(new JS.NameSpecifier(_extensionSymbolsModule)); | 498 imports.add(new JS.NameSpecifier(_extensionSymbolsModule)); |
481 } | 499 } |
482 | 500 |
483 items.add(new JS.ImportDeclaration( | 501 items.add(new JS.ImportDeclaration( |
484 namedImports: imports, from: js.string(module, "'"))); | 502 namedImports: imports, from: js.string(module, "'"))); |
485 }); | 503 }); |
486 } | 504 } |
487 | 505 |
488 /// Collect toplevel elements and nodes we need to emit, and returns | |
489 /// an ordered map of these. | |
490 static void _collectElements( | |
491 CompilationUnit unit, Map<Element, AstNode> map) { | |
492 for (var declaration in unit.declarations) { | |
493 if (declaration is TopLevelVariableDeclaration) { | |
494 for (var field in declaration.variables.variables) { | |
495 map[field.element] = field; | |
496 } | |
497 } else { | |
498 map[declaration.element] = declaration; | |
499 } | |
500 } | |
501 } | |
502 | |
503 /// Called to emit all top-level declarations. | 506 /// Called to emit all top-level declarations. |
504 /// | 507 /// |
505 /// During the course of emitting one item, we may emit another. For example | 508 /// During the course of emitting one item, we may emit another. For example |
506 /// | 509 /// |
507 /// class D extends B { C m() { ... } } | 510 /// class D extends B { C m() { ... } } |
508 /// | 511 /// |
509 /// Because D depends on B, we'll emit B first if needed. However C is not | 512 /// Because D depends on B, we'll emit B first if needed. However C is not |
510 /// used by top-level JavaScript code, so we can ignore that dependency. | 513 /// used by top-level JavaScript code, so we can ignore that dependency. |
511 void _emitDeclaration(Element e) { | 514 void _emitTypeDeclaration(TypeDefiningElement e) { |
512 var item = _loader.emitDeclaration(e, (AstNode node) { | 515 var node = _declarationNodes.remove(e); |
513 // TODO(jmesserly): this is not really the right place for this. | 516 if (node == null) return null; // not from this module or already loaded. |
514 // Ideally we do this per function body. | |
515 // | |
516 // We'll need to be consistent about when we're generating functions, and | |
517 // only run this on the outermost function, and not any closures. | |
518 inferNullableTypes(node); | |
519 return _visit(node) as JS.Node; | |
520 }); | |
521 | 517 |
522 if (item != null) _moduleItems.add(item); | 518 _currentElements.add(e); |
519 | |
520 // TODO(jmesserly): this is not really the right place for this. | |
521 // Ideally we do this per function body. | |
522 // | |
523 // We'll need to be consistent about when we're generating functions, and | |
524 // only run this on the outermost function, and not any closures. | |
525 inferNullableTypes(node); | |
526 | |
527 _moduleItems.add(_visit(node)); | |
528 | |
529 var last = _currentElements.removeLast(); | |
530 assert(identical(e, last)); | |
523 } | 531 } |
524 | 532 |
525 void _declareBeforeUse(Element e) { | 533 /// Start generating top-level code for the element [e]. |
526 _loader.declareBeforeUse(e, _emitDeclaration); | 534 /// |
535 /// Subsequent [emitDeclaration] calls will cause those elements to be | |
536 /// generated before this one, until [finishTopLevel] is called. | |
537 void _startTopLevelCodeForClass(TypeDefiningElement e) { | |
538 assert(identical(e, currentElement)); | |
539 _topLevelElements.add(e); | |
vsm
2017/04/04 22:58:43
Is it worth asserting that it's not there already?
Jennifer Messerly
2017/04/04 23:18:34
I think it's ~probably okay if we skip that assert
| |
527 } | 540 } |
528 | 541 |
529 void _finishDeclarationsInUnit(CompilationUnit unit) { | 542 /// Finishes the top-level code for the element [e]. |
543 void _finishTopLevelCodeForClass(TypeDefiningElement e) { | |
544 var last = _topLevelElements.removeLast(); | |
545 assert(identical(e, last)); | |
546 } | |
547 | |
548 /// To emit top-level module items, we sometimes need to reorder them. | |
549 /// | |
550 /// This function takes care of that, and also detects cases where reordering | |
551 /// failed, and we need to resort to lazy loading, by marking the element as | |
552 /// lazy. All elements need to be aware of this possibility and generate code | |
553 /// accordingly. | |
554 /// | |
555 /// If we are not emitting top-level code, this does nothing, because all | |
556 /// declarations are assumed to be available before we start execution. | |
557 /// See [startTopLevel]. | |
558 void _declareBeforeUse(TypeDefiningElement e) { | |
559 if (e == null) return; | |
560 | |
561 var topLevel = _topLevelElements; | |
562 if (topLevel.isNotEmpty && identical(currentElement, topLevel.last)) { | |
563 // If the item is from our library, try to emit it now. | |
564 _emitTypeDeclaration(e); | |
565 } | |
566 } | |
567 | |
568 void _emitCompilationUnit(CompilationUnit unit) { | |
530 // NOTE: this method isn't the right place to initialize | 569 // NOTE: this method isn't the right place to initialize |
531 // per-compilation-unit state. Declarations can be visited out of order, | 570 // per-compilation-unit state. Declarations can be visited out of order, |
532 // this is only to catch things that haven't been emitted yet. | 571 // this is only to catch things that haven't been emitted yet. |
533 // | 572 // |
534 // See _emitDeclaration. | 573 // See _emitTypeDeclaration. |
574 var library = unit.element.library; | |
575 bool internalSdk = isSdkInternalRuntime(library); | |
576 _currentElements.add(library); | |
577 | |
578 List<VariableDeclaration> fields; | |
535 for (var declaration in unit.declarations) { | 579 for (var declaration in unit.declarations) { |
580 if (declaration is TopLevelVariableDeclaration) { | |
581 inferNullableTypes(declaration); | |
582 if (internalSdk && declaration.variables.isFinal) { | |
583 _emitInternalSdkFields(declaration.variables.variables); | |
584 } else { | |
585 (fields ??= []).addAll(declaration.variables.variables); | |
586 } | |
587 continue; | |
588 } | |
589 | |
590 if (fields != null) { | |
591 _emitTopLevelFields(fields); | |
592 fields = null; | |
593 } | |
594 | |
536 var element = declaration.element; | 595 var element = declaration.element; |
537 if (element != null) { | 596 if (element is TypeDefiningElement) { |
538 _emitDeclaration(element); | 597 _emitTypeDeclaration(element); |
598 continue; | |
599 } | |
600 | |
601 inferNullableTypes(declaration); | |
602 var item = _visit(declaration); | |
603 if (internalSdk && element is FunctionElement) { | |
604 _internalSdkFunctions.add(item); | |
539 } else { | 605 } else { |
540 declaration.accept(this); | 606 _moduleItems.add(item); |
541 } | 607 } |
542 } | 608 } |
609 | |
610 if (fields != null) _emitTopLevelFields(fields); | |
611 | |
612 _currentElements.removeLast(); | |
613 } | |
614 | |
615 void _emitExportDirectives(CompilationUnit unit) { | |
543 for (var directive in unit.directives) { | 616 for (var directive in unit.directives) { |
617 _currentElements.add(directive.element); | |
544 directive.accept(this); | 618 directive.accept(this); |
619 _currentElements.removeLast(); | |
545 } | 620 } |
546 } | 621 } |
547 | 622 |
548 @override | 623 @override |
549 void visitLibraryDirective(LibraryDirective node) {} | 624 void visitLibraryDirective(LibraryDirective node) {} |
550 | 625 |
551 @override | 626 @override |
552 void visitImportDirective(ImportDirective node) { | 627 void visitImportDirective(ImportDirective node) { |
553 // We don't handle imports here. | 628 // We don't handle imports here. |
554 // | 629 // |
(...skipping 13 matching lines...) Expand all Loading... | |
568 | 643 |
569 @override | 644 @override |
570 void visitExportDirective(ExportDirective node) { | 645 void visitExportDirective(ExportDirective node) { |
571 ExportElement element = node.element; | 646 ExportElement element = node.element; |
572 var currentLibrary = element.library; | 647 var currentLibrary = element.library; |
573 | 648 |
574 var currentNames = currentLibrary.publicNamespace.definedNames; | 649 var currentNames = currentLibrary.publicNamespace.definedNames; |
575 var exportedNames = | 650 var exportedNames = |
576 new NamespaceBuilder().createExportNamespaceForDirective(element); | 651 new NamespaceBuilder().createExportNamespaceForDirective(element); |
577 | 652 |
578 var libraryName = emitLibraryName(currentLibrary); | 653 // We only need to export main as it is the only method part of the |
579 | |
580 // TODO(jmesserly): we could collect all of the names for bulk re-export, | |
581 // but this is easier to implement for now. | |
582 void emitExport(Element export, {String suffix: ''}) { | |
583 var name = _emitTopLevelName(export, suffix: suffix); | |
584 | |
585 if (export is TypeDefiningElement || | |
586 export is FunctionElement || | |
587 _eagerTopLevelFields.contains(export)) { | |
588 // classes, typedefs, functions, and eager init fields can be assigned | |
589 // directly. | |
590 // TODO(jmesserly): we don't know about eager init fields from other | |
591 // modules we import, so we will never go down this code path for them. | |
592 _moduleItems | |
593 .add(js.statement('#.# = #;', [libraryName, name.selector, name])); | |
594 } | |
595 } | |
596 | |
597 // We only need to export main as it is the only method party of the | |
598 // publicly exposed JS API for a library. | 654 // publicly exposed JS API for a library. |
599 // TODO(jacobr): add a library level annotation indicating that all | 655 // TODO(jacobr): add a library level annotation indicating that all |
600 // contents of a library need to be exposed to JS. | 656 // contents of a library need to be exposed to JS. |
601 // https://github.com/dart-lang/sdk/issues/26368 | 657 // https://github.com/dart-lang/sdk/issues/26368 |
602 | |
603 var export = exportedNames.get('main'); | 658 var export = exportedNames.get('main'); |
604 | 659 |
605 if (export == null) return; | 660 if (export is FunctionElement) { |
606 if (export is PropertyAccessorElement) { | 661 // Don't allow redefining names from this library. |
607 export = (export as PropertyAccessorElement).variable; | 662 if (currentNames.containsKey(export.name)) return; |
663 | |
664 var name = _emitTopLevelName(export); | |
665 _moduleItems.add(js.statement( | |
666 '#.# = #;', [emitLibraryName(currentLibrary), name.selector, name])); | |
608 } | 667 } |
609 | |
610 // Don't allow redefining names from this library. | |
611 if (currentNames.containsKey(export.name)) return; | |
612 | |
613 if (export.isSynthetic && export is PropertyInducingElement) { | |
614 _emitDeclaration(export.getter); | |
615 _emitDeclaration(export.setter); | |
616 } else { | |
617 _emitDeclaration(export); | |
618 } | |
619 emitExport(export); | |
620 } | 668 } |
621 | 669 |
622 @override | 670 @override |
623 visitAsExpression(AsExpression node) { | 671 visitAsExpression(AsExpression node) { |
624 Expression fromExpr = node.expression; | 672 Expression fromExpr = node.expression; |
625 var from = getStaticType(fromExpr); | 673 var from = getStaticType(fromExpr); |
626 var to = node.type.type; | 674 var to = node.type.type; |
627 | 675 |
628 JS.Expression jsFrom = _visit(fromExpr); | 676 JS.Expression jsFrom = _visit(fromExpr); |
629 if (_inWhitelistCode(node)) return jsFrom; | 677 if (_inWhitelistCode(node)) return jsFrom; |
(...skipping 676 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1306 var jsFields = fields?.map(_emitTypeScriptField)?.toList(); | 1354 var jsFields = fields?.map(_emitTypeScriptField)?.toList(); |
1307 | 1355 |
1308 return new JS.ClassExpression(new JS.Identifier(name), heritage, methods, | 1356 return new JS.ClassExpression(new JS.Identifier(name), heritage, methods, |
1309 typeParams: typeParams, fields: jsFields); | 1357 typeParams: typeParams, fields: jsFields); |
1310 } | 1358 } |
1311 | 1359 |
1312 JS.Expression _emitClassHeritage(ClassElement element) { | 1360 JS.Expression _emitClassHeritage(ClassElement element) { |
1313 var type = element.type; | 1361 var type = element.type; |
1314 if (type.isObject) return null; | 1362 if (type.isObject) return null; |
1315 | 1363 |
1316 _loader.startTopLevel(element); | 1364 _startTopLevelCodeForClass(element); |
1317 | 1365 |
1318 // List of "direct" supertypes (supertype + mixins) | 1366 // List of "direct" supertypes (supertype + mixins) |
1319 var basetypes = [type.superclass]..addAll(type.mixins); | 1367 var basetypes = [type.superclass]..addAll(type.mixins); |
1320 | 1368 |
1321 // If any of these are recursive (via type parameter), defer setting | 1369 // If any of these are recursive (via type parameter), defer setting |
1322 // the real superclass. | 1370 // the real superclass. |
1323 if (basetypes.any((t) => _deferIfNeeded(t, element))) { | 1371 if (basetypes.any((t) => _deferIfNeeded(t, element))) { |
1324 // Fall back to raw type | 1372 // Fall back to raw type |
1325 basetypes = | 1373 basetypes = |
1326 basetypes.map((t) => fillDynamicTypeArgs(t.element.type)).toList(); | 1374 basetypes.map((t) => fillDynamicTypeArgs(t.element.type)).toList(); |
1327 _hasDeferredSupertype.add(element); | 1375 _hasDeferredSupertype.add(element); |
1328 } | 1376 } |
1329 | 1377 |
1330 // List of "direct" JS superclasses | 1378 // List of "direct" JS superclasses |
1331 var baseclasses = basetypes | 1379 var baseclasses = basetypes |
1332 .map((t) => _emitConstructorAccess(t, nameType: false)) | 1380 .map((t) => _emitConstructorAccess(t, nameType: false)) |
1333 .toList(); | 1381 .toList(); |
1334 assert(baseclasses.isNotEmpty); | 1382 assert(baseclasses.isNotEmpty); |
1335 var heritage = (baseclasses.length == 1) | 1383 var heritage = (baseclasses.length == 1) |
1336 ? baseclasses.first | 1384 ? baseclasses.first |
1337 : _callHelper('mixin(#)', [baseclasses]); | 1385 : _callHelper('mixin(#)', [baseclasses]); |
1338 | 1386 |
1339 _loader.finishTopLevel(element); | 1387 _finishTopLevelCodeForClass(element); |
1340 | 1388 |
1341 return heritage; | 1389 return heritage; |
1342 } | 1390 } |
1343 | 1391 |
1344 /// Provide Dart getters and setters that forward to the underlying native | 1392 /// Provide Dart getters and setters that forward to the underlying native |
1345 /// field. Note that the Dart names are always symbolized to avoid | 1393 /// field. Note that the Dart names are always symbolized to avoid |
1346 /// conflicts. They will be installed as extension methods on the underlying | 1394 /// conflicts. They will be installed as extension methods on the underlying |
1347 /// native type. | 1395 /// native type. |
1348 List<JS.Method> _emitNativeFieldAccessors(FieldDeclaration node) { | 1396 List<JS.Method> _emitNativeFieldAccessors(FieldDeclaration node) { |
1349 // TODO(vsm): Can this by meta-programmed? | 1397 // TODO(vsm): Can this by meta-programmed? |
(...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1785 | 1833 |
1786 body.add(_callHelperStatement(code, args)); | 1834 body.add(_callHelperStatement(code, args)); |
1787 } | 1835 } |
1788 } | 1836 } |
1789 } | 1837 } |
1790 | 1838 |
1791 /// Emits static fields for a class, and initialize them eagerly if possible, | 1839 /// Emits static fields for a class, and initialize them eagerly if possible, |
1792 /// otherwise define them as lazy properties. | 1840 /// otherwise define them as lazy properties. |
1793 void _emitStaticFields(List<FieldDeclaration> staticFields, | 1841 void _emitStaticFields(List<FieldDeclaration> staticFields, |
1794 ClassElement classElem, List<JS.Statement> body) { | 1842 ClassElement classElem, List<JS.Statement> body) { |
1795 var lazyStatics = <VariableDeclaration>[]; | 1843 var lazyStatics = staticFields.expand((f) => f.fields.variables).toList(); |
1796 for (FieldDeclaration member in staticFields) { | |
1797 for (VariableDeclaration field in member.fields.variables) { | |
1798 JS.Statement eagerField = _emitConstantStaticField(classElem, field); | |
1799 if (eagerField != null) { | |
1800 body.add(eagerField); | |
1801 } else { | |
1802 lazyStatics.add(field); | |
1803 } | |
1804 } | |
1805 } | |
1806 if (lazyStatics.isNotEmpty) { | 1844 if (lazyStatics.isNotEmpty) { |
1807 body.add(_emitLazyFields(classElem, lazyStatics)); | 1845 body.add(_emitLazyFields(classElem, lazyStatics)); |
1808 } | 1846 } |
1809 } | 1847 } |
1810 | 1848 |
1811 void _emitClassMetadata(List<Annotation> metadata, JS.Expression className, | 1849 void _emitClassMetadata(List<Annotation> metadata, JS.Expression className, |
1812 List<JS.Statement> body) { | 1850 List<JS.Statement> body) { |
1813 // Metadata | 1851 // Metadata |
1814 if (options.emitMetadata && metadata.isNotEmpty) { | 1852 if (options.emitMetadata && metadata.isNotEmpty) { |
1815 body.add(js.statement('#[#.metadata] = () => #;', [ | 1853 body.add(js.statement('#[#.metadata] = () => #;', [ |
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2126 | 2164 |
2127 var fun = new JS.Fun( | 2165 var fun = new JS.Fun( |
2128 params, | 2166 params, |
2129 js.statement('{ return $newKeyword #(#); }', | 2167 js.statement('{ return $newKeyword #(#); }', |
2130 [_visit(redirect) as JS.Node, params]), | 2168 [_visit(redirect) as JS.Node, params]), |
2131 returnType: returnType); | 2169 returnType: returnType); |
2132 return annotate( | 2170 return annotate( |
2133 new JS.Method(name, fun, isStatic: true), node, node.element); | 2171 new JS.Method(name, fun, isStatic: true), node, node.element); |
2134 } | 2172 } |
2135 | 2173 |
2136 // For const constructors we need to ensure default values are | |
2137 // available for use by top-level constant initializers. | |
2138 ClassDeclaration cls = node.parent; | |
2139 if (node.constKeyword != null) _loader.startTopLevel(cls.element); | |
2140 var params = visitFormalParameterList(node.parameters); | 2174 var params = visitFormalParameterList(node.parameters); |
2141 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); | |
2142 | 2175 |
2143 // Factory constructors are essentially static methods. | 2176 // Factory constructors are essentially static methods. |
2144 if (node.factoryKeyword != null) { | 2177 if (node.factoryKeyword != null) { |
2145 var body = <JS.Statement>[]; | 2178 var body = <JS.Statement>[]; |
2146 var init = _emitArgumentInitializers(node, constructor: true); | 2179 var init = _emitArgumentInitializers(node, constructor: true); |
2147 if (init != null) body.add(init); | 2180 if (init != null) body.add(init); |
2148 body.add(_visit(node.body)); | 2181 body.add(_visit(node.body)); |
2149 var fun = new JS.Fun(params, new JS.Block(body), returnType: returnType); | 2182 var fun = new JS.Fun(params, new JS.Block(body), returnType: returnType); |
2150 return annotate( | 2183 return annotate( |
2151 new JS.Method(name, fun, isStatic: true), node, node.element); | 2184 new JS.Method(name, fun, isStatic: true), node, node.element); |
(...skipping 28 matching lines...) Expand all Loading... | |
2180 List<FieldDeclaration> fields, | 2213 List<FieldDeclaration> fields, |
2181 Map<FieldElement, JS.TemporaryId> virtualFields) { | 2214 Map<FieldElement, JS.TemporaryId> virtualFields) { |
2182 var body = <JS.Statement>[]; | 2215 var body = <JS.Statement>[]; |
2183 ClassDeclaration cls = node.parent; | 2216 ClassDeclaration cls = node.parent; |
2184 | 2217 |
2185 // Generate optional/named argument value assignment. These can not have | 2218 // Generate optional/named argument value assignment. These can not have |
2186 // side effects, and may be used by the constructor's initializers, so it's | 2219 // side effects, and may be used by the constructor's initializers, so it's |
2187 // nice to do them first. | 2220 // nice to do them first. |
2188 // Also for const constructors we need to ensure default values are | 2221 // Also for const constructors we need to ensure default values are |
2189 // available for use by top-level constant initializers. | 2222 // available for use by top-level constant initializers. |
2190 if (node.constKeyword != null) _loader.startTopLevel(cls.element); | |
2191 var init = _emitArgumentInitializers(node, constructor: true); | 2223 var init = _emitArgumentInitializers(node, constructor: true); |
2192 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); | |
2193 if (init != null) body.add(init); | 2224 if (init != null) body.add(init); |
2194 | 2225 |
2195 // Redirecting constructors: these are not allowed to have initializers, | 2226 // Redirecting constructors: these are not allowed to have initializers, |
2196 // and the redirecting ctor invocation runs before field initializers. | 2227 // and the redirecting ctor invocation runs before field initializers. |
2197 var redirectCall = node.initializers.firstWhere( | 2228 var redirectCall = node.initializers.firstWhere( |
2198 (i) => i is RedirectingConstructorInvocation, | 2229 (i) => i is RedirectingConstructorInvocation, |
2199 orElse: () => null); | 2230 orElse: () => null); |
2200 | 2231 |
2201 if (redirectCall != null) { | 2232 if (redirectCall != null) { |
2202 body.add(_visit(redirectCall)); | 2233 body.add(_visit(redirectCall)); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2290 /// | 2321 /// |
2291 /// 1. field declaration initializer if non-const, | 2322 /// 1. field declaration initializer if non-const, |
2292 /// 2. field initializing parameters, | 2323 /// 2. field initializing parameters, |
2293 /// 3. constructor field initializers, | 2324 /// 3. constructor field initializers, |
2294 /// 4. initialize fields not covered in 1-3 | 2325 /// 4. initialize fields not covered in 1-3 |
2295 JS.Statement _initializeFields( | 2326 JS.Statement _initializeFields( |
2296 ClassDeclaration cls, | 2327 ClassDeclaration cls, |
2297 List<FieldDeclaration> fieldDecls, | 2328 List<FieldDeclaration> fieldDecls, |
2298 Map<FieldElement, JS.TemporaryId> virtualFields, | 2329 Map<FieldElement, JS.TemporaryId> virtualFields, |
2299 [ConstructorDeclaration ctor]) { | 2330 [ConstructorDeclaration ctor]) { |
2300 bool isConst = ctor != null && ctor.constKeyword != null; | |
2301 if (isConst) _loader.startTopLevel(cls.element); | |
2302 | |
2303 // Run field initializers if they can have side-effects. | 2331 // Run field initializers if they can have side-effects. |
2304 var fields = new Map<FieldElement, JS.Expression>(); | 2332 var fields = new Map<FieldElement, JS.Expression>(); |
2305 var unsetFields = new Map<FieldElement, VariableDeclaration>(); | 2333 var unsetFields = new Map<FieldElement, VariableDeclaration>(); |
2306 for (var declaration in fieldDecls) { | 2334 for (var declaration in fieldDecls) { |
2307 for (var fieldNode in declaration.fields.variables) { | 2335 for (var fieldNode in declaration.fields.variables) { |
2308 var element = fieldNode.element; | 2336 var element = fieldNode.element; |
2309 if (_constants.isFieldInitConstant(fieldNode)) { | 2337 if (_constants.isFieldInitConstant(fieldNode)) { |
2310 unsetFields[element as FieldElement] = fieldNode; | 2338 unsetFields[element as FieldElement] = fieldNode; |
2311 } else { | 2339 } else { |
2312 fields[element as FieldElement] = _visitInitializer(fieldNode); | 2340 fields[element as FieldElement] = _visitInitializer(fieldNode); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2344 } | 2372 } |
2345 fields[element] = value; | 2373 fields[element] = value; |
2346 }); | 2374 }); |
2347 | 2375 |
2348 var body = <JS.Statement>[]; | 2376 var body = <JS.Statement>[]; |
2349 fields.forEach((FieldElement e, JS.Expression initialValue) { | 2377 fields.forEach((FieldElement e, JS.Expression initialValue) { |
2350 JS.Expression access = virtualFields[e] ?? _declareMemberName(e.getter); | 2378 JS.Expression access = virtualFields[e] ?? _declareMemberName(e.getter); |
2351 body.add(js.statement('this.# = #;', [access, initialValue])); | 2379 body.add(js.statement('this.# = #;', [access, initialValue])); |
2352 }); | 2380 }); |
2353 | 2381 |
2354 if (isConst) _loader.finishTopLevel(cls.element); | |
2355 return _statement(body); | 2382 return _statement(body); |
2356 } | 2383 } |
2357 | 2384 |
2358 FormalParameterList _parametersOf(node) { | 2385 FormalParameterList _parametersOf(node) { |
2359 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we | 2386 // TODO(jmesserly): clean this up. If we can model ES6 spread/rest args, we |
2360 // could handle argument initializers more consistently in a separate | 2387 // could handle argument initializers more consistently in a separate |
2361 // lowering pass. | 2388 // lowering pass. |
2362 if (node is ConstructorDeclaration) return node.parameters; | 2389 if (node is ConstructorDeclaration) return node.parameters; |
2363 if (node is MethodDeclaration) return node.parameters; | 2390 if (node is MethodDeclaration) return node.parameters; |
2364 if (node is FunctionDeclaration) node = node.functionExpression; | 2391 if (node is FunctionDeclaration) node = node.functionExpression; |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2513 typeParams: fn.typeParams, | 2540 typeParams: fn.typeParams, |
2514 returnType: fn.returnType)..sourceInformation = fn.sourceInformation; | 2541 returnType: fn.returnType)..sourceInformation = fn.sourceInformation; |
2515 } | 2542 } |
2516 | 2543 |
2517 @override | 2544 @override |
2518 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 2545 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
2519 assert(node.parent is CompilationUnit); | 2546 assert(node.parent is CompilationUnit); |
2520 | 2547 |
2521 if (_externalOrNative(node)) return null; | 2548 if (_externalOrNative(node)) return null; |
2522 | 2549 |
2523 // If we have a getter/setter pair, they need to be defined together. | 2550 if (node.isGetter || node.isSetter) { |
2524 if (node.isGetter) { | |
2525 PropertyAccessorElement element = node.element; | 2551 PropertyAccessorElement element = node.element; |
2526 var props = <JS.Method>[_emitTopLevelProperty(node)]; | 2552 var pairAccessor = node.isGetter |
2527 var setter = element.correspondingSetter; | 2553 ? element.correspondingSetter |
2528 if (setter != null) { | 2554 : element.correspondingGetter; |
2529 props.add(_loader.emitDeclaration( | 2555 |
2530 setter, (node) => _emitTopLevelProperty(node))); | 2556 var jsCode = _emitTopLevelProperty(node); |
2557 var props = <JS.Method>[jsCode]; | |
2558 if (pairAccessor != null) { | |
2559 // If we have a getter/setter pair, they need to be defined together. | |
2560 // If this is the first one, save the generated code for later. | |
2561 // If this is the second one, get the saved code and emit both. | |
2562 var pairCode = _deferredProperties.remove(pairAccessor); | |
2563 if (pairCode == null) { | |
2564 _deferredProperties[element] = jsCode; | |
2565 return null; | |
2566 } | |
2567 props.add(pairCode); | |
2531 } | 2568 } |
2532 return _callHelperStatement('copyProperties(#, { # });', | 2569 return _callHelperStatement('copyProperties(#, { # });', |
2533 [emitLibraryName(currentLibrary), props]); | 2570 [emitLibraryName(currentLibrary), props]); |
2534 } | |
2535 if (node.isSetter) { | |
2536 PropertyAccessorElement element = node.element; | |
2537 var props = <JS.Method>[_emitTopLevelProperty(node)]; | |
2538 var getter = element.correspondingGetter; | |
2539 if (getter != null) { | |
2540 props.add(_loader.emitDeclaration( | |
2541 getter, (node) => _emitTopLevelProperty(node))); | |
2542 } | |
2543 return _callHelperStatement('copyProperties(#, { # });', | |
2544 [emitLibraryName(currentLibrary), props]); | |
2545 } | 2571 } |
2546 | 2572 |
2547 var body = <JS.Statement>[]; | 2573 var body = <JS.Statement>[]; |
2548 var fn = _emitFunction(node.functionExpression); | 2574 var fn = _emitFunction(node.functionExpression); |
2549 | 2575 |
2550 if (currentLibrary.source.isInSystemLibrary && | 2576 if (currentLibrary.source.isInSystemLibrary && |
2551 _isInlineJSFunction(node.functionExpression)) { | 2577 _isInlineJSFunction(node.functionExpression)) { |
2552 fn = _simplifyPassThroughArrowFunCallBody(fn); | 2578 fn = _simplifyPassThroughArrowFunCallBody(fn); |
2553 } | 2579 } |
2554 | 2580 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2624 if (type is FunctionType && (type.name == '' || type.name == null)) { | 2650 if (type is FunctionType && (type.name == '' || type.name == null)) { |
2625 return (_typeIsLoaded(type.returnType) && | 2651 return (_typeIsLoaded(type.returnType) && |
2626 type.optionalParameterTypes.every(_typeIsLoaded) && | 2652 type.optionalParameterTypes.every(_typeIsLoaded) && |
2627 type.namedParameterTypes.values.every(_typeIsLoaded) && | 2653 type.namedParameterTypes.values.every(_typeIsLoaded) && |
2628 type.normalParameterTypes.every(_typeIsLoaded)); | 2654 type.normalParameterTypes.every(_typeIsLoaded)); |
2629 } | 2655 } |
2630 if (type.isDynamic || type.isVoid || type.isBottom) return true; | 2656 if (type.isDynamic || type.isVoid || type.isBottom) return true; |
2631 if (type is ParameterizedType && !type.typeArguments.every(_typeIsLoaded)) { | 2657 if (type is ParameterizedType && !type.typeArguments.every(_typeIsLoaded)) { |
2632 return false; | 2658 return false; |
2633 } | 2659 } |
2634 return _loader.isLoaded(type.element); | 2660 return !_declarationNodes.containsKey(type.element); |
2635 } | 2661 } |
2636 | 2662 |
2637 JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, | 2663 JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, |
2638 {topLevel: false}) { | 2664 {topLevel: false}) { |
2639 var lazy = topLevel && !_typeIsLoaded(type); | 2665 var lazy = topLevel && !_typeIsLoaded(type); |
2640 var typeRep = _emitFunctionType(type, definite: true); | 2666 var typeRep = _emitFunctionType(type, definite: true); |
2641 if (lazy) { | 2667 if (lazy) { |
2642 return _callHelper('lazyFn(#, () => #)', [fn, typeRep]); | 2668 return _callHelper('lazyFn(#, () => #)', [fn, typeRep]); |
2643 } else { | 2669 } else { |
2644 return _callHelper('fn(#, #)', [fn, typeRep]); | 2670 return _callHelper('fn(#, #)', [fn, typeRep]); |
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2845 if (accessor == null) { | 2871 if (accessor == null) { |
2846 return js.commentExpression( | 2872 return js.commentExpression( |
2847 'Unimplemented unknown name', new JS.Identifier(node.name)); | 2873 'Unimplemented unknown name', new JS.Identifier(node.name)); |
2848 } | 2874 } |
2849 | 2875 |
2850 // Get the original declaring element. If we had a property accessor, this | 2876 // Get the original declaring element. If we had a property accessor, this |
2851 // indirects back to a (possibly synthetic) field. | 2877 // indirects back to a (possibly synthetic) field. |
2852 var element = accessor; | 2878 var element = accessor; |
2853 if (accessor is PropertyAccessorElement) element = accessor.variable; | 2879 if (accessor is PropertyAccessorElement) element = accessor.variable; |
2854 | 2880 |
2855 _declareBeforeUse(element); | |
2856 | |
2857 // type literal | 2881 // type literal |
2858 if (element is TypeDefiningElement) { | 2882 if (element is TypeDefiningElement) { |
2883 _declareBeforeUse(element); | |
2884 | |
2859 var typeName = _emitType(fillDynamicTypeArgs(element.type)); | 2885 var typeName = _emitType(fillDynamicTypeArgs(element.type)); |
2860 | 2886 |
2861 // If the type is a type literal expression in Dart code, wrap the raw | 2887 // If the type is a type literal expression in Dart code, wrap the raw |
2862 // runtime type in a "Type" instance. | 2888 // runtime type in a "Type" instance. |
2863 if (!_isInForeignJS && _isTypeLiteral(node)) { | 2889 if (!_isInForeignJS && _isTypeLiteral(node)) { |
2864 typeName = _callHelper('wrapType(#)', typeName); | 2890 typeName = _callHelper('wrapType(#)', typeName); |
2865 } | 2891 } |
2866 | 2892 |
2867 return typeName; | 2893 return typeName; |
2868 } | 2894 } |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3150 // The void and dynamic types are not defined in core. | 3176 // The void and dynamic types are not defined in core. |
3151 if (type.isVoid) { | 3177 if (type.isVoid) { |
3152 return _callHelper('void'); | 3178 return _callHelper('void'); |
3153 } else if (type.isDynamic) { | 3179 } else if (type.isDynamic) { |
3154 return _callHelper('dynamic'); | 3180 return _callHelper('dynamic'); |
3155 } else if (type.isBottom) { | 3181 } else if (type.isBottom) { |
3156 return _callHelper('bottom'); | 3182 return _callHelper('bottom'); |
3157 } | 3183 } |
3158 | 3184 |
3159 var element = type.element; | 3185 var element = type.element; |
3160 _declareBeforeUse(element); | 3186 if (element is TypeDefiningElement) { |
3187 _declareBeforeUse(element); | |
3188 } | |
3161 | 3189 |
3162 var interop = _emitJSInterop(element); | 3190 var interop = _emitJSInterop(element); |
3163 // Type parameters don't matter as JS interop types cannot be reified. | 3191 // Type parameters don't matter as JS interop types cannot be reified. |
3164 // We have to use lazy JS types because until we have proper module | 3192 // We have to use lazy JS types because until we have proper module |
3165 // loading for JS libraries bundled with Dart libraries, we will sometimes | 3193 // loading for JS libraries bundled with Dart libraries, we will sometimes |
3166 // need to load Dart libraries before the corresponding JS libraries are | 3194 // need to load Dart libraries before the corresponding JS libraries are |
3167 // actually loaded. | 3195 // actually loaded. |
3168 // Given a JS type such as: | 3196 // Given a JS type such as: |
3169 // @JS('google.maps.Location') | 3197 // @JS('google.maps.Location') |
3170 // class Location { ... } | 3198 // class Location { ... } |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3384 } | 3412 } |
3385 | 3413 |
3386 var accessor = resolutionMap.staticElementForIdentifier(node); | 3414 var accessor = resolutionMap.staticElementForIdentifier(node); |
3387 if (accessor == null) return unimplemented(); | 3415 if (accessor == null) return unimplemented(); |
3388 | 3416 |
3389 // Get the original declaring element. If we had a property accessor, this | 3417 // Get the original declaring element. If we had a property accessor, this |
3390 // indirects back to a (possibly synthetic) field. | 3418 // indirects back to a (possibly synthetic) field. |
3391 var element = accessor; | 3419 var element = accessor; |
3392 if (accessor is PropertyAccessorElement) element = accessor.variable; | 3420 if (accessor is PropertyAccessorElement) element = accessor.variable; |
3393 | 3421 |
3394 _declareBeforeUse(element); | 3422 if (element is TypeDefiningElement) { |
3423 _declareBeforeUse(element); | |
3424 } | |
3395 | 3425 |
3396 if (element is LocalVariableElement || element is ParameterElement) { | 3426 if (element is LocalVariableElement || element is ParameterElement) { |
3397 return _emitSetLocal(node, element, rhs); | 3427 return _emitSetLocal(node, element, rhs); |
3398 } | 3428 } |
3399 | 3429 |
3400 if (element.enclosingElement is CompilationUnitElement) { | 3430 if (element.enclosingElement is CompilationUnitElement) { |
3401 // Top level library member. | 3431 // Top level library member. |
3402 return _emitSetTopLevel(node, element, rhs); | 3432 return _emitSetTopLevel(node, element, rhs); |
3403 } | 3433 } |
3404 | 3434 |
(...skipping 598 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4003 } | 4033 } |
4004 // A normal yield in a sync* | 4034 // A normal yield in a sync* |
4005 return jsExpr.toYieldStatement(star: star); | 4035 return jsExpr.toYieldStatement(star: star); |
4006 } | 4036 } |
4007 | 4037 |
4008 @override | 4038 @override |
4009 JS.Expression visitAwaitExpression(AwaitExpression node) { | 4039 JS.Expression visitAwaitExpression(AwaitExpression node) { |
4010 return new JS.Yield(_visit(node.expression)); | 4040 return new JS.Yield(_visit(node.expression)); |
4011 } | 4041 } |
4012 | 4042 |
4043 /// This is not used--we emit top-level fields as we are emitting the | |
4044 /// compilation unit, see [_emitCompilationUnit]. | |
4013 @override | 4045 @override |
4014 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 4046 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
4015 for (var variable in node.variables.variables) { | 4047 assert(false); |
4016 _emitDeclaration(variable.element); | |
4017 } | |
4018 } | 4048 } |
4019 | 4049 |
4020 /// This is not used--we emit fields as we are emitting the class, | 4050 /// This is not used--we emit fields as we are emitting the class, |
4021 /// see [visitClassDeclaration]. | 4051 /// see [visitClassDeclaration]. |
4022 @override | 4052 @override |
4023 visitFieldDeclaration(FieldDeclaration node) { | 4053 visitFieldDeclaration(FieldDeclaration node) { |
4024 assert(false); | 4054 assert(false); |
4025 } | 4055 } |
4026 | 4056 |
4027 @override | 4057 @override |
(...skipping 15 matching lines...) Expand all Loading... | |
4043 | 4073 |
4044 @override | 4074 @override |
4045 visitVariableDeclarationList(VariableDeclarationList node) { | 4075 visitVariableDeclarationList(VariableDeclarationList node) { |
4046 return new JS.VariableDeclarationList( | 4076 return new JS.VariableDeclarationList( |
4047 'let', _visitList(node.variables) as List<JS.VariableInitialization>); | 4077 'let', _visitList(node.variables) as List<JS.VariableInitialization>); |
4048 } | 4078 } |
4049 | 4079 |
4050 @override | 4080 @override |
4051 visitVariableDeclaration(VariableDeclaration node) { | 4081 visitVariableDeclaration(VariableDeclaration node) { |
4052 if (node.element is PropertyInducingElement) { | 4082 if (node.element is PropertyInducingElement) { |
4053 // Static and instance fields are handled elsewhere. | 4083 // All fields are handled elsewhere. |
4054 assert(node.element is TopLevelVariableElement); | 4084 assert(false); |
4055 return _emitTopLevelField(node); | 4085 return null; |
4056 } | 4086 } |
4057 | 4087 |
4058 var name = new JS.Identifier(node.name.name, | 4088 var name = new JS.Identifier(node.name.name, |
4059 type: emitTypeRef( | 4089 type: emitTypeRef( |
4060 resolutionMap.elementDeclaredByVariableDeclaration(node).type)); | 4090 resolutionMap.elementDeclaredByVariableDeclaration(node).type)); |
4061 return new JS.VariableInitialization(name, _visitInitializer(node)); | 4091 return new JS.VariableInitialization(name, _visitInitializer(node)); |
4062 } | 4092 } |
4063 | 4093 |
4064 /// Try to emit a constant static field. | 4094 /// Emits a list of top-level field. |
4065 /// | 4095 void _emitTopLevelFields(List<VariableDeclaration> fields) { |
4066 /// If the field's initializer does not cause side effects, and if all of | 4096 _moduleItems.add(_emitLazyFields(currentLibrary, fields)); |
4067 /// dependencies are safe to refer to while we are initializing the class, | |
4068 /// then we can initialize it eagerly: | |
4069 /// | |
4070 /// // Baz must be const constructor, and the name "Baz" must be defined | |
4071 /// // by this point. | |
4072 /// Foo.bar = dart.const(new Baz(42)); | |
4073 /// | |
4074 /// Otherwise, we'll need to generate a lazy-static field. That ensures | |
4075 /// correct visible behavior, as well as avoiding referencing something that | |
4076 /// isn't defined yet (because it is defined later in the module). | |
4077 JS.Statement _emitConstantStaticField( | |
4078 ClassElement classElem, VariableDeclaration field) { | |
vsm
2017/04/04 22:58:43
We could consider keeping this/below only for case
Jennifer Messerly
2017/04/04 23:05:17
yeah... thought about that. It'd certainly be a lo
| |
4079 PropertyInducingElement element = field.element; | |
4080 assert(element.isStatic); | |
4081 | |
4082 _loader.startCheckingReferences(); | |
4083 JS.Expression jsInit = _visitInitializer(field); | |
4084 bool isLoaded = _loader.finishCheckingReferences(); | |
4085 | |
4086 bool eagerInit = | |
4087 isLoaded && (field.isConst || _constants.isFieldInitConstant(field)); | |
4088 | |
4089 var fieldName = field.name.name; | |
4090 if (eagerInit && | |
4091 !JS.invalidStaticFieldName(fieldName) && | |
4092 !_classProperties.staticFieldOverrides.contains(element)) { | |
4093 return annotate( | |
4094 js.statement('#.# = #;', [ | |
4095 _emitTopLevelName(classElem), | |
4096 _emitMemberName(fieldName, isStatic: true), | |
4097 jsInit | |
4098 ]), | |
4099 field, | |
4100 field.element); | |
4101 } | |
4102 | |
4103 // This means it should be treated as a lazy field. | |
4104 // TODO(jmesserly): we're throwing away the initializer expression, | |
4105 // which will force us to regenerate it. | |
4106 return null; | |
4107 } | 4097 } |
4108 | 4098 |
4109 /// Emits a top-level field. | 4099 /// Treat dart:_runtime fields as safe to eagerly evaluate. |
4110 JS.ModuleItem _emitTopLevelField(VariableDeclaration field) { | 4100 // TODO(jmesserly): it'd be nice to avoid this special case. |
4111 TopLevelVariableElement element = field.element; | 4101 void _emitInternalSdkFields(List<VariableDeclaration> fields) { |
4112 assert(element.isStatic); | 4102 for (var field in fields) { |
4113 | 4103 _moduleItems.add(annotate( |
4114 bool eagerInit; | 4104 js.statement('# = #;', |
4115 JS.Expression jsInit; | 4105 [_emitTopLevelName(field.element), _visitInitializer(field)]), |
4116 if (field.isConst || _constants.isFieldInitConstant(field)) { | 4106 field, |
4117 // If the field is constant, try and generate it at the top level. | 4107 field.element)); |
4118 _loader.startTopLevel(element); | |
4119 jsInit = _visitInitializer(field); | |
4120 _loader.finishTopLevel(element); | |
4121 eagerInit = _loader.isLoaded(element); | |
4122 } else { | |
4123 // TODO(jmesserly): we're visiting the initializer here, and again | |
4124 // later on when we emit lazy fields. That seems busted. | |
4125 jsInit = _visitInitializer(field); | |
4126 eagerInit = false; | |
4127 } | 4108 } |
4128 | |
4129 // Treat dart:runtime stuff as safe to eagerly evaluate. | |
4130 // TODO(jmesserly): it'd be nice to avoid this special case. | |
4131 var isJSTopLevel = field.isFinal && isSdkInternalRuntime(element.library); | |
4132 if (eagerInit || isJSTopLevel) { | |
4133 // Remember that we emitted it this way, so re-export can take advantage | |
4134 // of this fact. | |
4135 _eagerTopLevelFields.add(element); | |
4136 | |
4137 return annotate( | |
4138 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), | |
4139 field, | |
4140 element); | |
4141 } | |
4142 | |
4143 assert(element.library == currentLibrary); | |
4144 return _emitLazyFields(element.library, [field]); | |
4145 } | 4109 } |
4146 | 4110 |
4147 JS.Expression _visitInitializer(VariableDeclaration node) { | 4111 JS.Expression _visitInitializer(VariableDeclaration node) { |
4148 var value = _visit(node.initializer); | 4112 var value = _visit(node.initializer); |
4149 // explicitly initialize to null, to avoid getting `undefined`. | 4113 // explicitly initialize to null, to avoid getting `undefined`. |
4150 // TODO(jmesserly): do this only for vars that aren't definitely assigned. | 4114 // TODO(jmesserly): do this only for vars that aren't definitely assigned. |
4151 return value ?? new JS.LiteralNull(); | 4115 return value ?? new JS.LiteralNull(); |
4152 } | 4116 } |
4153 | 4117 |
4154 JS.Statement _emitLazyFields( | 4118 JS.Statement _emitLazyFields( |
4155 Element target, List<VariableDeclaration> fields) { | 4119 Element target, List<VariableDeclaration> fields) { |
4156 var methods = []; | 4120 var methods = []; |
4157 for (var node in fields) { | 4121 for (var node in fields) { |
4158 var name = node.name.name; | 4122 var name = node.name.name; |
4159 var element = node.element; | 4123 var element = node.element; |
4124 assert(element.getAncestor((e) => identical(e, target)) != null, | |
4125 "target is $target but enclosing element is ${element.enclosingElement }"); | |
4160 var access = _emitMemberName(name, isStatic: true); | 4126 var access = _emitMemberName(name, isStatic: true); |
4161 methods.add(annotate( | 4127 methods.add(annotate( |
4162 new JS.Method( | 4128 new JS.Method( |
4163 access, | 4129 access, |
4164 js.call('function() { return #; }', _visitInitializer(node)) | 4130 js.call('function() { return #; }', _visitInitializer(node)) |
4165 as JS.Fun, | 4131 as JS.Fun, |
4166 isGetter: true), | 4132 isGetter: true), |
4167 node, | 4133 node, |
4168 _findAccessor(element, getter: true))); | 4134 _findAccessor(element, getter: true))); |
4169 | 4135 |
(...skipping 1757 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5927 }; | 5893 }; |
5928 | 5894 |
5929 static Set<String> _uncheckedWhitelistCalls = new Set() | 5895 static Set<String> _uncheckedWhitelistCalls = new Set() |
5930 ..add('ng_zone_impl.dart') | 5896 ..add('ng_zone_impl.dart') |
5931 ..add('stack_zone_specification.dart') | 5897 ..add('stack_zone_specification.dart') |
5932 ..add('view_manager.dart') | 5898 ..add('view_manager.dart') |
5933 ..add('view.dart'); | 5899 ..add('view.dart'); |
5934 | 5900 |
5935 bool _inWhitelistCode(AstNode node, {isCall: false}) { | 5901 bool _inWhitelistCode(AstNode node, {isCall: false}) { |
5936 if (!options.useAngular2Whitelist) return false; | 5902 if (!options.useAngular2Whitelist) return false; |
5937 var path = _loader.currentElement.source.fullName; | 5903 var path = currentElement.source.fullName; |
5938 var filename = path.split("/").last; | 5904 var filename = path.split("/").last; |
5939 if (_uncheckedWhitelist.containsKey(filename)) { | 5905 if (_uncheckedWhitelist.containsKey(filename)) { |
5940 var whitelisted = _uncheckedWhitelist[filename]; | 5906 var whitelisted = _uncheckedWhitelist[filename]; |
5941 if (whitelisted == null) return true; | 5907 if (whitelisted == null) return true; |
5942 var enclosing = node; | 5908 var enclosing = node; |
5943 while (enclosing != null && | 5909 while (enclosing != null && |
5944 !(enclosing is ClassMember || enclosing is FunctionDeclaration)) { | 5910 !(enclosing is ClassMember || enclosing is FunctionDeclaration)) { |
5945 enclosing = enclosing.parent; | 5911 enclosing = enclosing.parent; |
5946 } | 5912 } |
5947 String name = (enclosing as dynamic)?.element?.name; | 5913 String name = (enclosing as dynamic)?.element?.name; |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
6069 if (targetIdentifier.staticElement is! PrefixElement) return false; | 6035 if (targetIdentifier.staticElement is! PrefixElement) return false; |
6070 var prefix = targetIdentifier.staticElement as PrefixElement; | 6036 var prefix = targetIdentifier.staticElement as PrefixElement; |
6071 | 6037 |
6072 // The library the prefix is referring to must come from a deferred import. | 6038 // The library the prefix is referring to must come from a deferred import. |
6073 var containingLibrary = resolutionMap | 6039 var containingLibrary = resolutionMap |
6074 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) | 6040 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) |
6075 .library; | 6041 .library; |
6076 var imports = containingLibrary.getImportsWithPrefix(prefix); | 6042 var imports = containingLibrary.getImportsWithPrefix(prefix); |
6077 return imports.length == 1 && imports[0].isDeferred; | 6043 return imports.length == 1 && imports[0].isDeferred; |
6078 } | 6044 } |
OLD | NEW |