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 moduleName = _buildUnit.libraryToModule(library.source); | |
268 if (moduleName == null) { | |
269 throw new StateError('Could not find module containing "$library".'); | |
270 } | |
271 return moduleName; | |
272 } | |
273 | |
274 void _finishImports(List<JS.ModuleItem> items) { | |
275 var modules = new Map<String, List<LibraryElement>>(); | |
276 | |
277 for (var import in _imports.keys) { | |
278 modules.putIfAbsent(_libraryToModule(import), () => []).add(import); | |
279 } | |
280 | |
281 String coreModuleName; | |
282 if (!_libraries.containsKey(dartCoreLibrary)) { | |
283 coreModuleName = _libraryToModule(dartCoreLibrary); | |
284 } | |
285 modules.forEach((module, libraries) { | |
286 // Generate import directives. | |
287 // | |
288 // Our import variables are temps and can get renamed. Since our renaming | |
289 // is integrated into js_ast, it is aware of this possibility and will | |
290 // generate an "as" if needed. For example: | |
291 // | |
292 // import {foo} from 'foo'; // if no rename needed | |
293 // import {foo as foo$} from 'foo'; // if rename was needed | |
294 // | |
295 var imports = | |
296 libraries.map((l) => new JS.NameSpecifier(_imports[l])).toList(); | |
297 if (module == coreModuleName) { | |
298 imports.add(new JS.NameSpecifier(_runtimeLibVar)); | |
299 imports.add(new JS.NameSpecifier(_dartxVar)); | |
300 } | |
301 items.add(new JS.ImportDeclaration( | |
302 namedImports: imports, from: js.string(module, "'"))); | |
303 }); | |
304 } | |
305 | |
306 /// Collect toplevel elements and nodes we need to emit, and returns | |
307 /// an ordered map of these. | |
308 static Map<Element, AstNode> _collectElements(CompilationUnit unit) { | |
309 var map = <Element, AstNode>{}; | |
310 for (var declaration in unit.declarations) { | |
311 if (declaration is TopLevelVariableDeclaration) { | |
312 for (var field in declaration.variables.variables) { | |
313 map[field.element] = field; | |
314 } | |
315 } else { | |
316 map[declaration.element] = declaration; | |
317 } | |
318 } | |
319 return map; | |
320 } | |
321 | |
322 void _emitModuleItem(AstNode node) { | |
151 // TODO(jmesserly): ideally we could do this at a smaller granularity. | 323 // 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 | 324 // We'll need to be consistent about when we're generating functions, and |
153 // only run this on the outermost function. | 325 // only run this on the outermost function. |
154 inferNullableTypesInLibrary(units); | 326 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 | 327 |
222 var code = _visit(node); | 328 var code = _visit(node); |
223 if (code != null) _moduleItems.add(code); | 329 if (code != null) _moduleItems.add(code); |
224 } | 330 } |
225 | 331 |
226 @override | 332 @override |
227 void visitLibraryDirective(LibraryDirective node) { | 333 void visitCompilationUnit(CompilationUnit unit) { |
228 assert(_jsModuleValue == null); | 334 _constField = new ConstFieldVisitor(types, unit.element.source); |
229 | 335 |
230 var jsName = findAnnotation(node.element, isJSAnnotation); | 336 for (var declaration in unit.declarations) { |
231 _jsModuleValue = | 337 var element = declaration.element; |
232 getConstantField(jsName, 'name', types.stringType)?.toStringValue(); | 338 if (element != null) { |
233 } | 339 _loader.emitDeclaration(element); |
340 } else { | |
341 declaration.accept(this); | |
342 } | |
343 } | |
344 for (var directive in unit.directives) { | |
345 directive.accept(this); | |
346 } | |
347 } | |
348 | |
349 @override | |
350 void visitLibraryDirective(LibraryDirective node) {} | |
234 | 351 |
235 @override | 352 @override |
236 void visitImportDirective(ImportDirective node) { | 353 void visitImportDirective(ImportDirective node) { |
237 // Nothing to do yet, but we'll want to convert this to an ES6 import once | 354 // We don't handle imports here. |
238 // we have support for modules. | 355 // |
356 // Instead, we collect imports whenever we need to generate a reference | |
357 // to another library. This has the effect of collecting the actually used | |
358 // imports. | |
359 // | |
360 // TODO(jmesserly): if this is a prefixed import, consider adding the prefix | |
361 // as an alias? | |
239 } | 362 } |
240 | 363 |
241 @override | 364 @override |
242 void visitPartDirective(PartDirective node) {} | 365 void visitPartDirective(PartDirective node) {} |
366 | |
243 @override | 367 @override |
244 void visitPartOfDirective(PartOfDirective node) {} | 368 void visitPartOfDirective(PartOfDirective node) {} |
245 | 369 |
246 @override | 370 @override |
247 void visitExportDirective(ExportDirective node) { | 371 void visitExportDirective(ExportDirective node) { |
248 var exportName = emitLibraryName(node.uriElement); | 372 ExportElement element = node.element; |
249 | 373 var currentLibrary = element.library; |
250 var currentLibNames = currentLibrary.publicNamespace.definedNames; | 374 |
251 | 375 var currentNames = currentLibrary.publicNamespace.definedNames; |
252 var args = [_exportsVar, exportName]; | 376 var exportedNames = |
253 if (node.combinators.isNotEmpty) { | 377 new NamespaceBuilder().createExportNamespaceForDirective(element); |
254 var shownNames = <JS.Expression>[]; | 378 |
255 var hiddenNames = <JS.Expression>[]; | 379 // TODO(jmesserly): we could collect all of the names for bulk re-export, |
256 | 380 // but this is easier to implement for now. |
257 var show = node.combinators.firstWhere((c) => c is ShowCombinator, | 381 void emitExport(Element export, {String suffix: ''}) { |
258 orElse: () => null) as ShowCombinator; | 382 var name = _emitTopLevelName(export, suffix: suffix); |
259 var hide = node.combinators.firstWhere((c) => c is HideCombinator, | 383 _moduleItems.add(js.statement( |
260 orElse: () => null) as HideCombinator; | 384 '#.# = #;', [emitLibraryName(currentLibrary), name.selector, name])); |
261 if (show != null) { | 385 } |
262 shownNames.addAll(show.shownNames | 386 |
263 .map((i) => i.name) | 387 for (var export in exportedNames.definedNames.values) { |
264 .where((s) => !currentLibNames.containsKey(s)) | 388 // Don't allow redefining names from this library. |
265 .map((s) => js.string(s, "'"))); | 389 if (currentNames.containsKey(export.name)) continue; |
266 } | 390 |
267 if (hide != null) { | 391 _loader.emitDeclaration(export); |
268 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); | 392 if (export is ClassElement && export.typeParameters.isNotEmpty) { |
269 } | 393 // Export the generic name as well. |
270 args.add(new JS.ArrayInitializer(shownNames)); | 394 // TODO(jmesserly): revisit generic classes |
271 args.add(new JS.ArrayInitializer(hiddenNames)); | 395 emitExport(export, suffix: r'$'); |
272 } | 396 } |
273 | 397 emitExport(export); |
274 _moduleItems.add(js.statement('dart.export(#);', [args])); | 398 } |
275 } | 399 } |
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 | 400 |
294 @override | 401 @override |
295 visitAsExpression(AsExpression node) { | 402 visitAsExpression(AsExpression node) { |
296 var from = getStaticType(node.expression); | 403 var from = getStaticType(node.expression); |
297 var to = node.type.type; | 404 var to = node.type.type; |
298 | 405 |
299 var fromExpr = _visit(node.expression); | 406 var fromExpr = _visit(node.expression); |
300 | 407 |
301 // Skip the cast if it's not needed. | 408 // Skip the cast if it's not needed. |
302 if (rules.isSubtypeOf(from, to)) return fromExpr; | 409 if (rules.isSubtypeOf(from, to)) return fromExpr; |
303 | 410 |
304 // All Dart number types map to a JS double. | 411 // All Dart number types map to a JS double. |
305 if (_isNumberInJS(from) && _isNumberInJS(to)) { | 412 if (_isNumberInJS(from) && _isNumberInJS(to)) { |
306 // Make sure to check when converting to int. | 413 // Make sure to check when converting to int. |
307 if (from != _types.intType && to == _types.intType) { | 414 if (from != types.intType && to == types.intType) { |
308 return js.call('dart.asInt(#)', [fromExpr]); | 415 return js.call('dart.asInt(#)', [fromExpr]); |
309 } | 416 } |
310 | 417 |
311 // A no-op in JavaScript. | 418 // A no-op in JavaScript. |
312 return fromExpr; | 419 return fromExpr; |
313 } | 420 } |
314 | 421 |
315 return js.call('dart.as(#, #)', [fromExpr, _emitTypeName(to)]); | 422 return js.call('dart.as(#, #)', [fromExpr, _emitTypeName(to)]); |
316 } | 423 } |
317 | 424 |
(...skipping 12 matching lines...) Expand all Loading... | |
330 } | 437 } |
331 | 438 |
332 if (node.notOperator != null) { | 439 if (node.notOperator != null) { |
333 return js.call('!#', result); | 440 return js.call('!#', result); |
334 } | 441 } |
335 return result; | 442 return result; |
336 } | 443 } |
337 | 444 |
338 String _jsTypeofName(DartType t) { | 445 String _jsTypeofName(DartType t) { |
339 if (_isNumberInJS(t)) return 'number'; | 446 if (_isNumberInJS(t)) return 'number'; |
340 if (t == _types.stringType) return 'string'; | 447 if (t == types.stringType) return 'string'; |
341 if (t == _types.boolType) return 'boolean'; | 448 if (t == types.boolType) return 'boolean'; |
342 return null; | 449 return null; |
343 } | 450 } |
344 | 451 |
345 @override | 452 @override |
346 visitFunctionTypeAlias(FunctionTypeAlias node) { | 453 visitFunctionTypeAlias(FunctionTypeAlias node) { |
347 var element = node.element; | 454 FunctionTypeAliasElement element = node.element; |
348 var type = element.type; | |
349 var name = element.name; | |
350 | 455 |
351 var fnType = annotate( | 456 JS.Expression body = annotate( |
352 js.statement('const # = dart.typedef(#, () => #);', [ | 457 js.call('dart.typedef(#, () => #)', [ |
353 name, | 458 js.string(element.name, "'"), |
354 js.string(name, "'"), | 459 _emitTypeName(element.type, lowerTypedef: true) |
355 _emitTypeName(type, lowerTypedef: true) | |
356 ]), | 460 ]), |
357 node, | 461 node, |
358 node.element); | 462 element); |
359 | 463 |
360 return _finishClassDef(type, fnType); | 464 var typeFormals = element.typeParameters; |
465 if (typeFormals.isNotEmpty) { | |
466 return _defineClassTypeArguments(element, typeFormals, | |
467 js.statement('const # = #;', [element.name, body])); | |
468 } else { | |
469 return js.statement('# = #;', [_emitTopLevelName(element), body]); | |
470 } | |
361 } | 471 } |
362 | 472 |
363 @override | 473 @override |
364 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); | 474 JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type); |
365 | 475 |
366 @override | 476 @override |
367 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { | 477 JS.Statement visitClassTypeAlias(ClassTypeAlias node) { |
368 var element = node.element; | 478 ClassElement element = node.element; |
369 | 479 |
370 // Forward all generative constructors from the base class. | 480 // Forward all generative constructors from the base class. |
371 var body = <JS.Method>[]; | 481 var methods = <JS.Method>[]; |
372 | 482 |
373 var supertype = element.supertype; | 483 var supertype = element.supertype; |
374 if (!supertype.isObject) { | 484 if (!supertype.isObject) { |
375 for (var ctor in element.constructors) { | 485 for (var ctor in element.constructors) { |
376 var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library); | 486 var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library); |
377 var fun = js.call('function() { super.#(...arguments); }', | 487 var fun = js.call('function() { super.#(...arguments); }', |
378 [_constructorName(parentCtor)]) as JS.Fun; | 488 [_constructorName(parentCtor)]) as JS.Fun; |
379 body.add(new JS.Method(_constructorName(ctor), fun)); | 489 methods.add(new JS.Method(_constructorName(ctor), fun)); |
380 } | 490 } |
381 } | 491 } |
382 | 492 |
383 var cls = _emitClassDeclaration(element, body); | 493 var classExpr = _emitClassExpression(element, methods); |
384 return _finishClassDef(element.type, cls); | 494 |
495 var typeFormals = element.typeParameters; | |
496 if (typeFormals.isNotEmpty) { | |
497 return _defineClassTypeArguments( | |
498 element, typeFormals, new JS.ClassDeclaration(classExpr)); | |
499 } else { | |
500 return js.statement('# = #;', [_emitTopLevelName(element), classExpr]); | |
501 } | |
385 } | 502 } |
386 | 503 |
387 JS.Statement _emitJsType(String dartClassName, DartObject jsName) { | 504 JS.Statement _emitJsType(Element e) { |
388 var jsTypeName = | 505 var jsTypeName = getAnnotationName(e, isJSAnnotation); |
389 getConstantField(jsName, 'name', types.stringType)?.toStringValue(); | 506 if (jsTypeName == null || jsTypeName == e.name) return null; |
390 | 507 |
391 if (jsTypeName != null && jsTypeName != dartClassName) { | 508 // 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 | 509 // `dom.InputElement` to actually be HTMLInputElement. |
393 // `dom.InputElement` to actually be HTMLInputElement. | 510 // 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 | 511 // generate it correctly when we refer to it. |
395 // generate it correctly when we refer to it. | 512 return js.statement('# = #;', [_emitTopLevelName(e), jsTypeName]); |
396 if (isPublic(dartClassName)) _addExport(dartClassName); | |
397 return js.statement('const # = #;', [dartClassName, jsTypeName]); | |
398 } | |
399 return null; | |
400 } | 513 } |
401 | 514 |
402 @override | 515 @override |
403 JS.Statement visitClassDeclaration(ClassDeclaration node) { | 516 JS.Statement visitClassDeclaration(ClassDeclaration node) { |
404 var classElem = node.element; | 517 var classElem = node.element; |
405 var type = classElem.type; | |
406 var jsName = findAnnotation(classElem, isJSAnnotation); | |
407 | 518 |
408 if (jsName != null) return _emitJsType(node.name.name, jsName); | 519 // If this is a JavaScript type, emit it now and then exit. |
520 var jsTypeDef = _emitJsType(classElem); | |
521 if (jsTypeDef != null) return jsTypeDef; | |
409 | 522 |
410 var ctors = <ConstructorDeclaration>[]; | 523 var ctors = <ConstructorDeclaration>[]; |
411 var fields = <FieldDeclaration>[]; | 524 var fields = <FieldDeclaration>[]; |
412 var staticFields = <FieldDeclaration>[]; | 525 var staticFields = <FieldDeclaration>[]; |
413 var methods = <MethodDeclaration>[]; | 526 var methods = <MethodDeclaration>[]; |
414 for (var member in node.members) { | 527 for (var member in node.members) { |
415 if (member is ConstructorDeclaration) { | 528 if (member is ConstructorDeclaration) { |
416 ctors.add(member); | 529 ctors.add(member); |
417 } else if (member is FieldDeclaration) { | 530 } else if (member is FieldDeclaration) { |
418 (member.isStatic ? staticFields : fields).add(member); | 531 (member.isStatic ? staticFields : fields).add(member); |
419 } else if (member is MethodDeclaration) { | 532 } else if (member is MethodDeclaration) { |
420 methods.add(member); | 533 methods.add(member); |
421 } | 534 } |
422 } | 535 } |
423 | 536 |
424 var allFields = new List.from(fields)..addAll(staticFields); | 537 var allFields = new List.from(fields)..addAll(staticFields); |
425 | 538 var classExpr = _emitClassExpression( |
426 var classDecl = _emitClassDeclaration( | |
427 classElem, _emitClassMethods(node, ctors, fields), | 539 classElem, _emitClassMethods(node, ctors, fields), |
428 fields: allFields); | 540 fields: allFields); |
429 | 541 |
430 String jsPeerName; | 542 var body = <JS.Statement>[]; |
431 var jsPeer = findAnnotation(classElem, isJsPeerInterface); | 543 var extensions = _extensionsToImplement(classElem); |
432 // Only look at "Native" annotations on registered extension types. | 544 _initExtensionSymbols(classElem, methods, fields, body); |
433 // E.g., we're current ignoring the ones in dart:html. | 545 |
434 if (jsPeer == null && _extensionTypes.contains(classElem)) { | 546 // Emit the class, e.g. `core.Object = class Object { ... }` |
435 jsPeer = findAnnotation(classElem, isNativeAnnotation); | 547 JS.Expression className = _defineClass(classElem, classExpr, body); |
436 } | 548 |
437 if (jsPeer != null) { | 549 // Emit things that come after the ES6 `class ... { ... }`. |
438 jsPeerName = | 550 _setBaseClass(classElem, className, body); |
439 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); | 551 _defineNamedConstructors(ctors, body, className); |
440 if (jsPeerName.contains(',')) { | 552 _emitVirtualFields(fields, className, body); |
441 jsPeerName = jsPeerName.split(',')[0]; | 553 _emitClassSignature(methods, classElem, ctors, extensions, className, body); |
442 } | 554 _defineExtensionMembers(extensions, className, body); |
555 _emitClassMetadata(node.metadata, className, body); | |
556 | |
557 JS.Statement classDef = _statement(body); | |
558 var typeFormals = classElem.typeParameters; | |
559 if (typeFormals.isNotEmpty) { | |
560 classDef = _defineClassTypeArguments(classElem, typeFormals, classDef); | |
443 } | 561 } |
444 | 562 |
445 var body = _finishClassMembers(classElem, classDecl, ctors, fields, | 563 body = <JS.Statement>[classDef]; |
446 staticFields, methods, node.metadata, jsPeerName); | 564 _emitStaticFields(staticFields, classElem, body); |
565 _registerExtensionType(classElem, body); | |
566 return _statement(body); | |
567 } | |
447 | 568 |
448 var result = _finishClassDef(type, body); | 569 JS.Expression _defineClass(ClassElement classElem, |
449 | 570 JS.ClassExpression classExpr, List<JS.Statement> body) { |
450 if (jsPeerName != null) { | 571 JS.Expression className; |
451 // This class isn't allowed to be lazy, because we need to set up | 572 if (classElem.typeParameters.isNotEmpty) { |
452 // the native JS type eagerly at this point. | 573 // 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 | 574 // type parameter. So we can use their local variable name directly. |
454 // the end of the Dart library cycle load. | 575 className = classExpr.name; |
455 assert(_loader.isLoaded(classElem)); | 576 body.add(new JS.ClassDeclaration(classExpr)); |
456 | 577 } else { |
457 // TODO(jmesserly): this copies the dynamic members. | 578 className = _emitTopLevelName(classElem); |
458 // Probably fine for objects coming from JS, but not if we actually | 579 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 } | 580 } |
466 return result; | 581 return className; |
467 } | 582 } |
468 | 583 |
469 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { | 584 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { |
470 return typeFormals | 585 return typeFormals |
471 .map((t) => new JS.Identifier(t.name)) | 586 .map((t) => new JS.Identifier(t.name)) |
472 .toList(growable: false); | 587 .toList(growable: false); |
473 } | 588 } |
474 | 589 |
475 /// Emits a field declaration for TypeScript & Closure's ES6_TYPED | 590 /// Emits a field declaration for TypeScript & Closure's ES6_TYPED |
476 /// (e.g. `class Foo { i: string; }`) | 591 /// (e.g. `class Foo { i: string; }`) |
477 JS.VariableDeclarationList _emitTypeScriptField(FieldDeclaration field) { | 592 JS.VariableDeclarationList _emitTypeScriptField(FieldDeclaration field) { |
478 return new JS.VariableDeclarationList( | 593 return new JS.VariableDeclarationList( |
479 field.isStatic ? 'static' : null, | 594 field.isStatic ? 'static' : null, |
480 field.fields.variables | 595 field.fields.variables |
481 .map((decl) => new JS.VariableInitialization( | 596 .map((decl) => new JS.VariableInitialization( |
482 new JS.Identifier( | 597 new JS.Identifier( |
483 // TODO(ochafik): use a refactored _emitMemberName instead. | 598 // TODO(ochafik): use a refactored _emitMemberName instead. |
484 decl.name.name, | 599 decl.name.name, |
485 type: emitTypeRef(decl.element.type)), | 600 type: emitTypeRef(decl.element.type)), |
486 null)) | 601 null)) |
487 .toList(growable: false)); | 602 .toList(growable: false)); |
488 } | 603 } |
489 | 604 |
490 @override | 605 @override |
491 JS.Statement visitEnumDeclaration(EnumDeclaration node) { | 606 JS.Statement visitEnumDeclaration(EnumDeclaration node) { |
492 var element = node.element; | 607 var element = node.element; |
493 var type = element.type; | 608 var type = element.type; |
494 var name = js.string(type.name); | 609 var name = js.string(type.name); |
495 var id = new JS.Identifier(type.name); | |
496 | 610 |
497 // Generate a class per section 13 of the spec. | 611 // Generate a class per section 13 of the spec. |
498 // TODO(vsm): Generate any accompanying metadata | 612 // TODO(vsm): Generate any accompanying metadata |
499 | 613 |
500 // Create constructor and initialize index | 614 // Create constructor and initialize index |
501 var constructor = new JS.Method( | 615 var constructor = new JS.Method( |
502 name, js.call('function(index) { this.index = index; }') as JS.Fun); | 616 name, js.call('function(index) { this.index = index; }') as JS.Fun); |
503 var fields = new List<FieldElement>.from( | 617 var fields = new List<FieldElement>.from( |
504 element.fields.where((f) => f.type == type)); | 618 element.fields.where((f) => f.type == type)); |
505 | 619 |
506 // Create toString() method | 620 // Create toString() method |
507 var properties = new List<JS.Property>(); | 621 var properties = new List<JS.Property>(); |
508 for (var i = 0; i < fields.length; ++i) { | 622 for (var i = 0; i < fields.length; ++i) { |
509 properties.add(new JS.Property( | 623 properties.add(new JS.Property( |
510 js.number(i), js.string('${type.name}.${fields[i].name}'))); | 624 js.number(i), js.string('${type.name}.${fields[i].name}'))); |
511 } | 625 } |
512 var nameMap = new JS.ObjectInitializer(properties, multiline: true); | 626 var nameMap = new JS.ObjectInitializer(properties, multiline: true); |
513 var toStringF = new JS.Method(js.string('toString'), | 627 var toStringF = new JS.Method(js.string('toString'), |
514 js.call('function() { return #[this.index]; }', nameMap) as JS.Fun); | 628 js.call('function() { return #[this.index]; }', nameMap) as JS.Fun); |
515 | 629 |
516 // Create enum class | 630 // Create enum class |
517 var classExpr = new JS.ClassExpression( | 631 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), |
518 id, _emitClassHeritage(element), [constructor, toStringF]); | 632 _emitClassHeritage(element), [constructor, toStringF]); |
519 var result = <JS.Statement>[js.statement('#', classExpr)]; | 633 var id = _emitTopLevelName(element); |
634 var result = [ | |
635 js.statement('# = #', [id, classExpr]) | |
636 ]; | |
520 | 637 |
521 // Create static fields for each enum value | 638 // Create static fields for each enum value |
522 for (var i = 0; i < fields.length; ++i) { | 639 for (var i = 0; i < fields.length; ++i) { |
523 result.add(js.statement('#.# = dart.const(new #(#));', | 640 result.add(js.statement('#.# = dart.const(new #(#));', |
524 [id, fields[i].name, id, js.number(i)])); | 641 [id, fields[i].name, id, js.number(i)])); |
525 } | 642 } |
526 | 643 |
527 // Create static values list | 644 // Create static values list |
528 var values = new JS.ArrayInitializer(new List<JS.Expression>.from( | 645 var values = new JS.ArrayInitializer(new List<JS.Expression>.from( |
529 fields.map((f) => js.call('#.#', [id, f.name])))); | 646 fields.map((f) => js.call('#.#', [id, f.name])))); |
530 result.add(js.statement('#.values = dart.const(dart.list(#, #));', | 647 result.add(js.statement('#.values = dart.const(dart.list(#, #));', |
531 [id, values, _emitTypeName(type)])); | 648 [id, values, _emitTypeName(type)])); |
532 | 649 |
533 if (isPublic(type.name)) _addExport(type.name); | |
534 return _statement(result); | 650 return _statement(result); |
535 } | 651 } |
536 | 652 |
537 /// Given a class element and body, complete the class declaration. | 653 /// Wraps a possibly generic class in its type arguments. |
538 /// This handles generic type parameters, laziness (in library-cycle cases), | 654 JS.Statement _defineClassTypeArguments(TypeDefiningElement element, |
539 /// and ensuring dependencies are loaded first. | 655 List<TypeParameterElement> formals, JS.Statement body) { |
540 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { | 656 assert(formals.isNotEmpty); |
541 var name = type.name; | 657 var genericDef = |
542 var genericName = '$name\$'; | 658 js.statement('# = dart.generic((#) => { #; return #; });', [ |
659 _emitTopLevelName(element, suffix: r'$'), | |
660 _emitTypeFormals(formals), | |
661 body, | |
662 element.name | |
663 ]); | |
543 | 664 |
544 JS.Statement genericDef = null; | 665 var dynType = fillDynamicTypeArgs(element.type); |
545 if (_typeFormalsOf(type).isNotEmpty) { | 666 var genericInst = _emitTypeName(dynType, lowerGeneric: true); |
546 genericDef = _emitGenericClassDef(type, body); | 667 return js.statement( |
547 } | 668 '{ #; # = #; }', [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 } | 669 } |
573 | 670 |
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) { | 671 bool _deferIfNeeded(DartType type, ClassElement current) { |
586 if (type is ParameterizedType) { | 672 if (type is ParameterizedType) { |
587 var typeArguments = type.typeArguments; | 673 var typeArguments = type.typeArguments; |
588 for (var typeArg in typeArguments) { | 674 for (var typeArg in typeArguments) { |
589 var typeElement = typeArg.element; | 675 var typeElement = typeArg.element; |
590 // FIXME(vsm): This does not track mutual recursive dependences. | 676 // FIXME(vsm): This does not track mutual recursive dependences. |
591 if (current == typeElement || _deferIfNeeded(typeArg, current)) { | 677 if (current == typeElement || _deferIfNeeded(typeArg, current)) { |
592 return true; | 678 return true; |
593 } | 679 } |
594 } | 680 } |
595 } | 681 } |
596 return false; | 682 return false; |
597 } | 683 } |
598 | 684 |
599 JS.Statement _emitClassDeclaration( | 685 JS.ClassExpression _emitClassExpression( |
600 ClassElement element, List<JS.Method> methods, | 686 ClassElement element, List<JS.Method> methods, |
601 {List<FieldDeclaration> fields}) { | 687 {List<FieldDeclaration> fields}) { |
602 String name = element.name; | 688 String name = element.name; |
603 var heritage = _emitClassHeritage(element); | 689 var heritage = _emitClassHeritage(element); |
604 var typeParams = _emitTypeFormals(element.typeParameters); | 690 var typeParams = _emitTypeFormals(element.typeParameters); |
605 var jsFields = fields?.map(_emitTypeScriptField)?.toList(); | 691 var jsFields = fields?.map(_emitTypeScriptField)?.toList(); |
606 | 692 |
607 // Workaround for Closure: super classes must be qualified paths. | 693 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 | 694 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 } | 695 } |
624 | 696 |
625 JS.Expression _emitClassHeritage(ClassElement element) { | 697 JS.Expression _emitClassHeritage(ClassElement element) { |
626 var type = element.type; | 698 var type = element.type; |
627 if (type.isObject) return null; | 699 if (type.isObject) return null; |
628 | 700 |
629 // Assume we can load eagerly, until proven otherwise. | |
630 _loader.startTopLevel(element); | 701 _loader.startTopLevel(element); |
631 | 702 |
632 // Find the super type | 703 // Find the super type |
633 JS.Expression heritage; | 704 JS.Expression heritage; |
634 var supertype = type.superclass; | 705 var supertype = type.superclass; |
635 if (_deferIfNeeded(supertype, element)) { | 706 if (_deferIfNeeded(supertype, element)) { |
636 // Fall back to raw type. | 707 // Fall back to raw type. |
637 supertype = fillDynamicTypeArgs(supertype.element.type, _types); | 708 supertype = fillDynamicTypeArgs(supertype.element.type); |
638 _hasDeferredSupertype.add(element); | 709 _hasDeferredSupertype.add(element); |
639 } | 710 } |
640 heritage = _emitTypeName(supertype); | 711 heritage = _emitTypeName(supertype); |
641 | 712 |
642 if (type.mixins.isNotEmpty) { | 713 if (type.mixins.isNotEmpty) { |
643 var mixins = type.mixins.map(_emitTypeName).toList(); | 714 var mixins = type.mixins.map(_emitTypeName).toList(); |
644 mixins.insert(0, heritage); | 715 mixins.insert(0, heritage); |
645 heritage = js.call('dart.mixin(#)', [mixins]); | 716 heritage = js.call('dart.mixin(#)', [mixins]); |
646 } | 717 } |
647 | 718 |
648 _loader.finishTopLevel(element); | 719 _loader.finishTopLevel(element); |
720 | |
649 return heritage; | 721 return heritage; |
650 } | 722 } |
651 | 723 |
652 /// Provide Dart getters and setters that forward to the underlying native | 724 /// Provide Dart getters and setters that forward to the underlying native |
653 /// field. Note that the Dart names are always symbolized to avoid | 725 /// field. Note that the Dart names are always symbolized to avoid |
654 /// conflicts. They will be installed as extension methods on the underlying | 726 /// conflicts. They will be installed as extension methods on the underlying |
655 /// native type. | 727 /// native type. |
656 List<JS.Method> _emitNativeFieldAccessors(FieldDeclaration node) { | 728 List<JS.Method> _emitNativeFieldAccessors(FieldDeclaration node) { |
657 // TODO(vsm): Can this by meta-programmed? | 729 // TODO(vsm): Can this by meta-programmed? |
658 // E.g., dart.nativeField(symbol, jsName) | 730 // E.g., dart.nativeField(symbol, jsName) |
659 // Alternatively, perhaps it could be meta-programmed directly in | 731 // Alternatively, perhaps it could be meta-programmed directly in |
660 // dart.registerExtensions? | 732 // dart.registerExtensions? |
661 var jsMethods = <JS.Method>[]; | 733 var jsMethods = <JS.Method>[]; |
662 if (!node.isStatic) { | 734 if (!node.isStatic) { |
663 for (var decl in node.fields.variables) { | 735 for (var decl in node.fields.variables) { |
664 var field = decl.element; | 736 var field = decl.element; |
665 var name = decl.name.name; | 737 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 | 738 // Generate getter |
672 var fn = new JS.Fun([], js.statement('{ return this.#; }', [name])); | 739 var fn = new JS.Fun([], js.statement('{ return this.#; }', [name])); |
673 var method = | 740 var method = |
674 new JS.Method(_elementMemberName(field.getter), fn, isGetter: true); | 741 new JS.Method(_elementMemberName(field.getter), fn, isGetter: true); |
675 jsMethods.add(method); | 742 jsMethods.add(method); |
676 | 743 |
677 // Generate setter | 744 // Generate setter |
678 if (!decl.isFinal) { | 745 if (!decl.isFinal) { |
679 var value = new JS.TemporaryId('value'); | 746 var value = new JS.TemporaryId('value'); |
680 fn = new JS.Fun( | 747 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 | 817 // Iterable, we know the adapter is already there, so we can skip it as a |
751 // simple code size optimization. | 818 // simple code size optimization. |
752 var parent = t.lookUpGetterInSuperclass('iterator', t.element.library); | 819 var parent = t.lookUpGetterInSuperclass('iterator', t.element.library); |
753 if (parent != null) return null; | 820 if (parent != null) return null; |
754 var parentType = findSupertype(t, _implementsIterable); | 821 var parentType = findSupertype(t, _implementsIterable); |
755 if (parentType != null) return null; | 822 if (parentType != null) return null; |
756 | 823 |
757 // Otherwise, emit the adapter method, which wraps the Dart iterator in | 824 // Otherwise, emit the adapter method, which wraps the Dart iterator in |
758 // an ES6 iterator. | 825 // an ES6 iterator. |
759 return new JS.Method( | 826 return new JS.Method( |
760 js.call('$_SYMBOL.iterator'), | 827 js.call('Symbol.iterator'), |
761 js.call('function() { return new dart.JsIterator(this.#); }', | 828 js.call('function() { return new dart.JsIterator(this.#); }', |
762 [_emitMemberName('iterator', type: t)]) as JS.Fun); | 829 [_emitMemberName('iterator', type: t)]) as JS.Fun); |
763 } | 830 } |
764 | 831 |
765 JS.Expression _instantiateAnnotation(Annotation node) { | 832 JS.Expression _instantiateAnnotation(Annotation node) { |
766 var element = node.element; | 833 var element = node.element; |
767 if (element is ConstructorElement) { | 834 if (element is ConstructorElement) { |
768 return _emitInstanceCreationExpression(element, element.returnType, | 835 return _emitInstanceCreationExpression(element, element.returnType, |
769 node.constructorName, node.arguments, true); | 836 node.constructorName, node.arguments, true); |
770 } else { | 837 } else { |
771 return _visit(node.name); | 838 return _visit(node.name); |
772 } | 839 } |
773 } | 840 } |
774 | 841 |
775 /// Emit class members that need to come after the class declaration, such | 842 /// Gets the JS pper for this Dart type, if any, otherwise null. |
vsm
2016/04/14 00:18:24
pper -> peer
Jennifer Messerly
2016/04/14 18:28:40
Done.
| |
776 /// as static fields. See [_emitClassMethods] for things that are emitted | 843 /// |
777 /// inside the ES6 `class { ... }` node. | 844 /// For example for dart:_interceptors `JSArray` this will return "Array", |
778 JS.Statement _finishClassMembers( | 845 /// referring to the JavaScript built-in `Array` type. |
779 ClassElement classElem, | 846 String _getJSPeerName(ClassElement classElem) { |
780 JS.Statement classDecl, | 847 var jsPeerName = getAnnotationName( |
781 List<ConstructorDeclaration> ctors, | 848 classElem, |
782 List<FieldDeclaration> fields, | 849 (a) => |
783 List<FieldDeclaration> staticFields, | 850 isJsPeerInterface(a) || |
784 List<MethodDeclaration> methods, | 851 isNativeAnnotation(a) && _extensionTypes.contains(classElem)); |
785 List<Annotation> metadata, | 852 if (jsPeerName != null && jsPeerName.contains(',')) { |
786 String jsPeerName) { | 853 jsPeerName = jsPeerName.split(',')[0]; |
787 var name = classElem.name; | 854 } |
788 var body = <JS.Statement>[]; | 855 return jsPeerName; |
856 } | |
789 | 857 |
790 if (_extensionTypes.contains(classElem)) { | 858 void _registerExtensionType(ClassElement classElem, List<JS.Statement> body) { |
791 var dartxNames = <JS.Expression>[]; | 859 var jsPeerName = _getJSPeerName(classElem); |
792 for (var m in methods) { | 860 if (jsPeerName != null) { |
793 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { | 861 // TODO(jmesserly): this copies the dynamic members. |
794 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); | 862 // Probably fine for objects coming from JS, but not if we actually |
863 // want to support construction of instances with generic types other | |
864 // than dynamic. See issue #154 for Array and List<E> related bug. | |
865 body.add(js.statement('dart.registerExtension(dart.global.#, #);', | |
866 [_propertyName(jsPeerName), _emitTopLevelName(classElem)])); | |
867 } | |
868 } | |
869 | |
870 void _setBaseClass(ClassElement classElem, JS.Expression className, | |
871 List<JS.Statement> body) { | |
872 String jsPeerName = _getJSPeerName(classElem); | |
873 JS.Expression newBaseClass; | |
874 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { | |
875 // TODO(jmesserly): we should really just extend Array in the first place. | |
876 newBaseClass = js.call('dart.global.#', [jsPeerName]); | |
877 } else if (_hasDeferredSupertype.contains(classElem)) { | |
878 newBaseClass = _emitTypeName(classElem.type.superclass); | |
879 } | |
880 if (newBaseClass != null) { | |
881 body.add( | |
882 js.statement('dart.setBaseClass(#, #);', [className, newBaseClass])); | |
883 } | |
884 } | |
885 | |
886 /// Emits instance fields, if they are virtual | |
887 /// (in other words, they override a getter/setter pair). | |
888 void _emitVirtualFields(List<FieldDeclaration> fields, | |
889 JS.Expression className, List<JS.Statement> body) { | |
890 for (FieldDeclaration member in fields) { | |
891 for (VariableDeclaration field in member.fields.variables) { | |
892 if (_fieldsNeedingStorage.contains(field.element)) { | |
893 body.add(_overrideField(className, field.element)); | |
795 } | 894 } |
796 } | 895 } |
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 } | 896 } |
897 } | |
812 | 898 |
813 body.add(classDecl); | 899 void _defineNamedConstructors(List<ConstructorDeclaration> ctors, |
814 | 900 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) { | 901 for (ConstructorDeclaration member in ctors) { |
838 if (member.name != null && member.factoryKeyword == null) { | 902 if (member.name != null && member.factoryKeyword == null) { |
839 body.add(js.statement('dart.defineNamedConstructor(#, #);', | 903 body.add(js.statement('dart.defineNamedConstructor(#, #);', |
840 [name, _emitMemberName(member.name.name, isStatic: true)])); | 904 [className, _emitMemberName(member.name.name, isStatic: true)])); |
841 } | 905 } |
842 } | 906 } |
907 } | |
843 | 908 |
844 // Emits instance fields, if they are virtual | 909 /// Emits static fields for a class, and initialize them eagerly if possible, |
845 // (in other words, they override a getter/setter pair). | 910 /// otherwise define them as lazy properties. |
846 for (FieldDeclaration member in fields) { | 911 void _emitStaticFields(List<FieldDeclaration> staticFields, |
912 ClassElement classElem, List<JS.Statement> body) { | |
913 var lazyStatics = <VariableDeclaration>[]; | |
914 for (FieldDeclaration member in staticFields) { | |
847 for (VariableDeclaration field in member.fields.variables) { | 915 for (VariableDeclaration field in member.fields.variables) { |
848 if (_fieldsNeedingStorage.contains(field.element)) { | 916 JS.Statement eagerField = _emitConstantStaticField(classElem, field); |
849 body.add(_overrideField(field.element)); | 917 if (eagerField != null) { |
918 body.add(eagerField); | |
919 } else { | |
920 lazyStatics.add(field); | |
850 } | 921 } |
851 } | 922 } |
852 } | 923 } |
924 if (lazyStatics.isNotEmpty) { | |
925 body.add(_emitLazyFields(classElem, lazyStatics)); | |
926 } | |
927 } | |
853 | 928 |
854 // Emit the signature on the class recording the runtime type information | 929 void _emitClassMetadata(List<Annotation> metadata, JS.Expression className, |
855 var extensions = _extensionsToImplement(classElem); | 930 List<JS.Statement> body) { |
856 { | 931 // TODO(vsm): Make this optional per #268. |
857 var tStatics = <JS.Property>[]; | 932 // Metadata |
858 var tMethods = <JS.Property>[]; | 933 if (metadata.isNotEmpty) { |
859 var sNames = <JS.Expression>[]; | 934 body.add(js.statement('#[dart.metadata] = () => #;', [ |
860 for (MethodDeclaration node in methods) { | 935 className, |
861 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 936 new JS.ArrayInitializer( |
862 var name = node.name.name; | 937 new List<JS.Expression>.from(metadata.map(_instantiateAnnotation))) |
863 var element = node.element; | 938 ])); |
864 var inheritedElement = | 939 } |
865 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 940 } |
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 | 941 |
883 var tCtors = <JS.Property>[]; | 942 /// If a concrete class implements one of our extensions, we might need to |
884 for (ConstructorDeclaration node in ctors) { | 943 /// add forwarders. |
885 var memberName = _constructorName(node.element); | 944 void _defineExtensionMembers(List<ExecutableElement> extensions, |
886 var element = node.element; | 945 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 | 946 // If a concrete class implements one of our extensions, we might need to |
917 // add forwarders. | 947 // add forwarders. |
918 if (extensions.isNotEmpty) { | 948 if (extensions.isNotEmpty) { |
919 var methodNames = <JS.Expression>[]; | 949 var methodNames = <JS.Expression>[]; |
920 for (var e in extensions) { | 950 for (var e in extensions) { |
921 methodNames.add(_elementMemberName(e)); | 951 methodNames.add(_elementMemberName(e)); |
922 } | 952 } |
923 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ | 953 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ |
924 name, | 954 className, |
925 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) | 955 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) |
926 ])); | 956 ])); |
927 } | 957 } |
958 } | |
928 | 959 |
929 // TODO(vsm): Make this optional per #268. | 960 /// Emit the signature on the class recording the runtime type information |
930 // Metadata | 961 void _emitClassSignature( |
931 if (metadata.isNotEmpty) { | 962 List<MethodDeclaration> methods, |
932 body.add(js.statement('#[dart.metadata] = () => #;', [ | 963 ClassElement classElem, |
933 name, | 964 List<ConstructorDeclaration> ctors, |
934 new JS.ArrayInitializer( | 965 List<ExecutableElement> extensions, |
935 new List<JS.Expression>.from(metadata.map(_instantiateAnnotation))) | 966 JS.Expression className, |
967 List<JS.Statement> body) { | |
968 if (classElem.interfaces.isNotEmpty) { | |
969 body.add(js.statement('#[dart.implements] = () => #;', [ | |
970 className, | |
971 new JS.ArrayInitializer(new List<JS.Expression>.from( | |
972 classElem.interfaces.map(_emitTypeName))) | |
936 ])); | 973 ])); |
937 } | 974 } |
938 | 975 |
939 // Emits static fields. These are eager initialized if possible, otherwise | 976 var tStatics = <JS.Property>[]; |
940 // they are made lazy. | 977 var tMethods = <JS.Property>[]; |
941 var lazyStatics = <VariableDeclaration>[]; | 978 var sNames = <JS.Expression>[]; |
942 for (FieldDeclaration member in staticFields) { | 979 for (MethodDeclaration node in methods) { |
943 for (VariableDeclaration field in member.fields.variables) { | 980 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
944 JS.Statement eagerField = _emitConstantStaticField(classElem, field); | 981 var name = node.name.name; |
945 if (eagerField != null) { | 982 var element = node.element; |
946 body.add(eagerField); | 983 var inheritedElement = |
984 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | |
985 if (inheritedElement != null && inheritedElement.type == element.type) { | |
986 continue; | |
987 } | |
988 var memberName = _elementMemberName(element); | |
989 var parts = _emitFunctionTypeParts(element.type); | |
990 var property = | |
991 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | |
992 if (node.isStatic) { | |
993 tStatics.add(property); | |
994 sNames.add(memberName); | |
947 } else { | 995 } else { |
948 lazyStatics.add(field); | 996 tMethods.add(property); |
949 } | 997 } |
950 } | 998 } |
951 } | 999 } |
952 if (lazyStatics.isNotEmpty) { | 1000 |
953 body.add(_emitLazyFields(classElem, lazyStatics)); | 1001 var tCtors = <JS.Property>[]; |
1002 for (ConstructorDeclaration node in ctors) { | |
1003 var memberName = _constructorName(node.element); | |
1004 var element = node.element; | |
1005 var parts = _emitFunctionTypeParts(element.type, node.parameters); | |
1006 var property = | |
1007 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | |
1008 tCtors.add(property); | |
954 } | 1009 } |
955 | 1010 |
956 return _statement(body); | 1011 JS.Property build(String name, List<JS.Property> elements) { |
1012 var o = | |
1013 new JS.ObjectInitializer(elements, multiline: elements.length > 1); | |
1014 var e = js.call('() => #', o); | |
1015 return new JS.Property(_propertyName(name), e); | |
1016 } | |
1017 var sigFields = <JS.Property>[]; | |
1018 if (!tCtors.isEmpty) sigFields.add(build('constructors', tCtors)); | |
1019 if (!tMethods.isEmpty) sigFields.add(build('methods', tMethods)); | |
1020 if (!tStatics.isEmpty) { | |
1021 assert(!sNames.isEmpty); | |
1022 var aNames = new JS.Property( | |
1023 _propertyName('names'), new JS.ArrayInitializer(sNames)); | |
1024 sigFields.add(build('statics', tStatics)); | |
1025 sigFields.add(aNames); | |
1026 } | |
1027 if (!sigFields.isEmpty || extensions.isNotEmpty) { | |
1028 var sig = new JS.ObjectInitializer(sigFields); | |
1029 body.add(js.statement('dart.setSignature(#, #);', [className, sig])); | |
1030 } | |
1031 } | |
1032 | |
1033 /// Ensure `dartx.` symbols we will use are present. | |
1034 void _initExtensionSymbols( | |
1035 ClassElement classElem, | |
1036 List<MethodDeclaration> methods, | |
1037 List<FieldDeclaration> fields, | |
1038 List<JS.Statement> body) { | |
1039 if (_extensionTypes.contains(classElem)) { | |
1040 var dartxNames = <JS.Expression>[]; | |
1041 for (var m in methods) { | |
1042 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { | |
1043 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); | |
1044 } | |
1045 } | |
1046 for (var f in fields) { | |
1047 if (!f.isStatic) { | |
1048 for (var d in f.fields.variables) { | |
1049 if (d.element.isPublic) { | |
1050 dartxNames.add( | |
1051 _elementMemberName(d.element.getter, allowExtensions: false)); | |
1052 } | |
1053 } | |
1054 } | |
1055 } | |
1056 if (dartxNames.isNotEmpty) { | |
1057 body.add(js.statement('dart.defineExtensionNames(#)', | |
1058 [new JS.ArrayInitializer(dartxNames, multiline: true)])); | |
1059 } | |
1060 } | |
957 } | 1061 } |
958 | 1062 |
959 List<ExecutableElement> _extensionsToImplement(ClassElement element) { | 1063 List<ExecutableElement> _extensionsToImplement(ClassElement element) { |
960 var members = <ExecutableElement>[]; | 1064 var members = <ExecutableElement>[]; |
961 if (_extensionTypes.contains(element)) return members; | 1065 if (_extensionTypes.contains(element)) return members; |
962 | 1066 |
963 // Collect all extension types we implement. | 1067 // Collect all extension types we implement. |
964 var type = element.type; | 1068 var type = element.type; |
965 var types = new Set<ClassElement>(); | 1069 var types = new Set<ClassElement>(); |
966 _collectExtensions(type, types); | 1070 _collectExtensions(type, types); |
(...skipping 24 matching lines...) Expand all Loading... | |
991 if (_extensionTypes.contains(element)) types.add(element); | 1095 if (_extensionTypes.contains(element)) types.add(element); |
992 for (var m in type.mixins.reversed) { | 1096 for (var m in type.mixins.reversed) { |
993 _collectExtensions(m, types); | 1097 _collectExtensions(m, types); |
994 } | 1098 } |
995 for (var i in type.interfaces) { | 1099 for (var i in type.interfaces) { |
996 _collectExtensions(i, types); | 1100 _collectExtensions(i, types); |
997 } | 1101 } |
998 _collectExtensions(type.superclass, types); | 1102 _collectExtensions(type.superclass, types); |
999 } | 1103 } |
1000 | 1104 |
1001 JS.Statement _overrideField(FieldElement e) { | 1105 JS.Statement _overrideField(JS.Expression className, FieldElement e) { |
1002 var cls = e.enclosingElement; | 1106 var cls = e.enclosingElement; |
1003 return js.statement('dart.virtualField(#, #)', | 1107 return js.statement('dart.virtualField(#, #)', |
1004 [cls.name, _emitMemberName(e.name, type: cls.type)]); | 1108 [className, _emitMemberName(e.name, type: cls.type)]); |
1005 } | 1109 } |
1006 | 1110 |
1007 /// Generates the implicit default constructor for class C of the form | 1111 /// Generates the implicit default constructor for class C of the form |
1008 /// `C() : super() {}`. | 1112 /// `C() : super() {}`. |
1009 JS.Method _emitImplicitConstructor( | 1113 JS.Method _emitImplicitConstructor( |
1010 ClassDeclaration node, List<FieldDeclaration> fields) { | 1114 ClassDeclaration node, List<FieldDeclaration> fields) { |
1011 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); | 1115 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); |
1012 | 1116 |
1013 // If we don't have a method body, skip this. | 1117 // If we don't have a method body, skip this. |
1014 var superCall = _superConstructorCall(node.element); | 1118 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 | 1226 // Other default constructors use the class name, as they aren't called |
1123 // from call sites, but rather from Object's constructor. | 1227 // from call sites, but rather from Object's constructor. |
1124 // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning | 1228 // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning |
1125 // up constructors to integrate more closely with ES6. | 1229 // up constructors to integrate more closely with ES6. |
1126 return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name); | 1230 return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name); |
1127 } | 1231 } |
1128 | 1232 |
1129 JS.Block _emitConstructorBody( | 1233 JS.Block _emitConstructorBody( |
1130 ConstructorDeclaration node, List<FieldDeclaration> fields) { | 1234 ConstructorDeclaration node, List<FieldDeclaration> fields) { |
1131 var body = <JS.Statement>[]; | 1235 var body = <JS.Statement>[]; |
1236 ClassDeclaration cls = node.parent; | |
1132 | 1237 |
1133 // Generate optional/named argument value assignment. These can not have | 1238 // 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 | 1239 // side effects, and may be used by the constructor's initializers, so it's |
1135 // nice to do them first. | 1240 // nice to do them first. |
1136 // Also for const constructors we need to ensure default values are | 1241 // Also for const constructors we need to ensure default values are |
1137 // available for use by top-level constant initializers. | 1242 // available for use by top-level constant initializers. |
1138 ClassDeclaration cls = node.parent; | |
1139 if (node.constKeyword != null) _loader.startTopLevel(cls.element); | 1243 if (node.constKeyword != null) _loader.startTopLevel(cls.element); |
1140 var init = _emitArgumentInitializers(node, constructor: true); | 1244 var init = _emitArgumentInitializers(node, constructor: true); |
1141 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); | 1245 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); |
1142 if (init != null) body.add(init); | 1246 if (init != null) body.add(init); |
1143 | 1247 |
1144 // Redirecting constructors: these are not allowed to have initializers, | 1248 // Redirecting constructors: these are not allowed to have initializers, |
1145 // and the redirecting ctor invocation runs before field initializers. | 1249 // and the redirecting ctor invocation runs before field initializers. |
1146 var redirectCall = node.initializers.firstWhere( | 1250 var redirectCall = node.initializers.firstWhere( |
1147 (i) => i is RedirectingConstructorInvocation, | 1251 (i) => i is RedirectingConstructorInvocation, |
1148 orElse: () => null); | 1252 orElse: () => null); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1184 ConstructorElement superCtor; | 1288 ConstructorElement superCtor; |
1185 if (node != null) { | 1289 if (node != null) { |
1186 superCtor = node.staticElement; | 1290 superCtor = node.staticElement; |
1187 } else { | 1291 } else { |
1188 // Get the supertype's unnamed constructor. | 1292 // Get the supertype's unnamed constructor. |
1189 superCtor = element.supertype.element.unnamedConstructor; | 1293 superCtor = element.supertype.element.unnamedConstructor; |
1190 if (superCtor == null) { | 1294 if (superCtor == null) { |
1191 // This will only happen if the code has errors: | 1295 // This will only happen if the code has errors: |
1192 // we're trying to generate an implicit constructor for a type where | 1296 // we're trying to generate an implicit constructor for a type where |
1193 // we don't have a default constructor in the supertype. | 1297 // we don't have a default constructor in the supertype. |
1194 assert(options.forceCompile); | 1298 assert(options.unsafeForceCompile); |
1195 return null; | 1299 return null; |
1196 } | 1300 } |
1197 } | 1301 } |
1198 | 1302 |
1199 if (superCtor == null) { | 1303 if (superCtor == null) { |
1200 print('Error generating: ${element.displayName}'); | 1304 print('Error generating: ${element.displayName}'); |
1201 } | 1305 } |
1202 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { | 1306 if (superCtor.name == '' && !_shouldCallUnnamedSuperCtor(element)) { |
1203 return null; | 1307 return null; |
1204 } | 1308 } |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1363 } | 1467 } |
1364 } | 1468 } |
1365 | 1469 |
1366 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { | 1470 JS.Fun _emitNativeFunctionBody(MethodDeclaration node) { |
1367 if (node.isStatic) { | 1471 if (node.isStatic) { |
1368 // TODO(vsm): Do we need to handle this case? | 1472 // TODO(vsm): Do we need to handle this case? |
1369 return null; | 1473 return null; |
1370 } | 1474 } |
1371 | 1475 |
1372 var params = visitFormalParameterList(node.parameters, destructure: false); | 1476 var params = visitFormalParameterList(node.parameters, destructure: false); |
1373 String name = node.name.name; | 1477 String name = |
1374 var annotation = findAnnotation(node.element, isJsName); | 1478 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) { | 1479 if (node.isGetter) { |
1380 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); | 1480 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); |
1381 } else if (node.isSetter) { | 1481 } else if (node.isSetter) { |
1382 return new JS.Fun( | 1482 return new JS.Fun( |
1383 params, js.statement('{ this.# = #; }', [name, params.last])); | 1483 params, js.statement('{ this.# = #; }', [name, params.last])); |
1384 } else { | 1484 } else { |
1385 return new JS.Fun( | 1485 return new JS.Fun( |
1386 params, js.statement('{ return this.#(#); }', [name, params])); | 1486 params, js.statement('{ return this.#(#); }', [name, params])); |
1387 } | 1487 } |
1388 } | 1488 } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1438 returnType: fn.returnType)..sourceInformation = fn.sourceInformation; | 1538 returnType: fn.returnType)..sourceInformation = fn.sourceInformation; |
1439 } | 1539 } |
1440 | 1540 |
1441 @override | 1541 @override |
1442 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1542 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { |
1443 assert(node.parent is CompilationUnit); | 1543 assert(node.parent is CompilationUnit); |
1444 | 1544 |
1445 if (_externalOrNative(node)) return null; | 1545 if (_externalOrNative(node)) return null; |
1446 | 1546 |
1447 if (node.isGetter || node.isSetter) { | 1547 if (node.isGetter || node.isSetter) { |
1448 // Add these later so we can use getter/setter syntax. | 1548 // If we have a getter/setter pair, they need to be defined together. |
1449 _properties.add(node); | 1549 PropertyAccessorElement element = node.element; |
1450 return null; | 1550 var props = <JS.Method>[]; |
1551 var getter = element.variable.getter; | |
1552 if (getter != null) { | |
1553 props.add(_loader.customEmitDeclaration(getter, _emitTopLevelProperty)); | |
1554 } | |
1555 var setter = element.variable.setter; | |
1556 if (setter != null) { | |
1557 props.add(_loader.customEmitDeclaration(setter, _emitTopLevelProperty)); | |
1558 } | |
1559 | |
1560 return js.statement('dart.copyProperties(#, { # });', | |
1561 [emitLibraryName(currentLibrary), props]); | |
1451 } | 1562 } |
1452 | 1563 |
1453 var body = <JS.Statement>[]; | 1564 var body = <JS.Statement>[]; |
1454 _flushLibraryProperties(body); | |
1455 | |
1456 var name = node.name.name; | |
1457 var fn = _emitFunction(node.functionExpression); | 1565 var fn = _emitFunction(node.functionExpression); |
1458 | 1566 |
1459 if (currentLibrary.source.isInSystemLibrary && | 1567 if (currentLibrary.source.isInSystemLibrary && |
1460 _isInlineJSFunction(node.functionExpression)) { | 1568 _isInlineJSFunction(node.functionExpression)) { |
1461 fn = _simplifyPassThroughArrowFunCallBody(fn); | 1569 fn = _simplifyPassThroughArrowFunCallBody(fn); |
1462 } | 1570 } |
1463 | 1571 |
1464 var id = new JS.Identifier(name); | 1572 var element = node.element; |
1465 body.add(annotate(new JS.FunctionDeclaration(id, fn), node, node.element)); | 1573 var nameExpr = _emitTopLevelName(element); |
1466 if (!_isDartRuntime) { | 1574 body.add(annotate(js.statement('# = #', [nameExpr, fn]), node, element)); |
1467 body.add(_emitFunctionTagged(id, node.element.type, topLevel: true) | 1575 if (!_isDartRuntime(element.library)) { |
1576 body.add(_emitFunctionTagged(nameExpr, element.type, topLevel: true) | |
1468 .toStatement()); | 1577 .toStatement()); |
1469 } | 1578 } |
1470 | 1579 |
1471 if (isPublic(name)) { | |
1472 _addExport(name, getJSExportName(node.element, types) ?? name); | |
1473 } | |
1474 return _statement(body); | 1580 return _statement(body); |
1475 } | 1581 } |
1476 | 1582 |
1477 bool _isInlineJSFunction(FunctionExpression functionExpression) { | 1583 bool _isInlineJSFunction(FunctionExpression functionExpression) { |
1478 var body = functionExpression.body; | 1584 var body = functionExpression.body; |
1479 if (body is ExpressionFunctionBody) { | 1585 if (body is ExpressionFunctionBody) { |
1480 return _isJSInvocation(body.expression); | 1586 return _isJSInvocation(body.expression); |
1481 } else if (body is BlockFunctionBody) { | 1587 } else if (body is BlockFunctionBody) { |
1482 if (body.block.statements.length == 1) { | 1588 var statements = body.block.statements; |
1483 var stat = body.block.statements.single; | 1589 if (statements.length == 1) { |
1590 var stat = statements[0]; | |
1484 if (stat is ReturnStatement) { | 1591 if (stat is ReturnStatement) { |
1485 return _isJSInvocation(stat.expression); | 1592 return _isJSInvocation(stat.expression); |
1486 } | 1593 } |
1487 } | 1594 } |
1488 } | 1595 } |
1489 return false; | 1596 return false; |
1490 } | 1597 } |
1491 | 1598 |
1492 bool _isJSInvocation(Expression expr) => | 1599 bool _isJSInvocation(Expression expr) => |
1493 expr is MethodInvocation && isInlineJS(expr.methodName.staticElement); | 1600 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; | 1656 var name = type.name; |
1550 var lazy = topLevel && !_typeIsLoaded(type); | 1657 var lazy = topLevel && !_typeIsLoaded(type); |
1551 | 1658 |
1552 if (type is FunctionType && (name == '' || name == null)) { | 1659 if (type is FunctionType && (name == '' || name == null)) { |
1553 if (type.returnType.isDynamic && | 1660 if (type.returnType.isDynamic && |
1554 type.optionalParameterTypes.isEmpty && | 1661 type.optionalParameterTypes.isEmpty && |
1555 type.namedParameterTypes.isEmpty && | 1662 type.namedParameterTypes.isEmpty && |
1556 type.normalParameterTypes.every((t) => t.isDynamic)) { | 1663 type.normalParameterTypes.every((t) => t.isDynamic)) { |
1557 return js.call('dart.fn(#)', [fn]); | 1664 return js.call('dart.fn(#)', [fn]); |
1558 } | 1665 } |
1559 if (lazy) { | 1666 |
1560 return js.call('dart.fn(#, () => #)', [fn, _emitFunctionRTTI(type)]); | 1667 String code = lazy ? '() => dart.definiteFunctionType(#)' : '#'; |
1561 } | 1668 return js.call('dart.fn(#, $code)', [fn, _emitFunctionTypeParts(type)]); |
1562 return js.call('dart.fn(#, #)', [fn, _emitFunctionTypeParts(type)]); | |
1563 } | 1669 } |
1564 throw 'Function has non function type: $type'; | 1670 throw 'Function has non function type: $type'; |
1565 } | 1671 } |
1566 | 1672 |
1567 /// Emits an arrow FunctionExpression node. | 1673 /// Emits an arrow FunctionExpression node. |
1568 /// | 1674 /// |
1569 /// This should be used for all places in Dart's AST where FunctionExpression | 1675 /// 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 | 1676 /// appears and the function is actually in an Expression context. These |
1571 /// correspond to arrow functions in Dart. | 1677 /// correspond to arrow functions in Dart. |
1572 /// | 1678 /// |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1729 | 1835 |
1730 // Get the original declaring element. If we had a property accessor, this | 1836 // Get the original declaring element. If we had a property accessor, this |
1731 // indirects back to a (possibly synthetic) field. | 1837 // indirects back to a (possibly synthetic) field. |
1732 var element = accessor; | 1838 var element = accessor; |
1733 if (accessor is PropertyAccessorElement) element = accessor.variable; | 1839 if (accessor is PropertyAccessorElement) element = accessor.variable; |
1734 | 1840 |
1735 _loader.declareBeforeUse(element); | 1841 _loader.declareBeforeUse(element); |
1736 | 1842 |
1737 // type literal | 1843 // type literal |
1738 if (element is TypeDefiningElement) { | 1844 if (element is TypeDefiningElement) { |
1739 return _emitTypeName( | 1845 return _emitTypeName(fillDynamicTypeArgs(element.type)); |
1740 fillDynamicTypeArgs((element as dynamic).type, types)); | |
1741 } | 1846 } |
1742 | 1847 |
1743 // library member | 1848 // library member |
1744 if (element.enclosingElement is CompilationUnitElement) { | 1849 if (element.enclosingElement is CompilationUnitElement) { |
1745 return _emitTopLevelName(element); | 1850 return _emitTopLevelName(element); |
1746 } | 1851 } |
1747 | 1852 |
1748 var name = element.name; | 1853 var name = element.name; |
1749 | 1854 |
1750 // Unqualified class member. This could mean implicit-this, or implicit | 1855 // Unqualified class member. This could mean implicit-this, or implicit |
1751 // call to a static from the same class. | 1856 // call to a static from the same class. |
1752 if (element is ClassMemberElement && element is! ConstructorElement) { | 1857 if (element is ClassMemberElement && element is! ConstructorElement) { |
1753 bool isStatic = element.isStatic; | 1858 bool isStatic = element.isStatic; |
1754 var type = element.enclosingElement.type; | 1859 var type = element.enclosingElement.type; |
1755 var member = _emitMemberName(name, isStatic: isStatic, type: type); | 1860 var member = _emitMemberName(name, isStatic: isStatic, type: type); |
1756 | 1861 |
1757 // For static methods, we add the raw type name, without generics or | 1862 // 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 | 1863 // library prefix. We don't need those because static calls can't use |
1759 // the generic type. | 1864 // the generic type. |
1760 if (isStatic) { | 1865 if (isStatic) { |
1761 var dynType = _emitTypeName(fillDynamicTypeArgs(type, types)); | 1866 var dynType = _emitTypeName(fillDynamicTypeArgs(type)); |
1762 return new JS.PropertyAccess(dynType, member); | 1867 return new JS.PropertyAccess(dynType, member); |
1763 } | 1868 } |
1764 | 1869 |
1765 // For instance members, we add implicit-this. | 1870 // For instance members, we add implicit-this. |
1766 // For method tear-offs, we ensure it's a bound method. | 1871 // For method tear-offs, we ensure it's a bound method. |
1767 var tearOff = element is MethodElement && !inInvocationContext(node); | 1872 var tearOff = element is MethodElement && !inInvocationContext(node); |
1768 var code = (tearOff) ? 'dart.bind(this, #)' : 'this.#'; | 1873 var code = (tearOff) ? 'dart.bind(this, #)' : 'this.#'; |
1769 return js.call(code, member); | 1874 return js.call(code, member); |
1770 } | 1875 } |
1771 | 1876 |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1854 } | 1959 } |
1855 if (!optionalTypes.isEmpty) { | 1960 if (!optionalTypes.isEmpty) { |
1856 assert(namedTypes.isEmpty); | 1961 assert(namedTypes.isEmpty); |
1857 var oa = _emitTypeNames( | 1962 var oa = _emitTypeNames( |
1858 optionalTypes, parameters?.sublist(parameterTypes.length)); | 1963 optionalTypes, parameters?.sublist(parameterTypes.length)); |
1859 return [rt, ra, oa]; | 1964 return [rt, ra, oa]; |
1860 } | 1965 } |
1861 return [rt, ra]; | 1966 return [rt, ra]; |
1862 } | 1967 } |
1863 | 1968 |
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. | 1969 /// Emits a Dart [type] into code. |
1870 /// | 1970 /// |
1871 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a | 1971 /// 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 | 1972 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form |
1873 /// will be used instead of `List`. These flags are used when generating | 1973 /// will be used instead of `List`. These flags are used when generating |
1874 /// the definitions for typedefs and generic types, respectively. | 1974 /// the definitions for typedefs and generic types, respectively. |
1875 JS.Expression _emitTypeName(DartType type, | 1975 JS.Expression _emitTypeName(DartType type, |
1876 {bool lowerTypedef: false, bool lowerGeneric: false}) { | 1976 {bool lowerTypedef: false, bool lowerGeneric: false}) { |
1877 // The void and dynamic types are not defined in core. | 1977 // The void and dynamic types are not defined in core. |
1878 if (type.isVoid) { | 1978 if (type.isVoid) { |
(...skipping 10 matching lines...) Expand all Loading... | |
1889 // methods? Similar issue with generic types. For all of these, we may want | 1989 // 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. | 1990 // to canonicalize them too, at least when inside the same library. |
1891 var name = type.name; | 1991 var name = type.name; |
1892 var element = type.element; | 1992 var element = type.element; |
1893 if (name == '' || name == null || lowerTypedef) { | 1993 if (name == '' || name == null || lowerTypedef) { |
1894 var parts = _emitFunctionTypeParts(type as FunctionType); | 1994 var parts = _emitFunctionTypeParts(type as FunctionType); |
1895 return js.call('dart.functionType(#)', [parts]); | 1995 return js.call('dart.functionType(#)', [parts]); |
1896 } | 1996 } |
1897 // For now, reify generic method parameters as dynamic | 1997 // For now, reify generic method parameters as dynamic |
1898 bool _isGenericTypeParameter(DartType type) => | 1998 bool _isGenericTypeParameter(DartType type) => |
1899 (type is TypeParameterType) && | 1999 type is TypeParameterType && |
1900 !(type.element.enclosingElement is ClassElement || | 2000 type.element.enclosingElement is! TypeDefiningElement; |
1901 type.element.enclosingElement is FunctionTypeAliasElement); | |
1902 | 2001 |
1903 if (_isGenericTypeParameter(type)) { | 2002 if (_isGenericTypeParameter(type)) { |
1904 return js.call('dart.dynamic'); | 2003 return js.call('dart.dynamic'); |
1905 } | 2004 } |
1906 | 2005 |
1907 if (type is TypeParameterType) { | 2006 if (type is TypeParameterType) { |
1908 return new JS.Identifier(name); | 2007 return new JS.Identifier(name); |
1909 } | 2008 } |
1910 | 2009 |
1911 if (type is ParameterizedType) { | 2010 if (type is ParameterizedType) { |
1912 var args = type.typeArguments; | 2011 var args = type.typeArguments; |
1913 var isCurrentClass = | |
1914 args.isNotEmpty && _loader.isCurrentElement(type.element); | |
1915 Iterable jsArgs = null; | 2012 Iterable jsArgs = null; |
1916 if (args | 2013 if (args.any((a) => !a.isDynamic && !_isGenericTypeParameter(a))) { |
1917 .any((a) => a != types.dynamicType && !_isGenericTypeParameter(a))) { | |
1918 jsArgs = args.map(_emitTypeName); | 2014 jsArgs = args.map(_emitTypeName); |
1919 } else if (lowerGeneric || isCurrentClass) { | 2015 } 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 = []; | 2016 jsArgs = []; |
1924 } | 2017 } |
1925 if (jsArgs != null) { | 2018 if (jsArgs != null) { |
1926 var genericName = _emitTopLevelName(element, suffix: '\$'); | 2019 var genericName = _emitTopLevelName(element, suffix: '\$'); |
1927 return js.call('#(#)', [genericName, jsArgs]); | 2020 return js.call('#(#)', [genericName, jsArgs]); |
1928 } | 2021 } |
1929 } | 2022 } |
1930 | 2023 |
1931 return _emitTopLevelName(element); | 2024 return _emitTopLevelName(element); |
1932 } | 2025 } |
1933 | 2026 |
1934 JS.Expression _emitTopLevelName(Element e, {String suffix: ''}) { | 2027 JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { |
1935 var libName = emitLibraryName(e.library); | 2028 String name = getJSExportName(e) + suffix; |
1936 | 2029 return new JS.PropertyAccess( |
1937 // Always qualify: | 2030 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 } | 2031 } |
1958 | 2032 |
1959 @override | 2033 @override |
1960 JS.Expression visitAssignmentExpression(AssignmentExpression node) { | 2034 JS.Expression visitAssignmentExpression(AssignmentExpression node) { |
1961 var left = node.leftHandSide; | 2035 var left = node.leftHandSide; |
1962 var right = node.rightHandSide; | 2036 var right = node.rightHandSide; |
1963 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); | 2037 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); |
1964 var op = node.operator.lexeme; | 2038 var op = node.operator.lexeme; |
1965 assert(op.endsWith('=')); | 2039 assert(op.endsWith('=')); |
1966 op = op.substring(0, op.length - 1); // remove trailing '=' | 2040 op = op.substring(0, op.length - 1); // remove trailing '=' |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2015 } | 2089 } |
2016 | 2090 |
2017 target = _getTarget(lhs); | 2091 target = _getTarget(lhs); |
2018 id = lhs.propertyName; | 2092 id = lhs.propertyName; |
2019 } else if (lhs is PrefixedIdentifier) { | 2093 } else if (lhs is PrefixedIdentifier) { |
2020 target = lhs.prefix; | 2094 target = lhs.prefix; |
2021 id = lhs.identifier; | 2095 id = lhs.identifier; |
2022 } | 2096 } |
2023 | 2097 |
2024 if (target != null && DynamicInvoke.get(target)) { | 2098 if (target != null && DynamicInvoke.get(target)) { |
2025 return js.call('dart.$DPUT(#, #, #)', | 2099 return js.call('dart.dput(#, #, #)', |
2026 [_visit(target), _emitMemberName(id.name), _visit(rhs)]); | 2100 [_visit(target), _emitMemberName(id.name), _visit(rhs)]); |
2027 } | 2101 } |
2028 | 2102 |
2029 return _visit(rhs).toAssignExpression(_visit(lhs)); | 2103 return _visit(rhs).toAssignExpression(_visit(lhs)); |
2030 } | 2104 } |
2031 | 2105 |
2032 JS.Expression _emitNullSafeSet(PropertyAccess node, Expression right) { | 2106 JS.Expression _emitNullSafeSet(PropertyAccess node, Expression right) { |
2033 // Emit `obj?.prop = expr` as: | 2107 // Emit `obj?.prop = expr` as: |
2034 // | 2108 // |
2035 // (_ => _ == null ? null : _.prop = expr)(obj). | 2109 // (_ => _ == null ? null : _.prop = expr)(obj). |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2080 return _emitNullSafe(node); | 2154 return _emitNullSafe(node); |
2081 } | 2155 } |
2082 | 2156 |
2083 var target = _getTarget(node); | 2157 var target = _getTarget(node); |
2084 var result = _emitForeignJS(node); | 2158 var result = _emitForeignJS(node); |
2085 if (result != null) return result; | 2159 if (result != null) return result; |
2086 | 2160 |
2087 String code; | 2161 String code; |
2088 if (target == null || isLibraryPrefix(target)) { | 2162 if (target == null || isLibraryPrefix(target)) { |
2089 if (DynamicInvoke.get(node.methodName)) { | 2163 if (DynamicInvoke.get(node.methodName)) { |
2090 code = 'dart.$DCALL(#, #)'; | 2164 code = 'dart.dcall(#, #)'; |
2091 } else { | 2165 } else { |
2092 code = '#(#)'; | 2166 code = '#(#)'; |
2093 } | 2167 } |
2094 return js | 2168 return js |
2095 .call(code, [_visit(node.methodName), _visit(node.argumentList)]); | 2169 .call(code, [_visit(node.methodName), _visit(node.argumentList)]); |
2096 } | 2170 } |
2097 | 2171 |
2098 var type = getStaticType(target); | 2172 var type = getStaticType(target); |
2099 var name = node.methodName.name; | 2173 var name = node.methodName.name; |
2100 var element = node.methodName.staticElement; | 2174 var element = node.methodName.staticElement; |
2101 bool isStatic = element is ExecutableElement && element.isStatic; | 2175 bool isStatic = element is ExecutableElement && element.isStatic; |
2102 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); | 2176 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); |
2103 | 2177 |
2104 if (DynamicInvoke.get(target)) { | 2178 if (DynamicInvoke.get(target)) { |
2105 code = 'dart.$DSEND(#, #, #)'; | 2179 code = 'dart.dsend(#, #, #)'; |
2106 } else if (DynamicInvoke.get(node.methodName)) { | 2180 } else if (DynamicInvoke.get(node.methodName)) { |
2107 // This is a dynamic call to a statically known target. For example: | 2181 // This is a dynamic call to a statically known target. For example: |
2108 // class Foo { Function bar; } | 2182 // class Foo { Function bar; } |
2109 // new Foo().bar(); // dynamic call | 2183 // new Foo().bar(); // dynamic call |
2110 code = 'dart.$DCALL(#.#, #)'; | 2184 code = 'dart.dcall(#.#, #)'; |
2111 } else if (_requiresStaticDispatch(target, name)) { | 2185 } else if (_requiresStaticDispatch(target, name)) { |
2112 // Object methods require a helper for null checks. | 2186 // Object methods require a helper for null checks. |
2113 return js.call('dart.#(#, #)', | 2187 return js.call('dart.#(#, #)', |
2114 [memberName, _visit(target), _visit(node.argumentList)]); | 2188 [memberName, _visit(target), _visit(node.argumentList)]); |
2115 } else { | 2189 } else { |
2116 code = '#.#(#)'; | 2190 code = '#.#(#)'; |
2117 } | 2191 } |
2118 | 2192 |
2119 return js | 2193 return js |
2120 .call(code, [_visit(target), memberName, _visit(node.argumentList)]); | 2194 .call(code, [_visit(target), memberName, _visit(node.argumentList)]); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2155 return result; | 2229 return result; |
2156 } | 2230 } |
2157 return null; | 2231 return null; |
2158 } | 2232 } |
2159 | 2233 |
2160 @override | 2234 @override |
2161 JS.Expression visitFunctionExpressionInvocation( | 2235 JS.Expression visitFunctionExpressionInvocation( |
2162 FunctionExpressionInvocation node) { | 2236 FunctionExpressionInvocation node) { |
2163 var code; | 2237 var code; |
2164 if (DynamicInvoke.get(node.function)) { | 2238 if (DynamicInvoke.get(node.function)) { |
2165 code = 'dart.$DCALL(#, #)'; | 2239 code = 'dart.dcall(#, #)'; |
2166 } else { | 2240 } else { |
2167 code = '#(#)'; | 2241 code = '#(#)'; |
2168 } | 2242 } |
2169 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); | 2243 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); |
2170 } | 2244 } |
2171 | 2245 |
2172 @override | 2246 @override |
2173 List<JS.Expression> visitArgumentList(ArgumentList node) { | 2247 List<JS.Expression> visitArgumentList(ArgumentList node) { |
2174 var args = <JS.Expression>[]; | 2248 var args = <JS.Expression>[]; |
2175 var named = <JS.Property>[]; | 2249 var named = <JS.Property>[]; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2210 | 2284 |
2211 for (FormalParameter param in node.parameters) { | 2285 for (FormalParameter param in node.parameters) { |
2212 if (param.kind == ParameterKind.NAMED) { | 2286 if (param.kind == ParameterKind.NAMED) { |
2213 if (destructure) { | 2287 if (destructure) { |
2214 if (_jsObjectProperties.contains(param.identifier.name)) { | 2288 if (_jsObjectProperties.contains(param.identifier.name)) { |
2215 hasNamedArgsConflictingWithObjectProperties = true; | 2289 hasNamedArgsConflictingWithObjectProperties = true; |
2216 } | 2290 } |
2217 JS.Expression name; | 2291 JS.Expression name; |
2218 JS.SimpleBindingPattern structure = null; | 2292 JS.SimpleBindingPattern structure = null; |
2219 String paramName = param.identifier.name; | 2293 String paramName = param.identifier.name; |
2220 if (invalidVariableName(paramName)) { | 2294 if (JS.invalidVariableName(paramName)) { |
2221 name = js.string(paramName); | 2295 name = js.string(paramName); |
2222 structure = new JS.SimpleBindingPattern(_visit(param.identifier)); | 2296 structure = new JS.SimpleBindingPattern(_visit(param.identifier)); |
2223 } else { | 2297 } else { |
2224 name = _visit(param.identifier); | 2298 name = _visit(param.identifier); |
2225 } | 2299 } |
2226 namedVars.add(new JS.DestructuredVariable( | 2300 namedVars.add(new JS.DestructuredVariable( |
2227 name: name, | 2301 name: name, |
2228 structure: structure, | 2302 structure: structure, |
2229 defaultValue: _defaultParamValue(param))); | 2303 defaultValue: _defaultParamValue(param))); |
2230 } else { | 2304 } else { |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2318 return jsExpr.toYieldStatement(star: star); | 2392 return jsExpr.toYieldStatement(star: star); |
2319 } | 2393 } |
2320 | 2394 |
2321 @override | 2395 @override |
2322 JS.Expression visitAwaitExpression(AwaitExpression node) { | 2396 JS.Expression visitAwaitExpression(AwaitExpression node) { |
2323 return new JS.Yield(_visit(node.expression)); | 2397 return new JS.Yield(_visit(node.expression)); |
2324 } | 2398 } |
2325 | 2399 |
2326 @override | 2400 @override |
2327 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 2401 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
2328 for (var v in node.variables.variables) { | 2402 for (var variable in node.variables.variables) { |
2329 _loader.loadDeclaration(v, v.element); | 2403 _loader.emitDeclaration(variable.element); |
2330 } | 2404 } |
2331 } | 2405 } |
2332 | 2406 |
2333 /// Emits static fields. | 2407 /// This is not used--we emit fields as we are emitting the class, |
2334 /// | 2408 /// 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 | 2409 @override |
2340 visitFieldDeclaration(FieldDeclaration node) { | 2410 visitFieldDeclaration(FieldDeclaration node) { |
2341 if (!node.isStatic) return; | 2411 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 } | 2412 } |
2352 | 2413 |
2353 @override | 2414 @override |
2354 JS.Statement visitVariableDeclarationStatement( | 2415 JS.Statement visitVariableDeclarationStatement( |
2355 VariableDeclarationStatement node) { | 2416 VariableDeclarationStatement node) { |
2356 // Special case a single variable with an initializer. | 2417 // Special case a single variable with an initializer. |
2357 // This helps emit cleaner code for things like: | 2418 // This helps emit cleaner code for things like: |
2358 // var result = []..add(1)..add(2); | 2419 // var result = []..add(1)..add(2); |
2359 if (node.variables.variables.length == 1) { | 2420 var variables = node.variables.variables; |
2360 var v = node.variables.variables.single; | 2421 if (variables.length == 1) { |
2422 var v = variables[0]; | |
2361 if (v.initializer != null) { | 2423 if (v.initializer != null) { |
2362 var name = new JS.Identifier(v.name.name); | 2424 var name = new JS.Identifier(v.name.name); |
2363 return _visit(v.initializer).toVariableDeclaration(name); | 2425 return _visit(v.initializer).toVariableDeclaration(name); |
2364 } | 2426 } |
2365 } | 2427 } |
2366 return _visit(node.variables).toStatement(); | 2428 return _visit(node.variables).toStatement(); |
2367 } | 2429 } |
2368 | 2430 |
2369 @override | 2431 @override |
2370 visitVariableDeclarationList(VariableDeclarationList node) { | 2432 visitVariableDeclarationList(VariableDeclarationList node) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2412 JS.Expression jsInit = _visitInitializer(field); | 2474 JS.Expression jsInit = _visitInitializer(field); |
2413 bool isLoaded = _loader.finishCheckingReferences(); | 2475 bool isLoaded = _loader.finishCheckingReferences(); |
2414 | 2476 |
2415 bool eagerInit = | 2477 bool eagerInit = |
2416 isLoaded && (field.isConst || _constField.isFieldInitConstant(field)); | 2478 isLoaded && (field.isConst || _constField.isFieldInitConstant(field)); |
2417 | 2479 |
2418 var fieldName = field.name.name; | 2480 var fieldName = field.name.name; |
2419 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { | 2481 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { |
2420 return annotate( | 2482 return annotate( |
2421 js.statement('#.# = #;', [ | 2483 js.statement('#.# = #;', [ |
2422 classElem.name, | 2484 _emitTopLevelName(classElem), |
2423 _emitMemberName(fieldName, isStatic: true), | 2485 _emitMemberName(fieldName, isStatic: true), |
2424 jsInit | 2486 jsInit |
2425 ]), | 2487 ]), |
2426 field, | 2488 field, |
2427 field.element); | 2489 field.element); |
2428 } | 2490 } |
2429 | 2491 |
2430 // This means it should be treated as a lazy field. | 2492 // This means it should be treated as a lazy field. |
2431 // TODO(jmesserly): we're throwing away the initializer expression, | 2493 // TODO(jmesserly): we're throwing away the initializer expression, |
2432 // which will force us to regenerate it. | 2494 // which will force us to regenerate it. |
2433 return null; | 2495 return null; |
2434 } | 2496 } |
2435 | 2497 |
2436 /// Emits a top-level field. | 2498 /// Emits a top-level field. |
2437 JS.Statement _emitTopLevelField(VariableDeclaration field) { | 2499 JS.ModuleItem _emitTopLevelField(VariableDeclaration field) { |
2438 TopLevelVariableElement element = field.element; | 2500 TopLevelVariableElement element = field.element; |
2439 assert(element.isStatic); | 2501 assert(element.isStatic); |
2440 | 2502 |
2441 bool eagerInit; | 2503 bool eagerInit; |
2442 JS.Expression jsInit; | 2504 JS.Expression jsInit; |
2443 if (field.isConst || _constField.isFieldInitConstant(field)) { | 2505 if (field.isConst || _constField.isFieldInitConstant(field)) { |
2444 // If the field is constant, try and generate it at the top level. | 2506 // If the field is constant, try and generate it at the top level. |
2445 _loader.startTopLevel(element); | 2507 _loader.startTopLevel(element); |
2446 jsInit = _visitInitializer(field); | 2508 jsInit = _visitInitializer(field); |
2447 _loader.finishTopLevel(element); | 2509 _loader.finishTopLevel(element); |
2448 eagerInit = _loader.isLoaded(element); | 2510 eagerInit = _loader.isLoaded(element); |
2449 } else { | 2511 } else { |
2450 // TODO(jmesserly): we're visiting the initializer here, and again | 2512 // TODO(jmesserly): we're visiting the initializer here, and again |
2451 // later on when we emit lazy fields. That seems busted. | 2513 // later on when we emit lazy fields. That seems busted. |
2452 jsInit = _visitInitializer(field); | 2514 jsInit = _visitInitializer(field); |
2453 eagerInit = false; | 2515 eagerInit = false; |
2454 } | 2516 } |
2455 | 2517 |
2456 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 2518 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile |
2457 // runtime helpers. | 2519 // runtime helpers. |
2520 // TODO(jmesserly): we don't track dependencies correctly for these. | |
2458 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | 2521 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); |
2459 if (isJSTopLevel) eagerInit = true; | 2522 if (eagerInit || isJSTopLevel) { |
2460 | 2523 return annotate( |
2461 var fieldName = field.name.name; | 2524 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), |
2462 var exportName = fieldName; | 2525 field, |
2463 if (element is TopLevelVariableElement) { | 2526 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 } | 2527 } |
2489 | 2528 |
2490 if (eagerInit && !JS.invalidStaticFieldName(fieldName)) { | 2529 assert(element.library == currentLibrary); |
2491 return annotate(js.statement('# = #;', [_visit(field.name), jsInit]), | 2530 return _emitLazyFields(element.library, [field]); |
2492 field, field.element); | |
2493 } | |
2494 | |
2495 return _emitLazyFields(currentLibrary, [field]); | |
2496 } | 2531 } |
2497 | 2532 |
2498 JS.Expression _visitInitializer(VariableDeclaration node) { | 2533 JS.Expression _visitInitializer(VariableDeclaration node) { |
2499 var value = _visit(node.initializer); | 2534 var value = _visit(node.initializer); |
2500 // explicitly initialize to null, to avoid getting `undefined`. | 2535 // explicitly initialize to null, to avoid getting `undefined`. |
2501 // TODO(jmesserly): do this only for vars that aren't definitely assigned. | 2536 // TODO(jmesserly): do this only for vars that aren't definitely assigned. |
2502 return value ?? new JS.LiteralNull(); | 2537 return value ?? new JS.LiteralNull(); |
2503 } | 2538 } |
2504 | 2539 |
2505 JS.Statement _emitLazyFields( | 2540 JS.Statement _emitLazyFields( |
(...skipping 17 matching lines...) Expand all Loading... | |
2523 methods.add(annotate( | 2558 methods.add(annotate( |
2524 new JS.Method(access, js.call('function(_) {}') as JS.Fun, | 2559 new JS.Method(access, js.call('function(_) {}') as JS.Fun, |
2525 isSetter: true), | 2560 isSetter: true), |
2526 node, | 2561 node, |
2527 _findAccessor(element, getter: false))); | 2562 _findAccessor(element, getter: false))); |
2528 } | 2563 } |
2529 } | 2564 } |
2530 | 2565 |
2531 JS.Expression objExpr; | 2566 JS.Expression objExpr; |
2532 if (target is ClassElement) { | 2567 if (target is ClassElement) { |
2533 objExpr = new JS.Identifier(target.type.name); | 2568 objExpr = _emitTopLevelName(target); |
2534 } else { | 2569 } else { |
2535 objExpr = emitLibraryName(target); | 2570 objExpr = emitLibraryName(target); |
2536 } | 2571 } |
2537 | 2572 |
2538 return js | 2573 return js.statement('dart.defineLazy(#, { # });', [objExpr, methods]); |
2539 .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]); | |
2540 } | 2574 } |
2541 | 2575 |
2542 PropertyAccessorElement _findAccessor(VariableElement element, | 2576 PropertyAccessorElement _findAccessor(VariableElement element, |
2543 {bool getter}) { | 2577 {bool getter}) { |
2544 var parent = element.enclosingElement; | 2578 var parent = element.enclosingElement; |
2545 if (parent is ClassElement) { | 2579 if (parent is ClassElement) { |
2546 return getter | 2580 return getter |
2547 ? parent.getGetter(element.name) | 2581 ? parent.getGetter(element.name) |
2548 : parent.getSetter(element.name); | 2582 : parent.getSetter(element.name); |
2549 } | 2583 } |
2550 return null; | 2584 return null; |
2551 } | 2585 } |
2552 | 2586 |
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( | 2587 JS.Expression _emitConstructorName( |
2561 ConstructorElement element, DartType type, SimpleIdentifier name) { | 2588 ConstructorElement element, DartType type, SimpleIdentifier name) { |
2562 var typeName = _emitTypeName(type); | 2589 var typeName = _emitTypeName(type); |
2563 if (name != null || element.isFactory) { | 2590 if (name != null || element.isFactory) { |
2564 var namedCtor = _constructorName(element); | 2591 var namedCtor = _constructorName(element); |
2565 return new JS.PropertyAccess(typeName, namedCtor); | 2592 return new JS.PropertyAccess(typeName, namedCtor); |
2566 } | 2593 } |
2567 return typeName; | 2594 return typeName; |
2568 } | 2595 } |
2569 | 2596 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2608 var type = constructor.type.type; | 2635 var type = constructor.type.type; |
2609 return _emitInstanceCreationExpression( | 2636 return _emitInstanceCreationExpression( |
2610 element, type, name, node.argumentList, node.isConst); | 2637 element, type, name, node.argumentList, node.isConst); |
2611 } | 2638 } |
2612 | 2639 |
2613 /// True if this type is built-in to JS, and we use the values unwrapped. | 2640 /// 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 | 2641 /// For these types we generate a calling convention via static |
2615 /// "extension methods". This allows types to be extended without adding | 2642 /// "extension methods". This allows types to be extended without adding |
2616 /// extensions directly on the prototype. | 2643 /// extensions directly on the prototype. |
2617 bool isPrimitiveType(DartType t) => | 2644 bool isPrimitiveType(DartType t) => |
2618 typeIsPrimitiveInJS(t) || t == _types.stringType; | 2645 typeIsPrimitiveInJS(t) || t == types.stringType; |
2619 | 2646 |
2620 bool typeIsPrimitiveInJS(DartType t) => | 2647 bool typeIsPrimitiveInJS(DartType t) => |
2621 _isNumberInJS(t) || t == _types.boolType; | 2648 _isNumberInJS(t) || t == types.boolType; |
2622 | 2649 |
2623 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 2650 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
2624 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 2651 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
2625 | 2652 |
2626 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 2653 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
2627 | 2654 |
2628 JS.Expression notNull(Expression expr) { | 2655 JS.Expression notNull(Expression expr) { |
2629 if (expr == null) return null; | 2656 if (expr == null) return null; |
2630 var jsExpr = _visit(expr); | 2657 var jsExpr = _visit(expr); |
2631 if (!isNullable(expr)) return jsExpr; | 2658 if (!isNullable(expr)) return jsExpr; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2663 | 2690 |
2664 var vars = <String, JS.Expression>{}; | 2691 var vars = <String, JS.Expression>{}; |
2665 // Desugar `l ?? r` as `l != null ? l : r` | 2692 // Desugar `l ?? r` as `l != null ? l : r` |
2666 var l = _visit(_bindValue(vars, 'l', left, context: left)); | 2693 var l = _visit(_bindValue(vars, 'l', left, context: left)); |
2667 return new JS.MetaLet(vars, [ | 2694 return new JS.MetaLet(vars, [ |
2668 js.call('# != null ? # : #', [l, l, _visit(right)]) | 2695 js.call('# != null ? # : #', [l, l, _visit(right)]) |
2669 ]); | 2696 ]); |
2670 } | 2697 } |
2671 | 2698 |
2672 if (binaryOperationIsPrimitive(leftType, rightType) || | 2699 if (binaryOperationIsPrimitive(leftType, rightType) || |
2673 leftType == _types.stringType && op.type == TokenType.PLUS) { | 2700 leftType == types.stringType && op.type == TokenType.PLUS) { |
2674 // special cases where we inline the operation | 2701 // special cases where we inline the operation |
2675 // these values are assumed to be non-null (determined by the checker) | 2702 // 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, | 2703 // TODO(jmesserly): it would be nice to just inline the method from core, |
2677 // instead of special cases here. | 2704 // instead of special cases here. |
2678 if (op.type == TokenType.TILDE_SLASH) { | 2705 if (op.type == TokenType.TILDE_SLASH) { |
2679 // `a ~/ b` is equivalent to `(a / b).truncate()` | 2706 // `a ~/ b` is equivalent to `(a / b).truncate()` |
2680 var div = AstBuilder.binaryExpression(left, '/', right) | 2707 var div = AstBuilder.binaryExpression(left, '/', right) |
2681 ..staticType = node.staticType; | 2708 ..staticType = node.staticType; |
2682 return _emitSend(div, 'truncate', []); | 2709 return _emitSend(div, 'truncate', []); |
2683 } else { | 2710 } else { |
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3012 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 3039 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
3013 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { | 3040 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
3014 var member = memberId.staticElement; | 3041 var member = memberId.staticElement; |
3015 if (member is PropertyAccessorElement) { | 3042 if (member is PropertyAccessorElement) { |
3016 member = (member as PropertyAccessorElement).variable; | 3043 member = (member as PropertyAccessorElement).variable; |
3017 } | 3044 } |
3018 bool isStatic = member is ClassMemberElement && member.isStatic; | 3045 bool isStatic = member is ClassMemberElement && member.isStatic; |
3019 var name = _emitMemberName(memberId.name, | 3046 var name = _emitMemberName(memberId.name, |
3020 type: getStaticType(target), isStatic: isStatic); | 3047 type: getStaticType(target), isStatic: isStatic); |
3021 if (DynamicInvoke.get(target)) { | 3048 if (DynamicInvoke.get(target)) { |
3022 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); | 3049 return js.call('dart.dload(#, #)', [_visit(target), name]); |
3023 } | 3050 } |
3024 | 3051 |
3025 String code; | 3052 String code; |
3026 if (member != null && member is MethodElement && !isStatic) { | 3053 if (member != null && member is MethodElement && !isStatic) { |
3027 // Tear-off methods: explicitly bind it. | 3054 // Tear-off methods: explicitly bind it. |
3028 if (target is SuperExpression) { | 3055 if (target is SuperExpression) { |
3029 return js.call('dart.bind(this, #, #.#)', [name, _visit(target), name]); | 3056 return js.call('dart.bind(this, #, #.#)', [name, _visit(target), name]); |
3030 } else if (_requiresStaticDispatch(target, memberId.name)) { | 3057 } else if (_requiresStaticDispatch(target, memberId.name)) { |
3031 var type = member.type; | 3058 var type = member.type; |
3032 var clos = js.call('dart.#.bind(#)', [name, _visit(target)]); | 3059 var clos = js.call('dart.#.bind(#)', [name, _visit(target)]); |
(...skipping 13 matching lines...) Expand all Loading... | |
3046 /// | 3073 /// |
3047 /// **Please note** this function does not support method invocation syntax | 3074 /// **Please note** this function does not support method invocation syntax |
3048 /// `obj.name(args)` because that could be a getter followed by a call. | 3075 /// `obj.name(args)` because that could be a getter followed by a call. |
3049 /// See [visitMethodInvocation]. | 3076 /// See [visitMethodInvocation]. |
3050 JS.Expression _emitSend( | 3077 JS.Expression _emitSend( |
3051 Expression target, String name, List<Expression> args) { | 3078 Expression target, String name, List<Expression> args) { |
3052 var type = getStaticType(target); | 3079 var type = getStaticType(target); |
3053 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); | 3080 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); |
3054 if (DynamicInvoke.get(target)) { | 3081 if (DynamicInvoke.get(target)) { |
3055 // dynamic dispatch | 3082 // dynamic dispatch |
3056 var dynamicHelper = const {'[]': DINDEX, '[]=': DSETINDEX}[name]; | 3083 var dynamicHelper = const {'[]': 'dindex', '[]=': 'dsetindex'}[name]; |
3057 if (dynamicHelper != null) { | 3084 if (dynamicHelper != null) { |
3058 return js.call( | 3085 return js.call( |
3059 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); | 3086 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); |
3060 } | 3087 } |
3061 return js.call('dart.$DSEND(#, #, #)', | 3088 return js.call('dart.dsend(#, #, #)', |
3062 [_visit(target), memberName, _visitList(args)]); | 3089 [_visit(target), memberName, _visitList(args)]); |
3063 } | 3090 } |
3064 | 3091 |
3065 // Generic dispatch to a statically known method. | 3092 // Generic dispatch to a statically known method. |
3066 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); | 3093 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); |
3067 } | 3094 } |
3068 | 3095 |
3069 @override | 3096 @override |
3070 visitIndexExpression(IndexExpression node) { | 3097 visitIndexExpression(IndexExpression node) { |
3071 var target = _getTarget(node); | 3098 var target = _getTarget(node); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3182 // } | 3209 // } |
3183 // } finally { | 3210 // } finally { |
3184 // await iter.cancel(); | 3211 // await iter.cancel(); |
3185 // } | 3212 // } |
3186 // | 3213 // |
3187 // Like the Dart VM, we call cancel() always, as it's safe to call if the | 3214 // Like the Dart VM, we call cancel() always, as it's safe to call if the |
3188 // stream has already been cancelled. | 3215 // stream has already been cancelled. |
3189 // | 3216 // |
3190 // TODO(jmesserly): we may want a helper if these become common. For now the | 3217 // TODO(jmesserly): we may want a helper if these become common. For now the |
3191 // full desugaring seems okay. | 3218 // full desugaring seems okay. |
3192 var context = compiler.context; | 3219 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( | 3220 var createStreamIter = _emitInstanceCreationExpression( |
3199 _streamIteratorType.element.unnamedConstructor, | 3221 streamIterator.element.unnamedConstructor, |
3200 _streamIteratorType, | 3222 streamIterator, |
3201 null, | 3223 null, |
3202 AstBuilder.argumentList([node.iterable]), | 3224 AstBuilder.argumentList([node.iterable]), |
3203 false); | 3225 false); |
3204 var iter = | 3226 var iter = _visit(_createTemporary('it', streamIterator, nullable: false)); |
3205 _visit(_createTemporary('it', _streamIteratorType, nullable: false)); | |
3206 | 3227 |
3207 var init = _visit(node.identifier); | 3228 var init = _visit(node.identifier); |
3208 if (init == null) { | 3229 if (init == null) { |
3209 init = js | 3230 init = js |
3210 .call('let # = #.current', [node.loopVariable.identifier.name, iter]); | 3231 .call('let # = #.current', [node.loopVariable.identifier.name, iter]); |
3211 } else { | 3232 } else { |
3212 init = js.call('# = #.current', [init, iter]); | 3233 init = js.call('# = #.current', [init, iter]); |
3213 } | 3234 } |
3214 return js.statement( | 3235 return js.statement( |
3215 '{' | 3236 '{' |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3493 return result; | 3514 return result; |
3494 } | 3515 } |
3495 | 3516 |
3496 /// Visits a list of expressions, creating a comma expression if needed in JS. | 3517 /// Visits a list of expressions, creating a comma expression if needed in JS. |
3497 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 3518 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
3498 if (nodes == null || nodes.isEmpty) return null; | 3519 if (nodes == null || nodes.isEmpty) return null; |
3499 return new JS.Expression.binary( | 3520 return new JS.Expression.binary( |
3500 _visitList(nodes) as List<JS.Expression>, operator); | 3521 _visitList(nodes) as List<JS.Expression>, operator); |
3501 } | 3522 } |
3502 | 3523 |
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. | 3524 /// Like [_emitMemberName], but for declaration sites. |
3509 /// | 3525 /// |
3510 /// Unlike call sites, we always have an element available, so we can use it | 3526 /// Unlike call sites, we always have an element available, so we can use it |
3511 /// directly rather than computing the relevant options for [_emitMemberName]. | 3527 /// directly rather than computing the relevant options for [_emitMemberName]. |
3512 JS.Expression _elementMemberName(ExecutableElement e, | 3528 JS.Expression _elementMemberName(ExecutableElement e, |
3513 {bool allowExtensions: true}) { | 3529 {bool allowExtensions: true}) { |
3514 String name; | 3530 String name; |
3515 if (e is PropertyAccessorElement) { | 3531 if (e is PropertyAccessorElement) { |
3516 name = e.variable.name; | 3532 name = e.variable.name; |
3517 } else { | 3533 } else { |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3567 /// | 3583 /// |
3568 JS.Expression _emitMemberName(String name, | 3584 JS.Expression _emitMemberName(String name, |
3569 {DartType type, | 3585 {DartType type, |
3570 bool unary: false, | 3586 bool unary: false, |
3571 bool isStatic: false, | 3587 bool isStatic: false, |
3572 bool allowExtensions: true}) { | 3588 bool allowExtensions: true}) { |
3573 // Static members skip the rename steps. | 3589 // Static members skip the rename steps. |
3574 if (isStatic) return _propertyName(name); | 3590 if (isStatic) return _propertyName(name); |
3575 | 3591 |
3576 if (name.startsWith('_')) { | 3592 if (name.startsWith('_')) { |
3577 return _privateNames.putIfAbsent( | 3593 return _emitPrivateNameSymbol(currentLibrary, name); |
3578 name, () => _initSymbol(new JS.TemporaryId(name)) as JS.TemporaryId); | |
3579 } | 3594 } |
3580 | 3595 |
3581 if (name == '[]') { | 3596 if (name == '[]') { |
3582 name = 'get'; | 3597 name = 'get'; |
3583 } else if (name == '[]=') { | 3598 } else if (name == '[]=') { |
3584 name = 'set'; | 3599 name = 'set'; |
3585 } else if (name == '-' && unary) { | 3600 } else if (name == '-' && unary) { |
3586 name = 'unary-'; | 3601 name = 'unary-'; |
3587 } else if (name == 'constructor' || name == 'prototype') { | 3602 } else if (name == 'constructor' || name == 'prototype') { |
3588 // This uses an illegal (in Dart) character for a member, avoiding the | 3603 // This uses an illegal (in Dart) character for a member, avoiding the |
3589 // conflict. We could use practically any character for this. | 3604 // conflict. We could use practically any character for this. |
3590 name = '+$name'; | 3605 name = '+$name'; |
3591 } | 3606 } |
3592 | 3607 |
3593 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 3608 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
3594 var baseType = type; | 3609 var baseType = type; |
3595 while (baseType is TypeParameterType) { | 3610 while (baseType is TypeParameterType) { |
3596 baseType = baseType.element.bound; | 3611 baseType = baseType.element.bound; |
3597 } | 3612 } |
3598 if (allowExtensions && | 3613 if (allowExtensions && |
3599 baseType != null && | 3614 baseType != null && |
3600 _extensionTypes.contains(baseType.element) && | 3615 _extensionTypes.contains(baseType.element) && |
3601 !isObjectProperty(name)) { | 3616 !isObjectProperty(name)) { |
3602 return js.call('dartx.#', _propertyName(name)); | 3617 return js.call('dartx.#', _propertyName(name)); |
3603 } | 3618 } |
3604 | 3619 |
3605 return _propertyName(name); | 3620 return _propertyName(name); |
3606 } | 3621 } |
3607 | 3622 |
3623 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { | |
3624 return _privateNames | |
3625 .putIfAbsent(library, () => new HashMap()) | |
3626 .putIfAbsent(name, () { | |
3627 var id = new JS.TemporaryId(name); | |
3628 _moduleItems.add( | |
3629 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); | |
3630 return id; | |
3631 }); | |
3632 } | |
3633 | |
3608 bool _externalOrNative(node) => | 3634 bool _externalOrNative(node) => |
3609 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 3635 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
3610 | 3636 |
3611 FunctionBody _functionBody(node) => | 3637 FunctionBody _functionBody(node) => |
3612 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 3638 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
3613 | 3639 |
3614 /// Choose a canonical name from the library element. | 3640 /// 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) { | 3641 JS.Identifier emitLibraryName(LibraryElement library) { |
3618 if (library == currentLibrary) return _exportsVar; | 3642 // It's either one of the libraries in this module, or it's an import. |
3619 if (library.name == 'dart._runtime') return _runtimeLibVar; | 3643 return _libraries[library] ?? |
3620 return _imports.putIfAbsent( | 3644 _imports.putIfAbsent( |
3621 library, () => new JS.TemporaryId(jsLibraryName(library))); | 3645 library, () => new JS.TemporaryId(jsLibraryName(library))); |
3622 } | 3646 } |
3623 | 3647 |
3624 JS.Node annotate(JS.Node node, AstNode original, [Element element]) { | 3648 JS.Node annotate(JS.Node node, AstNode original, [Element element]) { |
3625 if (options.closure && element != null) { | 3649 if (options.closure && element != null) { |
3626 node = node.withClosureAnnotation(closureAnnotationFor( | 3650 node = node.withClosureAnnotation(closureAnnotationFor( |
3627 node, original, element, namedArgumentTemp.name)); | 3651 node, original, element, namedArgumentTemp.name)); |
3628 } | 3652 } |
3629 return node..sourceInformation = original; | 3653 return node..sourceInformation = original; |
3630 } | 3654 } |
3631 | 3655 |
3632 /// Returns true if this is any kind of object represented by `Number` in JS. | 3656 /// Returns true if this is any kind of object represented by `Number` in JS. |
3633 /// | 3657 /// |
3634 /// In practice, this is 4 types: num, int, double, and JSNumber. | 3658 /// In practice, this is 4 types: num, int, double, and JSNumber. |
3635 /// | 3659 /// |
3636 /// JSNumber is the type that actually "implements" all numbers, hence it's | 3660 /// 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". | 3661 /// a subtype of int and double (and num). It's in our "dart:_interceptors". |
3638 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType); | 3662 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); |
3639 | 3663 |
3640 bool _isObjectGetter(String name) { | 3664 bool _isObjectGetter(String name) { |
3641 PropertyAccessorElement element = _types.objectType.element.getGetter(name); | 3665 PropertyAccessorElement element = types.objectType.element.getGetter(name); |
3642 return (element != null && !element.isStatic); | 3666 return (element != null && !element.isStatic); |
3643 } | 3667 } |
3644 | 3668 |
3645 bool _isObjectMethod(String name) { | 3669 bool _isObjectMethod(String name) { |
3646 MethodElement element = _types.objectType.element.getMethod(name); | 3670 MethodElement element = types.objectType.element.getMethod(name); |
3647 return (element != null && !element.isStatic); | 3671 return (element != null && !element.isStatic); |
3648 } | 3672 } |
3649 | 3673 |
3650 bool isObjectProperty(String name) { | 3674 bool isObjectProperty(String name) { |
3651 return _isObjectGetter(name) || _isObjectMethod(name); | 3675 return _isObjectGetter(name) || _isObjectMethod(name); |
3652 } | 3676 } |
3653 | 3677 |
3654 // TODO(leafp): Various analyzer pieces computed similar things. | 3678 // TODO(leafp): Various analyzer pieces computed similar things. |
3655 // Share this logic somewhere? | 3679 // Share this logic somewhere? |
3656 DartType _getExpectedReturnType(ExecutableElement element) { | 3680 DartType _getExpectedReturnType(ExecutableElement element) { |
3657 FunctionType functionType = element.type; | 3681 FunctionType functionType = element.type; |
3658 if (functionType == null) { | 3682 if (functionType == null) { |
3659 return DynamicTypeImpl.instance; | 3683 return DynamicTypeImpl.instance; |
3660 } | 3684 } |
3661 var type = functionType.returnType; | 3685 var type = functionType.returnType; |
3662 | 3686 |
3663 InterfaceType expectedType = null; | 3687 InterfaceType expectedType = null; |
3664 if (element.isAsynchronous) { | 3688 if (element.isAsynchronous) { |
3665 if (element.isGenerator) { | 3689 if (element.isGenerator) { |
3666 // Stream<T> -> T | 3690 // Stream<T> -> T |
3667 expectedType = _types.streamType; | 3691 expectedType = types.streamType; |
3668 } else { | 3692 } else { |
3669 // Future<T> -> T | 3693 // Future<T> -> T |
3670 // TODO(vsm): Revisit with issue #228. | 3694 // TODO(vsm): Revisit with issue #228. |
3671 expectedType = _types.futureType; | 3695 expectedType = types.futureType; |
3672 } | 3696 } |
3673 } else { | 3697 } else { |
3674 if (element.isGenerator) { | 3698 if (element.isGenerator) { |
3675 // Iterable<T> -> T | 3699 // Iterable<T> -> T |
3676 expectedType = _types.iterableType; | 3700 expectedType = types.iterableType; |
3677 } else { | 3701 } else { |
3678 // T -> T | 3702 // T -> T |
3679 return type; | 3703 return type; |
3680 } | 3704 } |
3681 } | 3705 } |
3682 if (type.isDynamic) { | 3706 if (type.isDynamic) { |
3683 return type; | 3707 return type; |
3684 } else if (type is InterfaceType && type.element == expectedType.element) { | 3708 } else if (type is InterfaceType && type.element == expectedType.element) { |
3685 return type.typeArguments[0]; | 3709 return type.typeArguments[0]; |
3686 } else { | 3710 } else { |
3687 // TODO(leafp): The above only handles the case where the return type | 3711 // TODO(leafp): The above only handles the case where the return type |
3688 // is exactly Future/Stream/Iterable. Handle the subtype case. | 3712 // is exactly Future/Stream/Iterable. Handle the subtype case. |
3689 return DynamicTypeImpl.instance; | 3713 return DynamicTypeImpl.instance; |
3690 } | 3714 } |
3691 } | 3715 } |
3692 } | 3716 } |
3693 | 3717 |
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. | 3718 /// Choose a canonical name from the library element. |
3814 /// This never uses the library's name (the identifier in the `library` | 3719 /// This never uses the library's name (the identifier in the `library` |
3815 /// declaration) as it doesn't have any meaningful rules enforced. | 3720 /// declaration) as it doesn't have any meaningful rules enforced. |
3816 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); | 3721 String jsLibraryName(LibraryElement library) { |
3722 return pathToJSIdentifier(library.source.uri.pathSegments.last); | |
3723 } | |
3817 | 3724 |
3818 /// Shorthand for identifier-like property names. | 3725 /// Shorthand for identifier-like property names. |
3819 /// For now, we emit them as strings and the printer restores them to | 3726 /// For now, we emit them as strings and the printer restores them to |
3820 /// identifiers if it can. | 3727 /// identifiers if it can. |
3821 // TODO(jmesserly): avoid the round tripping through quoted form. | 3728 // TODO(jmesserly): avoid the round tripping through quoted form. |
3822 JS.LiteralString _propertyName(String name) => js.string(name, "'"); | 3729 JS.LiteralString _propertyName(String name) => js.string(name, "'"); |
3823 | 3730 |
3824 // TODO(jacobr): we would like to do something like the following | 3731 // TODO(jacobr): we would like to do something like the following |
3825 // but we don't have summary support yet. | 3732 // but we don't have summary support yet. |
3826 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 3733 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
3827 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 3734 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
3828 | 3735 |
3829 /// A special kind of element created by the compiler, signifying a temporary | 3736 /// A special kind of element created by the compiler, signifying a temporary |
3830 /// variable. These objects use instance equality, and should be shared | 3737 /// variable. These objects use instance equality, and should be shared |
3831 /// everywhere in the tree where they are treated as the same variable. | 3738 /// everywhere in the tree where they are treated as the same variable. |
3832 class TemporaryVariableElement extends LocalVariableElementImpl { | 3739 class TemporaryVariableElement extends LocalVariableElementImpl { |
3833 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3740 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3834 | 3741 |
3835 int get hashCode => identityHashCode(this); | 3742 int get hashCode => identityHashCode(this); |
3836 bool operator ==(Object other) => identical(this, other); | 3743 bool operator ==(Object other) => identical(this, other); |
3837 } | 3744 } |
3745 | |
3746 bool isLibraryPrefix(Expression node) => | |
3747 node is SimpleIdentifier && node.staticElement is PrefixElement; | |
3748 | |
3749 LibraryElement _getLibrary(AnalysisContext c, String uri) => | |
3750 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | |
3751 | |
3752 bool _isDartRuntime(LibraryElement l) => | |
3753 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | |
OLD | NEW |