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

Side by Side 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, 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/linked_list.dart ('k') | lib/src/observe/impl.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Code transform for @observable. The core transformation is relatively
7 * straightforward, and essentially like an editor refactoring. You can find the
8 * core implementation in [transformClass], which is ultimately called by
9 * [transformObservables], the entry point to this library.
10 */
11 library observable_transform;
12
13 import 'package:compiler_unsupported/implementation/elements/elements.dart';
14 import 'package:compiler_unsupported/implementation/scanner/scannerlib.dart';
15 import 'package:compiler_unsupported/implementation/tree/tree.dart';
16 import 'package:compiler_unsupported/implementation/util/util.dart';
17 import 'dart_parser.dart';
18 import 'info.dart';
19 import 'messages.dart';
20 import 'refactor.dart';
21
22 // TODO(jmesserly): this doesn't work for "this." in constructors.
23 /**
24 * Transform types in Dart [code] marked with `@observable` by hooking all field
25 * setters, and notifying the observation system of the change. If the code was
26 * changed this returns true, otherwise returns false. Modified code can be
27 * found in [info.userCode.code].
28 *
29 * Note: there is no special checking for transitive immutability. It is up to
30 * the rest of the observation system to handle check for this condition and.
31 * handle it appropriately. We do not want to violate reference equality of
32 * any fields that are set into the object.
33 */
34 bool transformObservables(LibraryInfo info, {Messages messages}) {
35 if (info.userCode == null) return false;
36 var oldCode = info.userCode.code;
37
38 // Avoid parsing unless we think it has @observable.
39 // TODO(jmesserly): investigate why the dart2js parser isn't fast enough.
40 // If we don't do this check, it adds ~100ms overhead, even for the simple
41 // TodoMVC example. Because that example uses @observable, it's not just VM
42 // warm up time for the dart2js code, so I'm not sure what's going on.
43 if (!oldCode.contains('@observable')) return false;
44
45 var parsed = new DartCodeParser.parse(info.inputPath, oldCode,
46 messages: messages);
47
48 var newCode = _transformParsedCode(parsed);
49 if (identical(oldCode, newCode)) return false;
50
51 // Replace the code
52 info.userCode.code = newCode;
53 return true;
54 }
55
56 String _transformParsedCode(DartCodeParser parser) {
57 // If parsing failed, don't try to transform the code.
58 if (!parser.success) return parser.code;
59
60 var transformedCode = new TextEditTransaction(parser.code);
61 for (var element in parser.unit.localMembers) {
62 if (element is PartialClassElement) {
63 transformClass(element, parser, transformedCode);
64 }
65 }
66 return transformedCode.commit();
67 }
68
69 void transformClass(PartialClassElement element, DartCodeParser parser,
70 TextEditTransaction code) {
71
72 var classNode = parser.parseClass(element);
73
74 // TODO(jmesserly): this isn't correct if observable has been imported
75 // with a prefix, or cases like that. We should technically be resolving, but
76 // that is expensive.
77 bool hasObservable = false;
78 for (var metadata in element.metadata) {
79 var node = metadata.parseNode(parser.diagnostics);
80 if (node is Send && node.selector is Identifier &&
81 node.selector.source.slowToString() == 'observable') {
82 hasObservable = true;
83 }
84 }
85
86 if (!hasObservable) return;
87
88 bool transformed = false;
89 for (var member in element.localMembers) {
90 if (member.isField()) {
91 VariableElement field = member;
92 if (transformFields(field.variables, parser.diagnostics, code)) {
93 // TODO(jmesserly): if class was transformed, mixin Observable
94 transformed = true;
95 }
96 }
97 }
98
99 }
100
101 bool transformFields(VariableListElement member, DiagnosticListener diagnostics,
102 TextEditTransaction code) {
103
104 if (member.cachedNode != null) {
105 // If the cached node already exists, it means we're seeing something like
106 // "bar" in this example code, and we've already generated it:
107 // var foo, bar;
108 // We need to transform both fields together.
109 return false;
110 }
111
112 var mod = member.modifiers;
113 if (mod.isStatic() || mod.isAbstract() || mod.isFinal() || mod.isConst()) {
114 return false;
115 }
116
117 var defs = member.parseNode(diagnostics);
118 int begin = defs.getBeginToken().charOffset;
119 int end = defs.getEndToken().charOffset + 1;
120 var indent = guessIndent(code.original, begin);
121 var replace = new StringBuffer();
122
123 for (var def in defs.definitions) {
124 Identifier identifier;
125 String initializer;
126 if (def is Identifier) {
127 identifier = def;
128 initializer = '';
129 } else {
130 SendSet send = def;
131 identifier = send.selector;
132 initializer = ' = ${send.arguments.head}';
133 }
134
135 var name = identifier.source.slowToString();
136 var type = defs.type;
137 if (type == null && mod.isVar()) {
138 type = 'var';
139 }
140
141 if (replace.length > 0) replace.add('\n\n$indent');
142 replace.add('''
143 $type __\$$name$initializer;
144 $type get $name {
145 if (autogenerated.observeReads) {
146 this.notifyRead(autogenerated.ChangeRecord.FIELD, '$name');
147 }
148 return __\$$name;
149 }
150 set $name($type value) {
151 if (this.hasObservers) {
152 this.notifyChange(autogenerated.ChangeRecord.FIELD, '$name', __\$$name, valu e);
153 }
154 __\$$name = value;
155 }'''.replaceAll('\n', '\n$indent'));
156 }
157
158 code.edit(begin, end, '$replace');
159 return true;
160 }
OLDNEW
« 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