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

Unified Diff: lib/src/observable_transform.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, 11 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/linked_list.dart ('k') | lib/src/observe/impl.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/observable_transform.dart
diff --git a/lib/src/observable_transform.dart b/lib/src/observable_transform.dart
new file mode 100644
index 0000000000000000000000000000000000000000..422864d2856739e8dd96b6c0ac4a7f0844dcf414
--- /dev/null
+++ b/lib/src/observable_transform.dart
@@ -0,0 +1,160 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * Code transform for @observable. The core transformation is relatively
+ * straightforward, and essentially like an editor refactoring. You can find the
+ * core implementation in [transformClass], which is ultimately called by
+ * [transformObservables], the entry point to this library.
+ */
+library observable_transform;
+
+import 'package:compiler_unsupported/implementation/elements/elements.dart';
+import 'package:compiler_unsupported/implementation/scanner/scannerlib.dart';
+import 'package:compiler_unsupported/implementation/tree/tree.dart';
+import 'package:compiler_unsupported/implementation/util/util.dart';
+import 'dart_parser.dart';
+import 'info.dart';
+import 'messages.dart';
+import 'refactor.dart';
+
+// TODO(jmesserly): this doesn't work for "this." in constructors.
+/**
+ * Transform types in Dart [code] marked with `@observable` by hooking all field
+ * setters, and notifying the observation system of the change. If the code was
+ * changed this returns true, otherwise returns false. Modified code can be
+ * found in [info.userCode.code].
+ *
+ * Note: there is no special checking for transitive immutability. It is up to
+ * the rest of the observation system to handle check for this condition and.
+ * handle it appropriately. We do not want to violate reference equality of
+ * any fields that are set into the object.
+ */
+bool transformObservables(LibraryInfo info, {Messages messages}) {
+ if (info.userCode == null) return false;
+ var oldCode = info.userCode.code;
+
+ // Avoid parsing unless we think it has @observable.
+ // TODO(jmesserly): investigate why the dart2js parser isn't fast enough.
+ // If we don't do this check, it adds ~100ms overhead, even for the simple
+ // TodoMVC example. Because that example uses @observable, it's not just VM
+ // warm up time for the dart2js code, so I'm not sure what's going on.
+ if (!oldCode.contains('@observable')) return false;
+
+ var parsed = new DartCodeParser.parse(info.inputPath, oldCode,
+ messages: messages);
+
+ var newCode = _transformParsedCode(parsed);
+ if (identical(oldCode, newCode)) return false;
+
+ // Replace the code
+ info.userCode.code = newCode;
+ return true;
+}
+
+String _transformParsedCode(DartCodeParser parser) {
+ // If parsing failed, don't try to transform the code.
+ if (!parser.success) return parser.code;
+
+ var transformedCode = new TextEditTransaction(parser.code);
+ for (var element in parser.unit.localMembers) {
+ if (element is PartialClassElement) {
+ transformClass(element, parser, transformedCode);
+ }
+ }
+ return transformedCode.commit();
+}
+
+void transformClass(PartialClassElement element, DartCodeParser parser,
+ TextEditTransaction code) {
+
+ var classNode = parser.parseClass(element);
+
+ // TODO(jmesserly): this isn't correct if observable has been imported
+ // with a prefix, or cases like that. We should technically be resolving, but
+ // that is expensive.
+ bool hasObservable = false;
+ for (var metadata in element.metadata) {
+ var node = metadata.parseNode(parser.diagnostics);
+ if (node is Send && node.selector is Identifier &&
+ node.selector.source.slowToString() == 'observable') {
+ hasObservable = true;
+ }
+ }
+
+ if (!hasObservable) return;
+
+ bool transformed = false;
+ for (var member in element.localMembers) {
+ if (member.isField()) {
+ VariableElement field = member;
+ if (transformFields(field.variables, parser.diagnostics, code)) {
+ // TODO(jmesserly): if class was transformed, mixin Observable
+ transformed = true;
+ }
+ }
+ }
+
+}
+
+bool transformFields(VariableListElement member, DiagnosticListener diagnostics,
+ TextEditTransaction code) {
+
+ if (member.cachedNode != null) {
+ // If the cached node already exists, it means we're seeing something like
+ // "bar" in this example code, and we've already generated it:
+ // var foo, bar;
+ // We need to transform both fields together.
+ return false;
+ }
+
+ var mod = member.modifiers;
+ if (mod.isStatic() || mod.isAbstract() || mod.isFinal() || mod.isConst()) {
+ return false;
+ }
+
+ var defs = member.parseNode(diagnostics);
+ int begin = defs.getBeginToken().charOffset;
+ int end = defs.getEndToken().charOffset + 1;
+ var indent = guessIndent(code.original, begin);
+ var replace = new StringBuffer();
+
+ for (var def in defs.definitions) {
+ Identifier identifier;
+ String initializer;
+ if (def is Identifier) {
+ identifier = def;
+ initializer = '';
+ } else {
+ SendSet send = def;
+ identifier = send.selector;
+ initializer = ' = ${send.arguments.head}';
+ }
+
+ var name = identifier.source.slowToString();
+ var type = defs.type;
+ if (type == null && mod.isVar()) {
+ type = 'var';
+ }
+
+ if (replace.length > 0) replace.add('\n\n$indent');
+ replace.add('''
+$type __\$$name$initializer;
+$type get $name {
+ if (autogenerated.observeReads) {
+ this.notifyRead(autogenerated.ChangeRecord.FIELD, '$name');
+ }
+ return __\$$name;
+}
+set $name($type value) {
+ if (this.hasObservers) {
+ this.notifyChange(autogenerated.ChangeRecord.FIELD, '$name', __\$$name, value);
+ }
+ __\$$name = value;
+}'''.replaceAll('\n', '\n$indent'));
+ }
+
+ code.edit(begin, end, '$replace');
+ return true;
+}
« no previous file with comments | « lib/src/linked_list.dart ('k') | lib/src/observe/impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698