OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library dev_compiler.src.codegen.dart_codegen; | |
6 | |
7 import 'dart:io' show File; | |
8 | |
9 import 'package:analyzer/analyzer.dart' as analyzer; | |
10 import 'package:analyzer/src/generated/ast.dart'; | |
11 import 'package:analyzer/src/generated/element.dart'; | |
12 import 'package:analyzer/src/generated/java_core.dart' as java_core; | |
13 import 'package:analyzer/src/generated/scanner.dart' show Token; | |
14 import 'package:logging/logging.dart' as logger; | |
15 import 'package:path/path.dart' as path; | |
16 | |
17 import 'package:dev_compiler/devc.dart' show AbstractCompiler; | |
18 import 'package:dev_compiler/src/info.dart'; | |
19 import 'package:dev_compiler/src/options.dart'; | |
20 import 'package:dev_compiler/src/utils.dart' as utils; | |
21 import 'ast_builder.dart'; | |
22 import 'code_generator.dart' as codegenerator; | |
23 import 'reify_coercions.dart' | |
24 show CoercionReifier, NewTypeIdDesc, InstrumentedRuntime; | |
25 | |
26 final _log = new logger.Logger('dev_compiler.dart_codegen'); | |
27 | |
28 class DevCompilerRuntime extends InstrumentedRuntime { | |
29 Identifier _runtimeId = AstBuilder.identifierFromString("DEVC\$RT"); | |
30 | |
31 Identifier _castId; | |
32 Identifier _typeToTypeId; | |
33 Identifier _wrapId; | |
34 | |
35 DevCompilerRuntime() { | |
36 _castId = _prefixId(AstBuilder.identifierFromString("cast")); | |
37 _typeToTypeId = _prefixId(AstBuilder.identifierFromString("type")); | |
38 _wrapId = _prefixId(AstBuilder.identifierFromString("wrap")); | |
39 } | |
40 | |
41 String get importString { | |
42 var name = _runtimeId; | |
43 var uri = "package:dev_compiler/runtime/dart_logging_runtime.dart"; | |
44 return "import '$uri' as $name;"; | |
45 } | |
46 | |
47 Identifier _prefixId(Identifier id) => | |
48 AstBuilder.prefixedIdentifier(_runtimeId, id); | |
49 | |
50 Identifier runtimeId(RuntimeOperation oper) { | |
51 if (oper.operation == "cast") return _castId; | |
52 if (oper.operation == "wrap") return _wrapId; | |
53 if (oper.operation == "type") return _typeToTypeId; | |
54 assert(false); | |
55 return null; | |
56 } | |
57 | |
58 Expression runtimeOperation(RuntimeOperation oper) { | |
59 var id = runtimeId(oper); | |
60 var args = oper.arguments; | |
61 return AstBuilder.application(id, args); | |
62 } | |
63 | |
64 @override | |
65 Expression wrap(Expression coercion, Expression e, Expression fromType, | |
66 Expression toType, Expression dartIs, String kind, String location) { | |
67 var k = AstBuilder.stringLiteral(kind); | |
68 var key = AstBuilder.multiLineStringLiteral(location); | |
69 var arguments = <Expression>[coercion, e, fromType, toType, k, key, dartIs]; | |
70 return new RuntimeOperation("wrap", arguments); | |
71 } | |
72 | |
73 Expression cast(Expression e, Expression fromType, Expression toType, | |
74 Expression dartIs, String kind, String location, bool ground) { | |
75 var k = AstBuilder.stringLiteral(kind); | |
76 var key = AstBuilder.multiLineStringLiteral(location); | |
77 var g = AstBuilder.booleanLiteral(ground); | |
78 var arguments = <Expression>[e, fromType, toType, k, key, dartIs, g]; | |
79 return new RuntimeOperation("cast", arguments); | |
80 } | |
81 | |
82 Expression type(Expression witnessFunction) { | |
83 return new RuntimeOperation("type", <Expression>[witnessFunction]); | |
84 } | |
85 } | |
86 | |
87 // TODO(leafp) This is kind of a hack, but it works for now. | |
88 class FileWriter extends java_core.PrintStringWriter { | |
89 final CompilerOptions options; | |
90 String _path; | |
91 FileWriter(this.options, this._path); | |
92 int indent = 0; | |
93 int withinInterpolationExpression = 0; | |
94 bool insideForLoop = false; | |
95 | |
96 void print(x) { | |
97 if (!options.formatOutput) { | |
98 super.print(x); | |
99 return; | |
100 } | |
101 | |
102 switch (x) { | |
103 case '{': | |
104 indent++; | |
105 x = '{\n${" " * indent}'; | |
106 break; | |
107 case ';': | |
108 if (!insideForLoop) { | |
109 x = ';\n${" " * indent}'; | |
110 } | |
111 break; | |
112 case 'for (': | |
113 insideForLoop = true; | |
114 break; | |
115 case ') ': | |
116 insideForLoop = false; | |
117 break; | |
118 case r'${': | |
119 withinInterpolationExpression++; | |
120 break; | |
121 case '}': | |
122 if (withinInterpolationExpression > 0) { | |
123 withinInterpolationExpression--; | |
124 } else { | |
125 indent--; | |
126 x = '}\n${" " * indent}'; | |
127 } | |
128 break; | |
129 } | |
130 super.print(x); | |
131 } | |
132 | |
133 void finalize() { | |
134 String s = toString(); | |
135 _log.fine("Writing file $_path"); | |
136 new File(_path).writeAsStringSync(s); | |
137 } | |
138 } | |
139 | |
140 bool _identifierNeedsQualification(Identifier id, NewTypeIdDesc desc) { | |
141 var library = desc.importedFrom; | |
142 if (library == null) return false; | |
143 if (library.isDartCore) return false; | |
144 if (desc.fromCurrent) return false; | |
145 return true; | |
146 } | |
147 | |
148 // This class just holds some additional syntactic helpers and | |
149 // fixes to the general ToSourceVisitor for use by subclasses. | |
150 abstract class UnitGeneratorCommon extends analyzer.ToSourceVisitor { | |
151 UnitGeneratorCommon(java_core.PrintWriter out) : super(out); | |
152 | |
153 void output(String s); | |
154 void outputln(String s); | |
155 | |
156 // Copied from ast.dart | |
157 void visitNodeListWithSeparatorAndSuffix( | |
158 NodeList<AstNode> nodes, String separator, String suffix) { | |
159 if (nodes != null) { | |
160 int size = nodes.length; | |
161 if (size > 0) { | |
162 for (int i = 0; i < size; i++) { | |
163 if (i > 0) { | |
164 output(separator); | |
165 } | |
166 nodes[i].accept(this); | |
167 } | |
168 output(suffix); | |
169 } | |
170 } | |
171 } | |
172 | |
173 // Copied from ast.dart | |
174 void visitListWithSeparatorAndPrefix( | |
175 String prefix, List<AstNode> nodes, String separator) { | |
176 if (nodes != null) { | |
177 int size = nodes.length; | |
178 if (size > 0) { | |
179 output(prefix); | |
180 for (int i = 0; i < size; i++) { | |
181 if (i > 0) { | |
182 output(separator); | |
183 } | |
184 nodes[i].accept(this); | |
185 } | |
186 } | |
187 } | |
188 } | |
189 | |
190 // Copied from ast.dart | |
191 void visitNodeListWithSeparatorAndPrefix( | |
192 String prefix, NodeList<AstNode> nodes, String separator) { | |
193 visitListWithSeparatorAndPrefix(prefix, nodes, separator); | |
194 } | |
195 | |
196 // Copied from ast.dart | |
197 void visitTokenWithSuffix(Token token, String suffix) { | |
198 if (token != null) { | |
199 output(token.lexeme); | |
200 output(suffix); | |
201 } | |
202 } | |
203 | |
204 // Copied from ast.dart | |
205 void safelyVisitNode(AstNode node) { | |
206 if (node != null) { | |
207 node.accept(this); | |
208 } | |
209 } | |
210 | |
211 // Copied from ast.dart | |
212 void visitNodeWithPrefix(String prefix, AstNode node) { | |
213 if (node != null) { | |
214 output(prefix); | |
215 node.accept(this); | |
216 } | |
217 } | |
218 | |
219 // Copied from ast.dart | |
220 void visitNodeWithSuffix(AstNode node, String suffix) { | |
221 if (node != null) { | |
222 node.accept(this); | |
223 output(suffix); | |
224 } | |
225 } | |
226 | |
227 // Overridden to add external keyword if present | |
228 @override | |
229 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
230 visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " "); | |
231 visitTokenWithSuffix(node.externalKeyword, " "); | |
232 visitNodeWithSuffix(node.returnType, " "); | |
233 visitTokenWithSuffix(node.propertyKeyword, " "); | |
234 safelyVisitNode(node.name); | |
235 safelyVisitNode(node.functionExpression); | |
236 return null; | |
237 } | |
238 } | |
239 | |
240 // TODO(leafp) Not sure if this is the right way to generate | |
241 // Dart source going forward, but it's a quick way to get started. | |
242 class UnitGenerator extends UnitGeneratorCommon with ConversionVisitor<Object> { | |
243 CompilationUnit unit; | |
244 final java_core.PrintWriter _out; | |
245 final String outDir; | |
246 Set<LibraryElement> _extraImports; | |
247 final _runtime; | |
248 bool _qualifyNames = true; | |
249 Map<Identifier, NewTypeIdDesc> _newIdentifiers; | |
250 | |
251 UnitGenerator(this.unit, java_core.PrintWriter out, String this.outDir, | |
252 this._extraImports, this._newIdentifiers, this._runtime) | |
253 : _out = out, | |
254 super(out); | |
255 | |
256 void output(String s) => _out.print(s); | |
257 void outputln(String s) => _out.println(s); | |
258 | |
259 // Choose a canonical prefix for a library that we are adding. | |
260 // Currently just chooses something unlikely to conflict with a user | |
261 // prefix. | |
262 // TODO(leafp): Make this robust. | |
263 String canonizeLibraryName(String name) { | |
264 name = name.replaceAll(".", "DOT"); | |
265 name = "DDC\$$name\$"; | |
266 return name; | |
267 } | |
268 | |
269 // Split the directives into the various kinds (since there are restrictions | |
270 // in the syntax as to order of directives). | |
271 Map<String, List<Directive>> splitDirectives(NodeList<Directive> directives) { | |
272 return { | |
273 'export': directives.where((d) => d is ExportDirective).toList(), | |
274 'import': directives.where((d) => d is ImportDirective).toList(), | |
275 'library': directives.where((d) => d is LibraryDirective).toList(), | |
276 'part': directives.where((d) => d is PartDirective).toList(), | |
277 'partof': directives.where((d) => d is PartOfDirective).toList(), | |
278 }; | |
279 } | |
280 | |
281 // Build the set of import directives corresponding to the | |
282 // extra imports required by the types that we add in via | |
283 // inference | |
284 List<String> buildExtraImportDirectives() { | |
285 return _extraImports.map((lib) { | |
286 var name = utils.canonicalLibraryName(lib); | |
287 name = canonizeLibraryName(name); | |
288 var uri = codegenerator.CodeGenerator.uriFor(lib); | |
289 return "import '$uri' as $name;"; | |
290 }).toList(); | |
291 } | |
292 | |
293 // Rewrite the import directives with additional library imports | |
294 // covering the inferred types added in as part of this pass. | |
295 void _visitDirectives(String prefix, List<Directive> directives) { | |
296 _qualifyNames = false; | |
297 var ds = splitDirectives(directives); | |
298 visitListWithSeparatorAndPrefix(prefix, ds['library'], " "); | |
299 if (ds['library'].length != 0) { | |
300 assert(ds['partof'].length == 0); | |
301 var es = buildExtraImportDirectives(); | |
302 es.add(_runtime.importString); | |
303 es.forEach(outputln); | |
304 } | |
305 visitListWithSeparatorAndPrefix(prefix, ds['partof'], " "); | |
306 visitListWithSeparatorAndPrefix(prefix, ds['import'], " "); | |
307 visitListWithSeparatorAndPrefix(prefix, ds['export'], " "); | |
308 visitListWithSeparatorAndPrefix(prefix, ds['part'], " "); | |
309 _qualifyNames = true; | |
310 } | |
311 | |
312 @override | |
313 Object visitNode(AstNode node) { | |
314 if (node != null) { | |
315 node.visitChildren(this); | |
316 } | |
317 return null; | |
318 } | |
319 | |
320 @override | |
321 Object visitRuntimeOperation(RuntimeOperation oper) { | |
322 var e = _runtime.runtimeOperation(oper); | |
323 e.accept(this); | |
324 return null; | |
325 } | |
326 | |
327 @override | |
328 Object visitAsExpression(AsExpression node) { | |
329 _log.severe("Unlowered as expression"); | |
330 assert(false); | |
331 return null; | |
332 } | |
333 | |
334 @override | |
335 Object visitCompilationUnit(CompilationUnit node) { | |
336 ScriptTag scriptTag = node.scriptTag; | |
337 NodeList<Directive> directives = node.directives; | |
338 safelyVisitNode(scriptTag); | |
339 String prefix = scriptTag == null ? "" : " "; | |
340 _visitDirectives(prefix, directives); | |
341 prefix = scriptTag == null && directives.isEmpty ? "" : " "; | |
342 visitNodeListWithSeparatorAndPrefix(prefix, node.declarations, " "); | |
343 return null; | |
344 } | |
345 | |
346 @override | |
347 Object visitPrefixedIdentifier(PrefixedIdentifier id) { | |
348 safelyVisitNode(id.prefix); | |
349 output('.'); | |
350 output(id.identifier.token.lexeme); | |
351 return null; | |
352 } | |
353 | |
354 @override | |
355 Object visitSimpleIdentifier(SimpleIdentifier id) { | |
356 if (!(_qualifyNames && | |
357 _newIdentifiers.containsKey(id) && | |
358 _identifierNeedsQualification(id, _newIdentifiers[id]))) { | |
359 return super.visitSimpleIdentifier(id); | |
360 } | |
361 var library = _newIdentifiers[id].importedFrom; | |
362 if (!utils.isDartPrivateLibrary(library)) { | |
363 var lib = utils.canonicalLibraryName(library); | |
364 var libname = canonizeLibraryName(lib); | |
365 output(libname); | |
366 output('.'); | |
367 } | |
368 output(id.name); | |
369 return null; | |
370 } | |
371 | |
372 void generate() { | |
373 visitCompilationUnit(unit); | |
374 } | |
375 } | |
376 | |
377 class DartGenerator extends codegenerator.CodeGenerator { | |
378 final DevCompilerRuntime _runtime = new DevCompilerRuntime(); | |
379 | |
380 DartGenerator(AbstractCompiler compiler) : super(compiler); | |
381 | |
382 Set<LibraryElement> computeExtraImports(Map<Identifier, NewTypeIdDesc> ids) { | |
383 var imports = new Set<LibraryElement>(); | |
384 void process(Identifier id, NewTypeIdDesc desc) { | |
385 if (_identifierNeedsQualification(id, desc)) { | |
386 var library = desc.importedFrom; | |
387 if (utils.isDartPrivateLibrary(library)) { | |
388 _log.severe("Dropping import of private library ${library}\n"); | |
389 return; | |
390 } | |
391 imports.add(library); | |
392 } | |
393 } | |
394 ids.forEach(process); | |
395 return imports; | |
396 } | |
397 | |
398 String generateLibrary(LibraryUnit library, LibraryInfo info) { | |
399 var r = new CoercionReifier(library, compiler, _runtime); | |
400 var ids = r.reify(); | |
401 var extraImports = computeExtraImports(ids); | |
402 | |
403 for (var unit in library.partsThenLibrary) { | |
404 var libraryDir = makeOutputDirectory(info, unit); | |
405 var uri = unit.element.source.uri; | |
406 _log.fine("Generating unit $uri"); | |
407 FileWriter out = new FileWriter( | |
408 options, path.join(libraryDir, '${uri.pathSegments.last}')); | |
409 var unitGen = | |
410 new UnitGenerator(unit, out, outDir, extraImports, ids, _runtime); | |
411 unitGen.generate(); | |
412 out.finalize(); | |
413 } | |
414 | |
415 return null; | |
416 } | |
417 } | |
418 | |
419 class EmptyUnitGenerator extends UnitGeneratorCommon { | |
420 final java_core.PrintWriter _out; | |
421 CompilationUnit unit; | |
422 | |
423 EmptyUnitGenerator(this.unit, java_core.PrintWriter out) | |
424 : _out = out, | |
425 super(out); | |
426 | |
427 void output(String s) => _out.print(s); | |
428 void outputln(String s) => _out.println(s); | |
429 | |
430 void generate() { | |
431 unit.visitChildren(this); | |
432 } | |
433 } | |
434 | |
435 // This class emits the code unchanged, for comparison purposes. | |
436 class EmptyDartGenerator extends codegenerator.CodeGenerator { | |
437 EmptyDartGenerator(AbstractCompiler compiler) : super(compiler); | |
438 | |
439 String generateLibrary(LibraryUnit library, LibraryInfo info) { | |
440 for (var unit in library.partsThenLibrary) { | |
441 var outputDir = makeOutputDirectory(info, unit); | |
442 generateUnit(unit, info, outputDir); | |
443 } | |
444 return null; | |
445 } | |
446 | |
447 void generateUnit(CompilationUnit unit, LibraryInfo info, String libraryDir) { | |
448 var uri = unit.element.source.uri; | |
449 _log.fine("Emitting original unit " + uri.toString()); | |
450 FileWriter out = new FileWriter( | |
451 options, path.join(libraryDir, '${uri.pathSegments.last}')); | |
452 var unitGen = new EmptyUnitGenerator(unit, out); | |
453 unitGen.generate(); | |
454 out.finalize(); | |
455 } | |
456 } | |
OLD | NEW |