Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(154)

Side by Side Diff: lib/dartdoc/frog/world.dart

Issue 10696191: Frog removed from dartdoc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « lib/dartdoc/frog/var_member.dart ('k') | tools/create_sdk.py » ('j') | tools/create_sdk.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698