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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/codegen.dart ('k') | lib/src/dart_parser.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/compiler.dart
diff --git a/lib/src/compiler.dart b/lib/src/compiler.dart
index f1a4fa2d7a96d08c201c6c78195919478c6963d0..66abf586caaf87c436abf96c6c5f9670ca32a0ae 100644
--- a/lib/src/compiler.dart
+++ b/lib/src/compiler.dart
@@ -6,15 +6,16 @@ library compiler;
import 'dart:async';
import 'dart:collection' show SplayTreeMap;
+import 'package:analyzer_experimental/src/generated/ast.dart' show Directive;
+import 'package:csslib/parser.dart' as css;
+import 'package:csslib/visitor.dart' show InfoVisitor, StyleSheet;
import 'package:html5lib/dom.dart';
import 'package:html5lib/parser.dart';
-import 'package:csslib/parser.dart' as css;
-import 'package:csslib/visitor.dart';
import 'analyzer.dart';
import 'code_printer.dart';
import 'codegen.dart' as codegen;
-import 'directive_parser.dart' show parseDartCode;
+import 'dart_parser.dart';
import 'emitters.dart';
import 'file_system.dart';
import 'file_system/path.dart';
@@ -22,6 +23,7 @@ import 'files.dart';
import 'html_cleaner.dart';
import 'info.dart';
import 'messages.dart';
+import 'observable_transform.dart' show transformObservables;
import 'options.dart';
import 'utils.dart';
@@ -56,6 +58,11 @@ class Compiler {
PathInfo _pathInfo;
Messages _messages;
+ FutureGroup _tasks;
+ Set _processed;
+
+ bool _useObservers = false;
+
/** Information about source [files] given their href. */
final Map<Path, FileInfo> info = new SplayTreeMap<Path, FileInfo>();
@@ -102,6 +109,7 @@ class Compiler {
}
return _parseAndDiscover(_mainPath).then((_) {
_analyze();
+ _transformDart();
_emit();
});
}
@@ -112,49 +120,54 @@ class Compiler {
* processed.
*/
Future _parseAndDiscover(Path inputFile) {
- var tasks = new FutureGroup();
- bool isEntry = !options.componentsOnly;
-
- var processed = new Set();
- processHtmlFile(SourceFile file) {
- if (file == null) return;
- if (!_pathInfo.checkInputPath(file.path, _messages)) return;
-
- files.add(file);
-
- var fileInfo = _time('Analyzed definitions', file.path,
- () => analyzeDefinitions(file, _messages, isEntryPoint: isEntry));
- isEntry = false;
- info[file.path] = fileInfo;
-
- // Load component files referenced by [file].
- for (var href in fileInfo.componentLinks) {
- if (!processed.contains(href)) {
- processed.add(href);
- tasks.add(_parseHtmlFile(href).then(processHtmlFile));
- }
- }
+ _tasks = new FutureGroup();
+ _processed = new Set();
+ _processed.add(inputFile);
+ _tasks.add(_parseHtmlFile(inputFile).then(_processHtmlFile));
+ return _tasks.future;
+ }
- // Load .dart files being referenced in the page.
- var src = fileInfo.externalFile;
- if (src != null && !processed.contains(src)) {
- processed.add(src);
- tasks.add(_parseDartFile(src).then(_addDartFile));
- }
+ bool _shouldProcessFile(SourceFile file) =>
+ file != null && _pathInfo.checkInputPath(file.path, _messages);
- // Load .dart files being referenced in components.
- for (var component in fileInfo.declaredComponents) {
- var src = component.externalFile;
- if (src != null && !processed.contains(src)) {
- processed.add(src);
- tasks.add(_parseDartFile(src).then(_addDartFile));
- }
+ void _processHtmlFile(SourceFile file) {
+ if (!_shouldProcessFile(file)) return;
+
+ bool isEntryPoint = _processed.length == 1;
+
+ files.add(file);
+
+ var fileInfo = _time('Analyzed definitions', file.path,
+ () => analyzeDefinitions(file, _messages, isEntryPoint: isEntryPoint));
+ info[file.path] = fileInfo;
+
+ _processImports(fileInfo);
+
+ // Load component files referenced by [file].
+ for (var href in fileInfo.componentLinks) {
+ if (!_processed.contains(href)) {
+ _processed.add(href);
+ _tasks.add(_parseHtmlFile(href).then(_processHtmlFile));
}
}
- processed.add(inputFile);
- tasks.add(_parseHtmlFile(inputFile).then(processHtmlFile));
- return tasks.future;
+ // Load .dart files being referenced in the page.
+ var src = fileInfo.externalFile;
+ if (src != null && !_processed.contains(src)) {
+ _processed.add(src);
+ _tasks.add(_parseDartFile(src).then(_processDartFile));
+ }
+
+ // Load .dart files being referenced in components.
+ for (var component in fileInfo.declaredComponents) {
+ var src = component.externalFile;
+ if (src != null && !_processed.contains(src)) {
+ _processed.add(src);
+ _tasks.add(_parseDartFile(src).then(_processDartFile));
+ } else if (component.userCode != null) {
+ _processImports(component);
+ }
+ }
}
/** Asynchronously parse [path] as an .html file. */
@@ -182,20 +195,188 @@ class Compiler {
return null;
}
- void _addDartFile(SourceFile dartFile) {
- if (dartFile == null) return;
- if (!_pathInfo.checkInputPath(dartFile.path, _messages)) return;
+ void _processDartFile(SourceFile dartFile) {
+ if (!_shouldProcessFile(dartFile)) return;
files.add(dartFile);
var fileInfo = new FileInfo(dartFile.path);
info[dartFile.path] = fileInfo;
- fileInfo.inlinedCode = dartFile.code;
- fileInfo.userCode = parseDartCode(fileInfo.inlinedCode,
- fileInfo.path, messages:_messages);
- if (fileInfo.userCode.partOf != null) {
- _messages.error('expected a library, not a part.', null,
- file: dartFile.path);
+ fileInfo.inlinedCode =
+ parseDartCode(fileInfo.path, dartFile.code, _messages);
+
+ _processImports(fileInfo);
+ }
+
+ void _processImports(LibraryInfo library) {
+ if (library.userCode == null) return;
+
+ for (var directive in library.userCode.directives) {
+ var src = _getDirectivePath(library, directive);
+ if (src == null) {
+ var uri = getDirectiveUri(directive).value;
+ if (uri.startsWith('package:web_ui/observe')) {
+ _useObservers = true;
+ }
+ } else if (!_processed.contains(src)) {
+ _processed.add(src);
+ _tasks.add(_parseDartFile(src).then(_processDartFile));
+ }
+ }
+ }
+
+ Path _getDirectivePath(LibraryInfo libInfo, Directive directive) {
+ var uri = getDirectiveUri(directive).value;
+ if (uri.startsWith('dart:')) return null;
+
+ if (uri.startsWith('package:')) {
+ // Don't process our own package -- we'll implement @observable manually.
+ if (uri.startsWith('package:web_ui/')) return null;
+
+ return _mainPath.directoryPath.join(new Path('packages'))
+ .join(new Path(uri.substring(8)));
+ } else {
+ return libInfo.inputPath.directoryPath.join(new Path(uri));
+ }
+ }
+
+ /**
+ * Transform Dart source code.
+ * Currently, the only transformation is [transformObservables].
+ * Calls _emitModifiedDartFiles to write the transformed files.
+ */
+ void _transformDart() {
+ var libraries = _findAllDartLibraries();
+
+ var transformed = [];
+ for (var library in libraries) {
+ if (transformObservables(library.userCode, _messages)) {
+ // TODO(jmesserly): what about ObservableList/Map/Set?
+ _useObservers = true;
+ transformed.add(library);
+ }
+ }
+
+ _findModifiedDartFiles(libraries, transformed);
+
+ libraries.forEach(_fixImports);
+
+ _emitModifiedDartFiles(libraries);
+ }
+
+ /**
+ * Finds all Dart code libraries.
+ * Each library will have [LibraryInfo.inlinedCode] that is non-null.
+ * Also each inlinedCode will be unique.
+ */
+ List<LibraryInfo> _findAllDartLibraries() {
+ var libs = <LibraryInfo>[];
+ void _addLibrary(LibraryInfo lib) {
+ if (lib.inlinedCode != null) libs.add(lib);
+ }
+
+ for (var sourceFile in files) {
+ var file = info[sourceFile.path];
+ _addLibrary(file);
+ file.declaredComponents.forEach(_addLibrary);
+ }
+
+ // Assert that each file path is unique.
+ assert(_uniquePaths(libs));
+ return libs;
+ }
+
+ bool _uniquePaths(List<LibraryInfo> libs) {
+ var seen = new Set();
+ for (var lib in libs) {
+ if (seen.contains(lib.inlinedCode)) {
+ throw new StateError('internal error: '
+ 'duplicate user code for ${lib.inputPath}. Files were: $files');
+ }
+ seen.add(lib.inlinedCode);
+ }
+ return true;
+ }
+
+ /**
+ * Queue modified Dart files to be written.
+ * This will not write files that are handled by [WebComponentEmitter] and
+ * [MainPageEmitter].
+ */
+ void _emitModifiedDartFiles(List<LibraryInfo> libraries) {
+ for (var lib in libraries) {
+ // Components will get emitted by WebComponentEmitter, and the
+ // entry point will get emitted by MainPageEmitter.
+ // So we only need to worry about other .dart files.
+ if (lib.modified && lib is FileInfo &&
+ lib.htmlFile == null && !lib.isEntryPoint) {
+
+ var printer = emitDartFile(lib, _pathInfo);
+ _emitFile(lib, printer, lib.inputPath);
+ }
+ }
+ }
+
+ /**
+ * This method computes which Dart files have been modified, starting
+ * from [transformed] and marking recursively through all files that import
+ * the modified files.
+ */
+ void _findModifiedDartFiles(List<LibraryInfo> libraries,
+ List<FileInfo> transformed) {
+
+ if (transformed.length == 0) return;
+
+ // Compute files that reference each file, then use this information to
+ // flip the modified bit transitively. This is a lot simpler than trying
+ // to compute it the other way because of circular references.
+ for (var library in libraries) {
+ for (var directive in library.userCode.directives) {
+ var importPath = _getDirectivePath(library, directive);
+ if (importPath == null) continue;
+
+ var importInfo = info[importPath];
+ if (importInfo != null) {
+ importInfo.referencedBy.add(library);
+ }
+ }
+ }
+
+ // Propegate the modified bit to anything that references a modified file.
+ void setModified(LibraryInfo library) {
+ if (library.modified) return;
+ library.modified = true;
+ library.referencedBy.forEach(setModified);
+ }
+ transformed.forEach(setModified);
+
+ for (var library in libraries) {
+ // We don't need this anymore, so free it.
+ library.referencedBy = null;
+ }
+ }
+
+ void _fixImports(LibraryInfo library) {
+ var fileOutputPath = _pathInfo.outputLibraryPath(library);
+
+ // Fix imports. Modified files must use the generated path, otherwise
+ // we need to make the path relative to the input.
+ for (var directive in library.userCode.directives) {
+ var importPath = _getDirectivePath(library, directive);
+ if (importPath == null) continue;
+ var importInfo = info[importPath];
+ if (importInfo == null) continue;
+
+ String newUri;
+ if (importInfo.modified) {
+ // Use the generated URI for this file.
+ newUri = _pathInfo.relativePath(library, importInfo).toString();
+ } else {
+ // Get the relative path to the input file.
+ newUri = _pathInfo.transformUrl(library.inputPath,
+ getDirectiveUri(directive).value);
+ }
+ setDirectiveUri(directive, createStringLiteral(newUri));
}
}
@@ -244,7 +425,8 @@ class Compiler {
var bootstrapPath = file.path.directoryPath.append(bootstrapName);
var bootstrapOutPath = _pathInfo.outputPath(bootstrapPath, '');
output.add(new OutputFile(bootstrapOutPath, codegen.bootstrapCode(
- _pathInfo.relativePath(new FileInfo(bootstrapPath), fileInfo))));
+ _pathInfo.relativePath(new FileInfo(bootstrapPath), fileInfo),
+ _useObservers)));
var document = file.document;
bool dartLoaderFound = false;
« 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