OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 /** The one true [World]. */ | |
6 World world; | |
7 | |
8 /** | |
9 * Experimental phase to enable await, only set when using the | |
10 * await/awaitc.dart entrypoint. | |
11 */ | |
12 Function experimentalAwaitPhase; | |
13 | |
14 /** | |
15 * Set when the leg compiler is available. Should always be set | |
16 * to frog_leg/compile. | |
17 */ | |
18 typedef bool LegCompile(World world); | |
19 LegCompile legCompile; | |
20 | |
21 typedef void MessageHandler(String prefix, String message, SourceSpan span); | |
22 | |
23 | |
24 /** | |
25 * Should be called exactly once to setup singleton world. | |
26 * Can use world.reset() to reinitialize. | |
27 */ | |
28 void initializeWorld(FileSystem files) { | |
29 assert(world == null); | |
30 world = new World(files); | |
31 if (!options.legOnly) world.init(); | |
32 } | |
33 | |
34 /** | |
35 * Compiles the [target] dart file using the given [corelib]. | |
36 */ | |
37 bool compile(String homedir, List<String> args, FileSystem files) { | |
38 parseOptions(homedir, args, files); | |
39 initializeWorld(files); | |
40 return world.compileAndSave(); | |
41 } | |
42 | |
43 | |
44 /** Can be thrown on any compiler error and includes source location. */ | |
45 class CompilerException implements Exception { | |
46 final String _message; | |
47 final SourceSpan _location; | |
48 | |
49 CompilerException(this._message, this._location); | |
50 | |
51 String toString() { | |
52 if (_location != null) { | |
53 return 'CompilerException: ${_location.toMessageString(_message)}'; | |
54 } else { | |
55 return 'CompilerException: $_message'; | |
56 } | |
57 } | |
58 } | |
59 | |
60 /** Counters that track statistics about the generated code. */ | |
61 class CounterLog { | |
62 int dynamicMethodCalls = 0; | |
63 int typeAsserts = 0; | |
64 int objectProtoMembers = 0; | |
65 int invokeCalls = 0; | |
66 int msetN = 0; | |
67 | |
68 info() { | |
69 if (options.legOnly) return; | |
70 world.info('Dynamically typed method calls: $dynamicMethodCalls'); | |
71 world.info('Generated type assertions: $typeAsserts'); | |
72 world.info('Members on Object.prototype: $objectProtoMembers'); | |
73 world.info('Invoke calls: $invokeCalls'); | |
74 world.info('msetN calls: $msetN'); | |
75 } | |
76 | |
77 // We need to push a new counter when we are abstract interpreting, so we | |
78 // can discard it if we discard the code. | |
79 // TODO(jmesserly): this is ugly but I'm not sure how to make it cleaner, | |
80 // other than splitting abstract interpretation and code generation (which | |
81 // has its own problems). | |
82 add(CounterLog other) { | |
83 dynamicMethodCalls += other.dynamicMethodCalls; | |
84 typeAsserts += other.typeAsserts; | |
85 objectProtoMembers += other.objectProtoMembers; | |
86 invokeCalls += other.invokeCalls; | |
87 msetN += other.msetN; | |
88 } | |
89 } | |
90 | |
91 | |
92 /** Represents a Dart "world" of code. */ | |
93 class World { | |
94 WorldGenerator gen; | |
95 String legCode; // TODO(kasperl): Remove this temporary kludge. | |
96 String frogCode; | |
97 | |
98 FileSystem files; | |
99 final LibraryReader reader; | |
100 | |
101 Map<String, Library> libraries; | |
102 Library corelib; | |
103 Library coreimpl; | |
104 | |
105 // TODO(jmesserly): we shouldn't be special casing DOM anywhere. | |
106 Library dom; | |
107 Library html; | |
108 Library isolatelib; | |
109 | |
110 List<Library> _todo; | |
111 | |
112 /** Internal map to track name conflicts in the generated javascript. */ | |
113 Map<String, Element> _topNames; | |
114 | |
115 /** | |
116 * Internal set of member names that should be avoided because they are known | |
117 * to exist on various objects. | |
118 */ | |
119 Set<String> _hazardousMemberNames; | |
120 | |
121 Map<String, MemberSet> _members; | |
122 | |
123 MessageHandler messageHandler; | |
124 | |
125 int errors = 0, warnings = 0; | |
126 int dartBytesRead = 0, jsBytesWritten = 0; | |
127 bool seenFatal = false; | |
128 | |
129 // Special types to Dart. | |
130 DefinedType varType; | |
131 // TODO(jimhug): Is this ever not === varType? | |
132 DefinedType dynamicType; | |
133 | |
134 DefinedType voidType; | |
135 DefinedType objectType; | |
136 DefinedType numType; | |
137 DefinedType intType; | |
138 DefinedType doubleType; | |
139 DefinedType boolType; | |
140 DefinedType stringType; | |
141 DefinedType listType; | |
142 DefinedType mapType; | |
143 DefinedType functionType; | |
144 DefinedType typeErrorType; | |
145 | |
146 // Types from dart:coreimpl that the compiler needs | |
147 DefinedType numImplType; | |
148 DefinedType stringImplType; | |
149 DefinedType functionImplType; | |
150 DefinedType listFactoryType; | |
151 DefinedType immutableListType; | |
152 | |
153 NonNullableType nonNullBool; | |
154 | |
155 CounterLog counters; | |
156 | |
157 | |
158 World(this.files) | |
159 : libraries = {}, _todo = [], _members = {}, _topNames = {}, | |
160 _hazardousMemberNames = new Set<String>(), | |
161 // TODO(jmesserly): these two types don't actually back our Date and | |
162 // RegExp yet, so we need to add them manually. | |
163 reader = new LibraryReader(), counters = new CounterLog() { | |
164 } | |
165 | |
166 void reset() { | |
167 // TODO(jimhug): Use a smaller hammer in the future. | |
168 libraries = {}; _todo = []; _members = {}; _topNames = {}; | |
169 _hazardousMemberNames = new Set<String>(); | |
170 counters = new CounterLog(); | |
171 errors = warnings = 0; | |
172 seenFatal = false; | |
173 init(); | |
174 } | |
175 | |
176 init() { | |
177 // Hack: We don't want Dart's String.split to overwrite the existing JS | |
178 // String.prototype.split method. By marking 'split' as 'hazardous' we | |
179 // ensure that frog will not use the 'split' name, thus leaving the original | |
180 // JS String.prototype.split untouched. | |
181 _addHazardousMemberName('split'); | |
182 | |
183 // Setup well-known libraries and types. | |
184 corelib = new Library(readFile('dart:core')); | |
185 libraries['dart:core'] = corelib; | |
186 _todo.add(corelib); | |
187 | |
188 coreimpl = getOrAddLibrary('dart:coreimpl'); | |
189 | |
190 voidType = corelib.addType('void', null, false); | |
191 dynamicType = corelib.addType('Dynamic', null, false); | |
192 varType = dynamicType; | |
193 | |
194 objectType = corelib.addType('Object', null, true); | |
195 numType = corelib.addType('num', null, false); | |
196 intType = corelib.addType('int', null, false); | |
197 doubleType = corelib.addType('double', null, false); | |
198 boolType = corelib.addType('bool', null, false); | |
199 stringType = corelib.addType('String', null, false); | |
200 listType = corelib.addType('List', null, false); | |
201 mapType = corelib.addType('Map', null, false); | |
202 functionType = corelib.addType('Function', null, false); | |
203 typeErrorType = corelib.addType('TypeError', null, true); | |
204 | |
205 numImplType = coreimpl.addType('NumImplementation', null, true); | |
206 stringImplType = coreimpl.addType('StringImplementation', null, true); | |
207 immutableListType = coreimpl.addType('ImmutableList', null, true); | |
208 listFactoryType = coreimpl.addType('ListFactory', null, true); | |
209 functionImplType = coreimpl.addType('_FunctionImplementation', null, true); | |
210 | |
211 nonNullBool = new NonNullableType(boolType); | |
212 } | |
213 | |
214 _addHazardousMemberName(String name) => _hazardousMemberNames.add(name); | |
215 | |
216 _addMember(Member member) { | |
217 // Private members are only visible in their own library. | |
218 assert(!member.isPrivate); | |
219 if (member.isStatic) { | |
220 if (member.declaringType.isTop) { | |
221 _addTopName(member); | |
222 } | |
223 return; | |
224 } | |
225 | |
226 var mset = _members[member.name]; | |
227 if (mset == null) { | |
228 mset = new MemberSet(member, isVar:true); | |
229 _members[mset.name] = mset; | |
230 } else { | |
231 mset.members.add(member); | |
232 } | |
233 } | |
234 | |
235 /** | |
236 * Adds a top level named [Element] so we track which names will be used in | |
237 * the generated JS. | |
238 */ | |
239 _addTopName(Element named) { | |
240 if (named is Type && named.isNative) { | |
241 if (named.avoidNativeName) { | |
242 // Mark the native name as unavailable for any type. | |
243 // Consider: | |
244 // #library('public'); | |
245 // interface DOMWindow { ... } | |
246 // #library('impl'); | |
247 // class DOMWindow implements public.DOMWindow native '*DOMWindow' { } | |
248 // #library('proxy'); | |
249 // class DOMWindow implements public.DOMWindow { ... } | |
250 // | |
251 // The global name 'DOMWindow' will be reserved for the native | |
252 // implementation, so all of them need to be renamed to avoid conflict. | |
253 _addJavascriptTopName(new ExistingJsGlobal(named.nativeName, named), | |
254 named.nativeName); | |
255 | |
256 } else { | |
257 // Reserve the native name for this type. This ensures no other type | |
258 // will take the native name. In the above example removing '*': | |
259 // | |
260 // class DOMWindow implements public.DOMWindow native 'DOMWindow' { } | |
261 // | |
262 // class impl.DOMWindow gets the JS name 'DOMWindow'. | |
263 _addJavascriptTopName(named, named.nativeName); | |
264 } | |
265 } | |
266 _addJavascriptTopName(named, named.jsname); | |
267 } | |
268 | |
269 /** | |
270 * Reserves [name] in the generated JS, or renames the lower priority | |
271 * [Element] if the name we wanted was already taken. | |
272 */ | |
273 _addJavascriptTopName(Element named, String name) { | |
274 var existing = _topNames[name]; | |
275 if (existing == null) { | |
276 // No one was using the name. Take it for ourselves. | |
277 _topNames[name] = named; | |
278 return; | |
279 } | |
280 if (existing === named) { | |
281 // This happens for a simple non-hidden native class where the native name | |
282 // is the same as the default jsname, e.g. class A native 'A' {}. | |
283 return; | |
284 } | |
285 | |
286 info('mangling matching top level name "${named.jsname}" in ' | |
287 + 'both "${named.library.jsname}" and "${existing.library.jsname}"'); | |
288 | |
289 // resolve conflicts based on priority | |
290 int existingPri = existing.jsnamePriority; | |
291 int namedPri = named.jsnamePriority; | |
292 if (existingPri > namedPri || namedPri == 0) { | |
293 // Either existing was higher priority, or they're both 0 so first one | |
294 // wins. | |
295 _renameJavascriptTopName(named); | |
296 } else if (namedPri > existingPri) { | |
297 // New one takes priority over existing | |
298 _renameJavascriptTopName(existing); | |
299 } else { | |
300 if (named.isNative) { | |
301 final msg = 'conflicting JS name "$name" of same ' | |
302 + 'priority $existingPri: (already defined in) ' | |
303 + '${existing.span.locationText} with priority $namedPri)'; | |
304 // We trust that conflicting native names in builtin libraries | |
305 // are harmless. Most cases there are no conflicts, currently | |
306 // isolates in coreimpl and dart:dom_deprecated both define | |
307 // web workers to avoid adding a dependency from corelib to | |
308 // dart:dom_deprecated. | |
309 world.info(msg, named.span, existing.span); | |
310 } else { | |
311 // Conflicting js name in same library. This happens because | |
312 // of two different type arguments with the same name but in | |
313 // different libraries. | |
314 _renameJavascriptTopName(existing); | |
315 } | |
316 } | |
317 } | |
318 | |
319 /** Renames an [Element] that had a name conflict in the generated JS. */ | |
320 _renameJavascriptTopName(Element named) { | |
321 named._jsname = '${named.library.jsname}_${named.jsname}'; | |
322 final existing = _topNames[named.jsname]; | |
323 if (existing != null && existing != named) { | |
324 // If this happens it means the library name wasn't unique enough. | |
325 world.internalError('name mangling failed for "${named.jsname}" ' | |
326 + '("${named.jsname}" defined also in ${existing.span.locationText})', | |
327 named.span); | |
328 } | |
329 _topNames[named.jsname] = named; | |
330 } | |
331 | |
332 _addType(Type type) { | |
333 // Top types don't have a name - we will capture their members in | |
334 // [_addMember]. | |
335 if (!type.isTop) _addTopName(type); | |
336 } | |
337 | |
338 // TODO(jimhug): Can this just be a const Set? | |
339 Set<String> _jsKeywords; | |
340 | |
341 /** Ensures that identifiers are legal in the generated JS. */ | |
342 String toJsIdentifier(String name) { | |
343 if (name == null) return null; | |
344 if (_jsKeywords == null) { | |
345 // TODO(jmesserly): this doesn't work if I write "new Set<String>.from" | |
346 // List of JS reserved words. | |
347 _jsKeywords = new Set.from([ | |
348 'break', 'case', 'catch', 'continue', 'debugger', 'default', | |
349 'delete', 'do', 'else', 'finally', 'for', 'function', 'if', | |
350 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw', | |
351 'try', 'typeof', 'var', 'void', 'while', 'with', | |
352 'class', 'enum', 'export', 'extends', 'import', 'super', | |
353 'implements', 'interface', 'let', 'package', 'private', | |
354 'protected', 'public', 'static', 'yield', 'native']); | |
355 } | |
356 if (_jsKeywords.contains(name)) { | |
357 return name + '_'; | |
358 } else { | |
359 // regexs for better perf? | |
360 return name.replaceAll(@'$', @'$$').replaceAll(':', @'$'); | |
361 } | |
362 } | |
363 | |
364 /** | |
365 * Runs the compiler and updates the output file. The output will either be | |
366 * the program, or a file that throws an error when run. | |
367 */ | |
368 bool compileAndSave() { | |
369 bool success = compile(); | |
370 if (options.outfile != null) { | |
371 if (success) { | |
372 var code = world.getGeneratedCode(); | |
373 var outfile = options.outfile; | |
374 if (!outfile.endsWith('.js') && !outfile.endsWith('.js_')) { | |
375 // Add in #! to invoke node.js on files with non-js | |
376 // extensions. Treat both '.js' and '.js_' as JS extensions | |
377 // to work around http://dartbug.com/3231. | |
378 code = '#!/usr/bin/env node\n${code}'; | |
379 } | |
380 world.files.writeString(outfile, code); | |
381 } else { | |
382 // Throw here so we get a non-zero exit code when running. | |
383 // TODO(jmesserly): make this an alert when compiling for the browser? | |
384 world.files.writeString(options.outfile, "throw " | |
385 "'Sorry, but I could not generate reasonable code to run.\\n';"); | |
386 } | |
387 } | |
388 return success; | |
389 } | |
390 | |
391 bool compile() { | |
392 // TODO(jimhug): Must have called setOptions - better errors. | |
393 if (options.dartScript == null) { | |
394 fatal('no script provided to compile'); | |
395 return false; | |
396 } | |
397 | |
398 try { | |
399 if (options.legOnly) { | |
400 info('[leg] compiling ${options.dartScript}'); | |
401 } else { | |
402 info('compiling ${options.dartScript} with corelib $corelib'); | |
403 } | |
404 if (!runLeg()) runCompilationPhases(); | |
405 } catch (var exc) { | |
406 if (hasErrors && !options.throwOnErrors) { | |
407 // TODO(jimhug): If dev mode then throw. | |
408 } else { | |
409 // TODO(jimhug): Handle these in world or in callers? | |
410 throw; | |
411 } | |
412 } | |
413 printStatus(); | |
414 return !hasErrors; | |
415 } | |
416 | |
417 /** Returns true if Leg handled the compilation job. */ | |
418 bool runLeg() { | |
419 if (!options.legOnly) return false; | |
420 if (legCompile == null) { | |
421 fatal('requested leg enabled, but no leg compiler available'); | |
422 } | |
423 bool res = withTiming('[leg] compile', () => legCompile(this)); | |
424 if (!res && options.legOnly) { | |
425 fatal("Leg could not compile ${options.dartScript}"); | |
426 return true; // In --leg_only, always "handle" the compilation. | |
427 } | |
428 return res; | |
429 } | |
430 | |
431 void runCompilationPhases() { | |
432 final lib = withTiming('first pass', () => processDartScript()); | |
433 withTiming('resolve top level', resolveAll); | |
434 withTiming('name safety', () { nameSafety(lib); }); | |
435 if (experimentalAwaitPhase != null) { | |
436 withTiming('await translation', experimentalAwaitPhase); | |
437 } | |
438 withTiming('analyze pass', () { analyzeCode(lib); }); | |
439 if (options.checkOnly) return; | |
440 | |
441 withTiming('generate code', () { generateCode(lib); }); | |
442 } | |
443 | |
444 String getGeneratedCode() { | |
445 // TODO(jimhug): Assert compilation is all done here. | |
446 if (legCode != null) { | |
447 assert(options.legOnly); | |
448 return legCode; | |
449 } else { | |
450 return frogCode; | |
451 } | |
452 } | |
453 | |
454 SourceFile readFile(String filename) { | |
455 try { | |
456 final sourceFile = reader.readFile(filename); | |
457 dartBytesRead += sourceFile.text.length; | |
458 return sourceFile; | |
459 } catch (var e) { | |
460 warning('Error reading file: $filename', null); | |
461 return new SourceFile(filename, ''); | |
462 } | |
463 } | |
464 | |
465 Library getOrAddLibrary(String filename) { | |
466 Library library = libraries[filename]; | |
467 | |
468 if (library == null) { | |
469 library = new Library(readFile(filename)); | |
470 info('read library ${filename}'); | |
471 if (!library.isCore && | |
472 !library.imports.some((li) => li.library.isCore)) { | |
473 library.imports.add(new LibraryImport(corelib)); | |
474 } | |
475 libraries[filename] = library; | |
476 _todo.add(library); | |
477 | |
478 if (filename == 'dart:dom_deprecated') { | |
479 dom = library; | |
480 } else if (filename == 'dart:html') { | |
481 html = library; | |
482 } else if (filename == 'dart:isolate') { | |
483 isolatelib = library; | |
484 } | |
485 } | |
486 return library; | |
487 } | |
488 | |
489 process() { | |
490 while (_todo.length > 0) { | |
491 final todo = _todo; | |
492 _todo = []; | |
493 for (var lib in todo) { | |
494 lib.visitSources(); | |
495 } | |
496 } | |
497 } | |
498 | |
499 Library processDartScript([String script = null]) { | |
500 if (script == null) script = options.dartScript; | |
501 Library library = getOrAddLibrary(script); | |
502 process(); | |
503 return library; | |
504 } | |
505 | |
506 resolveAll() { | |
507 for (var lib in libraries.getValues()) { | |
508 lib.resolve(); | |
509 } | |
510 for (var lib in libraries.getValues()) { | |
511 lib.postResolveChecks(); | |
512 } | |
513 } | |
514 | |
515 nameSafety(Library rootLib) { | |
516 makePrivateMembersUnique(rootLib); | |
517 avoidHazardousMemberNames(); | |
518 } | |
519 | |
520 makePrivateMembersUnique(Library rootLib) { | |
521 var usedNames = new Set<String>(); | |
522 process(lib) { | |
523 for (var name in lib._privateMembers.getKeys()) { | |
524 if (usedNames.contains(name)) { | |
525 var mset = lib._privateMembers[name]; | |
526 String uniqueName = '_${lib.jsname}${mset.jsname}'; | |
527 for (var member in mset.members) { | |
528 member._jsname = uniqueName; | |
529 } | |
530 } else { | |
531 usedNames.add(name); | |
532 } | |
533 } | |
534 } | |
535 | |
536 // Visit libraries in pre-order of imports. | |
537 var visited = new Set<Library>(); | |
538 visit(lib) { | |
539 if (visited.contains(lib)) return; | |
540 visited.add(lib); | |
541 process(lib); | |
542 for (var import in lib.imports) { | |
543 visit(import.library); | |
544 } | |
545 } | |
546 visit(rootLib); | |
547 } | |
548 | |
549 /** | |
550 * Ensures no non-native member has a jsname conflicting with a known native | |
551 * member jsname. | |
552 */ | |
553 avoidHazardousMemberNames() { | |
554 for (var name in _members.getKeys()) { | |
555 var mset = _members[name]; | |
556 for (var member in mset.members) { | |
557 if (!member.isNative || | |
558 (member is MethodMember && member.hasNativeBody)) { | |
559 var jsname = member._jsname; | |
560 if (_hazardousMemberNames.contains(jsname)) { | |
561 member._jsnameRoot = jsname; | |
562 member._jsname = '${jsname}\$_'; | |
563 } | |
564 } | |
565 } | |
566 } | |
567 } | |
568 | |
569 findMainMethod(Library lib) { | |
570 var main = lib.lookup('main', lib.span); | |
571 if (main == null) { | |
572 if (!options.checkOnly) fatal('no main method specified'); | |
573 } | |
574 return main; | |
575 } | |
576 | |
577 /** | |
578 * Walks all code in lib and imports for analysis. | |
579 */ | |
580 analyzeCode(Library lib) { | |
581 gen = new WorldGenerator(findMainMethod(lib), new CodeWriter()); | |
582 gen.analyze(); | |
583 } | |
584 | |
585 /** | |
586 * Walks all live code to generate JS source for output. | |
587 */ | |
588 generateCode(Library lib) { | |
589 gen.run(); | |
590 frogCode = gen.writer.text; | |
591 jsBytesWritten = frogCode.length; | |
592 gen = null; | |
593 } | |
594 | |
595 // ********************** Message support *********************** | |
596 | |
597 void _message(String color, String prefix, String message, | |
598 SourceSpan span, SourceSpan span1, SourceSpan span2, bool throwing) { | |
599 if (messageHandler != null) { | |
600 // TODO(jimhug): Multiple spans cleaner... | |
601 messageHandler(prefix, message, span); | |
602 if (span1 != null) { | |
603 messageHandler(prefix, message, span1); | |
604 } | |
605 if (span2 != null) { | |
606 messageHandler(prefix, message, span2); | |
607 } | |
608 } else { | |
609 final messageWithPrefix = options.useColors | |
610 ? (color + prefix + _NO_COLOR + message) : (prefix + message); | |
611 | |
612 var text = messageWithPrefix; | |
613 if (span != null) { | |
614 text = span.toMessageString(messageWithPrefix); | |
615 } | |
616 print(text); | |
617 if (span1 != null) { | |
618 print(span1.toMessageString(messageWithPrefix)); | |
619 } | |
620 if (span2 != null) { | |
621 print(span2.toMessageString(messageWithPrefix)); | |
622 } | |
623 } | |
624 | |
625 if (throwing) { | |
626 throw new CompilerException(prefix + message, span); | |
627 } | |
628 } | |
629 | |
630 /** [message] is considered a static compile-time error by the Dart lang. */ | |
631 void error(String message, | |
632 [SourceSpan span, SourceSpan span1, SourceSpan span2]) { | |
633 errors++; | |
634 _message(_RED_COLOR, 'error: ', message, | |
635 span, span1, span2, options.throwOnErrors); | |
636 } | |
637 | |
638 /** [message] is considered a type warning by the Dart lang. */ | |
639 void warning(String message, | |
640 [SourceSpan span, SourceSpan span1, SourceSpan span2]) { | |
641 if (options.warningsAsErrors) { | |
642 error(message, span, span1, span2); | |
643 return; | |
644 } | |
645 warnings++; | |
646 if (options.showWarnings) { | |
647 _message(_MAGENTA_COLOR, 'warning: ', message, | |
648 span, span1, span2, options.throwOnWarnings); | |
649 } | |
650 } | |
651 | |
652 /** [message] at [location] is so bad we can't generate runnable code. */ | |
653 void fatal(String message, | |
654 [SourceSpan span, SourceSpan span1, SourceSpan span2]) { | |
655 errors++; | |
656 seenFatal = true; | |
657 _message(_RED_COLOR, 'fatal: ', message, | |
658 span, span1, span2, options.throwOnFatal || options.throwOnErrors); | |
659 } | |
660 | |
661 /** [message] at [location] is about a bug in the compiler. */ | |
662 void internalError(String message, | |
663 [SourceSpan span, SourceSpan span1, SourceSpan span2]) { | |
664 _message(_NO_COLOR, | |
665 'We are sorry, but...', message, span, span1, span2, true); | |
666 } | |
667 | |
668 /** | |
669 * [message] at [location] will tell the user about what the compiler | |
670 * is doing. | |
671 */ | |
672 void info(String message, | |
673 [SourceSpan span, SourceSpan span1, SourceSpan span2]) { | |
674 if (options.showInfo) { | |
675 _message(_GREEN_COLOR, 'info: ', message, span, span1, span2, false); | |
676 } | |
677 } | |
678 | |
679 /** Run [fn] without the forceDynamic option enabeld. */ | |
680 withoutForceDynamic(void fn()) { | |
681 var oldForceDynamic = options.forceDynamic; | |
682 options.forceDynamic = false; | |
683 | |
684 try { | |
685 return fn(); | |
686 } finally { | |
687 options.forceDynamic = oldForceDynamic; | |
688 } | |
689 } | |
690 | |
691 bool get hasErrors() => errors > 0; | |
692 | |
693 void printStatus() { | |
694 counters.info(); | |
695 info('compiled $dartBytesRead bytes Dart -> $jsBytesWritten bytes JS'); | |
696 if (hasErrors) { | |
697 print('compilation failed with $errors errors'); | |
698 } else { | |
699 if (warnings > 0) { | |
700 info('compilation completed successfully with $warnings warnings'); | |
701 } else { | |
702 info('compilation completed sucessfully'); | |
703 } | |
704 } | |
705 } | |
706 | |
707 withTiming(String name, f()) { | |
708 final sw = new Stopwatch(); | |
709 sw.start(); | |
710 var result = f(); | |
711 sw.stop(); | |
712 info('$name in ${sw.elapsedInMs()}msec'); | |
713 return result; | |
714 } | |
715 } | |
OLD | NEW |