OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 5 import 'dart:collection' show HashMap, HashSet; |
6 | 6 |
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
8 import 'package:analyzer/dart/ast/token.dart'; | 8 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
9 import 'package:analyzer/dart/element/element.dart'; | 9 import 'package:analyzer/dart/element/element.dart'; |
10 import 'package:analyzer/dart/element/visitor.dart'; | |
11 import 'package:analyzer/dart/element/type.dart'; | 10 import 'package:analyzer/dart/element/type.dart'; |
12 import 'package:analyzer/dart/ast/ast.dart' hide ConstantEvaluator; | 11 import 'package:analyzer/dart/ast/ast.dart' hide ConstantEvaluator; |
13 import 'package:analyzer/src/generated/constant.dart'; | 12 |
14 //TODO(leafp): Remove deprecated dependency | 13 //TODO(leafp): Remove deprecated dependency |
15 //ignore: DEPRECATED_MEMBER_USE | 14 //ignore: DEPRECATED_MEMBER_USE |
16 import 'package:analyzer/src/generated/element.dart' | 15 import 'package:analyzer/src/generated/element.dart' |
17 show DynamicElementImpl, DynamicTypeImpl, LocalVariableElementImpl; | 16 show DynamicTypeImpl, LocalVariableElementImpl; |
18 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; | 17 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
19 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 18 import 'package:analyzer/src/generated/resolver.dart' |
20 import 'package:analyzer/src/dart/ast/token.dart' | 19 show TypeProvider, NamespaceBuilder; |
21 show StringToken, Token, TokenType; | 20 import 'package:analyzer/src/dart/ast/token.dart' show StringToken; |
22 import 'package:analyzer/src/generated/type_system.dart' | 21 import 'package:analyzer/src/generated/type_system.dart' |
23 show StrongTypeSystemImpl; | 22 show StrongTypeSystemImpl; |
24 import 'package:analyzer/src/task/strong/info.dart'; | 23 import 'package:analyzer/src/summary/summarize_elements.dart' |
| 24 show PackageBundleAssembler; |
| 25 import 'package:analyzer/src/task/strong/info.dart' show DynamicInvoke; |
| 26 |
| 27 import '../js_ast/js_ast.dart' as JS; |
| 28 import '../js_ast/js_ast.dart' show js; |
| 29 import '../closure/closure_annotator.dart' show ClosureAnnotator; |
25 | 30 |
26 import 'ast_builder.dart' show AstBuilder; | 31 import 'ast_builder.dart' show AstBuilder; |
27 import 'reify_coercions.dart' show CoercionReifier, Tuple2; | 32 import 'compiler.dart' |
28 | 33 show BuildUnit, CompilerOptions, JSModuleFile, ModuleFormat; |
29 import '../js/js_ast.dart' as JS; | 34 import 'element_helpers.dart'; |
30 import '../js/js_ast.dart' show js; | 35 import 'element_loader.dart' show ElementLoader; |
31 | 36 import 'extension_types.dart' show ExtensionTypeSet; |
32 import '../closure/closure_annotator.dart' show ClosureAnnotator; | 37 import 'js_field_storage.dart' show findFieldsNeedingStorage; |
33 import '../compiler.dart' | |
34 show AbstractCompiler, corelibOrder, getCorelibModuleName; | |
35 import '../options.dart' show CodegenOptions; | |
36 import '../utils.dart'; | |
37 | |
38 import 'js_field_storage.dart'; | |
39 import 'js_interop.dart'; | 38 import 'js_interop.dart'; |
40 import 'js_names.dart' as JS; | 39 import 'js_names.dart' as JS; |
41 import 'js_metalet.dart' as JS; | 40 import 'js_metalet.dart' as JS; |
42 import 'js_module_item_order.dart'; | 41 import 'js_typeref_codegen.dart' show JsTypeRefCodegen; |
43 import 'js_names.dart'; | 42 import 'module_builder.dart' |
44 import 'js_printer.dart' show writeJsLibrary; | 43 show LegacyModuleBuilder, NodeModuleBuilder, pathToJSIdentifier; |
45 import 'js_typeref_codegen.dart'; | 44 import 'nullable_type_inference.dart' show NullableTypeInference; |
46 import 'module_builder.dart'; | 45 import 'reify_coercions.dart' show CoercionReifier; |
47 import 'nullable_type_inference.dart'; | 46 import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless; |
48 import 'side_effect_analysis.dart'; | 47 import 'source_map_printer.dart' show SourceMapPrintingContext; |
| 48 import 'package:source_maps/source_maps.dart'; |
49 | 49 |
50 // Various dynamic helpers we call. | 50 class CodeGenerator extends GeneralizingAstVisitor |
51 // If renaming these, make sure to check other places like the | 51 with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference { |
52 // _runtime.js file and comments. | 52 final AnalysisContext context; |
53 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 53 final CompilerOptions options; |
54 // import and generate calls to, rather than dart_runtime.js | 54 final rules = new StrongTypeSystemImpl(); |
55 const DPUT = 'dput'; | |
56 const DLOAD = 'dload'; | |
57 const DINDEX = 'dindex'; | |
58 const DSETINDEX = 'dsetindex'; | |
59 const DCALL = 'dcall'; | |
60 const DSEND = 'dsend'; | |
61 | 55 |
62 class JSCodegenVisitor extends GeneralizingAstVisitor | 56 /// The set of libraries we are currently compiling, and the temporaries used |
63 with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference { | 57 /// to refer to them. |
64 final AbstractCompiler compiler; | 58 /// |
65 final CodegenOptions options; | 59 /// We sometimes special case codegen for a single library, as it simplifies |
66 final LibraryElement currentLibrary; | 60 /// name scoping requirements. |
67 final StrongTypeSystemImpl rules; | 61 final _libraries = new Map<LibraryElement, JS.Identifier>(); |
| 62 |
| 63 /// Imported libraries, and the temporaries used to refer to them. |
| 64 final _imports = new Map<LibraryElement, JS.TemporaryId>(); |
| 65 |
| 66 /// The list of output module items, in the order they need to be emitted in. |
| 67 final _moduleItems = <JS.ModuleItem>[]; |
68 | 68 |
69 /// The global extension type table. | 69 /// The global extension type table. |
70 final ExtensionTypeSet _extensionTypes; | 70 final ExtensionTypeSet _extensionTypes; |
71 | 71 |
72 /// Information that is precomputed for this library, indicates which fields | 72 /// Information that is precomputed for this library, indicates which fields |
73 /// need storage slots. | 73 /// need storage slots. |
74 final HashSet<FieldElement> _fieldsNeedingStorage; | 74 HashSet<FieldElement> _fieldsNeedingStorage; |
75 | 75 |
76 /// The variable for the target of the current `..` cascade expression. | 76 /// The variable for the target of the current `..` cascade expression. |
77 /// | 77 /// |
78 /// Usually a [SimpleIdentifier], but it can also be other expressions | 78 /// Usually a [SimpleIdentifier], but it can also be other expressions |
79 /// that are safe to evaluate multiple times, such as `this`. | 79 /// that are safe to evaluate multiple times, such as `this`. |
80 Expression _cascadeTarget; | 80 Expression _cascadeTarget; |
81 | 81 |
82 /// The variable for the current catch clause | 82 /// The variable for the current catch clause |
83 SimpleIdentifier _catchParameter; | 83 SimpleIdentifier _catchParameter; |
84 | 84 |
85 /// In an async* function, this represents the stream controller parameter. | 85 /// In an async* function, this represents the stream controller parameter. |
86 JS.TemporaryId _asyncStarController; | 86 JS.TemporaryId _asyncStarController; |
87 | 87 |
88 /// Imported libraries, and the temporaries used to refer to them. | 88 final _privateNames = |
89 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 89 new HashMap<LibraryElement, HashMap<String, JS.TemporaryId>>(); |
90 final _exports = <String, String>{}; | |
91 final _properties = <FunctionDeclaration>[]; | |
92 final _privateNames = new HashMap<String, JS.TemporaryId>(); | |
93 final _moduleItems = <JS.Statement>[]; | |
94 final _temps = new HashMap<Element, JS.TemporaryId>(); | 90 final _temps = new HashMap<Element, JS.TemporaryId>(); |
95 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); | 91 |
96 | |
97 /// The name for the library's exports inside itself. | |
98 /// `exports` was chosen as the most similar to ES module patterns. | |
99 final _dartxVar = new JS.Identifier('dartx'); | 92 final _dartxVar = new JS.Identifier('dartx'); |
100 final _exportsVar = new JS.TemporaryId('exports'); | |
101 final _runtimeLibVar = new JS.Identifier('dart'); | 93 final _runtimeLibVar = new JS.Identifier('dart'); |
102 final namedArgumentTemp = new JS.TemporaryId('opts'); | 94 final namedArgumentTemp = new JS.TemporaryId('opts'); |
103 | 95 |
104 final TypeProvider _types; | 96 final _hasDeferredSupertype = new HashSet<ClassElement>(); |
| 97 |
| 98 /// The type provider from the current Analysis [context]. |
| 99 final TypeProvider types; |
| 100 |
| 101 final LibraryElement dartCoreLibrary; |
| 102 final LibraryElement dartJSLibrary; |
| 103 |
| 104 /// The dart:async `StreamIterator<>` type. |
| 105 final InterfaceType _asyncStreamIterator; |
| 106 |
| 107 /// The dart:_interceptors JSArray element. |
| 108 final ClassElement _jsArray; |
105 | 109 |
106 ConstFieldVisitor _constField; | 110 ConstFieldVisitor _constField; |
107 | 111 |
108 ModuleItemLoadOrder _loader; | |
109 | |
110 /// _interceptors.JSArray<E>, used for List literals. | |
111 ClassElement _jsArray; | |
112 | |
113 /// The current function body being compiled. | 112 /// The current function body being compiled. |
114 FunctionBody _currentFunction; | 113 FunctionBody _currentFunction; |
115 | 114 |
116 /// The default value of the module object. See [visitLibraryDirective]. | 115 /// Helper class for emitting elements in the proper order to allow |
117 String _jsModuleValue; | 116 /// JS to load the module. |
118 | 117 ElementLoader _loader; |
119 bool _isDartRuntime; | 118 |
120 | 119 BuildUnit _buildUnit; |
121 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, | 120 |
122 this._extensionTypes, this._fieldsNeedingStorage) | 121 CodeGenerator(AnalysisContext c, this.options, this._extensionTypes) |
123 : compiler = compiler, | 122 : context = c, |
124 options = compiler.options.codegenOptions, | 123 types = c.typeProvider, |
125 _types = compiler.context.typeProvider { | 124 _asyncStreamIterator = |
126 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 125 _getLibrary(c, 'dart:async').getType('StreamIterator').type, |
127 | 126 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), |
128 var context = compiler.context; | 127 dartCoreLibrary = _getLibrary(c, 'dart:core'), |
129 var src = context.sourceFactory.forUri('dart:_interceptors'); | 128 dartJSLibrary = _getLibrary(c, 'dart:js'); |
130 var interceptors = context.computeLibraryElement(src); | 129 |
131 _jsArray = interceptors.getType('JSArray'); | 130 LibraryElement get currentLibrary => _loader.currentElement.library; |
132 _isDartRuntime = currentLibrary.source.uri.toString() == 'dart:_runtime'; | 131 |
133 } | 132 /// The main entry point to JavaScript code generation. |
134 | 133 /// |
135 TypeProvider get types => _types; | 134 /// Takes the metadata for the build unit, as well as resolved trees and |
136 | 135 /// errors, and computes the output module code and optionally the source map. |
137 JS.Program emitLibrary(List<CompilationUnit> units) { | 136 JSModuleFile compile(BuildUnit unit, List<CompilationUnit> compilationUnits, |
138 // Modify the AST to make coercions explicit. | 137 List<String> errors) { |
139 units = new CoercionReifier().reify(units); | 138 _buildUnit = unit; |
140 | 139 |
141 units.last.directives.forEach(_visit); | 140 var jsTree = _emitModule(compilationUnits); |
142 | 141 var codeAndSourceMap = _writeJSText(unit, jsTree); |
143 // Rather than directly visit declarations, we instead use [_loader] to | 142 |
144 // visit them. It has the ability to sort elements on demand, so | 143 List<int> summary; |
145 // dependencies between top level items are handled with a minimal | 144 if (options.summarizeApi) { |
146 // reordering of the user's input code. The loader will call back into | 145 var assembler = new PackageBundleAssembler(); |
147 // this visitor via [_emitModuleItem] when it's ready to visit the item | 146 compilationUnits |
148 // for real. | 147 .map((u) => u.element.library) |
149 _loader.collectElements(currentLibrary, units); | 148 .toSet() |
150 | 149 .forEach(assembler.serializeLibraryElement); |
| 150 summary = assembler.assemble().toBuffer(); |
| 151 } |
| 152 |
| 153 return new JSModuleFile( |
| 154 unit.name, errors, codeAndSourceMap.e0, codeAndSourceMap.e1, summary); |
| 155 } |
| 156 |
| 157 Tuple2<String, Map> _writeJSText(BuildUnit unit, JS.Program jsTree) { |
| 158 var opts = new JS.JavaScriptPrintingOptions( |
| 159 emitTypes: options.closure, |
| 160 allowKeywordsInProperties: true, |
| 161 allowSingleLineIfStatements: true); |
| 162 JS.SimpleJavaScriptPrintingContext printer; |
| 163 SourceMapBuilder sourceMap; |
| 164 if (options.sourceMap) { |
| 165 var sourceMapContext = new SourceMapPrintingContext(); |
| 166 sourceMap = sourceMapContext.sourceMap; |
| 167 printer = sourceMapContext; |
| 168 } else { |
| 169 printer = new JS.SimpleJavaScriptPrintingContext(); |
| 170 } |
| 171 |
| 172 jsTree.accept(new JS.Printer(opts, printer, |
| 173 localNamer: new JS.TemporaryNamer(jsTree))); |
| 174 |
| 175 if (options.sourceMap && options.sourceMapComment) { |
| 176 printer.emit('\n//# sourceMappingURL=${unit.name}.js.map\n'); |
| 177 } |
| 178 |
| 179 return new Tuple2(printer.getText(), sourceMap?.build(unit.name + '.js')); |
| 180 } |
| 181 |
| 182 JS.Program _emitModule(List<CompilationUnit> compilationUnits) { |
| 183 if (_moduleItems.isNotEmpty) { |
| 184 throw new StateError('Can only call emitModule once.'); |
| 185 } |
| 186 |
| 187 _fieldsNeedingStorage = findFieldsNeedingStorage( |
| 188 compilationUnits.map((u) => u.element), _extensionTypes); |
| 189 |
| 190 // Transform the AST to make coercions explicit. |
| 191 compilationUnits = CoercionReifier.reify(compilationUnits); |
| 192 |
| 193 // Initialize our library variables. |
| 194 var items = <JS.ModuleItem>[]; |
| 195 for (var unit in compilationUnits) { |
| 196 var library = unit.element.library; |
| 197 if (unit.element != library.definingCompilationUnit) continue; |
| 198 |
| 199 var libraryTemp = _isDartRuntime(library) |
| 200 ? _runtimeLibVar |
| 201 : new JS.TemporaryId(jsLibraryName(library)); |
| 202 _libraries[library] = libraryTemp; |
| 203 items.add(new JS.ExportDeclaration( |
| 204 js.call('const # = Object.create(null)', [libraryTemp]))); |
| 205 |
| 206 // dart:_runtime has a magic module that holds extenstion method symbols. |
| 207 // TODO(jmesserly): find a cleaner design for this. |
| 208 if (_isDartRuntime(library)) { |
| 209 items.add(new JS.ExportDeclaration( |
| 210 js.call('const # = Object.create(null)', [_dartxVar]))); |
| 211 } |
| 212 } |
| 213 |
| 214 // Collect all Element -> Node mappings, in case we need to forward declare |
| 215 // any nodes. |
| 216 var nodes = new HashMap<Element, AstNode>.identity(); |
| 217 compilationUnits.map(_collectElements).forEach(nodes.addAll); |
| 218 _loader = new ElementLoader(_emitModuleItem, nodes); |
| 219 |
| 220 // Add implicit dart:core dependency so it is first. |
| 221 emitLibraryName(dartCoreLibrary); |
| 222 |
| 223 // |
| 224 // Visit each compilation unit and emit its code. |
| 225 // |
| 226 // NOTE: declarations are not necessarily emitted in this order. |
| 227 // Order will be changed as needed so the resulting code can execute. |
| 228 // This is done by forward declaring items. |
| 229 compilationUnits.forEach(visitCompilationUnit); |
| 230 |
| 231 // Declare imports |
| 232 _finishImports(items); |
| 233 |
| 234 // Add the module's code (produced by visiting compilation units, above) |
| 235 _copyAndFlattenBlocks(items, _moduleItems); |
| 236 |
| 237 // Build the module. |
| 238 var module = new JS.Program(items, name: _buildUnit.name); |
| 239 |
| 240 // Optional: lower module format. Otherwise just return it. |
| 241 switch (options.moduleFormat) { |
| 242 case ModuleFormat.legacy: |
| 243 return new LegacyModuleBuilder().build(module); |
| 244 case ModuleFormat.node: |
| 245 return new NodeModuleBuilder().build(module); |
| 246 case ModuleFormat.es6: |
| 247 return module; |
| 248 } |
| 249 } |
| 250 |
| 251 /// Flattens blocks in [items] to a single list. |
| 252 /// |
| 253 /// This will not flatten blocks that are marked as being scopes. |
| 254 void _copyAndFlattenBlocks( |
| 255 List<JS.ModuleItem> result, Iterable<JS.ModuleItem> items) { |
| 256 for (var item in items) { |
| 257 if (item is JS.Block && !item.isScope) { |
| 258 _copyAndFlattenBlocks(result, item.statements); |
| 259 } else { |
| 260 result.add(item); |
| 261 } |
| 262 } |
| 263 } |
| 264 |
| 265 String _libraryToModule(LibraryElement library) { |
| 266 assert(!_libraries.containsKey(library)); |
| 267 var source = library.source; |
| 268 // TODO(jmesserly): we need to split out HTML. |
| 269 if (source.uri.scheme == 'dart') { |
| 270 return 'dart_sdk'; |
| 271 } |
| 272 var moduleName = _buildUnit.libraryToModule(source); |
| 273 if (moduleName == null) { |
| 274 throw new StateError('Could not find module containing "$library".'); |
| 275 } |
| 276 return moduleName; |
| 277 } |
| 278 |
| 279 void _finishImports(List<JS.ModuleItem> items) { |
| 280 var modules = new Map<String, List<LibraryElement>>(); |
| 281 |
| 282 for (var import in _imports.keys) { |
| 283 modules.putIfAbsent(_libraryToModule(import), () => []).add(import); |
| 284 } |
| 285 |
| 286 String coreModuleName; |
| 287 if (!_libraries.containsKey(dartCoreLibrary)) { |
| 288 coreModuleName = _libraryToModule(dartCoreLibrary); |
| 289 } |
| 290 modules.forEach((module, libraries) { |
| 291 // Generate import directives. |
| 292 // |
| 293 // Our import variables are temps and can get renamed. Since our renaming |
| 294 // is integrated into js_ast, it is aware of this possibility and will |
| 295 // generate an "as" if needed. For example: |
| 296 // |
| 297 // import {foo} from 'foo'; // if no rename needed |
| 298 // import {foo as foo$} from 'foo'; // if rename was needed |
| 299 // |
| 300 var imports = |
| 301 libraries.map((l) => new JS.NameSpecifier(_imports[l])).toList(); |
| 302 if (module == coreModuleName) { |
| 303 imports.add(new JS.NameSpecifier(_runtimeLibVar)); |
| 304 imports.add(new JS.NameSpecifier(_dartxVar)); |
| 305 } |
| 306 items.add(new JS.ImportDeclaration( |
| 307 namedImports: imports, from: js.string(module, "'"))); |
| 308 }); |
| 309 } |
| 310 |
| 311 /// Collect toplevel elements and nodes we need to emit, and returns |
| 312 /// an ordered map of these. |
| 313 static Map<Element, AstNode> _collectElements(CompilationUnit unit) { |
| 314 var map = <Element, AstNode>{}; |
| 315 for (var declaration in unit.declarations) { |
| 316 if (declaration is TopLevelVariableDeclaration) { |
| 317 for (var field in declaration.variables.variables) { |
| 318 map[field.element] = field; |
| 319 } |
| 320 } else { |
| 321 map[declaration.element] = declaration; |
| 322 } |
| 323 } |
| 324 return map; |
| 325 } |
| 326 |
| 327 void _emitModuleItem(AstNode node) { |
151 // TODO(jmesserly): ideally we could do this at a smaller granularity. | 328 // TODO(jmesserly): ideally we could do this at a smaller granularity. |
152 // We'll need to be consistent about when we're generating functions, and | 329 // We'll need to be consistent about when we're generating functions, and |
153 // only run this on the outermost function. | 330 // only run this on the outermost function. |
154 inferNullableTypesInLibrary(units); | 331 inferNullableTypes(node); |
155 | |
156 _constField = new ConstFieldVisitor(types, currentLibrary.source); | |
157 | |
158 for (var unit in units) { | |
159 for (var decl in unit.declarations) { | |
160 if (decl is TopLevelVariableDeclaration) { | |
161 visitTopLevelVariableDeclaration(decl); | |
162 } else { | |
163 _loader.loadDeclaration(decl, decl.element); | |
164 } | |
165 } | |
166 } | |
167 | |
168 // Flush any unwritten fields/properties. | |
169 _flushLibraryProperties(_moduleItems); | |
170 | |
171 // Mark all qualified names as qualified or not, depending on if they need | |
172 // to be loaded lazily or not. | |
173 for (var elementIdPairs in _qualifiedIds) { | |
174 var element = elementIdPairs.e0; | |
175 var id = elementIdPairs.e1; | |
176 id.setQualified(!_loader.isLoaded(element)); | |
177 } | |
178 | |
179 var moduleBuilder = new ModuleBuilder(options.moduleFormat); | |
180 | |
181 _exports.forEach(moduleBuilder.addExport); | |
182 | |
183 var currentModuleName = compiler.getModuleName(currentLibrary.source.uri); | |
184 var items = <JS.ModuleItem>[]; | |
185 if (!_isDartRuntime) { | |
186 if (currentLibrary.source.isInSystemLibrary) { | |
187 // Force the import order of runtime libs. | |
188 // TODO(ochafik): Reduce this to a minimum. | |
189 for (var libUri in corelibOrder.reversed) { | |
190 var moduleName = compiler.getModuleName(libUri); | |
191 if (moduleName == currentModuleName) continue; | |
192 moduleBuilder.addImport(moduleName, null); | |
193 } | |
194 } | |
195 moduleBuilder.addImport('dart/_runtime', _runtimeLibVar); | |
196 | |
197 var dartxImport = | |
198 js.statement("let # = #.dartx;", [_dartxVar, _runtimeLibVar]); | |
199 items.add(dartxImport); | |
200 } | |
201 items.addAll(_moduleItems); | |
202 | |
203 _imports.forEach((LibraryElement lib, JS.TemporaryId temp) { | |
204 moduleBuilder.addImport(compiler.getModuleName(lib.source.uri), temp, | |
205 isLazy: _isDartRuntime || !_loader.libraryIsLoaded(lib)); | |
206 }); | |
207 | |
208 // TODO(jmesserly): scriptTag support. | |
209 // Enable this if we know we're targetting command line environment? | |
210 // It doesn't work in browser. | |
211 // var jsBin = compiler.options.runnerOptions.v8Binary; | |
212 // String scriptTag = null; | |
213 // if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin'; | |
214 return moduleBuilder.build( | |
215 currentModuleName, _jsModuleValue, _exportsVar, items); | |
216 } | |
217 | |
218 void _emitModuleItem(AstNode node) { | |
219 // Attempt to group adjacent properties. | |
220 if (node is! FunctionDeclaration) _flushLibraryProperties(_moduleItems); | |
221 | 332 |
222 var code = _visit(node); | 333 var code = _visit(node); |
223 if (code != null) _moduleItems.add(code); | 334 if (code != null) _moduleItems.add(code); |
224 } | 335 } |
225 | 336 |
226 @override | 337 @override |
227 void visitLibraryDirective(LibraryDirective node) { | 338 void visitCompilationUnit(CompilationUnit unit) { |
228 assert(_jsModuleValue == null); | 339 _constField = new ConstFieldVisitor(types, unit.element.source); |
229 | 340 |
230 var jsName = findAnnotation(node.element, isJSAnnotation); | 341 for (var declaration in unit.declarations) { |
231 _jsModuleValue = | 342 var element = declaration.element; |
232 getConstantField(jsName, 'name', types.stringType)?.toStringValue(); | 343 if (element != null) { |
233 } | 344 _loader.emitDeclaration(element); |
| 345 } else { |
| 346 declaration.accept(this); |
| 347 } |
| 348 } |
| 349 for (var directive in unit.directives) { |
| 350 directive.accept(this); |
| 351 } |
| 352 } |
| 353 |
| 354 @override |
| 355 void visitLibraryDirective(LibraryDirective node) {} |
234 | 356 |
235 @override | 357 @override |
236 void visitImportDirective(ImportDirective node) { | 358 void visitImportDirective(ImportDirective node) { |
237 // Nothing to do yet, but we'll want to convert this to an ES6 import once | 359 // We don't handle imports here. |
238 // we have support for modules. | 360 // |
| 361 // Instead, we collect imports whenever we need to generate a reference |
| 362 // to another library. This has the effect of collecting the actually used |
| 363 // imports. |
| 364 // |
| 365 // TODO(jmesserly): if this is a prefixed import, consider adding the prefix |
| 366 // as an alias? |
239 } | 367 } |
240 | 368 |
241 @override | 369 @override |
242 void visitPartDirective(PartDirective node) {} | 370 void visitPartDirective(PartDirective node) {} |
| 371 |
243 @override | 372 @override |
244 void visitPartOfDirective(PartOfDirective node) {} | 373 void visitPartOfDirective(PartOfDirective node) {} |
245 | 374 |
246 @override | 375 @override |
247 void visitExportDirective(ExportDirective node) { | 376 void visitExportDirective(ExportDirective node) { |
248 var exportName = emitLibraryName(node.uriElement); | 377 ExportElement element = node.element; |
249 | 378 var currentLibrary = element.library; |
250 var currentLibNames = currentLibrary.publicNamespace.definedNames; | 379 |
251 | 380 var currentNames = currentLibrary.publicNamespace.definedNames; |
252 var args = [_exportsVar, exportName]; | 381 var exportedNames = |
253 if (node.combinators.isNotEmpty) { | 382 new NamespaceBuilder().createExportNamespaceForDirective(element); |
254 var shownNames = <JS.Expression>[]; | 383 |
255 var hiddenNames = <JS.Expression>[]; | 384 // TODO(jmesserly): we could collect all of the names for bulk re-export, |
256 | 385 // but this is easier to implement for now. |
257 var show = node.combinators.firstWhere((c) => c is ShowCombinator, | 386 void emitExport(Element export, {String suffix: ''}) { |
258 orElse: () => null) as ShowCombinator; | 387 var name = _emitTopLevelName(export, suffix: suffix); |
259 var hide = node.combinators.firstWhere((c) => c is HideCombinator, | 388 _moduleItems.add(js.statement( |
260 orElse: () => null) as HideCombinator; | 389 '#.# = #;', [emitLibraryName(currentLibrary), name.selector, name])); |
261 if (show != null) { | 390 } |
262 shownNames.addAll(show.shownNames | 391 |
263 .map((i) => i.name) | 392 for (var export in exportedNames.definedNames.values) { |
264 .where((s) => !currentLibNames.containsKey(s)) | 393 // Don't allow redefining names from this library. |
265 .map((s) => js.string(s, "'"))); | 394 if (currentNames.containsKey(export.name)) continue; |
266 } | 395 |
267 if (hide != null) { | 396 _loader.emitDeclaration(export); |
268 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); | 397 if (export is ClassElement && export.typeParameters.isNotEmpty) { |
269 } | 398 // Export the generic name as well. |
270 args.add(new JS.ArrayInitializer(shownNames)); | 399 // TODO(jmesserly): revisit generic classes |
271 args.add(new JS.ArrayInitializer(hiddenNames)); | 400 emitExport(export, suffix: r'$'); |
272 } | 401 } |
273 | 402 emitExport(export); |
274 _moduleItems.add(js.statement('dart.export(#);', [args])); | 403 } |
275 } | 404 } |
276 | |
277 JS.Identifier _initSymbol(JS.Identifier id) { | |
278 var s = | |
279 js.statement('const # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | |
280 _moduleItems.add(s); | |
281 return id; | |
282 } | |
283 | |
284 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | |
285 // until we have better name tracking. | |
286 String get _SYMBOL { | |
287 var name = currentLibrary.name; | |
288 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; | |
289 return 'Symbol'; | |
290 } | |
291 | |
292 bool isPublic(String name) => !name.startsWith('_'); | |
293 | 405 |
294 @override | 406 @override |
295 visitAsExpression(AsExpression node) { | 407 visitAsExpression(AsExpression node) { |
296 var from = getStaticType(node.expression); | 408 var from = getStaticType(node.expression); |
297 var to = node.type.type; | 409 var to = node.type.type; |
298 | 410 |
299 var fromExpr = _visit(node.expression); | 411 var fromExpr = _visit(node.expression); |
300 | 412 |
301 // Skip the cast if it's not needed. | 413 // Skip the cast if it's not needed. |
302 if (rules.isSubtypeOf(from, to)) return fromExpr; | 414 if (rules.isSubtypeOf(from, to)) return fromExpr; |
303 | 415 |
304 // All Dart number types map to a JS double. | 416 // All Dart number types map to a JS double. |
305 if (_isNumberInJS(from) && _isNumberInJS(to)) { | 417 if (_isNumberInJS(from) && _isNumberInJS(to)) { |
306 // Make sure to check when converting to int. | 418 // Make sure to check when converting to int. |
307 if (from != _types.intType && to == _types.intType) { | 419 if (from != types.intType && to == types.intType) { |
308 return js.call('dart.asInt(#)', [fromExpr]); | 420 return js.call('dart.asInt(#)', [fromExpr]); |
309 } | 421 } |
310 | 422 |
311 // A no-op in JavaScript. | 423 // A no-op in JavaScript. |
312 return fromExpr; | 424 return fromExpr; |
313 } | 425 } |
314 | 426 |
315 return js.call('dart.as(#, #)', [fromExpr, _emitTypeName(to)]); | 427 return js.call('dart.as(#, #)', [fromExpr, _emitTypeName(to)]); |
316 } | 428 } |
317 | 429 |
(...skipping 12 matching lines...) Expand all Loading... |
330 } | 442 } |
331 | 443 |
332 if (node.notOperator != null) { | 444 if (node.notOperator != null) { |
333 return js.call('!#', result); | 445 return js.call('!#', result); |
334 } | 446 } |
335 return result; | 447 return result; |
336 } | 448 } |
337 | 449 |
338 String _jsTypeofName(DartType t) { | 450 String _jsTypeofName(DartType t) { |
339 if (_isNumberInJS(t)) return 'number'; | 451 if (_isNumberInJS(t)) return 'number'; |
340 if (t == _types.stringType) return 'string'; | 452 if (t == types.stringType) return 'string'; |
341 if (t == _types.boolType) return 'boolean'; | 453 if (t == types.boolType) return 'boolean'; |
342 return null; | 454 return null; |
343 } | 455 } |
344 | 456 |
345 @override | 457 @override |
346 visitFunctionTypeAlias(FunctionTypeAlias node) { | 458 visitFunctionTypeAlias(FunctionTypeAlias node) { |
347 var element = node.element; | 459 FunctionTypeAliasElement element = node.element; |
348 var type = element.type; | |
349 var name = element.name; | |
350 | 460 |
351 var fnType = annotate( | 461 JS.Expression body = annotate( |
352 js.statement('const # = dart.typedef(#, () => #);', [ | 462 js.call('dart.typedef(#, () => #)', [ |
353 name, | 463 js.string(element.name, "'"), |
354 js.string(name, "'"), | 464 _emitTypeName(element.type, lowerTypedef: true) |
355 _emitTypeName(type, lowerTypedef: true) | |
356 ]), | 465 ]), |
357 node, | 466 node, |
358 node.element); | 467 element); |
359 | 468 |
360 return _finishClassDef(type, fnType); | 469 var typeFormals = element.typeParameters; |
| 470 if (typeFormals.isNotEmpty) { |
| 471 return _defineClassTypeArguments(element, typeFormals, |
| 472 js.statement('const # = #;', [element.name, body])); |
| 473 } else { |
| 474 return js.statement('# = #;', [_emitTopLevelName(element), body]); |
| 475 } |
361 } | 476 } |
362 | 477 |
363 @override | 478 @override |
364 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); | 479 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); |
365 | 480 |
366 @override | 481 @override |
367 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { | 482 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { |
368 var element = node.element; | 483 ClassElement element = node.element; |
369 | 484 |
370 // Forward all generative constructors from the base class. | 485 // Forward all generative constructors from the base class. |
371 var body = <JS.Method>[]; | 486 var methods = <JS.Method>[]; |
372 | 487 |
373 var supertype = element.supertype; | 488 var supertype = element.supertype; |
374 if (!supertype.isObject) { | 489 if (!supertype.isObject) { |
375 for (var ctor in element.constructors) { | 490 for (var ctor in element.constructors) { |
376 var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library); | 491 var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library); |
377 var fun = js.call('function() { super.#(...arguments); }', | 492 var fun = js.call('function() { super.#(...arguments); }', |
378 [_constructorName(parentCtor)]) as JS.Fun; | 493 [_constructorName(parentCtor)]) as JS.Fun; |
379 body.add(new JS.Method(_constructorName(ctor), fun)); | 494 methods.add(new JS.Method(_constructorName(ctor), fun)); |
380 } | 495 } |
381 } | 496 } |
382 | 497 |
383 var cls = _emitClassDeclaration(element, body); | 498 var classExpr = _emitClassExpression(element, methods); |
384 return _finishClassDef(element.type, cls); | 499 |
| 500 var typeFormals = element.typeParameters; |
| 501 if (typeFormals.isNotEmpty) { |
| 502 return _defineClassTypeArguments( |
| 503 element, typeFormals, new JS.ClassDeclaration(classExpr)); |
| 504 } else { |
| 505 return js.statement('# = #;', [_emitTopLevelName(element), classExpr]); |
| 506 } |
385 } | 507 } |
386 | 508 |
387 JS.Statement _emitJsType(String dartClassName, DartObject jsName) { | 509 JS.Statement _emitJsType(Element e) { |
388 var jsTypeName = | 510 var jsTypeName = getAnnotationName(e, isJSAnnotation); |
389 getConstantField(jsName, 'name', types.stringType)?.toStringValue(); | 511 if (jsTypeName == null || jsTypeName == e.name) return null; |
390 | 512 |
391 if (jsTypeName != null && jsTypeName != dartClassName) { | 513 // We export the JS type as if it was a Dart type. For example this allows |
392 // We export the JS type as if it was a Dart type. For example this allows | 514 // `dom.InputElement` to actually be HTMLInputElement. |
393 // `dom.InputElement` to actually be HTMLInputElement. | 515 // TODO(jmesserly): if we had the JS name on the Element, we could just |
394 // TODO(jmesserly): if we had the JS name on the Element, we could just | 516 // generate it correctly when we refer to it. |
395 // generate it correctly when we refer to it. | 517 return js.statement('# = #;', [_emitTopLevelName(e), jsTypeName]); |
396 if (isPublic(dartClassName)) _addExport(dartClassName); | |
397 return js.statement('const # = #;', [dartClassName, jsTypeName]); | |
398 } | |
399 return null; | |
400 } | 518 } |
401 | 519 |
402 @override | 520 @override |
403 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 521 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
404 var classElem = node.element; | 522 var classElem = node.element; |
405 var type = classElem.type; | |
406 var jsName = findAnnotation(classElem, isJSAnnotation); | |
407 | 523 |
408 if (jsName != null) return _emitJsType(node.name.name, jsName); | 524 // If this is a JavaScript type, emit it now and then exit. |
| 525 var jsTypeDef = _emitJsType(classElem); |
| 526 if (jsTypeDef != null) return jsTypeDef; |
409 | 527 |
410 var ctors = <ConstructorDeclaration>[]; | 528 var ctors = <ConstructorDeclaration>[]; |
411 var fields = <FieldDeclaration>[]; | 529 var fields = <FieldDeclaration>[]; |
412 var staticFields = <FieldDeclaration>[]; | 530 var staticFields = <FieldDeclaration>[]; |
413 var methods = <MethodDeclaration>[]; | 531 var methods = <MethodDeclaration>[]; |
414 for (var member in node.members) { | 532 for (var member in node.members) { |
415 if (member is ConstructorDeclaration) { | 533 if (member is ConstructorDeclaration) { |
416 ctors.add(member); | 534 ctors.add(member); |
417 } else if (member is FieldDeclaration) { | 535 } else if (member is FieldDeclaration) { |
418 (member.isStatic ? staticFields : fields).add(member); | 536 (member.isStatic ? staticFields : fields).add(member); |
419 } else if (member is MethodDeclaration) { | 537 } else if (member is MethodDeclaration) { |
420 methods.add(member); | 538 methods.add(member); |
421 } | 539 } |
422 } | 540 } |
423 | 541 |
424 var allFields = new List.from(fields)..addAll(staticFields); | 542 var allFields = new List.from(fields)..addAll(staticFields); |
425 | 543 var classExpr = _emitClassExpression( |
426 var classDecl = _emitClassDeclaration( | |
427 classElem, _emitClassMethods(node, ctors, fields), | 544 classElem, _emitClassMethods(node, ctors, fields), |
428 fields: allFields); | 545 fields: allFields); |
429 | 546 |
430 String jsPeerName; | 547 var body = <JS.Statement>[]; |
431 var jsPeer = findAnnotation(classElem, isJsPeerInterface); | 548 var extensions = _extensionsToImplement(classElem); |
432 // Only look at "Native" annotations on registered extension types. | 549 _initExtensionSymbols(classElem, methods, fields, body); |
433 // E.g., we're current ignoring the ones in dart:html. | 550 |
434 if (jsPeer == null && _extensionTypes.contains(classElem)) { | 551 // Emit the class, e.g. `core.Object = class Object { ... }` |
435 jsPeer = findAnnotation(classElem, isNativeAnnotation); | 552 JS.Expression className = _defineClass(classElem, classExpr, body); |
436 } | 553 |
437 if (jsPeer != null) { | 554 // Emit things that come after the ES6 `class ... { ... }`. |
438 jsPeerName = | 555 _setBaseClass(classElem, className, body); |
439 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); | 556 _defineNamedConstructors(ctors, body, className); |
440 if (jsPeerName.contains(',')) { | 557 _emitVirtualFields(fields, className, body); |
441 jsPeerName = jsPeerName.split(',')[0]; | 558 _emitClassSignature(methods, classElem, ctors, extensions, className, body); |
442 } | 559 _defineExtensionMembers(extensions, className, body); |
| 560 _emitClassMetadata(node.metadata, className, body); |
| 561 |
| 562 JS.Statement classDef = _statement(body); |
| 563 var typeFormals = classElem.typeParameters; |
| 564 if (typeFormals.isNotEmpty) { |
| 565 classDef = _defineClassTypeArguments(classElem, typeFormals, classDef); |
443 } | 566 } |
444 | 567 |
445 var body = _finishClassMembers(classElem, classDecl, ctors, fields, | 568 body = <JS.Statement>[classDef]; |
446 staticFields, methods, node.metadata, jsPeerName); | 569 _emitStaticFields(staticFields, classElem, body); |
| 570 _registerExtensionType(classElem, body); |
| 571 return _statement(body); |
| 572 } |
447 | 573 |
448 var result = _finishClassDef(type, body); | 574 JS.Expression _defineClass(ClassElement classElem, |
449 | 575 JS.ClassExpression classExpr, List<JS.Statement> body) { |
450 if (jsPeerName != null) { | 576 JS.Expression className; |
451 // This class isn't allowed to be lazy, because we need to set up | 577 if (classElem.typeParameters.isNotEmpty) { |
452 // the native JS type eagerly at this point. | 578 // Generic classes will be defined inside a function that closes over the |
453 // If we wanted to support laziness, we could defer the hookup until | 579 // type parameter. So we can use their local variable name directly. |
454 // the end of the Dart library cycle load. | 580 className = classExpr.name; |
455 assert(_loader.isLoaded(classElem)); | 581 body.add(new JS.ClassDeclaration(classExpr)); |
456 | 582 } else { |
457 // TODO(jmesserly): this copies the dynamic members. | 583 className = _emitTopLevelName(classElem); |
458 // Probably fine for objects coming from JS, but not if we actually | 584 body.add(js.statement('# = #;', [className, classExpr])); |
459 // want to support construction of instances with generic types other | |
460 // than dynamic. See issue #154 for Array and List<E> related bug. | |
461 var copyMembers = js.statement( | |
462 'dart.registerExtension(dart.global.#, #);', | |
463 [_propertyName(jsPeerName), classElem.name]); | |
464 return _statement([result, copyMembers]); | |
465 } | 585 } |
466 return result; | 586 return className; |
467 } | 587 } |
468 | 588 |
469 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { | 589 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { |
470 return typeFormals | 590 return typeFormals |
471 .map((t) => new JS.Identifier(t.name)) | 591 .map((t) => new JS.Identifier(t.name)) |
472 .toList(growable: false); | 592 .toList(growable: false); |
473 } | 593 } |
474 | 594 |
475 /// Emits a field declaration for TypeScript & Closure's ES6_TYPED | 595 /// Emits a field declaration for TypeScript & Closure's ES6_TYPED |
476 /// (e.g. `class Foo { i: string; }`) | 596 /// (e.g. `class Foo { i: string; }`) |
477 JS.VariableDeclarationList _emitTypeScriptField(FieldDeclaration field) { | 597 JS.VariableDeclarationList _emitTypeScriptField(FieldDeclaration field) { |
478 return new JS.VariableDeclarationList( | 598 return new JS.VariableDeclarationList( |
479 field.isStatic ? 'static' : null, | 599 field.isStatic ? 'static' : null, |
480 field.fields.variables | 600 field.fields.variables |
481 .map((decl) => new JS.VariableInitialization( | 601 .map((decl) => new JS.VariableInitialization( |
482 new JS.Identifier( | 602 new JS.Identifier( |
483 // TODO(ochafik): use a refactored _emitMemberName instead. | 603 // TODO(ochafik): use a refactored _emitMemberName instead. |
484 decl.name.name, | 604 decl.name.name, |
485 type: emitTypeRef(decl.element.type)), | 605 type: emitTypeRef(decl.element.type)), |
486 null)) | 606 null)) |
487 .toList(growable: false)); | 607 .toList(growable: false)); |
488 } | 608 } |
489 | 609 |
490 @override | 610 @override |
491 JS.Statement visitEnumDeclaration(EnumDeclaration node) { | 611 JS.Statement visitEnumDeclaration(EnumDeclaration node) { |
492 var element = node.element; | 612 var element = node.element; |
493 var type = element.type; | 613 var type = element.type; |
494 var name = js.string(type.name); | 614 var name = js.string(type.name); |
495 var id = new JS.Identifier(type.name); | |
496 | 615 |
497 // Generate a class per section 13 of the spec. | 616 // Generate a class per section 13 of the spec. |
498 // TODO(vsm): Generate any accompanying metadata | 617 // TODO(vsm): Generate any accompanying metadata |
499 | 618 |
500 // Create constructor and initialize index | 619 // Create constructor and initialize index |
501 var constructor = new JS.Method( | 620 var constructor = new JS.Method( |
502 name, js.call('function(index) { this.index = index; }') as JS.Fun); | 621 name, js.call('function(index) { this.index = index; }') as JS.Fun); |
503 var fields = new List<FieldElement>.from( | 622 var fields = new List<FieldElement>.from( |
504 element.fields.where((f) => f.type == type)); | 623 element.fields.where((f) => f.type == type)); |
505 | 624 |
506 // Create toString() method | 625 // Create toString() method |
507 var properties = new List<JS.Property>(); | 626 var properties = new List<JS.Property>(); |
508 for (var i = 0; i < fields.length; ++i) { | 627 for (var i = 0; i < fields.length; ++i) { |
509 properties.add(new JS.Property( | 628 properties.add(new JS.Property( |
510 js.number(i), js.string('${type.name}.${fields[i].name}'))); | 629 js.number(i), js.string('${type.name}.${fields[i].name}'))); |
511 } | 630 } |
512 var nameMap = new JS.ObjectInitializer(properties, multiline: true); | 631 var nameMap = new JS.ObjectInitializer(properties, multiline: true); |
513 var toStringF = new JS.Method(js.string('toString'), | 632 var toStringF = new JS.Method(js.string('toString'), |
514 js.call('function() { return #[this.index]; }', nameMap) as JS.Fun); | 633 js.call('function() { return #[this.index]; }', nameMap) as JS.Fun); |
515 | 634 |
516 // Create enum class | 635 // Create enum class |
517 var classExpr = new JS.ClassExpression( | 636 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
518 id, _emitClassHeritage(element), [constructor, toStringF]); | 637 _emitClassHeritage(element), [constructor, toStringF]); |
519 var result = <JS.Statement>[js.statement('#', classExpr)]; | 638 var id = _emitTopLevelName(element); |
| 639 var result = [ |
| 640 js.statement('# = #', [id, classExpr]) |
| 641 ]; |
520 | 642 |
521 // Create static fields for each enum value | 643 // Create static fields for each enum value |
522 for (var i = 0; i < fields.length; ++i) { | 644 for (var i = 0; i < fields.length; ++i) { |
523 result.add(js.statement('#.# = dart.const(new #(#));', | 645 result.add(js.statement('#.# = dart.const(new #(#));', |
524 [id, fields[i].name, id, js.number(i)])); | 646 [id, fields[i].name, id, js.number(i)])); |
525 } | 647 } |
526 | 648 |
527 // Create static values list | 649 // Create static values list |
528 var values = new JS.ArrayInitializer(new List<JS.Expression>.from( | 650 var values = new JS.ArrayInitializer(new List<JS.Expression>.from( |
529 fields.map((f) => js.call('#.#', [id, f.name])))); | 651 fields.map((f) => js.call('#.#', [id, f.name])))); |
530 result.add(js.statement('#.values = dart.const(dart.list(#, #));', | 652 result.add(js.statement('#.values = dart.const(dart.list(#, #));', |
531 [id, values, _emitTypeName(type)])); | 653 [id, values, _emitTypeName(type)])); |
532 | 654 |
533 if (isPublic(type.name)) _addExport(type.name); | |
534 return _statement(result); | 655 return _statement(result); |
535 } | 656 } |
536 | 657 |
537 /// Given a class element and body, complete the class declaration. | 658 /// Wraps a possibly generic class in its type arguments. |
538 /// This handles generic type parameters, laziness (in library-cycle cases), | 659 JS.Statement _defineClassTypeArguments(TypeDefiningElement element, |
539 /// and ensuring dependencies are loaded first. | 660 List<TypeParameterElement> formals, JS.Statement body) { |
540 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { | 661 assert(formals.isNotEmpty); |
541 var name = type.name; | 662 var genericDef = |
542 var genericName = '$name\$'; | 663 js.statement('# = dart.generic((#) => { #; return #; });', [ |
| 664 _emitTopLevelName(element, suffix: r'$'), |
| 665 _emitTypeFormals(formals), |
| 666 body, |
| 667 element.name |
| 668 ]); |
543 | 669 |
544 JS.Statement genericDef = null; | 670 var dynType = fillDynamicTypeArgs(element.type); |
545 if (_typeFormalsOf(type).isNotEmpty) { | 671 var genericInst = _emitTypeName(dynType, lowerGeneric: true); |
546 genericDef = _emitGenericClassDef(type, body); | 672 return js.statement( |
547 } | 673 '{ #; # = #; }', [genericDef, _emitTopLevelName(element), genericInst]); |
548 // The base class and all mixins must be declared before this class. | |
549 if (!_loader.isLoaded(type.element)) { | |
550 // TODO(jmesserly): the lazy class def is a simple solution for now. | |
551 // We may want to consider other options in the future. | |
552 | |
553 if (genericDef != null) { | |
554 return js.statement( | |
555 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', | |
556 [genericDef, _exportsVar, _propertyName(name), genericName]); | |
557 } | |
558 | |
559 return js.statement( | |
560 'dart.defineLazyClass(#, { get #() { #; return #; } });', | |
561 [_exportsVar, _propertyName(name), body, name]); | |
562 } | |
563 | |
564 if (isPublic(name)) _addExport(name); | |
565 | |
566 if (genericDef != null) { | |
567 var dynType = fillDynamicTypeArgs(type, types); | |
568 var genericInst = _emitTypeName(dynType, lowerGeneric: true); | |
569 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | |
570 } | |
571 return body; | |
572 } | 674 } |
573 | 675 |
574 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { | |
575 var name = type.name; | |
576 var genericName = '$name\$'; | |
577 var typeParams = _typeFormalsOf(type).map((p) => p.name); | |
578 if (isPublic(name)) _addExport(genericName); | |
579 return js.statement('const # = dart.generic(function(#) { #; return #; });', | |
580 [genericName, typeParams, body, name]); | |
581 } | |
582 | |
583 final _hasDeferredSupertype = new HashSet<ClassElement>(); | |
584 | |
585 bool _deferIfNeeded(DartType type, ClassElement current) { | 676 bool _deferIfNeeded(DartType type, ClassElement current) { |
586 if (type is ParameterizedType) { | 677 if (type is ParameterizedType) { |
587 var typeArguments = type.typeArguments; | 678 var typeArguments = type.typeArguments; |
588 for (var typeArg in typeArguments) { | 679 for (var typeArg in typeArguments) { |
589 var typeElement = typeArg.element; | 680 var typeElement = typeArg.element; |
590 // FIXME(vsm): This does not track mutual recursive dependences. | 681 // FIXME(vsm): This does not track mutual recursive dependences. |
591 if (current == typeElement || _deferIfNeeded(typeArg, current)) { | 682 if (current == typeElement || _deferIfNeeded(typeArg, current)) { |
592 return true; | 683 return true; |
593 } | 684 } |
594 } | 685 } |
595 } | 686 } |
596 return false; | 687 return false; |
597 } | 688 } |
598 | 689 |
599 JS.Statement _emitClassDeclaration( | 690 JS.ClassExpression _emitClassExpression( |
600 ClassElement element, List<JS.Method> methods, | 691 ClassElement element, List<JS.Method> methods, |
601 {List<FieldDeclaration> fields}) { | 692 {List<FieldDeclaration> fields}) { |
602 String name = element.name; | 693 String name = element.name; |
603 var heritage = _emitClassHeritage(element); | 694 var heritage = _emitClassHeritage(element); |
604 var typeParams = _emitTypeFormals(element.typeParameters); | 695 var typeParams = _emitTypeFormals(element.typeParameters); |
605 var jsFields = fields?.map(_emitTypeScriptField)?.toList(); | 696 var jsFields = fields?.map(_emitTypeScriptField)?.toList(); |
606 | 697 |
607 // Workaround for Closure: super classes must be qualified paths. | 698 return new JS.ClassExpression(new JS.Identifier(name), heritage, methods, |
608 // TODO(jmesserly): is there a bug filed? we need to get out of the business | 699 typeParams: typeParams, fields: jsFields); |
609 // of working around bugs in other transpilers... | |
610 JS.Statement workaroundSuper = null; | |
611 if (options.closure && heritage is JS.Call) { | |
612 var superVar = new JS.TemporaryId('$name\$super'); | |
613 workaroundSuper = js.statement('const # = #;', [superVar, heritage]); | |
614 heritage = superVar; | |
615 } | |
616 var decl = new JS.ClassDeclaration(new JS.ClassExpression( | |
617 new JS.Identifier(name), heritage, methods, | |
618 typeParams: typeParams, fields: jsFields)); | |
619 if (workaroundSuper != null) { | |
620 return new JS.Block([workaroundSuper, decl]); | |
621 } | |
622 return decl; | |
623 } | 700 } |
624 | 701 |
625 JS.Expression _emitClassHeritage(ClassElement element) { | 702 JS.Expression _emitClassHeritage(ClassElement element) { |
626 var type = element.type; | 703 var type = element.type; |
627 if (type.isObject) return null; | 704 if (type.isObject) return null; |
628 | 705 |
629 // Assume we can load eagerly, until proven otherwise. | |
630 _loader.startTopLevel(element); | 706 _loader.startTopLevel(element); |
631 | 707 |
632 // Find the super type | 708 // Find the super type |
633 JS.Expression heritage; | 709 JS.Expression heritage; |
634 var supertype = type.superclass; | 710 var supertype = type.superclass; |
635 if (_deferIfNeeded(supertype, element)) { | 711 if (_deferIfNeeded(supertype, element)) { |
636 // Fall back to raw type. | 712 // Fall back to raw type. |
637 supertype = fillDynamicTypeArgs(supertype.element.type, _types); | 713 supertype = fillDynamicTypeArgs(supertype.element.type); |
638 _hasDeferredSupertype.add(element); | 714 _hasDeferredSupertype.add(element); |
639 } | 715 } |
640 heritage = _emitTypeName(supertype); | 716 heritage = _emitTypeName(supertype); |
641 | 717 |
642 if (type.mixins.isNotEmpty) { | 718 if (type.mixins.isNotEmpty) { |
643 var mixins = type.mixins.map(_emitTypeName).toList(); | 719 var mixins = type.mixins.map(_emitTypeName).toList(); |
644 mixins.insert(0, heritage); | 720 mixins.insert(0, heritage); |
645 heritage = js.call('dart.mixin(#)', [mixins]); | 721 heritage = js.call('dart.mixin(#)', [mixins]); |
646 } | 722 } |
647 | 723 |
648 _loader.finishTopLevel(element); | 724 _loader.finishTopLevel(element); |
| 725 |
649 return heritage; | 726 return heritage; |
650 } | 727 } |
651 | 728 |
652 /// Provide Dart getters and setters that forward to the underlying native | 729 /// Provide Dart getters and setters that forward to the underlying native |
653 /// field. Note that the Dart names are always symbolized to avoid | 730 /// field. Note that the Dart names are always symbolized to avoid |
654 /// conflicts. They will be installed as extension methods on the underlying | 731 /// conflicts. They will be installed as extension methods on the underlying |
655 /// native type. | 732 /// native type. |
656 List<JS.Method> _emitNativeFieldAccessors(FieldDeclaration node) { | 733 List<JS.Method> _emitNativeFieldAccessors(FieldDeclaration node) { |
657 // TODO(vsm): Can this by meta-programmed? | 734 // TODO(vsm): Can this by meta-programmed? |
658 // E.g., dart.nativeField(symbol, jsName) | 735 // E.g., dart.nativeField(symbol, jsName) |
659 // Alternatively, perhaps it could be meta-programmed directly in | 736 // Alternatively, perhaps it could be meta-programmed directly in |
660 // dart.registerExtensions? | 737 // dart.registerExtensions? |
661 var jsMethods = <JS.Method>[]; | 738 var jsMethods = <JS.Method>[]; |
662 if (!node.isStatic) { | 739 if (!node.isStatic) { |
663 for (var decl in node.fields.variables) { | 740 for (var decl in node.fields.variables) { |
664 var field = decl.element; | 741 var field = decl.element; |
665 var name = decl.name.name; | 742 var name = getAnnotationName(field, isJsName) ?? field.name; |
666 var annotation = findAnnotation(field, isJsName); | |
667 if (annotation != null) { | |
668 name = getConstantField(annotation, 'name', types.stringType) | |
669 ?.toStringValue(); | |
670 } | |
671 // Generate getter | 743 // Generate getter |
672 var fn = new JS.Fun([], js.statement('{ return this.#; }', [name])); | 744 var fn = new JS.Fun([], js.statement('{ return this.#; }', [name])); |
673 var method = | 745 var method = |
674 new JS.Method(_elementMemberName(field.getter), fn, isGetter: true); | 746 new JS.Method(_elementMemberName(field.getter), fn, isGetter: true); |
675 jsMethods.add(method); | 747 jsMethods.add(method); |
676 | 748 |
677 // Generate setter | 749 // Generate setter |
678 if (!decl.isFinal) { | 750 if (!decl.isFinal) { |
679 var value = new JS.TemporaryId('value'); | 751 var value = new JS.TemporaryId('value'); |
680 fn = new JS.Fun( | 752 fn = new JS.Fun( |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
750 // Iterable, we know the adapter is already there, so we can skip it as a | 822 // Iterable, we know the adapter is already there, so we can skip it as a |
751 // simple code size optimization. | 823 // simple code size optimization. |
752 var parent = t.lookUpGetterInSuperclass('iterator', t.element.library); | 824 var parent = t.lookUpGetterInSuperclass('iterator', t.element.library); |
753 if (parent != null) return null; | 825 if (parent != null) return null; |
754 var parentType = findSupertype(t, _implementsIterable); | 826 var parentType = findSupertype(t, _implementsIterable); |
755 if (parentType != null) return null; | 827 if (parentType != null) return null; |
756 | 828 |
757 // Otherwise, emit the adapter method, which wraps the Dart iterator in | 829 // Otherwise, emit the adapter method, which wraps the Dart iterator in |
758 // an ES6 iterator. | 830 // an ES6 iterator. |
759 return new JS.Method( | 831 return new JS.Method( |
760 js.call('$_SYMBOL.iterator'), | 832 js.call('Symbol.iterator'), |
761 js.call('function() { return new dart.JsIterator(this.#); }', | 833 js.call('function() { return new dart.JsIterator(this.#); }', |
762 [_emitMemberName('iterator', type: t)]) as JS.Fun); | 834 [_emitMemberName('iterator', type: t)]) as JS.Fun); |
763 } | 835 } |
764 | 836 |
765 JS.Expression _instantiateAnnotation(Annotation node) { | 837 JS.Expression _instantiateAnnotation(Annotation node) { |
766 var element = node.element; | 838 var element = node.element; |
767 if (element is ConstructorElement) { | 839 if (element is ConstructorElement) { |
768 return _emitInstanceCreationExpression(element, element.returnType, | 840 return _emitInstanceCreationExpression(element, element.returnType, |
769 node.constructorName, node.arguments, true); | 841 node.constructorName, node.arguments, true); |
770 } else { | 842 } else { |
771 return _visit(node.name); | 843 return _visit(node.name); |
772 } | 844 } |
773 } | 845 } |
774 | 846 |
775 /// Emit class members that need to come after the class declaration, such | 847 /// Gets the JS peer for this Dart type if any, otherwise null. |
776 /// as static fields. See [_emitClassMethods] for things that are emitted | 848 /// |
777 /// inside the ES6 `class { ... }` node. | 849 /// For example for dart:_interceptors `JSArray` this will return "Array", |
778 JS.Statement _finishClassMembers( | 850 /// referring to the JavaScript built-in `Array` type. |
779 ClassElement classElem, | 851 String _getJSPeerName(ClassElement classElem) { |
780 JS.Statement classDecl, | 852 var jsPeerName = getAnnotationName( |
781 List<ConstructorDeclaration> ctors, | 853 classElem, |
782 List<FieldDeclaration> fields, | 854 (a) => |
783 List<FieldDeclaration> staticFields, | 855 isJsPeerInterface(a) || |
784 List<MethodDeclaration> methods, | 856 isNativeAnnotation(a) && _extensionTypes.contains(classElem)); |
785 List<Annotation> metadata, | 857 if (jsPeerName != null && jsPeerName.contains(',')) { |
786 String jsPeerName) { | 858 jsPeerName = jsPeerName.split(',')[0]; |
787 var name = classElem.name; | 859 } |
788 var body = <JS.Statement>[]; | 860 return jsPeerName; |
| 861 } |
789 | 862 |
790 if (_extensionTypes.contains(classElem)) { | 863 void _registerExtensionType(ClassElement classElem, List<JS.Statement> body) { |
791 var dartxNames = <JS.Expression>[]; | 864 var jsPeerName = _getJSPeerName(classElem); |
792 for (var m in methods) { | 865 if (jsPeerName != null) { |
793 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { | 866 // TODO(jmesserly): this copies the dynamic members. |
794 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); | 867 // Probably fine for objects coming from JS, but not if we actually |
| 868 // want to support construction of instances with generic types other |
| 869 // than dynamic. See issue #154 for Array and List<E> related bug. |
| 870 body.add(js.statement('dart.registerExtension(dart.global.#, #);', |
| 871 [_propertyName(jsPeerName), _emitTopLevelName(classElem)])); |
| 872 } |
| 873 } |
| 874 |
| 875 void _setBaseClass(ClassElement classElem, JS.Expression className, |
| 876 List<JS.Statement> body) { |
| 877 String jsPeerName = _getJSPeerName(classElem); |
| 878 JS.Expression newBaseClass; |
| 879 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { |
| 880 // TODO(jmesserly): we should really just extend Array in the first place. |
| 881 newBaseClass = js.call('dart.global.#', [jsPeerName]); |
| 882 } else if (_hasDeferredSupertype.contains(classElem)) { |
| 883 newBaseClass = _emitTypeName(classElem.type.superclass); |
| 884 } |
| 885 if (newBaseClass != null) { |
| 886 body.add( |
| 887 js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); |
| 888 } |
| 889 } |
| 890 |
| 891 /// Emits instance fields, if they are virtual |
| 892 /// (in other words, they override a getter/setter pair). |
| 893 void _emitVirtualFields(List<FieldDeclaration> fields, |
| 894 JS.Expression className, List<JS.Statement> body) { |
| 895 for (FieldDeclaration member in fields) { |
| 896 for (VariableDeclaration field in member.fields.variables) { |
| 897 if (_fieldsNeedingStorage.contains(field.element)) { |
| 898 body.add(_overrideField(className, field.element)); |
795 } | 899 } |
796 } | 900 } |
797 for (var f in fields) { | |
798 if (!f.isStatic) { | |
799 for (var d in f.fields.variables) { | |
800 if (d.element.isPublic) { | |
801 dartxNames.add( | |
802 _elementMemberName(d.element.getter, allowExtensions: false)); | |
803 } | |
804 } | |
805 } | |
806 } | |
807 if (dartxNames.isNotEmpty) { | |
808 body.add(js.statement('dart.defineExtensionNames(#)', | |
809 [new JS.ArrayInitializer(dartxNames, multiline: true)])); | |
810 } | |
811 } | 901 } |
| 902 } |
812 | 903 |
813 body.add(classDecl); | 904 void _defineNamedConstructors(List<ConstructorDeclaration> ctors, |
814 | 905 List<JS.Statement> body, JS.Expression className) { |
815 // TODO(jmesserly): we should really just extend native Array. | |
816 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { | |
817 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', | |
818 [classElem.name, _propertyName(jsPeerName)])); | |
819 } | |
820 | |
821 // Deferred Superclass | |
822 if (_hasDeferredSupertype.contains(classElem)) { | |
823 body.add(js.statement('#.prototype.__proto__ = #.prototype;', | |
824 [name, _emitTypeName(classElem.type.superclass)])); | |
825 } | |
826 | |
827 // Interfaces | |
828 if (classElem.interfaces.isNotEmpty) { | |
829 body.add(js.statement('#[dart.implements] = () => #;', [ | |
830 name, | |
831 new JS.ArrayInitializer(new List<JS.Expression>.from( | |
832 classElem.interfaces.map(_emitTypeName))) | |
833 ])); | |
834 } | |
835 | |
836 // Named constructors | |
837 for (ConstructorDeclaration member in ctors) { | 906 for (ConstructorDeclaration member in ctors) { |
838 if (member.name != null && member.factoryKeyword == null) { | 907 if (member.name != null && member.factoryKeyword == null) { |
839 body.add(js.statement('dart.defineNamedConstructor(#, #);', | 908 body.add(js.statement('dart.defineNamedConstructor(#, #);', |
840 [name, _emitMemberName(member.name.name, isStatic: true)])); | 909 [className, _emitMemberName(member.name.name, isStatic: true)])); |
841 } | 910 } |
842 } | 911 } |
| 912 } |
843 | 913 |
844 // Emits instance fields, if they are virtual | 914 /// Emits static fields for a class, and initialize them eagerly if possible, |
845 // (in other words, they override a getter/setter pair). | 915 /// otherwise define them as lazy properties. |
846 for (FieldDeclaration member in fields) { | 916 void _emitStaticFields(List<FieldDeclaration> staticFields, |
| 917 ClassElement classElem, List<JS.Statement> body) { |
| 918 var lazyStatics = <VariableDeclaration>[]; |
| 919 for (FieldDeclaration member in staticFields) { |
847 for (VariableDeclaration field in member.fields.variables) { | 920 for (VariableDeclaration field in member.fields.variables) { |
848 if (_fieldsNeedingStorage.contains(field.element)) { | 921 JS.Statement eagerField = _emitConstantStaticField(classElem, field); |
849 body.add(_overrideField(field.element)); | 922 if (eagerField != null) { |
| 923 body.add(eagerField); |
| 924 } else { |
| 925 lazyStatics.add(field); |
850 } | 926 } |
851 } | 927 } |
852 } | 928 } |
| 929 if (lazyStatics.isNotEmpty) { |
| 930 body.add(_emitLazyFields(classElem, lazyStatics)); |
| 931 } |
| 932 } |
853 | 933 |
854 // Emit the signature on the class recording the runtime type information | 934 void _emitClassMetadata(List<Annotation> metadata, JS.Expression className, |
855 var extensions = _extensionsToImplement(classElem); | 935 List<JS.Statement> body) { |
856 { | 936 // TODO(vsm): Make this optional per #268. |
857 var tStatics = <JS.Property>[]; | 937 // Metadata |
858 var tMethods = <JS.Property>[]; | 938 if (metadata.isNotEmpty) { |
859 var sNames = <JS.Expression>[]; | 939 body.add(js.statement('#[dart.metadata] = () => #;', [ |
860 for (MethodDeclaration node in methods) { | 940 className, |
861 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 941 new JS.ArrayInitializer( |
862 var name = node.name.name; | 942 new List<JS.Expression>.from(metadata.map(_instantiateAnnotation))) |
863 var element = node.element; | 943 ])); |
864 var inheritedElement = | 944 } |
865 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 945 } |
866 if (inheritedElement != null && | |
867 inheritedElement.type == element.type) { | |
868 continue; | |
869 } | |
870 var memberName = _elementMemberName(element); | |
871 var parts = _emitFunctionTypeParts(element.type); | |
872 var property = | |
873 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | |
874 if (node.isStatic) { | |
875 tStatics.add(property); | |
876 sNames.add(memberName); | |
877 } else { | |
878 tMethods.add(property); | |
879 } | |
880 } | |
881 } | |
882 | 946 |
883 var tCtors = <JS.Property>[]; | 947 /// If a concrete class implements one of our extensions, we might need to |
884 for (ConstructorDeclaration node in ctors) { | 948 /// add forwarders. |
885 var memberName = _constructorName(node.element); | 949 void _defineExtensionMembers(List<ExecutableElement> extensions, |
886 var element = node.element; | 950 JS.Expression className, List<JS.Statement> body) { |
887 var parts = _emitFunctionTypeParts(element.type, node.parameters); | |
888 var property = | |
889 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | |
890 tCtors.add(property); | |
891 } | |
892 | |
893 JS.Property build(String name, List<JS.Property> elements) { | |
894 var o = | |
895 new JS.ObjectInitializer(elements, multiline: elements.length > 1); | |
896 var e = js.call('() => #', o); | |
897 return new JS.Property(_propertyName(name), e); | |
898 } | |
899 var sigFields = <JS.Property>[]; | |
900 if (!tCtors.isEmpty) sigFields.add(build('constructors', tCtors)); | |
901 if (!tMethods.isEmpty) sigFields.add(build('methods', tMethods)); | |
902 if (!tStatics.isEmpty) { | |
903 assert(!sNames.isEmpty); | |
904 var aNames = new JS.Property( | |
905 _propertyName('names'), new JS.ArrayInitializer(sNames)); | |
906 sigFields.add(build('statics', tStatics)); | |
907 sigFields.add(aNames); | |
908 } | |
909 if (!sigFields.isEmpty || extensions.isNotEmpty) { | |
910 var sig = new JS.ObjectInitializer(sigFields); | |
911 var classExpr = new JS.Identifier(name); | |
912 body.add(js.statement('dart.setSignature(#, #);', [classExpr, sig])); | |
913 } | |
914 } | |
915 | |
916 // If a concrete class implements one of our extensions, we might need to | 951 // If a concrete class implements one of our extensions, we might need to |
917 // add forwarders. | 952 // add forwarders. |
918 if (extensions.isNotEmpty) { | 953 if (extensions.isNotEmpty) { |
919 var methodNames = <JS.Expression>[]; | 954 var methodNames = <JS.Expression>[]; |
920 for (var e in extensions) { | 955 for (var e in extensions) { |
921 methodNames.add(_elementMemberName(e)); | 956 methodNames.add(_elementMemberName(e)); |
922 } | 957 } |
923 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ | 958 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ |
924 name, | 959 className, |
925 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) | 960 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) |
926 ])); | 961 ])); |
927 } | 962 } |
| 963 } |
928 | 964 |
929 // TODO(vsm): Make this optional per #268. | 965 /// Emit the signature on the class recording the runtime type information |
930 // Metadata | 966 void _emitClassSignature( |
931 if (metadata.isNotEmpty) { | 967 List<MethodDeclaration> methods, |
932 body.add(js.statement('#[dart.metadata] = () => #;', [ | 968 ClassElement classElem, |
933 name, | 969 List<ConstructorDeclaration> ctors, |
934 new JS.ArrayInitializer( | 970 List<ExecutableElement> extensions, |
935 new List<JS.Expression>.from(metadata.map(_instantiateAnnotation))) | 971 JS.Expression className, |
| 972 List<JS.Statement> body) { |
| 973 if (classElem.interfaces.isNotEmpty) { |
| 974 body.add(js.statement('#[dart.implements] = () => #;', [ |
| 975 className, |
| 976 new JS.ArrayInitializer(new List<JS.Expression>.from( |
| 977 classElem.interfaces.map(_emitTypeName))) |
936 ])); | 978 ])); |
937 } | 979 } |
938 | 980 |
939 // Emits static fields. These are eager initialized if possible, otherwise | 981 var tStatics = <JS.Property>[]; |
940 // they are made lazy. | 982 var tMethods = <JS.Property>[]; |
941 var lazyStatics = <VariableDeclaration>[]; | 983 var sNames = <JS.Expression>[]; |
942 for (FieldDeclaration member in staticFields) { | 984 for (MethodDeclaration node in methods) { |
943 for (VariableDeclaration field in member.fields.variables) { | 985 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
944 JS.Statement eagerField = _emitConstantStaticField(classElem, field); | 986 var name = node.name.name; |
945 if (eagerField != null) { | 987 var element = node.element; |
946 body.add(eagerField); | 988 var inheritedElement = |
| 989 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
| 990 if (inheritedElement != null && inheritedElement.type == element.type) { |
| 991 continue; |
| 992 } |
| 993 var memberName = _elementMemberName(element); |
| 994 var parts = _emitFunctionTypeParts(element.type); |
| 995 var property = |
| 996 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| 997 if (node.isStatic) { |
| 998 tStatics.add(property); |
| 999 sNames.add(memberName); |
947 } else { | 1000 } else { |
948 lazyStatics.add(field); | 1001 tMethods.add(property); |
949 } | 1002 } |
950 } | 1003 } |
951 } | 1004 } |
952 if (lazyStatics.isNotEmpty) { | 1005 |
953 body.add(_emitLazyFields(classElem, lazyStatics)); | 1006 var tCtors = <JS.Property>[]; |
| 1007 for (ConstructorDeclaration node in ctors) { |
| 1008 var memberName = _constructorName(node.element); |
| 1009 var element = node.element; |
| 1010 var parts = _emitFunctionTypeParts(element.type, node.parameters); |
| 1011 var property = |
| 1012 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| 1013 tCtors.add(property); |
954 } | 1014 } |
955 | 1015 |
956 return _statement(body); | 1016 JS.Property build(String name, List<JS.Property> elements) { |
| 1017 var o = |
| 1018 new JS.ObjectInitializer(elements, multiline: elements.length > 1); |
| 1019 var e = js.call('() => #', o); |
| 1020 return new JS.Property(_propertyName(name), e); |
| 1021 } |
| 1022 var sigFields = <JS.Property>[]; |
| 1023 if (!tCtors.isEmpty) sigFields.add(build('constructors', tCtors)); |
| 1024 if (!tMethods.isEmpty) sigFields.add(build('methods', tMethods)); |
| 1025 if (!tStatics.isEmpty) { |
| 1026 assert(!sNames.isEmpty); |
| 1027 var aNames = new JS.Property( |
| 1028 _propertyName('names'), new JS.ArrayInitializer(sNames)); |
| 1029 sigFields.add(build('statics', tStatics)); |
| 1030 sigFields.add(aNames); |
| 1031 } |
| 1032 if (!sigFields.isEmpty || extensions.isNotEmpty) { |
| 1033 var sig = new JS.ObjectInitializer(sigFields); |
| 1034 body.add(js.statement('dart.setSignature(#, #);', [className, sig])); |
| 1035 } |
| 1036 } |
| 1037 |
| 1038 /// Ensure `dartx.` symbols we will use are present. |
| 1039 void _initExtensionSymbols( |
| 1040 ClassElement classElem, |
| 1041 List<MethodDeclaration> methods, |
| 1042 List<FieldDeclaration> fields, |
| 1043 List<JS.Statement> body) { |
| 1044 if (_extensionTypes.contains(classElem)) { |
| 1045 var dartxNames = <JS.Expression>[]; |
| 1046 for (var m in methods) { |
| 1047 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { |
| 1048 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); |
| 1049 } |
| 1050 } |
| 1051 for (var f in fields) { |
| 1052 if (!f.isStatic) { |
| 1053 for (var d in f.fields.variables) { |
| 1054 if (d.element.isPublic) { |
| 1055 dartxNames.add( |
| 1056 _elementMemberName(d.element.getter, allowExtensions: false)); |
| 1057 } |
| 1058 } |
| 1059 } |
| 1060 } |
| 1061 if (dartxNames.isNotEmpty) { |
| 1062 body.add(js.statement('dart.defineExtensionNames(#)', |
| 1063 [new JS.ArrayInitializer(dartxNames, multiline: true)])); |
| 1064 } |
| 1065 } |
957 } | 1066 } |
958 | 1067 |
959 List<ExecutableElement> _extensionsToImplement(ClassElement element) { | 1068 List<ExecutableElement> _extensionsToImplement(ClassElement element) { |
960 var members = <ExecutableElement>[]; | 1069 var members = <ExecutableElement>[]; |
961 if (_extensionTypes.contains(element)) return members; | 1070 if (_extensionTypes.contains(element)) return members; |
962 | 1071 |
963 // Collect all extension types we implement. | 1072 // Collect all extension types we implement. |
964 var type = element.type; | 1073 var type = element.type; |
965 var types = new Set<ClassElement>(); | 1074 var types = new Set<ClassElement>(); |
966 _collectExtensions(type, types); | 1075 _collectExtensions(type, types); |
(...skipping 24 matching lines...) Expand all Loading... |
991 if (_extensionTypes.contains(element)) types.add(element); | 1100 if (_extensionTypes.contains(element)) types.add(element); |
992 for (var m in type.mixins.reversed) { | 1101 for (var m in type.mixins.reversed) { |
993 _collectExtensions(m, types); | 1102 _collectExtensions(m, types); |
994 } | 1103 } |
995 for (var i in type.interfaces) { | 1104 for (var i in type.interfaces) { |
996 _collectExtensions(i, types); | 1105 _collectExtensions(i, types); |
997 } | 1106 } |
998 _collectExtensions(type.superclass, types); | 1107 _collectExtensions(type.superclass, types); |
999 } | 1108 } |
1000 | 1109 |
1001 JS.Statement _overrideField(FieldElement e) { | 1110 JS.Statement _overrideField(JS.Expression className, FieldElement e) { |
1002 var cls = e.enclosingElement; | 1111 var cls = e.enclosingElement; |
1003 return js.statement('dart.virtualField(#, #)', | 1112 return js.statement('dart.virtualField(#, #)', |
1004 [cls.name, _emitMemberName(e.name, type: cls.type)]); | 1113 [className, _emitMemberName(e.name, type: cls.type)]); |
1005 } | 1114 } |
1006 | 1115 |
1007 /// Generates the implicit default constructor for class C of the form | 1116 /// Generates the implicit default constructor for class C of the form |
1008 /// `C() : super() {}`. | 1117 /// `C() : super() {}`. |
1009 JS.Method _emitImplicitConstructor( | 1118 JS.Method _emitImplicitConstructor( |
1010 ClassDeclaration node, List<FieldDeclaration> fields) { | 1119 ClassDeclaration node, List<FieldDeclaration> fields) { |
1011 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); | 1120 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); |
1012 | 1121 |
1013 // If we don't have a method body, skip this. | 1122 // If we don't have a method body, skip this. |
1014 var superCall = _superConstructorCall(node.element); | 1123 var superCall = _superConstructorCall(node.element); |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1122 // Other default constructors use the class name, as they aren't called | 1231 // Other default constructors use the class name, as they aren't called |
1123 // from call sites, but rather from Object's constructor. | 1232 // from call sites, but rather from Object's constructor. |
1124 // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning | 1233 // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning |
1125 // up constructors to integrate more closely with ES6. | 1234 // up constructors to integrate more closely with ES6. |
1126 return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name); | 1235 return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name); |
1127 } | 1236 } |
1128 | 1237 |
1129 JS.Block _emitConstructorBody( | 1238 JS.Block _emitConstructorBody( |
1130 ConstructorDeclaration node, List<FieldDeclaration> fields) { | 1239 ConstructorDeclaration node, List<FieldDeclaration> fields) { |
1131 var body = <JS.Statement>[]; | 1240 var body = <JS.Statement>[]; |
| 1241 ClassDeclaration cls = node.parent; |
1132 | 1242 |
1133 // Generate optional/named argument value assignment. These can not have | 1243 // Generate optional/named argument value assignment. These can not have |
1134 // side effects, and may be used by the constructor's initializers, so it's | 1244 // side effects, and may be used by the constructor's initializers, so it's |
1135 // nice to do them first. | 1245 // nice to do them first. |
1136 // Also for const constructors we need to ensure default values are | 1246 // Also for const constructors we need to ensure default values are |
1137 // available for use by top-level constant initializers. | 1247 // available for use by top-level constant initializers. |
1138 ClassDeclaration cls = node.parent; | |
1139 if (node.constKeyword != null) _loader.startTopLevel(cls.element); | 1248 if (node.constKeyword != null) _loader.startTopLevel(cls.element); |
1140 var init = _emitArgumentInitializers(node, constructor: true); | 1249 var init = _emitArgumentInitializers(node, constructor: true); |
1141 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); | 1250 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); |
1142 if (init != null) body.add(init); | 1251 if (init != null) body.add(init); |
1143 | 1252 |
1144 // Redirecting constructors: these are not allowed to have initializers, | 1253 // Redirecting constructors: these are not allowed to have initializers, |
1145 // and the redirecting ctor invocation runs before field initializers. | 1254 // and the redirecting ctor invocation runs before field initializers. |
1146 var redirectCall = node.initializers.firstWhere( | 1255 var redirectCall = node.initializers.firstWhere( |
1147 (i) => i is RedirectingConstructorInvocation, | 1256 (i) => i is RedirectingConstructorInvocation, |
1148 orElse: () => null); | 1257 orElse: () => null); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1184 ConstructorElement superCtor; | 1293 ConstructorElement superCtor; |
1185 if (node != null) { | 1294 if (node != null) { |
1186 superCtor = node.staticElement; | 1295 superCtor = node.staticElement; |
1187 } else { | 1296 } else { |
1188 // Get the supertype's unnamed constructor. | 1297 // Get the supertype's unnamed constructor. |
1189 superCtor = element.supertype.element.unnamedConstructor; | 1298 superCtor = element.supertype.element.unnamedConstructor; |
1190 if (superCtor == null) { | 1299 if (superCtor == null) { |
1191 // This will only happen if the code has errors: | 1300 // This will only happen if the code has errors: |
1192 // we're trying to generate an implicit constructor for a type where | 1301 // we're trying to generate an implicit constructor for a type where |
1193 // we don't have a default constructor in the supertype. | 1302 // we don't have a default constructor in the supertype. |
1194 assert(options.forceCompile); | 1303 assert(options.unsafeForceCompile); |
1195 return null; | 1304 return null; |
1196 } | 1305 } |
1197 } | 1306 } |
1198 | 1307 |
1199 if (superCtor == null) { | 1308 if (superCtor == null) { |
1200 print('Error generating: ${element.displayName}'); | 1309 print('Error generating: ${element.displayName}'); |
1201 } | 1310 } |
1202 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { | 1311 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { |
1203 return null; | 1312 return null; |
1204 } | 1313 } |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1363 } | 1472 } |
1364 } | 1473 } |
1365 | 1474 |
1366 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { | 1475 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { |
1367 if (node.isStatic) { | 1476 if (node.isStatic) { |
1368 // TODO(vsm): Do we need to handle this case? | 1477 // TODO(vsm): Do we need to handle this case? |
1369 return null; | 1478 return null; |
1370 } | 1479 } |
1371 | 1480 |
1372 var params = visitFormalParameterList(node.parameters, destructure: false); | 1481 var params = visitFormalParameterList(node.parameters, destructure: false); |
1373 String name = node.name.name; | 1482 String name = |
1374 var annotation = findAnnotation(node.element, isJsName); | 1483 getAnnotationName(node.element, isJSAnnotation) ?? node.name.name; |
1375 if (annotation != null) { | |
1376 name = getConstantField(annotation, 'name', types.stringType) | |
1377 ?.toStringValue(); | |
1378 } | |
1379 if (node.isGetter) { | 1484 if (node.isGetter) { |
1380 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); | 1485 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); |
1381 } else if (node.isSetter) { | 1486 } else if (node.isSetter) { |
1382 return new JS.Fun( | 1487 return new JS.Fun( |
1383 params, js.statement('{ this.# = #; }', [name, params.last])); | 1488 params, js.statement('{ this.# = #; }', [name, params.last])); |
1384 } else { | 1489 } else { |
1385 return new JS.Fun( | 1490 return new JS.Fun( |
1386 params, js.statement('{ return this.#(#); }', [name, params])); | 1491 params, js.statement('{ return this.#(#); }', [name, params])); |
1387 } | 1492 } |
1388 } | 1493 } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1438 returnType: fn.returnType)..sourceInformation = fn.sourceInformation; | 1543 returnType: fn.returnType)..sourceInformation = fn.sourceInformation; |
1439 } | 1544 } |
1440 | 1545 |
1441 @override | 1546 @override |
1442 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1547 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
1443 assert(node.parent is CompilationUnit); | 1548 assert(node.parent is CompilationUnit); |
1444 | 1549 |
1445 if (_externalOrNative(node)) return null; | 1550 if (_externalOrNative(node)) return null; |
1446 | 1551 |
1447 if (node.isGetter || node.isSetter) { | 1552 if (node.isGetter || node.isSetter) { |
1448 // Add these later so we can use getter/setter syntax. | 1553 // If we have a getter/setter pair, they need to be defined together. |
1449 _properties.add(node); | 1554 PropertyAccessorElement element = node.element; |
1450 return null; | 1555 var props = <JS.Method>[]; |
| 1556 var getter = element.variable.getter; |
| 1557 if (getter != null) { |
| 1558 props.add(_loader.customEmitDeclaration(getter, _emitTopLevelProperty)); |
| 1559 } |
| 1560 var setter = element.variable.setter; |
| 1561 if (setter != null) { |
| 1562 props.add(_loader.customEmitDeclaration(setter, _emitTopLevelProperty)); |
| 1563 } |
| 1564 |
| 1565 return js.statement('dart.copyProperties(#, { # });', |
| 1566 [emitLibraryName(currentLibrary), props]); |
1451 } | 1567 } |
1452 | 1568 |
1453 var body = <JS.Statement>[]; | 1569 var body = <JS.Statement>[]; |
1454 _flushLibraryProperties(body); | |
1455 | |
1456 var name = node.name.name; | |
1457 var fn = _emitFunction(node.functionExpression); | 1570 var fn = _emitFunction(node.functionExpression); |
1458 | 1571 |
1459 if (currentLibrary.source.isInSystemLibrary && | 1572 if (currentLibrary.source.isInSystemLibrary && |
1460 _isInlineJSFunction(node.functionExpression)) { | 1573 _isInlineJSFunction(node.functionExpression)) { |
1461 fn = _simplifyPassThroughArrowFunCallBody(fn); | 1574 fn = _simplifyPassThroughArrowFunCallBody(fn); |
1462 } | 1575 } |
1463 | 1576 |
1464 var id = new JS.Identifier(name); | 1577 var element = node.element; |
1465 body.add(annotate(new JS.FunctionDeclaration(id, fn), node, node.element)); | 1578 var nameExpr = _emitTopLevelName(element); |
1466 if (!_isDartRuntime) { | 1579 body.add(annotate(js.statement('# = #', [nameExpr, fn]), node, element)); |
1467 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) | 1580 if (!_isDartRuntime(element.library)) { |
| 1581 body.add(_emitFunctionTagged(nameExpr, element.type, topLevel: true) |
1468 .toStatement()); | 1582 .toStatement()); |
1469 } | 1583 } |
1470 | 1584 |
1471 if (isPublic(name)) { | |
1472 _addExport(name, getJSExportName(node.element, types) ?? name); | |
1473 } | |
1474 return _statement(body); | 1585 return _statement(body); |
1475 } | 1586 } |
1476 | 1587 |
1477 bool _isInlineJSFunction(FunctionExpression functionExpression) { | 1588 bool _isInlineJSFunction(FunctionExpression functionExpression) { |
1478 var body = functionExpression.body; | 1589 var body = functionExpression.body; |
1479 if (body is ExpressionFunctionBody) { | 1590 if (body is ExpressionFunctionBody) { |
1480 return _isJSInvocation(body.expression); | 1591 return _isJSInvocation(body.expression); |
1481 } else if (body is BlockFunctionBody) { | 1592 } else if (body is BlockFunctionBody) { |
1482 if (body.block.statements.length == 1) { | 1593 var statements = body.block.statements; |
1483 var stat = body.block.statements.single; | 1594 if (statements.length == 1) { |
| 1595 var stat = statements[0]; |
1484 if (stat is ReturnStatement) { | 1596 if (stat is ReturnStatement) { |
1485 return _isJSInvocation(stat.expression); | 1597 return _isJSInvocation(stat.expression); |
1486 } | 1598 } |
1487 } | 1599 } |
1488 } | 1600 } |
1489 return false; | 1601 return false; |
1490 } | 1602 } |
1491 | 1603 |
1492 bool _isJSInvocation(Expression expr) => | 1604 bool _isJSInvocation(Expression expr) => |
1493 expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); | 1605 expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1549 var name = type.name; | 1661 var name = type.name; |
1550 var lazy = topLevel && !_typeIsLoaded(type); | 1662 var lazy = topLevel && !_typeIsLoaded(type); |
1551 | 1663 |
1552 if (type is FunctionType && (name == '' || name == null)) { | 1664 if (type is FunctionType && (name == '' || name == null)) { |
1553 if (type.returnType.isDynamic && | 1665 if (type.returnType.isDynamic && |
1554 type.optionalParameterTypes.isEmpty && | 1666 type.optionalParameterTypes.isEmpty && |
1555 type.namedParameterTypes.isEmpty && | 1667 type.namedParameterTypes.isEmpty && |
1556 type.normalParameterTypes.every((t) => t.isDynamic)) { | 1668 type.normalParameterTypes.every((t) => t.isDynamic)) { |
1557 return js.call('dart.fn(#)', [fn]); | 1669 return js.call('dart.fn(#)', [fn]); |
1558 } | 1670 } |
1559 if (lazy) { | 1671 |
1560 return js.call('dart.fn(#, () => #)', [fn, _emitFunctionRTTI(type)]); | 1672 String code = lazy ? '() => dart.definiteFunctionType(#)' : '#'; |
1561 } | 1673 return js.call('dart.fn(#, $code)', [fn, _emitFunctionTypeParts(type)]); |
1562 return js.call('dart.fn(#, #)', [fn, _emitFunctionTypeParts(type)]); | |
1563 } | 1674 } |
1564 throw 'Function has non function type: $type'; | 1675 throw 'Function has non function type: $type'; |
1565 } | 1676 } |
1566 | 1677 |
1567 /// Emits an arrow FunctionExpression node. | 1678 /// Emits an arrow FunctionExpression node. |
1568 /// | 1679 /// |
1569 /// This should be used for all places in Dart's AST where FunctionExpression | 1680 /// This should be used for all places in Dart's AST where FunctionExpression |
1570 /// appears and the function is actually in an Expression context. These | 1681 /// appears and the function is actually in an Expression context. These |
1571 /// correspond to arrow functions in Dart. | 1682 /// correspond to arrow functions in Dart. |
1572 /// | 1683 /// |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1729 | 1840 |
1730 // Get the original declaring element. If we had a property accessor, this | 1841 // Get the original declaring element. If we had a property accessor, this |
1731 // indirects back to a (possibly synthetic) field. | 1842 // indirects back to a (possibly synthetic) field. |
1732 var element = accessor; | 1843 var element = accessor; |
1733 if (accessor is PropertyAccessorElement) element = accessor.variable; | 1844 if (accessor is PropertyAccessorElement) element = accessor.variable; |
1734 | 1845 |
1735 _loader.declareBeforeUse(element); | 1846 _loader.declareBeforeUse(element); |
1736 | 1847 |
1737 // type literal | 1848 // type literal |
1738 if (element is TypeDefiningElement) { | 1849 if (element is TypeDefiningElement) { |
1739 return _emitTypeName( | 1850 return _emitTypeName(fillDynamicTypeArgs(element.type)); |
1740 fillDynamicTypeArgs((element as dynamic).type, types)); | |
1741 } | 1851 } |
1742 | 1852 |
1743 // library member | 1853 // library member |
1744 if (element.enclosingElement is CompilationUnitElement) { | 1854 if (element.enclosingElement is CompilationUnitElement) { |
1745 return _emitTopLevelName(element); | 1855 return _emitTopLevelName(element); |
1746 } | 1856 } |
1747 | 1857 |
1748 var name = element.name; | 1858 var name = element.name; |
1749 | 1859 |
1750 // Unqualified class member. This could mean implicit-this, or implicit | 1860 // Unqualified class member. This could mean implicit-this, or implicit |
1751 // call to a static from the same class. | 1861 // call to a static from the same class. |
1752 if (element is ClassMemberElement && element is! ConstructorElement) { | 1862 if (element is ClassMemberElement && element is! ConstructorElement) { |
1753 bool isStatic = element.isStatic; | 1863 bool isStatic = element.isStatic; |
1754 var type = element.enclosingElement.type; | 1864 var type = element.enclosingElement.type; |
1755 var member = _emitMemberName(name, isStatic: isStatic, type: type); | 1865 var member = _emitMemberName(name, isStatic: isStatic, type: type); |
1756 | 1866 |
1757 // For static methods, we add the raw type name, without generics or | 1867 // For static methods, we add the raw type name, without generics or |
1758 // library prefix. We don't need those because static calls can't use | 1868 // library prefix. We don't need those because static calls can't use |
1759 // the generic type. | 1869 // the generic type. |
1760 if (isStatic) { | 1870 if (isStatic) { |
1761 var dynType = _emitTypeName(fillDynamicTypeArgs(type, types)); | 1871 var dynType = _emitTypeName(fillDynamicTypeArgs(type)); |
1762 return new JS.PropertyAccess(dynType, member); | 1872 return new JS.PropertyAccess(dynType, member); |
1763 } | 1873 } |
1764 | 1874 |
1765 // For instance members, we add implicit-this. | 1875 // For instance members, we add implicit-this. |
1766 // For method tear-offs, we ensure it's a bound method. | 1876 // For method tear-offs, we ensure it's a bound method. |
1767 var tearOff = element is MethodElement && !inInvocationContext(node); | 1877 var tearOff = element is MethodElement && !inInvocationContext(node); |
1768 var code = (tearOff) ? 'dart.bind(this, #)' : 'this.#'; | 1878 var code = (tearOff) ? 'dart.bind(this, #)' : 'this.#'; |
1769 return js.call(code, member); | 1879 return js.call(code, member); |
1770 } | 1880 } |
1771 | 1881 |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1854 } | 1964 } |
1855 if (!optionalTypes.isEmpty) { | 1965 if (!optionalTypes.isEmpty) { |
1856 assert(namedTypes.isEmpty); | 1966 assert(namedTypes.isEmpty); |
1857 var oa = _emitTypeNames( | 1967 var oa = _emitTypeNames( |
1858 optionalTypes, parameters?.sublist(parameterTypes.length)); | 1968 optionalTypes, parameters?.sublist(parameterTypes.length)); |
1859 return [rt, ra, oa]; | 1969 return [rt, ra, oa]; |
1860 } | 1970 } |
1861 return [rt, ra]; | 1971 return [rt, ra]; |
1862 } | 1972 } |
1863 | 1973 |
1864 JS.Expression _emitFunctionRTTI(FunctionType type) { | |
1865 var parts = _emitFunctionTypeParts(type); | |
1866 return js.call('dart.definiteFunctionType(#)', [parts]); | |
1867 } | |
1868 | |
1869 /// Emits a Dart [type] into code. | 1974 /// Emits a Dart [type] into code. |
1870 /// | 1975 /// |
1871 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a | 1976 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a |
1872 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form | 1977 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form |
1873 /// will be used instead of `List`. These flags are used when generating | 1978 /// will be used instead of `List`. These flags are used when generating |
1874 /// the definitions for typedefs and generic types, respectively. | 1979 /// the definitions for typedefs and generic types, respectively. |
1875 JS.Expression _emitTypeName(DartType type, | 1980 JS.Expression _emitTypeName(DartType type, |
1876 {bool lowerTypedef: false, bool lowerGeneric: false}) { | 1981 {bool lowerTypedef: false, bool lowerGeneric: false}) { |
1877 // The void and dynamic types are not defined in core. | 1982 // The void and dynamic types are not defined in core. |
1878 if (type.isVoid) { | 1983 if (type.isVoid) { |
(...skipping 10 matching lines...) Expand all Loading... |
1889 // methods? Similar issue with generic types. For all of these, we may want | 1994 // methods? Similar issue with generic types. For all of these, we may want |
1890 // to canonicalize them too, at least when inside the same library. | 1995 // to canonicalize them too, at least when inside the same library. |
1891 var name = type.name; | 1996 var name = type.name; |
1892 var element = type.element; | 1997 var element = type.element; |
1893 if (name == '' || name == null || lowerTypedef) { | 1998 if (name == '' || name == null || lowerTypedef) { |
1894 var parts = _emitFunctionTypeParts(type as FunctionType); | 1999 var parts = _emitFunctionTypeParts(type as FunctionType); |
1895 return js.call('dart.functionType(#)', [parts]); | 2000 return js.call('dart.functionType(#)', [parts]); |
1896 } | 2001 } |
1897 // For now, reify generic method parameters as dynamic | 2002 // For now, reify generic method parameters as dynamic |
1898 bool _isGenericTypeParameter(DartType type) => | 2003 bool _isGenericTypeParameter(DartType type) => |
1899 (type is TypeParameterType) && | 2004 type is TypeParameterType && |
1900 !(type.element.enclosingElement is ClassElement || | 2005 type.element.enclosingElement is! TypeDefiningElement; |
1901 type.element.enclosingElement is FunctionTypeAliasElement); | |
1902 | 2006 |
1903 if (_isGenericTypeParameter(type)) { | 2007 if (_isGenericTypeParameter(type)) { |
1904 return js.call('dart.dynamic'); | 2008 return js.call('dart.dynamic'); |
1905 } | 2009 } |
1906 | 2010 |
1907 if (type is TypeParameterType) { | 2011 if (type is TypeParameterType) { |
1908 return new JS.Identifier(name); | 2012 return new JS.Identifier(name); |
1909 } | 2013 } |
1910 | 2014 |
1911 if (type is ParameterizedType) { | 2015 if (type is ParameterizedType) { |
1912 var args = type.typeArguments; | 2016 var args = type.typeArguments; |
1913 var isCurrentClass = | |
1914 args.isNotEmpty && _loader.isCurrentElement(type.element); | |
1915 Iterable jsArgs = null; | 2017 Iterable jsArgs = null; |
1916 if (args | 2018 if (args.any((a) => !a.isDynamic && !_isGenericTypeParameter(a))) { |
1917 .any((a) => a != types.dynamicType && !_isGenericTypeParameter(a))) { | |
1918 jsArgs = args.map(_emitTypeName); | 2019 jsArgs = args.map(_emitTypeName); |
1919 } else if (lowerGeneric || isCurrentClass) { | 2020 } else if (lowerGeneric) { |
1920 // When creating a `new S<dynamic>` we try and use the raw form | |
1921 // `new S()`, but this does not work if we're inside the same class, | |
1922 // because `S` refers to the current S<T> we are generating. | |
1923 jsArgs = []; | 2021 jsArgs = []; |
1924 } | 2022 } |
1925 if (jsArgs != null) { | 2023 if (jsArgs != null) { |
1926 var genericName = _emitTopLevelName(element, suffix: '\$'); | 2024 var genericName = _emitTopLevelName(element, suffix: '\$'); |
1927 return js.call('#(#)', [genericName, jsArgs]); | 2025 return js.call('#(#)', [genericName, jsArgs]); |
1928 } | 2026 } |
1929 } | 2027 } |
1930 | 2028 |
1931 return _emitTopLevelName(element); | 2029 return _emitTopLevelName(element); |
1932 } | 2030 } |
1933 | 2031 |
1934 JS.Expression _emitTopLevelName(Element e, {String suffix: ''}) { | 2032 JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { |
1935 var libName = emitLibraryName(e.library); | 2033 String name = getJSExportName(e) + suffix; |
1936 | 2034 return new JS.PropertyAccess( |
1937 // Always qualify: | 2035 emitLibraryName(e.library), _propertyName(name)); |
1938 // * mutable top-level fields | |
1939 // * elements from other libraries | |
1940 bool mutableTopLevel = e is TopLevelVariableElement && | |
1941 !e.isConst && | |
1942 !_isFinalJSDecl(e.computeNode()); | |
1943 bool fromAnotherLibrary = e.library != currentLibrary; | |
1944 var nameExpr; | |
1945 if (fromAnotherLibrary) { | |
1946 nameExpr = _propertyName((getJSExportName(e, types) ?? e.name) + suffix); | |
1947 } else { | |
1948 nameExpr = _propertyName(e.name + suffix); | |
1949 } | |
1950 if (mutableTopLevel || fromAnotherLibrary) { | |
1951 return new JS.PropertyAccess(libName, nameExpr); | |
1952 } | |
1953 | |
1954 var id = new JS.MaybeQualifiedId(libName, nameExpr); | |
1955 _qualifiedIds.add(new Tuple2(e, id)); | |
1956 return id; | |
1957 } | 2036 } |
1958 | 2037 |
1959 @override | 2038 @override |
1960 JS.Expression visitAssignmentExpression(AssignmentExpression node) { | 2039 JS.Expression visitAssignmentExpression(AssignmentExpression node) { |
1961 var left = node.leftHandSide; | 2040 var left = node.leftHandSide; |
1962 var right = node.rightHandSide; | 2041 var right = node.rightHandSide; |
1963 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); | 2042 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); |
1964 var op = node.operator.lexeme; | 2043 var op = node.operator.lexeme; |
1965 assert(op.endsWith('=')); | 2044 assert(op.endsWith('=')); |
1966 op = op.substring(0, op.length - 1); // remove trailing '=' | 2045 op = op.substring(0, op.length - 1); // remove trailing '=' |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2015 } | 2094 } |
2016 | 2095 |
2017 target = _getTarget(lhs); | 2096 target = _getTarget(lhs); |
2018 id = lhs.propertyName; | 2097 id = lhs.propertyName; |
2019 } else if (lhs is PrefixedIdentifier) { | 2098 } else if (lhs is PrefixedIdentifier) { |
2020 target = lhs.prefix; | 2099 target = lhs.prefix; |
2021 id = lhs.identifier; | 2100 id = lhs.identifier; |
2022 } | 2101 } |
2023 | 2102 |
2024 if (target != null && DynamicInvoke.get(target)) { | 2103 if (target != null && DynamicInvoke.get(target)) { |
2025 return js.call('dart.$DPUT(#, #, #)', | 2104 return js.call('dart.dput(#, #, #)', |
2026 [_visit(target), _emitMemberName(id.name), _visit(rhs)]); | 2105 [_visit(target), _emitMemberName(id.name), _visit(rhs)]); |
2027 } | 2106 } |
2028 | 2107 |
2029 return _visit(rhs).toAssignExpression(_visit(lhs)); | 2108 return _visit(rhs).toAssignExpression(_visit(lhs)); |
2030 } | 2109 } |
2031 | 2110 |
2032 JS.Expression _emitNullSafeSet(PropertyAccess node, Expression right) { | 2111 JS.Expression _emitNullSafeSet(PropertyAccess node, Expression right) { |
2033 // Emit `obj?.prop = expr` as: | 2112 // Emit `obj?.prop = expr` as: |
2034 // | 2113 // |
2035 // (_ => _ == null ? null : _.prop = expr)(obj). | 2114 // (_ => _ == null ? null : _.prop = expr)(obj). |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2080 return _emitNullSafe(node); | 2159 return _emitNullSafe(node); |
2081 } | 2160 } |
2082 | 2161 |
2083 var target = _getTarget(node); | 2162 var target = _getTarget(node); |
2084 var result = _emitForeignJS(node); | 2163 var result = _emitForeignJS(node); |
2085 if (result != null) return result; | 2164 if (result != null) return result; |
2086 | 2165 |
2087 String code; | 2166 String code; |
2088 if (target == null || isLibraryPrefix(target)) { | 2167 if (target == null || isLibraryPrefix(target)) { |
2089 if (DynamicInvoke.get(node.methodName)) { | 2168 if (DynamicInvoke.get(node.methodName)) { |
2090 code = 'dart.$DCALL(#, #)'; | 2169 code = 'dart.dcall(#, #)'; |
2091 } else { | 2170 } else { |
2092 code = '#(#)'; | 2171 code = '#(#)'; |
2093 } | 2172 } |
2094 return js | 2173 return js |
2095 .call(code, [_visit(node.methodName), _visit(node.argumentList)]); | 2174 .call(code, [_visit(node.methodName), _visit(node.argumentList)]); |
2096 } | 2175 } |
2097 | 2176 |
2098 var type = getStaticType(target); | 2177 var type = getStaticType(target); |
2099 var name = node.methodName.name; | 2178 var name = node.methodName.name; |
2100 var element = node.methodName.staticElement; | 2179 var element = node.methodName.staticElement; |
2101 bool isStatic = element is ExecutableElement && element.isStatic; | 2180 bool isStatic = element is ExecutableElement && element.isStatic; |
2102 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); | 2181 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); |
2103 | 2182 |
2104 if (DynamicInvoke.get(target)) { | 2183 if (DynamicInvoke.get(target)) { |
2105 code = 'dart.$DSEND(#, #, #)'; | 2184 code = 'dart.dsend(#, #, #)'; |
2106 } else if (DynamicInvoke.get(node.methodName)) { | 2185 } else if (DynamicInvoke.get(node.methodName)) { |
2107 // This is a dynamic call to a statically known target. For example: | 2186 // This is a dynamic call to a statically known target. For example: |
2108 // class Foo { Function bar; } | 2187 // class Foo { Function bar; } |
2109 // new Foo().bar(); // dynamic call | 2188 // new Foo().bar(); // dynamic call |
2110 code = 'dart.$DCALL(#.#, #)'; | 2189 code = 'dart.dcall(#.#, #)'; |
2111 } else if (_requiresStaticDispatch(target, name)) { | 2190 } else if (_requiresStaticDispatch(target, name)) { |
2112 // Object methods require a helper for null checks. | 2191 // Object methods require a helper for null checks. |
2113 return js.call('dart.#(#, #)', | 2192 return js.call('dart.#(#, #)', |
2114 [memberName, _visit(target), _visit(node.argumentList)]); | 2193 [memberName, _visit(target), _visit(node.argumentList)]); |
2115 } else { | 2194 } else { |
2116 code = '#.#(#)'; | 2195 code = '#.#(#)'; |
2117 } | 2196 } |
2118 | 2197 |
2119 return js | 2198 return js |
2120 .call(code, [_visit(target), memberName, _visit(node.argumentList)]); | 2199 .call(code, [_visit(target), memberName, _visit(node.argumentList)]); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2155 return result; | 2234 return result; |
2156 } | 2235 } |
2157 return null; | 2236 return null; |
2158 } | 2237 } |
2159 | 2238 |
2160 @override | 2239 @override |
2161 JS.Expression visitFunctionExpressionInvocation( | 2240 JS.Expression visitFunctionExpressionInvocation( |
2162 FunctionExpressionInvocation node) { | 2241 FunctionExpressionInvocation node) { |
2163 var code; | 2242 var code; |
2164 if (DynamicInvoke.get(node.function)) { | 2243 if (DynamicInvoke.get(node.function)) { |
2165 code = 'dart.$DCALL(#, #)'; | 2244 code = 'dart.dcall(#, #)'; |
2166 } else { | 2245 } else { |
2167 code = '#(#)'; | 2246 code = '#(#)'; |
2168 } | 2247 } |
2169 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); | 2248 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); |
2170 } | 2249 } |
2171 | 2250 |
2172 @override | 2251 @override |
2173 List<JS.Expression> visitArgumentList(ArgumentList node) { | 2252 List<JS.Expression> visitArgumentList(ArgumentList node) { |
2174 var args = <JS.Expression>[]; | 2253 var args = <JS.Expression>[]; |
2175 var named = <JS.Property>[]; | 2254 var named = <JS.Property>[]; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2210 | 2289 |
2211 for (FormalParameter param in node.parameters) { | 2290 for (FormalParameter param in node.parameters) { |
2212 if (param.kind == ParameterKind.NAMED) { | 2291 if (param.kind == ParameterKind.NAMED) { |
2213 if (destructure) { | 2292 if (destructure) { |
2214 if (_jsObjectProperties.contains(param.identifier.name)) { | 2293 if (_jsObjectProperties.contains(param.identifier.name)) { |
2215 hasNamedArgsConflictingWithObjectProperties = true; | 2294 hasNamedArgsConflictingWithObjectProperties = true; |
2216 } | 2295 } |
2217 JS.Expression name; | 2296 JS.Expression name; |
2218 JS.SimpleBindingPattern structure = null; | 2297 JS.SimpleBindingPattern structure = null; |
2219 String paramName = param.identifier.name; | 2298 String paramName = param.identifier.name; |
2220 if (invalidVariableName(paramName)) { | 2299 if (JS.invalidVariableName(paramName)) { |
2221 name = js.string(paramName); | 2300 name = js.string(paramName); |
2222 structure = new JS.SimpleBindingPattern(_visit(param.identifier)); | 2301 structure = new JS.SimpleBindingPattern(_visit(param.identifier)); |
2223 } else { | 2302 } else { |
2224 name = _visit(param.identifier); | 2303 name = _visit(param.identifier); |
2225 } | 2304 } |
2226 namedVars.add(new JS.DestructuredVariable( | 2305 namedVars.add(new JS.DestructuredVariable( |
2227 name: name, | 2306 name: name, |
2228 structure: structure, | 2307 structure: structure, |
2229 defaultValue: _defaultParamValue(param))); | 2308 defaultValue: _defaultParamValue(param))); |
2230 } else { | 2309 } else { |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2318 return jsExpr.toYieldStatement(star: star); | 2397 return jsExpr.toYieldStatement(star: star); |
2319 } | 2398 } |
2320 | 2399 |
2321 @override | 2400 @override |
2322 JS.Expression visitAwaitExpression(AwaitExpression node) { | 2401 JS.Expression visitAwaitExpression(AwaitExpression node) { |
2323 return new JS.Yield(_visit(node.expression)); | 2402 return new JS.Yield(_visit(node.expression)); |
2324 } | 2403 } |
2325 | 2404 |
2326 @override | 2405 @override |
2327 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 2406 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
2328 for (var v in node.variables.variables) { | 2407 for (var variable in node.variables.variables) { |
2329 _loader.loadDeclaration(v, v.element); | 2408 _loader.emitDeclaration(variable.element); |
2330 } | 2409 } |
2331 } | 2410 } |
2332 | 2411 |
2333 /// Emits static fields. | 2412 /// This is not used--we emit fields as we are emitting the class, |
2334 /// | 2413 /// see [visitClassDeclaration]. |
2335 /// Instance fields are emitted in [_initializeFields]. | |
2336 /// | |
2337 /// These are generally treated the same as top-level fields, see | |
2338 /// [visitTopLevelVariableDeclaration]. | |
2339 @override | 2414 @override |
2340 visitFieldDeclaration(FieldDeclaration node) { | 2415 visitFieldDeclaration(FieldDeclaration node) { |
2341 if (!node.isStatic) return; | 2416 assert(false); |
2342 | |
2343 node.fields.variables.forEach(_emitModuleItem); | |
2344 } | |
2345 | |
2346 _addExport(String name, [String exportName]) { | |
2347 if (_exports.containsKey(name)) { | |
2348 throw 'Duplicate top level name found: $name'; | |
2349 } | |
2350 _exports[name] = exportName ?? name; | |
2351 } | 2417 } |
2352 | 2418 |
2353 @override | 2419 @override |
2354 JS.Statement visitVariableDeclarationStatement( | 2420 JS.Statement visitVariableDeclarationStatement( |
2355 VariableDeclarationStatement node) { | 2421 VariableDeclarationStatement node) { |
2356 // Special case a single variable with an initializer. | 2422 // Special case a single variable with an initializer. |
2357 // This helps emit cleaner code for things like: | 2423 // This helps emit cleaner code for things like: |
2358 // var result = []..add(1)..add(2); | 2424 // var result = []..add(1)..add(2); |
2359 if (node.variables.variables.length == 1) { | 2425 var variables = node.variables.variables; |
2360 var v = node.variables.variables.single; | 2426 if (variables.length == 1) { |
| 2427 var v = variables[0]; |
2361 if (v.initializer != null) { | 2428 if (v.initializer != null) { |
2362 var name = new JS.Identifier(v.name.name); | 2429 var name = new JS.Identifier(v.name.name); |
2363 return _visit(v.initializer).toVariableDeclaration(name); | 2430 return _visit(v.initializer).toVariableDeclaration(name); |
2364 } | 2431 } |
2365 } | 2432 } |
2366 return _visit(node.variables).toStatement(); | 2433 return _visit(node.variables).toStatement(); |
2367 } | 2434 } |
2368 | 2435 |
2369 @override | 2436 @override |
2370 visitVariableDeclarationList(VariableDeclarationList node) { | 2437 visitVariableDeclarationList(VariableDeclarationList node) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2412 JS.Expression jsInit = _visitInitializer(field); | 2479 JS.Expression jsInit = _visitInitializer(field); |
2413 bool isLoaded = _loader.finishCheckingReferences(); | 2480 bool isLoaded = _loader.finishCheckingReferences(); |
2414 | 2481 |
2415 bool eagerInit = | 2482 bool eagerInit = |
2416 isLoaded && (field.isConst || _constField.isFieldInitConstant(field)); | 2483 isLoaded && (field.isConst || _constField.isFieldInitConstant(field)); |
2417 | 2484 |
2418 var fieldName = field.name.name; | 2485 var fieldName = field.name.name; |
2419 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { | 2486 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { |
2420 return annotate( | 2487 return annotate( |
2421 js.statement('#.# = #;', [ | 2488 js.statement('#.# = #;', [ |
2422 classElem.name, | 2489 _emitTopLevelName(classElem), |
2423 _emitMemberName(fieldName, isStatic: true), | 2490 _emitMemberName(fieldName, isStatic: true), |
2424 jsInit | 2491 jsInit |
2425 ]), | 2492 ]), |
2426 field, | 2493 field, |
2427 field.element); | 2494 field.element); |
2428 } | 2495 } |
2429 | 2496 |
2430 // This means it should be treated as a lazy field. | 2497 // This means it should be treated as a lazy field. |
2431 // TODO(jmesserly): we're throwing away the initializer expression, | 2498 // TODO(jmesserly): we're throwing away the initializer expression, |
2432 // which will force us to regenerate it. | 2499 // which will force us to regenerate it. |
2433 return null; | 2500 return null; |
2434 } | 2501 } |
2435 | 2502 |
2436 /// Emits a top-level field. | 2503 /// Emits a top-level field. |
2437 JS.Statement _emitTopLevelField(VariableDeclaration field) { | 2504 JS.ModuleItem _emitTopLevelField(VariableDeclaration field) { |
2438 TopLevelVariableElement element = field.element; | 2505 TopLevelVariableElement element = field.element; |
2439 assert(element.isStatic); | 2506 assert(element.isStatic); |
2440 | 2507 |
2441 bool eagerInit; | 2508 bool eagerInit; |
2442 JS.Expression jsInit; | 2509 JS.Expression jsInit; |
2443 if (field.isConst || _constField.isFieldInitConstant(field)) { | 2510 if (field.isConst || _constField.isFieldInitConstant(field)) { |
2444 // If the field is constant, try and generate it at the top level. | 2511 // If the field is constant, try and generate it at the top level. |
2445 _loader.startTopLevel(element); | 2512 _loader.startTopLevel(element); |
2446 jsInit = _visitInitializer(field); | 2513 jsInit = _visitInitializer(field); |
2447 _loader.finishTopLevel(element); | 2514 _loader.finishTopLevel(element); |
2448 eagerInit = _loader.isLoaded(element); | 2515 eagerInit = _loader.isLoaded(element); |
2449 } else { | 2516 } else { |
2450 // TODO(jmesserly): we're visiting the initializer here, and again | 2517 // TODO(jmesserly): we're visiting the initializer here, and again |
2451 // later on when we emit lazy fields. That seems busted. | 2518 // later on when we emit lazy fields. That seems busted. |
2452 jsInit = _visitInitializer(field); | 2519 jsInit = _visitInitializer(field); |
2453 eagerInit = false; | 2520 eagerInit = false; |
2454 } | 2521 } |
2455 | 2522 |
2456 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 2523 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile |
2457 // runtime helpers. | 2524 // runtime helpers. |
| 2525 // TODO(jmesserly): we don't track dependencies correctly for these. |
2458 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | 2526 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); |
2459 if (isJSTopLevel) eagerInit = true; | 2527 if (eagerInit || isJSTopLevel) { |
2460 | 2528 return annotate( |
2461 var fieldName = field.name.name; | 2529 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), |
2462 var exportName = fieldName; | 2530 field, |
2463 if (element is TopLevelVariableElement) { | 2531 element); |
2464 exportName = getJSExportName(element, types) ?? fieldName; | |
2465 } | |
2466 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || | |
2467 isJSTopLevel) { | |
2468 // constant fields don't change, so we can generate them as `let` | |
2469 // but add them to the module's exports. However, make sure we generate | |
2470 // anything they depend on first. | |
2471 | |
2472 if (isPublic(fieldName)) _addExport(fieldName, exportName); | |
2473 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; | |
2474 if (isJSTopLevel && jsInit is JS.ClassExpression) { | |
2475 return new JS.ClassDeclaration(jsInit); | |
2476 } | |
2477 return js.statement('#;', [ | |
2478 annotate( | |
2479 new JS.VariableDeclarationList(declKeyword, [ | |
2480 new JS.VariableInitialization( | |
2481 new JS.Identifier(fieldName, | |
2482 type: emitTypeRef(field.element.type)), | |
2483 jsInit) | |
2484 ]), | |
2485 field, | |
2486 field.element) | |
2487 ]); | |
2488 } | 2532 } |
2489 | 2533 |
2490 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { | 2534 assert(element.library == currentLibrary); |
2491 return annotate(js.statement('# = #;', [_visit(field.name), jsInit]), | 2535 return _emitLazyFields(element.library, [field]); |
2492 field, field.element); | |
2493 } | |
2494 | |
2495 return _emitLazyFields(currentLibrary, [field]); | |
2496 } | 2536 } |
2497 | 2537 |
2498 JS.Expression _visitInitializer(VariableDeclaration node) { | 2538 JS.Expression _visitInitializer(VariableDeclaration node) { |
2499 var value = _visit(node.initializer); | 2539 var value = _visit(node.initializer); |
2500 // explicitly initialize to null, to avoid getting `undefined`. | 2540 // explicitly initialize to null, to avoid getting `undefined`. |
2501 // TODO(jmesserly): do this only for vars that aren't definitely assigned. | 2541 // TODO(jmesserly): do this only for vars that aren't definitely assigned. |
2502 return value ?? new JS.LiteralNull(); | 2542 return value ?? new JS.LiteralNull(); |
2503 } | 2543 } |
2504 | 2544 |
2505 JS.Statement _emitLazyFields( | 2545 JS.Statement _emitLazyFields( |
(...skipping 17 matching lines...) Expand all Loading... |
2523 methods.add(annotate( | 2563 methods.add(annotate( |
2524 new JS.Method(access, js.call('function(_) {}') as JS.Fun, | 2564 new JS.Method(access, js.call('function(_) {}') as JS.Fun, |
2525 isSetter: true), | 2565 isSetter: true), |
2526 node, | 2566 node, |
2527 _findAccessor(element, getter: false))); | 2567 _findAccessor(element, getter: false))); |
2528 } | 2568 } |
2529 } | 2569 } |
2530 | 2570 |
2531 JS.Expression objExpr; | 2571 JS.Expression objExpr; |
2532 if (target is ClassElement) { | 2572 if (target is ClassElement) { |
2533 objExpr = new JS.Identifier(target.type.name); | 2573 objExpr = _emitTopLevelName(target); |
2534 } else { | 2574 } else { |
2535 objExpr = emitLibraryName(target); | 2575 objExpr = emitLibraryName(target); |
2536 } | 2576 } |
2537 | 2577 |
2538 return js | 2578 return js.statement('dart.defineLazy(#, { # });', [objExpr, methods]); |
2539 .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]); | |
2540 } | 2579 } |
2541 | 2580 |
2542 PropertyAccessorElement _findAccessor(VariableElement element, | 2581 PropertyAccessorElement _findAccessor(VariableElement element, |
2543 {bool getter}) { | 2582 {bool getter}) { |
2544 var parent = element.enclosingElement; | 2583 var parent = element.enclosingElement; |
2545 if (parent is ClassElement) { | 2584 if (parent is ClassElement) { |
2546 return getter | 2585 return getter |
2547 ? parent.getGetter(element.name) | 2586 ? parent.getGetter(element.name) |
2548 : parent.getSetter(element.name); | 2587 : parent.getSetter(element.name); |
2549 } | 2588 } |
2550 return null; | 2589 return null; |
2551 } | 2590 } |
2552 | 2591 |
2553 void _flushLibraryProperties(List<JS.Statement> body) { | |
2554 if (_properties.isEmpty) return; | |
2555 body.add(js.statement('dart.copyProperties(#, { # });', | |
2556 [_exportsVar, _properties.map(_emitTopLevelProperty)])); | |
2557 _properties.clear(); | |
2558 } | |
2559 | |
2560 JS.Expression _emitConstructorName( | 2592 JS.Expression _emitConstructorName( |
2561 ConstructorElement element, DartType type, SimpleIdentifier name) { | 2593 ConstructorElement element, DartType type, SimpleIdentifier name) { |
2562 var typeName = _emitTypeName(type); | 2594 var typeName = _emitTypeName(type); |
2563 if (name != null || element.isFactory) { | 2595 if (name != null || element.isFactory) { |
2564 var namedCtor = _constructorName(element); | 2596 var namedCtor = _constructorName(element); |
2565 return new JS.PropertyAccess(typeName, namedCtor); | 2597 return new JS.PropertyAccess(typeName, namedCtor); |
2566 } | 2598 } |
2567 return typeName; | 2599 return typeName; |
2568 } | 2600 } |
2569 | 2601 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2608 var type = constructor.type.type; | 2640 var type = constructor.type.type; |
2609 return _emitInstanceCreationExpression( | 2641 return _emitInstanceCreationExpression( |
2610 element, type, name, node.argumentList, node.isConst); | 2642 element, type, name, node.argumentList, node.isConst); |
2611 } | 2643 } |
2612 | 2644 |
2613 /// True if this type is built-in to JS, and we use the values unwrapped. | 2645 /// True if this type is built-in to JS, and we use the values unwrapped. |
2614 /// For these types we generate a calling convention via static | 2646 /// For these types we generate a calling convention via static |
2615 /// "extension methods". This allows types to be extended without adding | 2647 /// "extension methods". This allows types to be extended without adding |
2616 /// extensions directly on the prototype. | 2648 /// extensions directly on the prototype. |
2617 bool isPrimitiveType(DartType t) => | 2649 bool isPrimitiveType(DartType t) => |
2618 typeIsPrimitiveInJS(t) || t == _types.stringType; | 2650 typeIsPrimitiveInJS(t) || t == types.stringType; |
2619 | 2651 |
2620 bool typeIsPrimitiveInJS(DartType t) => | 2652 bool typeIsPrimitiveInJS(DartType t) => |
2621 _isNumberInJS(t) || t == _types.boolType; | 2653 _isNumberInJS(t) || t == types.boolType; |
2622 | 2654 |
2623 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 2655 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
2624 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 2656 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
2625 | 2657 |
2626 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 2658 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
2627 | 2659 |
2628 JS.Expression notNull(Expression expr) { | 2660 JS.Expression notNull(Expression expr) { |
2629 if (expr == null) return null; | 2661 if (expr == null) return null; |
2630 var jsExpr = _visit(expr); | 2662 var jsExpr = _visit(expr); |
2631 if (!isNullable(expr)) return jsExpr; | 2663 if (!isNullable(expr)) return jsExpr; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2663 | 2695 |
2664 var vars = <String, JS.Expression>{}; | 2696 var vars = <String, JS.Expression>{}; |
2665 // Desugar `l ?? r` as `l != null ? l : r` | 2697 // Desugar `l ?? r` as `l != null ? l : r` |
2666 var l = _visit(_bindValue(vars, 'l', left, context: left)); | 2698 var l = _visit(_bindValue(vars, 'l', left, context: left)); |
2667 return new JS.MetaLet(vars, [ | 2699 return new JS.MetaLet(vars, [ |
2668 js.call('# != null ? # : #', [l, l, _visit(right)]) | 2700 js.call('# != null ? # : #', [l, l, _visit(right)]) |
2669 ]); | 2701 ]); |
2670 } | 2702 } |
2671 | 2703 |
2672 if (binaryOperationIsPrimitive(leftType, rightType) || | 2704 if (binaryOperationIsPrimitive(leftType, rightType) || |
2673 leftType == _types.stringType && op.type == TokenType.PLUS) { | 2705 leftType == types.stringType && op.type == TokenType.PLUS) { |
2674 // special cases where we inline the operation | 2706 // special cases where we inline the operation |
2675 // these values are assumed to be non-null (determined by the checker) | 2707 // these values are assumed to be non-null (determined by the checker) |
2676 // TODO(jmesserly): it would be nice to just inline the method from core, | 2708 // TODO(jmesserly): it would be nice to just inline the method from core, |
2677 // instead of special cases here. | 2709 // instead of special cases here. |
2678 if (op.type == TokenType.TILDE_SLASH) { | 2710 if (op.type == TokenType.TILDE_SLASH) { |
2679 // `a ~/ b` is equivalent to `(a / b).truncate()` | 2711 // `a ~/ b` is equivalent to `(a / b).truncate()` |
2680 var div = AstBuilder.binaryExpression(left, '/', right) | 2712 var div = AstBuilder.binaryExpression(left, '/', right) |
2681 ..staticType = node.staticType; | 2713 ..staticType = node.staticType; |
2682 return _emitSend(div, 'truncate', []); | 2714 return _emitSend(div, 'truncate', []); |
2683 } else { | 2715 } else { |
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3012 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 3044 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
3013 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { | 3045 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
3014 var member = memberId.staticElement; | 3046 var member = memberId.staticElement; |
3015 if (member is PropertyAccessorElement) { | 3047 if (member is PropertyAccessorElement) { |
3016 member = (member as PropertyAccessorElement).variable; | 3048 member = (member as PropertyAccessorElement).variable; |
3017 } | 3049 } |
3018 bool isStatic = member is ClassMemberElement && member.isStatic; | 3050 bool isStatic = member is ClassMemberElement && member.isStatic; |
3019 var name = _emitMemberName(memberId.name, | 3051 var name = _emitMemberName(memberId.name, |
3020 type: getStaticType(target), isStatic: isStatic); | 3052 type: getStaticType(target), isStatic: isStatic); |
3021 if (DynamicInvoke.get(target)) { | 3053 if (DynamicInvoke.get(target)) { |
3022 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); | 3054 return js.call('dart.dload(#, #)', [_visit(target), name]); |
3023 } | 3055 } |
3024 | 3056 |
3025 String code; | 3057 String code; |
3026 if (member != null && member is MethodElement && !isStatic) { | 3058 if (member != null && member is MethodElement && !isStatic) { |
3027 // Tear-off methods: explicitly bind it. | 3059 // Tear-off methods: explicitly bind it. |
3028 if (target is SuperExpression) { | 3060 if (target is SuperExpression) { |
3029 return js.call('dart.bind(this, #, #.#)', [name, _visit(target), name]); | 3061 return js.call('dart.bind(this, #, #.#)', [name, _visit(target), name]); |
3030 } else if (_requiresStaticDispatch(target, memberId.name)) { | 3062 } else if (_requiresStaticDispatch(target, memberId.name)) { |
3031 var type = member.type; | 3063 var type = member.type; |
3032 var clos = js.call('dart.#.bind(#)', [name, _visit(target)]); | 3064 var clos = js.call('dart.#.bind(#)', [name, _visit(target)]); |
(...skipping 13 matching lines...) Expand all Loading... |
3046 /// | 3078 /// |
3047 /// **Please note** this function does not support method invocation syntax | 3079 /// **Please note** this function does not support method invocation syntax |
3048 /// `obj.name(args)` because that could be a getter followed by a call. | 3080 /// `obj.name(args)` because that could be a getter followed by a call. |
3049 /// See [visitMethodInvocation]. | 3081 /// See [visitMethodInvocation]. |
3050 JS.Expression _emitSend( | 3082 JS.Expression _emitSend( |
3051 Expression target, String name, List<Expression> args) { | 3083 Expression target, String name, List<Expression> args) { |
3052 var type = getStaticType(target); | 3084 var type = getStaticType(target); |
3053 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); | 3085 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); |
3054 if (DynamicInvoke.get(target)) { | 3086 if (DynamicInvoke.get(target)) { |
3055 // dynamic dispatch | 3087 // dynamic dispatch |
3056 var dynamicHelper = const {'[]': DINDEX, '[]=': DSETINDEX}[name]; | 3088 var dynamicHelper = const {'[]': 'dindex', '[]=': 'dsetindex'}[name]; |
3057 if (dynamicHelper != null) { | 3089 if (dynamicHelper != null) { |
3058 return js.call( | 3090 return js.call( |
3059 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); | 3091 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); |
3060 } | 3092 } |
3061 return js.call('dart.$DSEND(#, #, #)', | 3093 return js.call('dart.dsend(#, #, #)', |
3062 [_visit(target), memberName, _visitList(args)]); | 3094 [_visit(target), memberName, _visitList(args)]); |
3063 } | 3095 } |
3064 | 3096 |
3065 // Generic dispatch to a statically known method. | 3097 // Generic dispatch to a statically known method. |
3066 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); | 3098 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); |
3067 } | 3099 } |
3068 | 3100 |
3069 @override | 3101 @override |
3070 visitIndexExpression(IndexExpression node) { | 3102 visitIndexExpression(IndexExpression node) { |
3071 var target = _getTarget(node); | 3103 var target = _getTarget(node); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3182 // } | 3214 // } |
3183 // } finally { | 3215 // } finally { |
3184 // await iter.cancel(); | 3216 // await iter.cancel(); |
3185 // } | 3217 // } |
3186 // | 3218 // |
3187 // Like the Dart VM, we call cancel() always, as it's safe to call if the | 3219 // Like the Dart VM, we call cancel() always, as it's safe to call if the |
3188 // stream has already been cancelled. | 3220 // stream has already been cancelled. |
3189 // | 3221 // |
3190 // TODO(jmesserly): we may want a helper if these become common. For now the | 3222 // TODO(jmesserly): we may want a helper if these become common. For now the |
3191 // full desugaring seems okay. | 3223 // full desugaring seems okay. |
3192 var context = compiler.context; | 3224 var streamIterator = rules.instantiateToBounds(_asyncStreamIterator); |
3193 var dart_async = context | |
3194 .computeLibraryElement(context.sourceFactory.forUri('dart:async')); | |
3195 var _streamIteratorType = | |
3196 rules.instantiateToBounds(dart_async.getType('StreamIterator').type); | |
3197 | |
3198 var createStreamIter = _emitInstanceCreationExpression( | 3225 var createStreamIter = _emitInstanceCreationExpression( |
3199 _streamIteratorType.element.unnamedConstructor, | 3226 streamIterator.element.unnamedConstructor, |
3200 _streamIteratorType, | 3227 streamIterator, |
3201 null, | 3228 null, |
3202 AstBuilder.argumentList([node.iterable]), | 3229 AstBuilder.argumentList([node.iterable]), |
3203 false); | 3230 false); |
3204 var iter = | 3231 var iter = _visit(_createTemporary('it', streamIterator, nullable: false)); |
3205 _visit(_createTemporary('it', _streamIteratorType, nullable: false)); | |
3206 | 3232 |
3207 var init = _visit(node.identifier); | 3233 var init = _visit(node.identifier); |
3208 if (init == null) { | 3234 if (init == null) { |
3209 init = js | 3235 init = js |
3210 .call('let # = #.current', [node.loopVariable.identifier.name, iter]); | 3236 .call('let # = #.current', [node.loopVariable.identifier.name, iter]); |
3211 } else { | 3237 } else { |
3212 init = js.call('# = #.current', [init, iter]); | 3238 init = js.call('# = #.current', [init, iter]); |
3213 } | 3239 } |
3214 return js.statement( | 3240 return js.statement( |
3215 '{' | 3241 '{' |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3493 return result; | 3519 return result; |
3494 } | 3520 } |
3495 | 3521 |
3496 /// Visits a list of expressions, creating a comma expression if needed in JS. | 3522 /// Visits a list of expressions, creating a comma expression if needed in JS. |
3497 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 3523 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
3498 if (nodes == null || nodes.isEmpty) return null; | 3524 if (nodes == null || nodes.isEmpty) return null; |
3499 return new JS.Expression.binary( | 3525 return new JS.Expression.binary( |
3500 _visitList(nodes) as List<JS.Expression>, operator); | 3526 _visitList(nodes) as List<JS.Expression>, operator); |
3501 } | 3527 } |
3502 | 3528 |
3503 /// Return the bound type parameters for a ParameterizedType | |
3504 List<TypeParameterElement> _typeFormalsOf(ParameterizedType type) { | |
3505 return type is FunctionType ? type.typeFormals : type.typeParameters; | |
3506 } | |
3507 | |
3508 /// Like [_emitMemberName], but for declaration sites. | 3529 /// Like [_emitMemberName], but for declaration sites. |
3509 /// | 3530 /// |
3510 /// Unlike call sites, we always have an element available, so we can use it | 3531 /// Unlike call sites, we always have an element available, so we can use it |
3511 /// directly rather than computing the relevant options for [_emitMemberName]. | 3532 /// directly rather than computing the relevant options for [_emitMemberName]. |
3512 JS.Expression _elementMemberName(ExecutableElement e, | 3533 JS.Expression _elementMemberName(ExecutableElement e, |
3513 {bool allowExtensions: true}) { | 3534 {bool allowExtensions: true}) { |
3514 String name; | 3535 String name; |
3515 if (e is PropertyAccessorElement) { | 3536 if (e is PropertyAccessorElement) { |
3516 name = e.variable.name; | 3537 name = e.variable.name; |
3517 } else { | 3538 } else { |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3567 /// | 3588 /// |
3568 JS.Expression _emitMemberName(String name, | 3589 JS.Expression _emitMemberName(String name, |
3569 {DartType type, | 3590 {DartType type, |
3570 bool unary: false, | 3591 bool unary: false, |
3571 bool isStatic: false, | 3592 bool isStatic: false, |
3572 bool allowExtensions: true}) { | 3593 bool allowExtensions: true}) { |
3573 // Static members skip the rename steps. | 3594 // Static members skip the rename steps. |
3574 if (isStatic) return _propertyName(name); | 3595 if (isStatic) return _propertyName(name); |
3575 | 3596 |
3576 if (name.startsWith('_')) { | 3597 if (name.startsWith('_')) { |
3577 return _privateNames.putIfAbsent( | 3598 return _emitPrivateNameSymbol(currentLibrary, name); |
3578 name, () => _initSymbol(new JS.TemporaryId(name)) as JS.TemporaryId); | |
3579 } | 3599 } |
3580 | 3600 |
3581 if (name == '[]') { | 3601 if (name == '[]') { |
3582 name = 'get'; | 3602 name = 'get'; |
3583 } else if (name == '[]=') { | 3603 } else if (name == '[]=') { |
3584 name = 'set'; | 3604 name = 'set'; |
3585 } else if (name == '-' && unary) { | 3605 } else if (name == '-' && unary) { |
3586 name = 'unary-'; | 3606 name = 'unary-'; |
3587 } else if (name == 'constructor' || name == 'prototype') { | 3607 } else if (name == 'constructor' || name == 'prototype') { |
3588 // This uses an illegal (in Dart) character for a member, avoiding the | 3608 // This uses an illegal (in Dart) character for a member, avoiding the |
3589 // conflict. We could use practically any character for this. | 3609 // conflict. We could use practically any character for this. |
3590 name = '+$name'; | 3610 name = '+$name'; |
3591 } | 3611 } |
3592 | 3612 |
3593 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 3613 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
3594 var baseType = type; | 3614 var baseType = type; |
3595 while (baseType is TypeParameterType) { | 3615 while (baseType is TypeParameterType) { |
3596 baseType = baseType.element.bound; | 3616 baseType = baseType.element.bound; |
3597 } | 3617 } |
3598 if (allowExtensions && | 3618 if (allowExtensions && |
3599 baseType != null && | 3619 baseType != null && |
3600 _extensionTypes.contains(baseType.element) && | 3620 _extensionTypes.contains(baseType.element) && |
3601 !isObjectProperty(name)) { | 3621 !isObjectProperty(name)) { |
3602 return js.call('dartx.#', _propertyName(name)); | 3622 return js.call('dartx.#', _propertyName(name)); |
3603 } | 3623 } |
3604 | 3624 |
3605 return _propertyName(name); | 3625 return _propertyName(name); |
3606 } | 3626 } |
3607 | 3627 |
| 3628 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { |
| 3629 return _privateNames |
| 3630 .putIfAbsent(library, () => new HashMap()) |
| 3631 .putIfAbsent(name, () { |
| 3632 var id = new JS.TemporaryId(name); |
| 3633 _moduleItems.add( |
| 3634 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); |
| 3635 return id; |
| 3636 }); |
| 3637 } |
| 3638 |
3608 bool _externalOrNative(node) => | 3639 bool _externalOrNative(node) => |
3609 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 3640 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
3610 | 3641 |
3611 FunctionBody _functionBody(node) => | 3642 FunctionBody _functionBody(node) => |
3612 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 3643 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
3613 | 3644 |
3614 /// Choose a canonical name from the library element. | 3645 /// Returns the canonical name to refer to the Dart library. |
3615 /// This never uses the library's name (the identifier in the `library` | |
3616 /// declaration) as it doesn't have any meaningful rules enforced. | |
3617 JS.Identifier emitLibraryName(LibraryElement library) { | 3646 JS.Identifier emitLibraryName(LibraryElement library) { |
3618 if (library == currentLibrary) return _exportsVar; | 3647 // It's either one of the libraries in this module, or it's an import. |
3619 if (library.name == 'dart._runtime') return _runtimeLibVar; | 3648 return _libraries[library] ?? |
3620 return _imports.putIfAbsent( | 3649 _imports.putIfAbsent( |
3621 library, () => new JS.TemporaryId(jsLibraryName(library))); | 3650 library, () => new JS.TemporaryId(jsLibraryName(library))); |
3622 } | 3651 } |
3623 | 3652 |
3624 JS.Node annotate(JS.Node node, AstNode original, [Element element]) { | 3653 JS.Node annotate(JS.Node node, AstNode original, [Element element]) { |
3625 if (options.closure && element != null) { | 3654 if (options.closure && element != null) { |
3626 node = node.withClosureAnnotation(closureAnnotationFor( | 3655 node = node.withClosureAnnotation(closureAnnotationFor( |
3627 node, original, element, namedArgumentTemp.name)); | 3656 node, original, element, namedArgumentTemp.name)); |
3628 } | 3657 } |
3629 return node..sourceInformation = original; | 3658 return node..sourceInformation = original; |
3630 } | 3659 } |
3631 | 3660 |
3632 /// Returns true if this is any kind of object represented by `Number` in JS. | 3661 /// Returns true if this is any kind of object represented by `Number` in JS. |
3633 /// | 3662 /// |
3634 /// In practice, this is 4 types: num, int, double, and JSNumber. | 3663 /// In practice, this is 4 types: num, int, double, and JSNumber. |
3635 /// | 3664 /// |
3636 /// JSNumber is the type that actually "implements" all numbers, hence it's | 3665 /// JSNumber is the type that actually "implements" all numbers, hence it's |
3637 /// a subtype of int and double (and num). It's in our "dart:_interceptors". | 3666 /// a subtype of int and double (and num). It's in our "dart:_interceptors". |
3638 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType); | 3667 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); |
3639 | 3668 |
3640 bool _isObjectGetter(String name) { | 3669 bool _isObjectGetter(String name) { |
3641 PropertyAccessorElement element = _types.objectType.element.getGetter(name); | 3670 PropertyAccessorElement element = types.objectType.element.getGetter(name); |
3642 return (element != null && !element.isStatic); | 3671 return (element != null && !element.isStatic); |
3643 } | 3672 } |
3644 | 3673 |
3645 bool _isObjectMethod(String name) { | 3674 bool _isObjectMethod(String name) { |
3646 MethodElement element = _types.objectType.element.getMethod(name); | 3675 MethodElement element = types.objectType.element.getMethod(name); |
3647 return (element != null && !element.isStatic); | 3676 return (element != null && !element.isStatic); |
3648 } | 3677 } |
3649 | 3678 |
3650 bool isObjectProperty(String name) { | 3679 bool isObjectProperty(String name) { |
3651 return _isObjectGetter(name) || _isObjectMethod(name); | 3680 return _isObjectGetter(name) || _isObjectMethod(name); |
3652 } | 3681 } |
3653 | 3682 |
3654 // TODO(leafp): Various analyzer pieces computed similar things. | 3683 // TODO(leafp): Various analyzer pieces computed similar things. |
3655 // Share this logic somewhere? | 3684 // Share this logic somewhere? |
3656 DartType _getExpectedReturnType(ExecutableElement element) { | 3685 DartType _getExpectedReturnType(ExecutableElement element) { |
3657 FunctionType functionType = element.type; | 3686 FunctionType functionType = element.type; |
3658 if (functionType == null) { | 3687 if (functionType == null) { |
3659 return DynamicTypeImpl.instance; | 3688 return DynamicTypeImpl.instance; |
3660 } | 3689 } |
3661 var type = functionType.returnType; | 3690 var type = functionType.returnType; |
3662 | 3691 |
3663 InterfaceType expectedType = null; | 3692 InterfaceType expectedType = null; |
3664 if (element.isAsynchronous) { | 3693 if (element.isAsynchronous) { |
3665 if (element.isGenerator) { | 3694 if (element.isGenerator) { |
3666 // Stream<T> -> T | 3695 // Stream<T> -> T |
3667 expectedType = _types.streamType; | 3696 expectedType = types.streamType; |
3668 } else { | 3697 } else { |
3669 // Future<T> -> T | 3698 // Future<T> -> T |
3670 // TODO(vsm): Revisit with issue #228. | 3699 // TODO(vsm): Revisit with issue #228. |
3671 expectedType = _types.futureType; | 3700 expectedType = types.futureType; |
3672 } | 3701 } |
3673 } else { | 3702 } else { |
3674 if (element.isGenerator) { | 3703 if (element.isGenerator) { |
3675 // Iterable<T> -> T | 3704 // Iterable<T> -> T |
3676 expectedType = _types.iterableType; | 3705 expectedType = types.iterableType; |
3677 } else { | 3706 } else { |
3678 // T -> T | 3707 // T -> T |
3679 return type; | 3708 return type; |
3680 } | 3709 } |
3681 } | 3710 } |
3682 if (type.isDynamic) { | 3711 if (type.isDynamic) { |
3683 return type; | 3712 return type; |
3684 } else if (type is InterfaceType && type.element == expectedType.element) { | 3713 } else if (type is InterfaceType && type.element == expectedType.element) { |
3685 return type.typeArguments[0]; | 3714 return type.typeArguments[0]; |
3686 } else { | 3715 } else { |
3687 // TODO(leafp): The above only handles the case where the return type | 3716 // TODO(leafp): The above only handles the case where the return type |
3688 // is exactly Future/Stream/Iterable. Handle the subtype case. | 3717 // is exactly Future/Stream/Iterable. Handle the subtype case. |
3689 return DynamicTypeImpl.instance; | 3718 return DynamicTypeImpl.instance; |
3690 } | 3719 } |
3691 } | 3720 } |
3692 } | 3721 } |
3693 | 3722 |
3694 class ExtensionTypeSet extends GeneralizingElementVisitor { | |
3695 final AnalysisContext _context; | |
3696 final TypeProvider _types; | |
3697 | |
3698 final _extensionTypes = new HashSet<ClassElement>(); | |
3699 final _pendingLibraries = new HashSet<String>(); | |
3700 | |
3701 ExtensionTypeSet(AbstractCompiler compiler) | |
3702 : _context = compiler.context, | |
3703 _types = compiler.context.typeProvider; | |
3704 | |
3705 visitClassElement(ClassElement element) { | |
3706 if (findAnnotation(element, isJsPeerInterface) != null || | |
3707 findAnnotation(element, isNativeAnnotation) != null) { | |
3708 _addExtensionType(element.type); | |
3709 } | |
3710 } | |
3711 | |
3712 void _addExtensionType(InterfaceType t) { | |
3713 if (t.isObject || !_extensionTypes.add(t.element)) return; | |
3714 t = fillDynamicTypeArgs(t, _types) as InterfaceType; | |
3715 t.interfaces.forEach(_addExtensionType); | |
3716 t.mixins.forEach(_addExtensionType); | |
3717 _addExtensionType(t.superclass); | |
3718 } | |
3719 | |
3720 void _addExtensionTypesForLibrary(String libraryUri, List<String> typeNames) { | |
3721 var sourceFactory = _context.sourceFactory.forUri(libraryUri); | |
3722 var library = _context.computeLibraryElement(sourceFactory); | |
3723 for (var typeName in typeNames) { | |
3724 var element = library.getType(typeName); | |
3725 _addExtensionType(element.type); | |
3726 } | |
3727 } | |
3728 | |
3729 void _addExtensionTypes(String libraryUri) { | |
3730 var sourceFactory = _context.sourceFactory.forUri(libraryUri); | |
3731 var library = _context.computeLibraryElement(sourceFactory); | |
3732 visitLibraryElement(library); | |
3733 } | |
3734 | |
3735 void _addPendingExtensionTypes(String libraryUri) { | |
3736 _pendingLibraries.add(libraryUri); | |
3737 } | |
3738 | |
3739 bool contains(Element element) { | |
3740 if (_extensionTypes.contains(element)) return true; | |
3741 if (_pendingLibraries.isEmpty) return false; | |
3742 if (element is ClassElement) { | |
3743 var uri = element.library.source.uri.toString(); | |
3744 if (_pendingLibraries.contains(uri)) { | |
3745 // Load all pending libraries | |
3746 for (var libraryUri in _pendingLibraries) { | |
3747 _addExtensionTypes(libraryUri); | |
3748 } | |
3749 _pendingLibraries.clear(); | |
3750 return _extensionTypes.contains(element); | |
3751 } | |
3752 } | |
3753 return false; | |
3754 } | |
3755 } | |
3756 | |
3757 class JSGenerator { | |
3758 final AbstractCompiler compiler; | |
3759 final ExtensionTypeSet _extensionTypes; | |
3760 final TypeProvider _types; | |
3761 | |
3762 JSGenerator(AbstractCompiler compiler) | |
3763 : compiler = compiler, | |
3764 _types = compiler.context.typeProvider, | |
3765 _extensionTypes = new ExtensionTypeSet(compiler) { | |
3766 // TODO(vsm): Eventually, we want to make this extensible - i.e., find | |
3767 // annotations in user code as well. It would need to be summarized in | |
3768 // the element model - not searched this way on every compile. To make this | |
3769 // a little more efficient now, we do this in two phases. | |
3770 | |
3771 // First, core types: | |
3772 _extensionTypes._addExtensionTypes('dart:_interceptors'); | |
3773 _extensionTypes._addExtensionTypes('dart:_native_typed_data'); | |
3774 // TODO(vsm): If we're analyzing against the main SDK, those | |
3775 // types are not explicitly annotated. | |
3776 _extensionTypes._addExtensionType(_types.intType); | |
3777 _extensionTypes._addExtensionType(_types.doubleType); | |
3778 _extensionTypes._addExtensionType(_types.boolType); | |
3779 _extensionTypes._addExtensionType(_types.stringType); | |
3780 // These are used natively by dart:html but also not annotated. | |
3781 _extensionTypes | |
3782 ._addExtensionTypesForLibrary('dart:core', ['Comparable', 'Map']); | |
3783 _extensionTypes | |
3784 ._addExtensionTypesForLibrary('dart:collection', ['ListMixin']); | |
3785 _extensionTypes._addExtensionTypesForLibrary('dart:math', ['Rectangle']); | |
3786 | |
3787 // Second, html types - these are only searched if we use dart:html, etc.: | |
3788 _extensionTypes._addPendingExtensionTypes('dart:html'); | |
3789 _extensionTypes._addPendingExtensionTypes('dart:indexed_db'); | |
3790 _extensionTypes._addPendingExtensionTypes('dart:svg'); | |
3791 _extensionTypes._addPendingExtensionTypes('dart:web_audio'); | |
3792 _extensionTypes._addPendingExtensionTypes('dart:web_gl'); | |
3793 _extensionTypes._addPendingExtensionTypes('dart:web_sql'); | |
3794 } | |
3795 | |
3796 void generateLibrary(List<CompilationUnit> units) { | |
3797 var library = units.first.element.library; | |
3798 var fields = | |
3799 findFieldsNeedingStorage(units.map((c) => c.element), _extensionTypes); | |
3800 var rules = new StrongTypeSystemImpl(); | |
3801 var codegen = | |
3802 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); | |
3803 var module = codegen.emitLibrary(units); | |
3804 var out = compiler.getOutputPath(library.source.uri); | |
3805 var options = compiler.options.codegenOptions; | |
3806 writeJsLibrary(module, out, compiler.inputBaseDir, | |
3807 emitTypes: options.closure, | |
3808 emitSourceMaps: options.emitSourceMaps, | |
3809 fileSystem: compiler.fileSystem); | |
3810 } | |
3811 } | |
3812 | |
3813 /// Choose a canonical name from the library element. | 3723 /// Choose a canonical name from the library element. |
3814 /// This never uses the library's name (the identifier in the `library` | 3724 /// This never uses the library's name (the identifier in the `library` |
3815 /// declaration) as it doesn't have any meaningful rules enforced. | 3725 /// declaration) as it doesn't have any meaningful rules enforced. |
3816 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 3726 String jsLibraryName(LibraryElement library) { |
| 3727 return pathToJSIdentifier(library.source.uri.pathSegments.last); |
| 3728 } |
3817 | 3729 |
3818 /// Shorthand for identifier-like property names. | 3730 /// Shorthand for identifier-like property names. |
3819 /// For now, we emit them as strings and the printer restores them to | 3731 /// For now, we emit them as strings and the printer restores them to |
3820 /// identifiers if it can. | 3732 /// identifiers if it can. |
3821 // TODO(jmesserly): avoid the round tripping through quoted form. | 3733 // TODO(jmesserly): avoid the round tripping through quoted form. |
3822 JS.LiteralString _propertyName(String name) => js.string(name, "'"); | 3734 JS.LiteralString _propertyName(String name) => js.string(name, "'"); |
3823 | 3735 |
3824 // TODO(jacobr): we would like to do something like the following | 3736 // TODO(jacobr): we would like to do something like the following |
3825 // but we don't have summary support yet. | 3737 // but we don't have summary support yet. |
3826 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 3738 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
3827 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 3739 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
3828 | 3740 |
3829 /// A special kind of element created by the compiler, signifying a temporary | 3741 /// A special kind of element created by the compiler, signifying a temporary |
3830 /// variable. These objects use instance equality, and should be shared | 3742 /// variable. These objects use instance equality, and should be shared |
3831 /// everywhere in the tree where they are treated as the same variable. | 3743 /// everywhere in the tree where they are treated as the same variable. |
3832 class TemporaryVariableElement extends LocalVariableElementImpl { | 3744 class TemporaryVariableElement extends LocalVariableElementImpl { |
3833 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3745 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3834 | 3746 |
3835 int get hashCode => identityHashCode(this); | 3747 int get hashCode => identityHashCode(this); |
3836 bool operator ==(Object other) => identical(this, other); | 3748 bool operator ==(Object other) => identical(this, other); |
3837 } | 3749 } |
| 3750 |
| 3751 bool isLibraryPrefix(Expression node) => |
| 3752 node is SimpleIdentifier && node.staticElement is PrefixElement; |
| 3753 |
| 3754 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
| 3755 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
| 3756 |
| 3757 bool _isDartRuntime(LibraryElement l) => |
| 3758 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
OLD | NEW |