| 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 |