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

Side by Side Diff: lib/src/compiler.dart

Issue 12225039: Support for observable models, fixes #259 (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: small formatting fixes Created 7 years, 10 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
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 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 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 library compiler; 5 library compiler;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection' show SplayTreeMap; 8 import 'dart:collection' show SplayTreeMap;
9 import 'package:analyzer_experimental/src/generated/ast.dart' show Directive;
10 import 'package:csslib/parser.dart' as css;
11 import 'package:csslib/visitor.dart' show InfoVisitor, StyleSheet;
9 import 'package:html5lib/dom.dart'; 12 import 'package:html5lib/dom.dart';
10 import 'package:html5lib/parser.dart'; 13 import 'package:html5lib/parser.dart';
11 import 'package:csslib/parser.dart' as css;
12 import 'package:csslib/visitor.dart';
13 14
14 import 'analyzer.dart'; 15 import 'analyzer.dart';
15 import 'code_printer.dart'; 16 import 'code_printer.dart';
16 import 'codegen.dart' as codegen; 17 import 'codegen.dart' as codegen;
17 import 'directive_parser.dart' show parseDartCode; 18 import 'dart_parser.dart';
18 import 'emitters.dart'; 19 import 'emitters.dart';
19 import 'file_system.dart'; 20 import 'file_system.dart';
20 import 'file_system/path.dart'; 21 import 'file_system/path.dart';
21 import 'files.dart'; 22 import 'files.dart';
22 import 'html_cleaner.dart'; 23 import 'html_cleaner.dart';
23 import 'info.dart'; 24 import 'info.dart';
24 import 'messages.dart'; 25 import 'messages.dart';
26 import 'observable_transform.dart' show transformObservables;
25 import 'options.dart'; 27 import 'options.dart';
26 import 'utils.dart'; 28 import 'utils.dart';
27 29
28 /** 30 /**
29 * Parses an HTML file [contents] and returns a DOM-like tree. 31 * Parses an HTML file [contents] and returns a DOM-like tree.
30 * Note that [contents] will be a [String] if coming from a browser-based 32 * Note that [contents] will be a [String] if coming from a browser-based
31 * [FileSystem], or it will be a [List<int>] if running on the command line. 33 * [FileSystem], or it will be a [List<int>] if running on the command line.
32 * 34 *
33 * Adds emitted error/warning to [messages], if [messages] is supplied. 35 * Adds emitted error/warning to [messages], if [messages] is supplied.
34 */ 36 */
(...skipping 14 matching lines...) Expand all
49 class Compiler { 51 class Compiler {
50 final FileSystem fileSystem; 52 final FileSystem fileSystem;
51 final CompilerOptions options; 53 final CompilerOptions options;
52 final List<SourceFile> files = <SourceFile>[]; 54 final List<SourceFile> files = <SourceFile>[];
53 final List<OutputFile> output = <OutputFile>[]; 55 final List<OutputFile> output = <OutputFile>[];
54 56
55 Path _mainPath; 57 Path _mainPath;
56 PathInfo _pathInfo; 58 PathInfo _pathInfo;
57 Messages _messages; 59 Messages _messages;
58 60
61 FutureGroup _tasks;
62 Set _processed;
63
64 bool _useObservers = false;
65
59 /** Information about source [files] given their href. */ 66 /** Information about source [files] given their href. */
60 final Map<Path, FileInfo> info = new SplayTreeMap<Path, FileInfo>(); 67 final Map<Path, FileInfo> info = new SplayTreeMap<Path, FileInfo>();
61 68
62 /** 69 /**
63 * Creates a compiler with [options] using [fileSystem]. 70 * Creates a compiler with [options] using [fileSystem].
64 * 71 *
65 * Adds emitted error/warning messages to [messages], if [messages] is 72 * Adds emitted error/warning messages to [messages], if [messages] is
66 * supplied. 73 * supplied.
67 */ 74 */
68 Compiler(this.fileSystem, this.options, this._messages, {String currentDir}) { 75 Compiler(this.fileSystem, this.options, this._messages, {String currentDir}) {
(...skipping 26 matching lines...) Expand all
95 102
96 /** Compile the application starting from the given [mainFile]. */ 103 /** Compile the application starting from the given [mainFile]. */
97 Future run() { 104 Future run() {
98 if (_mainPath.filename.endsWith('.dart')) { 105 if (_mainPath.filename.endsWith('.dart')) {
99 _messages.error("Please provide an HTML file as your entry point.", 106 _messages.error("Please provide an HTML file as your entry point.",
100 null, file: _mainPath); 107 null, file: _mainPath);
101 return new Future.immediate(null); 108 return new Future.immediate(null);
102 } 109 }
103 return _parseAndDiscover(_mainPath).then((_) { 110 return _parseAndDiscover(_mainPath).then((_) {
104 _analyze(); 111 _analyze();
112 _transformDart();
105 _emit(); 113 _emit();
106 }); 114 });
107 } 115 }
108 116
109 /** 117 /**
110 * Asynchronously parse [inputFile] and transitively discover web components 118 * Asynchronously parse [inputFile] and transitively discover web components
111 * to load and parse. Returns a future that completes when all files are 119 * to load and parse. Returns a future that completes when all files are
112 * processed. 120 * processed.
113 */ 121 */
114 Future _parseAndDiscover(Path inputFile) { 122 Future _parseAndDiscover(Path inputFile) {
115 var tasks = new FutureGroup(); 123 _tasks = new FutureGroup();
116 bool isEntry = !options.componentsOnly; 124 _processed = new Set();
125 _processed.add(inputFile);
126 _tasks.add(_parseHtmlFile(inputFile).then(_processHtmlFile));
127 return _tasks.future;
128 }
117 129
118 var processed = new Set(); 130 bool _shouldProcessFile(SourceFile file) =>
119 processHtmlFile(SourceFile file) { 131 file != null && _pathInfo.checkInputPath(file.path, _messages);
120 if (file == null) return;
121 if (!_pathInfo.checkInputPath(file.path, _messages)) return;
122 132
123 files.add(file); 133 void _processHtmlFile(SourceFile file) {
134 if (!_shouldProcessFile(file)) return;
124 135
125 var fileInfo = _time('Analyzed definitions', file.path, 136 bool isEntryPoint = _processed.length == 1;
126 () => analyzeDefinitions(file, _messages, isEntryPoint: isEntry));
127 isEntry = false;
128 info[file.path] = fileInfo;
129 137
130 // Load component files referenced by [file]. 138 files.add(file);
131 for (var href in fileInfo.componentLinks) {
132 if (!processed.contains(href)) {
133 processed.add(href);
134 tasks.add(_parseHtmlFile(href).then(processHtmlFile));
135 }
136 }
137 139
138 // Load .dart files being referenced in the page. 140 var fileInfo = _time('Analyzed definitions', file.path,
139 var src = fileInfo.externalFile; 141 () => analyzeDefinitions(file, _messages, isEntryPoint: isEntryPoint));
140 if (src != null && !processed.contains(src)) { 142 info[file.path] = fileInfo;
141 processed.add(src);
142 tasks.add(_parseDartFile(src).then(_addDartFile));
143 }
144 143
145 // Load .dart files being referenced in components. 144 _processImports(fileInfo);
146 for (var component in fileInfo.declaredComponents) { 145
147 var src = component.externalFile; 146 // Load component files referenced by [file].
148 if (src != null && !processed.contains(src)) { 147 for (var href in fileInfo.componentLinks) {
149 processed.add(src); 148 if (!_processed.contains(href)) {
150 tasks.add(_parseDartFile(src).then(_addDartFile)); 149 _processed.add(href);
151 } 150 _tasks.add(_parseHtmlFile(href).then(_processHtmlFile));
152 } 151 }
153 } 152 }
154 153
155 processed.add(inputFile); 154 // Load .dart files being referenced in the page.
156 tasks.add(_parseHtmlFile(inputFile).then(processHtmlFile)); 155 var src = fileInfo.externalFile;
157 return tasks.future; 156 if (src != null && !_processed.contains(src)) {
157 _processed.add(src);
158 _tasks.add(_parseDartFile(src).then(_processDartFile));
159 }
160
161 // Load .dart files being referenced in components.
162 for (var component in fileInfo.declaredComponents) {
163 var src = component.externalFile;
164 if (src != null && !_processed.contains(src)) {
165 _processed.add(src);
166 _tasks.add(_parseDartFile(src).then(_processDartFile));
167 } else if (component.userCode != null) {
168 _processImports(component);
169 }
170 }
158 } 171 }
159 172
160 /** Asynchronously parse [path] as an .html file. */ 173 /** Asynchronously parse [path] as an .html file. */
161 Future<SourceFile> _parseHtmlFile(Path path) { 174 Future<SourceFile> _parseHtmlFile(Path path) {
162 return fileSystem.readTextOrBytes(path).then((source) { 175 return fileSystem.readTextOrBytes(path).then((source) {
163 var file = new SourceFile(path); 176 var file = new SourceFile(path);
164 file.document = _time('Parsed', path, 177 file.document = _time('Parsed', path,
165 () => parseHtml(source, path, _messages)); 178 () => parseHtml(source, path, _messages));
166 return file; 179 return file;
167 }) 180 })
168 .catchError((e) => _readError(e, path)); 181 .catchError((e) => _readError(e, path));
169 } 182 }
170 183
171 /** Parse [filename] and treat it as a .dart file. */ 184 /** Parse [filename] and treat it as a .dart file. */
172 Future<SourceFile> _parseDartFile(Path path) { 185 Future<SourceFile> _parseDartFile(Path path) {
173 return fileSystem.readText(path) 186 return fileSystem.readText(path)
174 .then((code) => new SourceFile(path, isDart: true)..code = code) 187 .then((code) => new SourceFile(path, isDart: true)..code = code)
175 .catchError((e) => _readError(e, path)); 188 .catchError((e) => _readError(e, path));
176 } 189 }
177 190
178 SourceFile _readError(error, Path path) { 191 SourceFile _readError(error, Path path) {
179 _messages.error('exception while reading file, original message:\n $error', 192 _messages.error('exception while reading file, original message:\n $error',
180 null, file: path); 193 null, file: path);
181 194
182 return null; 195 return null;
183 } 196 }
184 197
185 void _addDartFile(SourceFile dartFile) { 198 void _processDartFile(SourceFile dartFile) {
186 if (dartFile == null) return; 199 if (!_shouldProcessFile(dartFile)) return;
187 if (!_pathInfo.checkInputPath(dartFile.path, _messages)) return;
188 200
189 files.add(dartFile); 201 files.add(dartFile);
190 202
191 var fileInfo = new FileInfo(dartFile.path); 203 var fileInfo = new FileInfo(dartFile.path);
192 info[dartFile.path] = fileInfo; 204 info[dartFile.path] = fileInfo;
193 fileInfo.inlinedCode = dartFile.code; 205 fileInfo.userCode = parseDartCode(fileInfo.path, dartFile.code, _messages);
194 fileInfo.userCode = parseDartCode(fileInfo.inlinedCode, 206
195 fileInfo.path, messages:_messages); 207 _processImports(fileInfo);
196 if (fileInfo.userCode.partOf != null) { 208 }
197 _messages.error('expected a library, not a part.', null, 209
198 file: dartFile.path); 210 void _processImports(LibraryInfo library) {
211 if (library.userCode == null) return;
212
213 for (var directive in library.userCode.directives) {
214 var src = _getDirectivePath(library, directive);
215 if (src == null) continue;
216 if (!_processed.contains(src)) {
217 _processed.add(src);
218 _tasks.add(_parseDartFile(src).then(_processDartFile));
219 }
199 } 220 }
200 } 221 }
201 222
223 Path _getDirectivePath(LibraryInfo libInfo, Directive directive) {
224 var uri = getDirectiveUri(directive).value;
225 if (uri.startsWith('dart:')) return null;
226
227 if (uri.startsWith('package:')) {
228 // Don't process our own package -- we'll implement @observable manually.
229 if (uri.startsWith('package:web_ui/')) return null;
230
231 return _mainPath.directoryPath.join(new Path('packages'))
232 .join(new Path(uri.substring(8)));
233 } else {
234 return libInfo.inputPath.directoryPath.join(new Path(uri));
235 }
236 }
237
238 /**
239 * Transform Dart source code.
240 * Currently, the only transformation is [transformObservables].
241 * Calls _emitModifiedDartFiles to write the transformed files.
242 */
243 void _transformDart() {
244 var libraries = _findAllDartLibraries();
245
246 var transformed = [];
247 for (var library in libraries) {
248 if (transformObservables(library.userCode, _messages)) {
249 // TODO(jmesserly): what about ObservableList/Map/Set?
Siggi Cherem (dart-lang) 2013/02/13 01:43:24 how about if you have an import to those libraries
Jennifer Messerly 2013/02/13 05:43:15 Yeah, the tricky part is they can be imported tran
250 _useObservers = true;
251 transformed.add(library);
252 }
253 }
254
255 _findModifiedDartFiles(libraries, transformed);
256
257 libraries.forEach(_fixImports);
258
259 _emitModifiedDartFiles(libraries);
260 }
261
262 /**
263 * Finds all Dart code libraries.
264 * Each library will have [LibraryInfo.userCode] that is non-null.
265 * Also each userCode will be unique.
266 */
267 List<LibraryInfo> _findAllDartLibraries() {
268 var libs = <LibraryInfo>[];
269 void _addLibrary(LibraryInfo lib) {
270 if (lib.userCode != null && lib.externalCode == null) {
271 libs.add(lib);
272 }
273 }
274
275 for (var sourceFile in files) {
276 var file = info[sourceFile.path];
277 _addLibrary(file);
278 file.declaredComponents.forEach(_addLibrary);
279 }
280
281 // Assert that each file path is unique.
282 assert(_uniquePaths(libs));
283 return libs;
284 }
285
286 bool _uniquePaths(List<LibraryInfo> libs) {
287 var seen = new Set();
288 for (var lib in libs) {
289 if (seen.contains(lib.userCode)) {
290 throw new StateError('internal error: '
291 'duplicate user code for ${lib.inputPath}. Files were: $files');
292 }
293 seen.add(lib.userCode);
294 }
295 return true;
296 }
297
298 /**
299 * Queue modified Dart files to be written.
300 * This will not write files that are handled by [WebComponentEmitter] and
301 * [MainPageEmitter].
302 */
303 void _emitModifiedDartFiles(List<LibraryInfo> libraries) {
304 for (var lib in libraries) {
305 // Components will get emitted by WebComponentEmitter, and the
306 // entry point will get emitted by MainPageEmitter.
307 // So we only need to worry about other .dart files.
308 if (lib.modified && lib is FileInfo &&
309 lib.htmlFile == null && !lib.isEntryPoint) {
310
311 var printer = emitDartFile(lib, _pathInfo);
312 _emitFile(lib, printer, lib.inputPath);
313 }
314 }
315 }
316
317 /**
318 * This method computes which Dart files have been modified, starting
319 * from [transformed] and marking recursively through all files that import
320 * the modified files.
321 */
322 void _findModifiedDartFiles(List<LibraryInfo> libraries,
323 List<FileInfo> transformed) {
324
325 if (transformed.length == 0) return;
326
327 // Compute files that reference each file, then use this information to
328 // flip the modified bit transitively. This is a lot simpler than trying
329 // to compute it the other way because of circular references.
330 for (var library in libraries) {
331 for (var directive in library.userCode.directives) {
332 var importPath = _getDirectivePath(library, directive);
333 if (importPath == null) continue;
334
335 var importInfo = info[importPath];
336 if (importInfo != null) {
337 importInfo.referencedBy.add(library);
338 }
339 }
340 }
341
342 // Propegate the modified bit to anything that references a modified file.
343 void setModified(LibraryInfo library) {
344 if (library.modified) return;
345 library.modified = true;
346 library.referencedBy.forEach(setModified);
347 }
348 transformed.forEach(setModified);
349
350 for (var library in libraries) {
351 // We don't need this anymore, so free it.
352 library.referencedBy = null;
353 }
354 }
355
356 void _fixImports(LibraryInfo library) {
357 var fileOutputPath = _pathInfo.outputLibraryPath(library);
358
359 // Fix imports. Modified files must use the generated path, otherwise
360 // we need to make the path relative to the input.
361 for (var directive in library.userCode.directives) {
362 var importPath = _getDirectivePath(library, directive);
363 if (importPath == null) continue;
364 var importInfo = info[importPath];
365 if (importInfo == null) continue;
366
367 String newUri;
368 if (importInfo.modified) {
369 // Use the generated URI for this file.
370 newUri = _pathInfo.outputLibraryPath(importInfo)
371 .relativeTo(fileOutputPath.directoryPath).toString();
Siggi Cherem (dart-lang) 2013/02/13 01:43:24 newUri = _pathInfo.relativePath(library, importInf
Jennifer Messerly 2013/02/13 05:43:15 Done.
372 } else {
373 // Get the relative path to the input file.
374 newUri = _pathInfo.transformUrl(library.inputPath,
375 getDirectiveUri(directive).value);
376 }
377 setDirectiveUri(directive, createStringLiteral(newUri));
378 }
379 }
380
202 /** Run the analyzer on every input html file. */ 381 /** Run the analyzer on every input html file. */
203 void _analyze() { 382 void _analyze() {
204 var uniqueIds = new IntIterator(); 383 var uniqueIds = new IntIterator();
205 for (var file in files) { 384 for (var file in files) {
206 if (file.isDart) continue; 385 if (file.isDart) continue;
207 _time('Analyzed contents', file.path, 386 _time('Analyzed contents', file.path,
208 () => analyzeFile(file, info, uniqueIds, _messages, 387 () => analyzeFile(file, info, uniqueIds, _messages,
209 cssPolyfill: options.processCss)); 388 cssPolyfill: options.processCss));
210 } 389 }
211 } 390 }
(...skipping 25 matching lines...) Expand all
237 } 416 }
238 417
239 /** Generate an html file with the (trimmed down) main html page. */ 418 /** Generate an html file with the (trimmed down) main html page. */
240 void _emitMainHtml(SourceFile file) { 419 void _emitMainHtml(SourceFile file) {
241 var fileInfo = info[file.path]; 420 var fileInfo = info[file.path];
242 421
243 var bootstrapName = '${file.path.filename}_bootstrap.dart'; 422 var bootstrapName = '${file.path.filename}_bootstrap.dart';
244 var bootstrapPath = file.path.directoryPath.append(bootstrapName); 423 var bootstrapPath = file.path.directoryPath.append(bootstrapName);
245 var bootstrapOutPath = _pathInfo.outputPath(bootstrapPath, ''); 424 var bootstrapOutPath = _pathInfo.outputPath(bootstrapPath, '');
246 output.add(new OutputFile(bootstrapOutPath, codegen.bootstrapCode( 425 output.add(new OutputFile(bootstrapOutPath, codegen.bootstrapCode(
247 _pathInfo.relativePath(new FileInfo(bootstrapPath), fileInfo)))); 426 _pathInfo.relativePath(new FileInfo(bootstrapPath), fileInfo),
427 _useObservers)));
248 428
249 var document = file.document; 429 var document = file.document;
250 bool dartLoaderFound = false; 430 bool dartLoaderFound = false;
251 for (var script in document.queryAll('script')) { 431 for (var script in document.queryAll('script')) {
252 var src = script.attributes['src']; 432 var src = script.attributes['src'];
253 if (src != null && src.split('/').last == 'dart.js') { 433 if (src != null && src.split('/').last == 'dart.js') {
254 dartLoaderFound = true; 434 dartLoaderFound = true;
255 break; 435 break;
256 } 436 }
257 } 437 }
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 print('\nComponent: ${info.tagName}'); 544 print('\nComponent: ${info.tagName}');
365 print('==========\n'); 545 print('==========\n');
366 print(treeToDebugString(info.styleSheet)); 546 print(treeToDebugString(info.styleSheet));
367 print(emitStyleSheet(info.styleSheet)); 547 print(emitStyleSheet(info.styleSheet));
368 } 548 }
369 } 549 }
370 550
371 super.visitComponentInfo(info); 551 super.visitComponentInfo(info);
372 } 552 }
373 } 553 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698