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

Side by Side Diff: dart/pkg/dart2js_incremental/lib/library_updater.dart

Issue 705253002: Include reasons for failing in StateError. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Have poi.dart recover when LibraryUpdater failed. Created 6 years, 1 month 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | dart/site/try/poi/poi.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) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 dart2js_incremental.library_updater; 5 library dart2js_incremental.library_updater;
6 6
7 import 'dart:async' show 7 import 'dart:async' show
8 Future; 8 Future;
9 9
10 import 'dart:convert' show 10 import 'dart:convert' show
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 Difference, 57 Difference,
58 computeDifference; 58 computeDifference;
59 59
60 typedef void Logger(message); 60 typedef void Logger(message);
61 61
62 typedef bool Reuser( 62 typedef bool Reuser(
63 Token diffToken, 63 Token diffToken,
64 PartialElement before, 64 PartialElement before,
65 PartialElement after); 65 PartialElement after);
66 66
67 class FailedUpdate {
68 /// Either an [Element] or a [Difference].
69 final context;
70 final String message;
71
72 FailedUpdate(this.context, this.message);
73
74 String toString() {
75 if (context == null) return '$message';
76 return 'In $context:\n $message';
77 }
78 }
79
67 // TODO(ahe): Generalize this class. For now only works for Compiler.mainApp, 80 // TODO(ahe): Generalize this class. For now only works for Compiler.mainApp,
68 // and only if that library has exactly one compilation unit. 81 // and only if that library has exactly one compilation unit.
69 class LibraryUpdater { 82 class LibraryUpdater {
70 final Compiler compiler; 83 final Compiler compiler;
71 84
72 final api.CompilerInputProvider inputProvider; 85 final api.CompilerInputProvider inputProvider;
73 86
74 final Logger logTime; 87 final Logger logTime;
75 88
76 final Logger logVerbose; 89 final Logger logVerbose;
77 90
78 // TODO(ahe): Get rid of this field. It assumes that only one library has 91 // TODO(ahe): Get rid of this field. It assumes that only one library has
79 // changed. 92 // changed.
80 final Uri uri; 93 final Uri uri;
81 94
82 // When [true], updates must be applied (using [applyUpdates]) before the 95 final List<Update> updates = <Update>[];
83 // [compiler]'s state correctly reflects the updated program.
84 bool hasPendingUpdates = false;
85 96
86 bool onlySimpleUpdates = true; 97 final List<FailedUpdate> _failedUpdates = <FailedUpdate>[];
87
88 final List<Update> updates = <Update>[];
89 98
90 LibraryUpdater( 99 LibraryUpdater(
91 this.compiler, 100 this.compiler,
92 this.inputProvider, 101 this.inputProvider,
93 this.uri, 102 this.uri,
94 this.logTime, 103 this.logTime,
95 this.logVerbose); 104 this.logVerbose);
96 105
106 /// When [true], updates must be applied (using [applyUpdates]) before the
107 /// [compiler]'s state correctly reflects the updated program.
108 bool get hasPendingUpdates => !updates.isEmpty;
109
110 bool get failed => !_failedUpdates.isEmpty;
111
97 JavaScriptBackend get backend => compiler.backend; 112 JavaScriptBackend get backend => compiler.backend;
98 113
99 Namer get namer => backend.namer; 114 Namer get namer => backend.namer;
100 115
101 CodeEmitterTask get emitter => backend.emitter; 116 CodeEmitterTask get emitter => backend.emitter;
102 117
103 /// Used as tear-off passed to [LibraryLoaderTask.resetAsync]. 118 /// Used as tear-off passed to [LibraryLoaderTask.resetAsync].
104 Future<bool> reuseLibrary(LibraryElement library) { 119 Future<bool> reuseLibrary(LibraryElement library) {
105 assert(compiler != null); 120 assert(compiler != null);
106 if (library.isPlatformLibrary || library.isPackageLibrary) { 121 if (library.isPlatformLibrary || library.isPackageLibrary) {
(...skipping 27 matching lines...) Expand all
134 logTime("Source did change"); 149 logTime("Source did change");
135 Script sourceScript = new Script( 150 Script sourceScript = new Script(
136 uri, uri, new StringSourceFile('$uri', newSource)); 151 uri, uri, new StringSourceFile('$uri', newSource));
137 var dartPrivacyIsBroken = compiler.libraryLoader; 152 var dartPrivacyIsBroken = compiler.libraryLoader;
138 LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync( 153 LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync(
139 null, sourceScript, uri); 154 null, sourceScript, uri);
140 logTime('New library synthesized.'); 155 logTime('New library synthesized.');
141 return canReuseScopeContainerElement(library, newLibrary); 156 return canReuseScopeContainerElement(library, newLibrary);
142 } 157 }
143 158
159 bool cannotReuse(context, String message) {
160 _failedUpdates.add(new FailedUpdate(context, message));
161 logVerbose(message);
162 return false;
163 }
164
144 bool canReuseScopeContainerElement( 165 bool canReuseScopeContainerElement(
145 ScopeContainerElement element, 166 ScopeContainerElement element,
146 ScopeContainerElement newElement) { 167 ScopeContainerElement newElement) {
147 List<Difference> differences = computeDifference(element, newElement); 168 List<Difference> differences = computeDifference(element, newElement);
148 logTime('Differences computed.'); 169 logTime('Differences computed.');
149 for (Difference difference in differences) { 170 for (Difference difference in differences) {
150 logTime('Looking at difference: $difference'); 171 logTime('Looking at difference: $difference');
151 if (difference.before == null || difference.after == null) { 172 if (difference.before == null || difference.after == null) {
152 logVerbose('Scope changed in $difference'); 173 cannotReuse(difference, "Can't reuse; Scope changed.");
153 // Scope changed, don't reuse library. 174 continue;
154 onlySimpleUpdates = false;
155 return false;
156 } 175 }
157 Token diffToken = difference.token; 176 Token diffToken = difference.token;
158 if (diffToken == null) { 177 if (diffToken == null) {
159 logVerbose('No token stored in difference.'); 178 cannotReuse(difference, "No difference token.");
160 onlySimpleUpdates = false; 179 continue;
161 return false;
162 } 180 }
163 if (difference.after is! PartialElement && 181 if (difference.after is! PartialElement &&
164 difference.before is! PartialElement) { 182 difference.before is! PartialElement) {
165 logVerbose('Not a PartialElement: $difference'); 183 cannotReuse(difference, "Don't know how to recompile.");
166 // Don't know how to recompile element. 184 continue;
167 onlySimpleUpdates = false;
168 return false;
169 } 185 }
170 PartialElement before = difference.before; 186 PartialElement before = difference.before;
171 PartialElement after = difference.after; 187 PartialElement after = difference.after;
172 188
173 Reuser reuser; 189 Reuser reuser;
174 190
175 if (before is PartialFunctionElement && after is PartialFunctionElement) { 191 if (before is PartialFunctionElement && after is PartialFunctionElement) {
176 reuser = canReuseFunction; 192 reuser = canReuseFunction;
177 } else if (before is PartialClassElement && 193 } else if (before is PartialClassElement &&
178 after is PartialClassElement) { 194 after is PartialClassElement) {
179 reuser = canReuseClass; 195 reuser = canReuseClass;
180 } else { 196 } else {
181 reuser = cannotReuse; 197 reuser = unableToReuse;
182 } 198 }
183 if (!reuser(diffToken, before, after)) { 199 if (!reuser(diffToken, before, after)) {
184 onlySimpleUpdates = false; 200 assert(!_failedUpdates.isEmpty);
185 return false; 201 continue;
186 } 202 }
187 } 203 }
188 hasPendingUpdates = true;
189 204
190 return true; 205 return _failedUpdates.isEmpty;
191 } 206 }
192 207
193 /// Returns true if function [before] can be reused to reflect the changes in 208 /// Returns true if function [before] can be reused to reflect the changes in
194 /// [after]. 209 /// [after].
195 /// 210 ///
196 /// If [before] can be reused, an update (patch) is added to [updates]. 211 /// If [before] can be reused, an update (patch) is added to [updates].
197 bool canReuseFunction( 212 bool canReuseFunction(
198 Token diffToken, 213 Token diffToken,
199 PartialFunctionElement before, 214 PartialFunctionElement before,
200 PartialFunctionElement after) { 215 PartialFunctionElement after) {
201 FunctionExpression node = 216 FunctionExpression node =
202 after.parseNode(compiler).asFunctionExpression(); 217 after.parseNode(compiler).asFunctionExpression();
203 if (node == null) { 218 if (node == null) {
204 logVerbose('Not a function expression.'); 219 return cannotReuse(after, "Not a function expression: '$node'");
205 return false;
206 } 220 }
207 Token last = after.endToken; 221 Token last = after.endToken;
208 if (node.body != null) { 222 if (node.body != null) {
209 last = node.body.getBeginToken(); 223 last = node.body.getBeginToken();
210 } 224 }
211 if (isTokenBetween(diffToken, after.beginToken, last)) { 225 if (isTokenBetween(diffToken, after.beginToken, last)) {
212 logVerbose('Signature changed.'); 226 return cannotReuse(after, 'Signature changed.');
213 return false;
214 } 227 }
215 logVerbose('Simple modification of ${after} detected'); 228 logVerbose('Simple modification of ${after} detected');
216 updates.add(new FunctionUpdate(compiler, before, after)); 229 updates.add(new FunctionUpdate(compiler, before, after));
217 return true; 230 return true;
218 } 231 }
219 232
220 bool canReuseClass( 233 bool canReuseClass(
221 Token diffToken, 234 Token diffToken,
222 PartialClassElement before, 235 PartialClassElement before,
223 PartialClassElement after) { 236 PartialClassElement after) {
224 ClassNode node = after.parseNode(compiler).asClassNode(); 237 ClassNode node = after.parseNode(compiler).asClassNode();
225 if (node == null) { 238 if (node == null) {
226 logVerbose('Not a ClassNode.'); 239 return cannotReuse(after, "Not a ClassNode: '$node'");
227 return false;
228 } 240 }
229 NodeList body = node.body; 241 NodeList body = node.body;
230 if (body == null) { 242 if (body == null) {
231 logVerbose('Class has no body.'); 243 return cannotReuse(after, "Class has no body.");
232 return false;
233 } 244 }
234 if (isTokenBetween(diffToken, node.beginToken, body.beginToken)) { 245 if (isTokenBetween(diffToken, node.beginToken, body.beginToken)) {
235 logVerbose('Class header changed.'); 246 return cannotReuse(after, "Class header changed.");
236 return false;
237 } 247 }
238 logVerbose('Simple modification of ${after} detected'); 248 logVerbose('Simple modification of ${after} detected');
239 return canReuseScopeContainerElement(before, after); 249 return canReuseScopeContainerElement(before, after);
240 } 250 }
241 251
242 bool isTokenBetween(Token token, Token first, Token last) { 252 bool isTokenBetween(Token token, Token first, Token last) {
243 Token current = first; 253 Token current = first;
244 while (current != last && current.kind != EOF_TOKEN) { 254 while (current != last && current.kind != EOF_TOKEN) {
245 if (current == token) { 255 if (current == token) {
246 return true; 256 return true;
247 } 257 }
248 current = current.next; 258 current = current.next;
249 } 259 }
250 return false; 260 return false;
251 } 261 }
252 262
253 bool cannotReuse( 263 bool unableToReuse(
254 Token diffToken, 264 Token diffToken,
255 PartialElement before, 265 PartialElement before,
256 PartialElement after) { 266 PartialElement after) {
257 logVerbose( 267 return cannotReuse(
268 after,
258 'Unhandled change:' 269 'Unhandled change:'
259 ' ${before} (${before.runtimeType} -> ${after.runtimeType}).'); 270 ' ${before} (${before.runtimeType} -> ${after.runtimeType}).');
260 return false;
261 } 271 }
262 272
263 List<Element> applyUpdates() { 273 List<Element> applyUpdates() {
264 if (!onlySimpleUpdates) { 274 if (!_failedUpdates.isEmpty) {
265 throw new StateError("Can't compute update."); 275 throw new StateError(
276 "Can't compute update.\n\n${_failedUpdates.join('\n\n')}");
266 } 277 }
267 return updates.map((Update update) => update.apply()).toList(); 278 return updates.map((Update update) => update.apply()).toList();
268 } 279 }
269 280
270 String computeUpdateJs() { 281 String computeUpdateJs() {
271 List<Element> updatedElements = applyUpdates(); 282 List<Element> updatedElements = applyUpdates();
272 if (compiler.progress != null) { 283 if (compiler.progress != null) {
273 compiler.progress.reset(); 284 compiler.progress.reset();
274 } 285 }
275 for (Element element in updatedElements) { 286 for (Element element in updatedElements) {
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 before.getOrSet = after.getOrSet; 396 before.getOrSet = after.getOrSet;
386 } 397 }
387 398
388 /// Reset various caches and remove this element from the compiler's internal 399 /// Reset various caches and remove this element from the compiler's internal
389 /// state. 400 /// state.
390 void reuseElement() { 401 void reuseElement() {
391 compiler.forgetElement(before); 402 compiler.forgetElement(before);
392 before.reuseElement(); 403 before.reuseElement();
393 } 404 }
394 } 405 }
OLDNEW
« no previous file with comments | « no previous file | dart/site/try/poi/poi.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698