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

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: 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
« no previous file with comments | « lib/src/codegen.dart ('k') | lib/src/dart_parser.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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.inlinedCode =
194 fileInfo.userCode = parseDartCode(fileInfo.inlinedCode, 206 parseDartCode(fileInfo.path, dartFile.code, _messages);
195 fileInfo.path, messages:_messages); 207
196 if (fileInfo.userCode.partOf != null) { 208 _processImports(fileInfo);
197 _messages.error('expected a library, not a part.', null, 209 }
198 file: dartFile.path); 210
211 void _processImports(LibraryInfo library) {
212 if (library.userCode == null) return;
213
214 for (var directive in library.userCode.directives) {
215 var src = _getDirectivePath(library, directive);
216 if (src == null) {
217 var uri = getDirectiveUri(directive).value;
218 if (uri.startsWith('package:web_ui/observe')) {
219 _useObservers = true;
220 }
221 } else if (!_processed.contains(src)) {
222 _processed.add(src);
223 _tasks.add(_parseDartFile(src).then(_processDartFile));
224 }
199 } 225 }
200 } 226 }
201 227
228 Path _getDirectivePath(LibraryInfo libInfo, Directive directive) {
229 var uri = getDirectiveUri(directive).value;
230 if (uri.startsWith('dart:')) return null;
231
232 if (uri.startsWith('package:')) {
233 // Don't process our own package -- we'll implement @observable manually.
234 if (uri.startsWith('package:web_ui/')) return null;
235
236 return _mainPath.directoryPath.join(new Path('packages'))
237 .join(new Path(uri.substring(8)));
238 } else {
239 return libInfo.inputPath.directoryPath.join(new Path(uri));
240 }
241 }
242
243 /**
244 * Transform Dart source code.
245 * Currently, the only transformation is [transformObservables].
246 * Calls _emitModifiedDartFiles to write the transformed files.
247 */
248 void _transformDart() {
249 var libraries = _findAllDartLibraries();
250
251 var transformed = [];
252 for (var library in libraries) {
253 if (transformObservables(library.userCode, _messages)) {
254 // TODO(jmesserly): what about ObservableList/Map/Set?
255 _useObservers = true;
256 transformed.add(library);
257 }
258 }
259
260 _findModifiedDartFiles(libraries, transformed);
261
262 libraries.forEach(_fixImports);
263
264 _emitModifiedDartFiles(libraries);
265 }
266
267 /**
268 * Finds all Dart code libraries.
269 * Each library will have [LibraryInfo.inlinedCode] that is non-null.
270 * Also each inlinedCode will be unique.
271 */
272 List<LibraryInfo> _findAllDartLibraries() {
273 var libs = <LibraryInfo>[];
274 void _addLibrary(LibraryInfo lib) {
275 if (lib.inlinedCode != null) libs.add(lib);
276 }
277
278 for (var sourceFile in files) {
279 var file = info[sourceFile.path];
280 _addLibrary(file);
281 file.declaredComponents.forEach(_addLibrary);
282 }
283
284 // Assert that each file path is unique.
285 assert(_uniquePaths(libs));
286 return libs;
287 }
288
289 bool _uniquePaths(List<LibraryInfo> libs) {
290 var seen = new Set();
291 for (var lib in libs) {
292 if (seen.contains(lib.inlinedCode)) {
293 throw new StateError('internal error: '
294 'duplicate user code for ${lib.inputPath}. Files were: $files');
295 }
296 seen.add(lib.inlinedCode);
297 }
298 return true;
299 }
300
301 /**
302 * Queue modified Dart files to be written.
303 * This will not write files that are handled by [WebComponentEmitter] and
304 * [MainPageEmitter].
305 */
306 void _emitModifiedDartFiles(List<LibraryInfo> libraries) {
307 for (var lib in libraries) {
308 // Components will get emitted by WebComponentEmitter, and the
309 // entry point will get emitted by MainPageEmitter.
310 // So we only need to worry about other .dart files.
311 if (lib.modified && lib is FileInfo &&
312 lib.htmlFile == null && !lib.isEntryPoint) {
313
314 var printer = emitDartFile(lib, _pathInfo);
315 _emitFile(lib, printer, lib.inputPath);
316 }
317 }
318 }
319
320 /**
321 * This method computes which Dart files have been modified, starting
322 * from [transformed] and marking recursively through all files that import
323 * the modified files.
324 */
325 void _findModifiedDartFiles(List<LibraryInfo> libraries,
326 List<FileInfo> transformed) {
327
328 if (transformed.length == 0) return;
329
330 // Compute files that reference each file, then use this information to
331 // flip the modified bit transitively. This is a lot simpler than trying
332 // to compute it the other way because of circular references.
333 for (var library in libraries) {
334 for (var directive in library.userCode.directives) {
335 var importPath = _getDirectivePath(library, directive);
336 if (importPath == null) continue;
337
338 var importInfo = info[importPath];
339 if (importInfo != null) {
340 importInfo.referencedBy.add(library);
341 }
342 }
343 }
344
345 // Propegate the modified bit to anything that references a modified file.
346 void setModified(LibraryInfo library) {
347 if (library.modified) return;
348 library.modified = true;
349 library.referencedBy.forEach(setModified);
350 }
351 transformed.forEach(setModified);
352
353 for (var library in libraries) {
354 // We don't need this anymore, so free it.
355 library.referencedBy = null;
356 }
357 }
358
359 void _fixImports(LibraryInfo library) {
360 var fileOutputPath = _pathInfo.outputLibraryPath(library);
361
362 // Fix imports. Modified files must use the generated path, otherwise
363 // we need to make the path relative to the input.
364 for (var directive in library.userCode.directives) {
365 var importPath = _getDirectivePath(library, directive);
366 if (importPath == null) continue;
367 var importInfo = info[importPath];
368 if (importInfo == null) continue;
369
370 String newUri;
371 if (importInfo.modified) {
372 // Use the generated URI for this file.
373 newUri = _pathInfo.relativePath(library, importInfo).toString();
374 } else {
375 // Get the relative path to the input file.
376 newUri = _pathInfo.transformUrl(library.inputPath,
377 getDirectiveUri(directive).value);
378 }
379 setDirectiveUri(directive, createStringLiteral(newUri));
380 }
381 }
382
202 /** Run the analyzer on every input html file. */ 383 /** Run the analyzer on every input html file. */
203 void _analyze() { 384 void _analyze() {
204 var uniqueIds = new IntIterator(); 385 var uniqueIds = new IntIterator();
205 for (var file in files) { 386 for (var file in files) {
206 if (file.isDart) continue; 387 if (file.isDart) continue;
207 _time('Analyzed contents', file.path, 388 _time('Analyzed contents', file.path,
208 () => analyzeFile(file, info, uniqueIds, _messages, 389 () => analyzeFile(file, info, uniqueIds, _messages,
209 cssPolyfill: options.processCss)); 390 cssPolyfill: options.processCss));
210 } 391 }
211 } 392 }
(...skipping 25 matching lines...) Expand all
237 } 418 }
238 419
239 /** Generate an html file with the (trimmed down) main html page. */ 420 /** Generate an html file with the (trimmed down) main html page. */
240 void _emitMainHtml(SourceFile file) { 421 void _emitMainHtml(SourceFile file) {
241 var fileInfo = info[file.path]; 422 var fileInfo = info[file.path];
242 423
243 var bootstrapName = '${file.path.filename}_bootstrap.dart'; 424 var bootstrapName = '${file.path.filename}_bootstrap.dart';
244 var bootstrapPath = file.path.directoryPath.append(bootstrapName); 425 var bootstrapPath = file.path.directoryPath.append(bootstrapName);
245 var bootstrapOutPath = _pathInfo.outputPath(bootstrapPath, ''); 426 var bootstrapOutPath = _pathInfo.outputPath(bootstrapPath, '');
246 output.add(new OutputFile(bootstrapOutPath, codegen.bootstrapCode( 427 output.add(new OutputFile(bootstrapOutPath, codegen.bootstrapCode(
247 _pathInfo.relativePath(new FileInfo(bootstrapPath), fileInfo)))); 428 _pathInfo.relativePath(new FileInfo(bootstrapPath), fileInfo),
429 _useObservers)));
248 430
249 var document = file.document; 431 var document = file.document;
250 bool dartLoaderFound = false; 432 bool dartLoaderFound = false;
251 for (var script in document.queryAll('script')) { 433 for (var script in document.queryAll('script')) {
252 var src = script.attributes['src']; 434 var src = script.attributes['src'];
253 if (src != null && src.split('/').last == 'dart.js') { 435 if (src != null && src.split('/').last == 'dart.js') {
254 dartLoaderFound = true; 436 dartLoaderFound = true;
255 break; 437 break;
256 } 438 }
257 } 439 }
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 print('\nComponent: ${info.tagName}'); 546 print('\nComponent: ${info.tagName}');
365 print('==========\n'); 547 print('==========\n');
366 print(treeToDebugString(info.styleSheet)); 548 print(treeToDebugString(info.styleSheet));
367 print(emitStyleSheet(info.styleSheet)); 549 print(emitStyleSheet(info.styleSheet));
368 } 550 }
369 } 551 }
370 552
371 super.visitComponentInfo(info); 553 super.visitComponentInfo(info);
372 } 554 }
373 } 555 }
OLDNEW
« no previous file with comments | « lib/src/codegen.dart ('k') | lib/src/dart_parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698