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

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

Issue 12096106: work in progress: observable implementation using detailed change records (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 'dart:uri';
9 import 'package:html5lib/dom.dart'; 10 import 'package:html5lib/dom.dart';
10 import 'package:html5lib/parser.dart'; 11 import 'package:html5lib/parser.dart';
11 12
12 import 'analyzer.dart'; 13 import 'analyzer.dart';
13 import 'code_printer.dart'; 14 import 'code_printer.dart';
14 import 'codegen.dart' as codegen; 15 import 'codegen.dart' as codegen;
15 import 'directive_parser.dart' show parseDartCode; 16 import 'directive_parser.dart' show parseDartCode;
16 import 'emitters.dart'; 17 import 'emitters.dart';
17 import 'file_system.dart'; 18 import 'file_system.dart';
18 import 'file_system/path.dart'; 19 import 'file_system/path.dart';
19 import 'files.dart'; 20 import 'files.dart';
20 import 'html_cleaner.dart'; 21 import 'html_cleaner.dart';
21 import 'info.dart'; 22 import 'info.dart';
22 import 'messages.dart'; 23 import 'messages.dart';
24 import 'observable_transform.dart' show transformObservables;
23 import 'options.dart'; 25 import 'options.dart';
24 import 'utils.dart'; 26 import 'utils.dart';
25 27
26 /** 28 /**
27 * Parses an HTML file [contents] and returns a DOM-like tree. 29 * Parses an HTML file [contents] and returns a DOM-like tree.
28 * Note that [contents] will be a [String] if coming from a browser-based 30 * Note that [contents] will be a [String] if coming from a browser-based
29 * [FileSystem], or it will be a [List<int>] if running on the command line. 31 * [FileSystem], or it will be a [List<int>] if running on the command line.
30 * 32 *
31 * Adds emitted error/warning to [messages], if [messages] is supplied. 33 * Adds emitted error/warning to [messages], if [messages] is supplied.
32 */ 34 */
(...skipping 13 matching lines...) Expand all
46 class Compiler { 48 class Compiler {
47 final FileSystem fileSystem; 49 final FileSystem fileSystem;
48 final CompilerOptions options; 50 final CompilerOptions options;
49 final List<SourceFile> files = <SourceFile>[]; 51 final List<SourceFile> files = <SourceFile>[];
50 final List<OutputFile> output = <OutputFile>[]; 52 final List<OutputFile> output = <OutputFile>[];
51 53
52 Path _mainPath; 54 Path _mainPath;
53 PathInfo _pathInfo; 55 PathInfo _pathInfo;
54 Messages _messages; 56 Messages _messages;
55 57
58 FutureGroup _tasks;
59 Set _processed;
60
56 /** Information about source [files] given their href. */ 61 /** Information about source [files] given their href. */
57 final Map<Path, FileInfo> info = new SplayTreeMap<Path, FileInfo>(); 62 final Map<Path, FileInfo> info = new SplayTreeMap<Path, FileInfo>();
58 63
59 /** 64 /**
60 * Creates a compiler with [options] using [fileSystem]. 65 * Creates a compiler with [options] using [fileSystem].
61 * 66 *
62 * Adds emitted error/warning messages to [messages], if [messages] is 67 * Adds emitted error/warning messages to [messages], if [messages] is
63 * supplied. 68 * supplied.
64 */ 69 */
65 Compiler(this.fileSystem, this.options, this._messages, {String currentDir}) { 70 Compiler(this.fileSystem, this.options, this._messages, {String currentDir}) {
(...skipping 26 matching lines...) Expand all
92 97
93 /** Compile the application starting from the given [mainFile]. */ 98 /** Compile the application starting from the given [mainFile]. */
94 Future run() { 99 Future run() {
95 if (_mainPath.filename.endsWith('.dart')) { 100 if (_mainPath.filename.endsWith('.dart')) {
96 _messages.error("Please provide an HTML file as your entry point.", 101 _messages.error("Please provide an HTML file as your entry point.",
97 null, file: _mainPath); 102 null, file: _mainPath);
98 return new Future.immediate(null); 103 return new Future.immediate(null);
99 } 104 }
100 return _parseAndDiscover(_mainPath).then((_) { 105 return _parseAndDiscover(_mainPath).then((_) {
101 _analyze(); 106 _analyze();
107 _transformDart();
102 _emit(); 108 _emit();
103 }); 109 });
104 } 110 }
105 111
106 /** 112 /**
107 * Asynchronously parse [inputFile] and transitively discover web components 113 * Asynchronously parse [inputFile] and transitively discover web components
108 * to load and parse. Returns a future that completes when all files are 114 * to load and parse. Returns a future that completes when all files are
109 * processed. 115 * processed.
110 */ 116 */
111 Future _parseAndDiscover(Path inputFile) { 117 Future _parseAndDiscover(Path inputFile) {
112 var tasks = new FutureGroup(); 118 _tasks = new FutureGroup();
113 bool isEntry = !options.componentsOnly; 119 _processed = new Set();
120 _processed.add(inputFile);
121 _tasks.add(_parseHtmlFile(inputFile).then(_processHtmlFile));
122 return _tasks.future;
123 }
114 124
115 var processed = new Set(); 125 bool _shouldProcessFile(SourceFile file) =>
116 processHtmlFile(SourceFile file) { 126 file != null && _pathInfo.checkInputPath(file.path, _messages);
117 if (file == null) return;
118 if (!_pathInfo.checkInputPath(file.path, _messages)) return;
119 127
120 files.add(file); 128 void _processHtmlFile(SourceFile file) {
129 if (!_shouldProcessFile(file)) return;
121 130
122 var fileInfo = _time('Analyzed definitions', file.path, 131 bool isEntryPoint = _processed.length == 1;
123 () => analyzeDefinitions(file, _messages, isEntryPoint: isEntry));
124 isEntry = false;
125 info[file.path] = fileInfo;
126 132
127 // Load component files referenced by [file]. 133 files.add(file);
128 for (var href in fileInfo.componentLinks) {
129 if (!processed.contains(href)) {
130 processed.add(href);
131 tasks.add(_parseHtmlFile(href).then(processHtmlFile));
132 }
133 }
134 134
135 // Load .dart files being referenced in the page. 135 var fileInfo = _time('Analyzed definitions', file.path,
136 var src = fileInfo.externalFile; 136 () => analyzeDefinitions(file, _messages, isEntryPoint: isEntryPoint));
137 if (src != null && !processed.contains(src)) { 137 info[file.path] = fileInfo;
138 processed.add(src);
139 tasks.add(_parseDartFile(src).then(_addDartFile));
140 }
141 138
142 // Load .dart files being referenced in components. 139 _processImports(fileInfo);
143 for (var component in fileInfo.declaredComponents) { 140
144 var src = component.externalFile; 141 // Load component files referenced by [file].
145 if (src != null && !processed.contains(src)) { 142 for (var href in fileInfo.componentLinks) {
146 processed.add(src); 143 if (!_processed.contains(href)) {
147 tasks.add(_parseDartFile(src).then(_addDartFile)); 144 _processed.add(href);
148 } 145 _tasks.add(_parseHtmlFile(href).then(_processHtmlFile));
149 } 146 }
150 } 147 }
151 148
152 processed.add(inputFile); 149 // Load .dart files being referenced in the page.
153 tasks.add(_parseHtmlFile(inputFile).then(processHtmlFile)); 150 var src = fileInfo.externalFile;
154 return tasks.future; 151 if (src != null && !_processed.contains(src)) {
152 _processed.add(src);
153 _tasks.add(_parseDartFile(src).then(_processDartFile));
154 }
155
156 // Load .dart files being referenced in components.
157 for (var component in fileInfo.declaredComponents) {
158 var src = component.externalFile;
159 if (src != null && !_processed.contains(src)) {
160 _processed.add(src);
161 _tasks.add(_parseDartFile(src).then(_processDartFile));
162 }
163 }
155 } 164 }
156 165
157 /** Asynchronously parse [path] as an .html file. */ 166 /** Asynchronously parse [path] as an .html file. */
158 Future<SourceFile> _parseHtmlFile(Path path) { 167 Future<SourceFile> _parseHtmlFile(Path path) {
159 return fileSystem.readTextOrBytes(path).then((source) { 168 return fileSystem.readTextOrBytes(path).then((source) {
160 var file = new SourceFile(path); 169 var file = new SourceFile(path);
161 file.document = _time('Parsed', path, 170 file.document = _time('Parsed', path,
162 () => parseHtml(source, path, _messages)); 171 () => parseHtml(source, path, _messages));
163 return file; 172 return file;
164 }) 173 })
165 .catchError((e) => _readError(e, path)); 174 .catchError((e) => _readError(e, path));
166 } 175 }
167 176
168 /** Parse [filename] and treat it as a .dart file. */ 177 /** Parse [filename] and treat it as a .dart file. */
169 Future<SourceFile> _parseDartFile(Path path) { 178 Future<SourceFile> _parseDartFile(Path path) {
170 return fileSystem.readText(path) 179 return fileSystem.readText(path)
171 .then((code) => new SourceFile(path, isDart: true)..code = code) 180 .then((code) => new SourceFile(path, isDart: true)..code = code)
172 .catchError((e) => _readError(e, path)); 181 .catchError((e) => _readError(e, path));
173 } 182 }
174 183
175 SourceFile _readError(error, Path path) { 184 SourceFile _readError(error, Path path) {
176 _messages.error('exception while reading file, original message:\n $error', 185 _messages.error('exception while reading file, original message:\n $error',
177 null, file: path); 186 null, file: path);
178 187
179 return null; 188 return null;
180 } 189 }
181 190
182 void _addDartFile(SourceFile dartFile) { 191 void _processDartFile(SourceFile dartFile) {
183 if (dartFile == null) return; 192 if (!_shouldProcessFile(dartFile)) return;
184 if (!_pathInfo.checkInputPath(dartFile.path, _messages)) return;
185 193
186 files.add(dartFile); 194 files.add(dartFile);
187 195
188 var fileInfo = new FileInfo(dartFile.path); 196 var fileInfo = new FileInfo(dartFile.path);
189 info[dartFile.path] = fileInfo; 197 info[dartFile.path] = fileInfo;
190 fileInfo.inlinedCode = dartFile.code; 198 fileInfo.userCode = parseDartCode(dartFile.code,
191 fileInfo.userCode = parseDartCode(fileInfo.inlinedCode, 199 fileInfo.path, messages: _messages);
192 fileInfo.path, messages:_messages); 200
193 if (fileInfo.userCode.partOf != null) { 201 _processImports(fileInfo);
194 _messages.error('expected a library, not a part.', null, 202 }
195 file: dartFile.path); 203
204 void _processImports(FileInfo fileInfo) {
205 if (fileInfo.userCode == null) return;
206
207 for (var directive in fileInfo.userCode.directives) {
208 var src = _getDirectivePath(fileInfo, directive.uri);
209 if (src == null) continue;
210 if (!_processed.contains(src)) {
211 _processed.add(src);
212 _tasks.add(_parseDartFile(src).then(_processDartFile));
213 }
196 } 214 }
197 } 215 }
198 216
217 Path _getDirectivePath(LibraryInfo libInfo, String uri) {
218 if (uri.startsWith('dart:')) return null;
219
220 if (uri.startsWith('package:')) {
221 // Don't process our own package -- we'll implement @observable manually.
222 if (uri.startsWith('package:web_ui/')) return null;
223
224 return _mainPath.directoryPath.join(new Path('packages'))
225 .join(new Path(uri.substring(8)));
226 } else {
227 return libInfo.inputPath.directoryPath.join(new Path(uri));
228 }
229 }
230
231 /**
232 * Transform Dart source code.
233 * Currently, the only transformation is [transformObservables].
234 * Calls _emitModifiedDartFiles to write the transformed files.
235 */
236 void _transformDart() {
237 var libraries = <LibraryInfo>[];
238 for (var sourceFile in files) {
239 var file = info[sourceFile.path];
240 libraries.add(file);
241 libraries.addAll(file.declaredComponents);
242 }
243 // Prefer to process the .dart file if it is external.
244 for (var i = 0; i < libraries.length; i++) {
245 var external = libraries[i].externalCode;
246 if (external != null) libraries[i] = external;
247 }
248 libraries = libraries.where((lib) => lib.userCode != null).toList();
249
250 var transformed = [];
251 for (var library in libraries) {
252 // TODO(jmesserly): does it make sense for us to warn about Dart parse
253 // errors? It seems useful, but the VM or dart2js would still issue these
254 // messages anyway later.
255 if (transformObservables(library, messages: _messages)) {
256 transformed.add(library);
257 }
258 }
259
260 _emitModifiedDartFiles(libraries, transformed);
261 }
262
263 /**
264 * This method rewrites imports transitively for any modified dart files,
265 * and queues the [outputs] to be written. This will not write files that
266 * are handled by [WebComponentEmitter] and [MainPageEmitter].
267 */
268 void _emitModifiedDartFiles(List<LibraryInfo> libraries,
269 List<FileInfo> transformed) {
270
271 if (transformed.length == 0) return;
272
273 // Compute files that reference each file, then use this information to
274 // flip the modified bit transitively. This is a lot simpler than trying
275 // to compute it the other way because of circular references.
276 for (var library in libraries) {
277 for (var directive in library.userCode.directives) {
278 var importPath = _getDirectivePath(library, directive.uri);
279 if (importPath == null) continue;
280
281 info[importPath].referencedBy.add(library);
282 }
283 }
284
285 // Propegate the modified bit to anything that references a modified file.
286 void setModified(LibraryInfo library) {
287 if (library.modified) return;
288 library.modified = true;
289 library.referencedBy.forEach(setModified);
290 }
291 transformed.forEach(setModified);
292
293 for (var library in libraries) {
294 // We don't need this anymore, so free it.
295 library.referencedBy = null;
296
297 if (!library.modified) continue;
298
299 var fileOutputPath = _pathInfo.outputLibraryPath(library);
300
301 // Fix imports of modified files to use the generated path.
302 for (var directive in library.userCode.directives) {
303 var importPath = _getDirectivePath(library, directive.uri);
304 if (importPath == null) continue;
305 var importInfo = info[importPath];
306
307 if (importInfo.modified && !directive.generated) {
308 // Use the generated URI for this file.
309 directive.generated = true;
310 directive.uri = _pathInfo.outputLibraryPath(importInfo)
311 .relativeTo(fileOutputPath.directoryPath).toString();
312 }
313 }
314
315 // Components will get emitted by WebComponentEmitter, and the
316 // entry point will get emitted by MainPageEmitter.
317 // So we only need to worry about other .dart files.
318 if (library is FileInfo && library.htmlFile == null) {
319 // Add an output file for the transformed .dart code:
320 output.add(new OutputFile(fileOutputPath,
321 emitDartFile(library, _pathInfo), source: library.inputPath));
322 }
323 }
324 }
325
199 /** Run the analyzer on every input html file. */ 326 /** Run the analyzer on every input html file. */
200 void _analyze() { 327 void _analyze() {
201 var uniqueIds = new IntIterator(); 328 var uniqueIds = new IntIterator();
202 for (var file in files) { 329 for (var file in files) {
203 if (file.isDart) continue; 330 if (file.isDart) continue;
204 _time('Analyzed contents', file.path, 331 _time('Analyzed contents', file.path,
205 () => analyzeFile(file, info, uniqueIds, _messages)); 332 () => analyzeFile(file, info, uniqueIds, _messages));
206 } 333 }
207 } 334 }
208 335
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
311 _messages.warning('file should start with <!DOCTYPE html> ' 438 _messages.warning('file should start with <!DOCTYPE html> '
312 'to avoid the possibility of it being parsed in quirks mode in IE. ' 439 'to avoid the possibility of it being parsed in quirks mode in IE. '
313 'See http://www.w3.org/TR/html5-diff/#doctype', 440 'See http://www.w3.org/TR/html5-diff/#doctype',
314 doctype.sourceSpan, file: file.path); 441 doctype.sourceSpan, file: file.path);
315 } 442 }
316 } 443 }
317 document.nodes.insertAt(commentIndex, parseFragment( 444 document.nodes.insertAt(commentIndex, parseFragment(
318 '\n<!-- This file was auto-generated from ${file.path}. -->\n')); 445 '\n<!-- This file was auto-generated from ${file.path}. -->\n'));
319 } 446 }
320 } 447 }
321
322
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