Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| 11 UTF8; | 11 UTF8; |
| 12 | 12 |
| 13 import 'package:compiler/compiler.dart' as api; | 13 import 'package:compiler/compiler.dart' as api; |
| 14 | 14 |
| 15 import 'package:compiler/src/dart2jslib.dart' show | 15 import 'package:compiler/src/dart2jslib.dart' show |
| 16 Compiler, | 16 Compiler, |
| 17 Script; | 17 Script; |
| 18 | 18 |
| 19 import 'package:compiler/src/elements/elements.dart' show | 19 import 'package:compiler/src/elements/elements.dart' show |
| 20 ClassElement, | |
| 20 Element, | 21 Element, |
| 21 FunctionElement, | 22 FunctionElement, |
| 22 LibraryElement, | 23 LibraryElement, |
| 23 ScopeContainerElement; | 24 ScopeContainerElement; |
| 24 | 25 |
| 25 import 'package:compiler/src/scanner/scannerlib.dart' show | 26 import 'package:compiler/src/scanner/scannerlib.dart' show |
| 26 EOF_TOKEN, | 27 EOF_TOKEN, |
| 27 PartialClassElement, | 28 PartialClassElement, |
| 28 PartialElement, | 29 PartialElement, |
| 29 PartialFunctionElement, | 30 PartialFunctionElement, |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 46 CodeEmitterTask, | 47 CodeEmitterTask, |
| 47 MemberInfo; | 48 MemberInfo; |
| 48 | 49 |
| 49 import 'package:_internal/compiler/js_lib/shared/embedded_names.dart' | 50 import 'package:_internal/compiler/js_lib/shared/embedded_names.dart' |
| 50 as embeddedNames; | 51 as embeddedNames; |
| 51 | 52 |
| 52 import 'package:compiler/src/js_backend/js_backend.dart' show | 53 import 'package:compiler/src/js_backend/js_backend.dart' show |
| 53 JavaScriptBackend, | 54 JavaScriptBackend, |
| 54 Namer; | 55 Namer; |
| 55 | 56 |
| 57 import 'package:compiler/src/util/util.dart' show | |
| 58 Link; | |
| 59 | |
| 60 import 'package:compiler/src/elements/modelx.dart' show | |
| 61 DeclarationSite, | |
| 62 ElementX; | |
| 63 | |
| 56 import 'diff.dart' show | 64 import 'diff.dart' show |
| 57 Difference, | 65 Difference, |
| 58 computeDifference; | 66 computeDifference; |
| 59 | 67 |
| 60 typedef void Logger(message); | 68 typedef void Logger(message); |
| 61 | 69 |
| 62 typedef bool Reuser( | 70 typedef bool Reuser( |
| 63 Token diffToken, | 71 Token diffToken, |
| 64 PartialElement before, | 72 PartialElement before, |
| 65 PartialElement after); | 73 PartialElement after); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 89 final Logger logVerbose; | 97 final Logger logVerbose; |
| 90 | 98 |
| 91 // TODO(ahe): Get rid of this field. It assumes that only one library has | 99 // TODO(ahe): Get rid of this field. It assumes that only one library has |
| 92 // changed. | 100 // changed. |
| 93 final Uri uri; | 101 final Uri uri; |
| 94 | 102 |
| 95 final List<Update> updates = <Update>[]; | 103 final List<Update> updates = <Update>[]; |
| 96 | 104 |
| 97 final List<FailedUpdate> _failedUpdates = <FailedUpdate>[]; | 105 final List<FailedUpdate> _failedUpdates = <FailedUpdate>[]; |
| 98 | 106 |
| 107 final Set<ElementX> _elementsToInvalidate = new Set<ElementX>(); | |
| 108 | |
| 99 LibraryUpdater( | 109 LibraryUpdater( |
| 100 this.compiler, | 110 this.compiler, |
| 101 this.inputProvider, | 111 this.inputProvider, |
| 102 this.uri, | 112 this.uri, |
| 103 this.logTime, | 113 this.logTime, |
| 104 this.logVerbose); | 114 this.logVerbose); |
| 105 | 115 |
| 106 /// When [true], updates must be applied (using [applyUpdates]) before the | 116 /// When [true], updates must be applied (using [applyUpdates]) before the |
| 107 /// [compiler]'s state correctly reflects the updated program. | 117 /// [compiler]'s state correctly reflects the updated program. |
| 108 bool get hasPendingUpdates => !updates.isEmpty; | 118 bool get hasPendingUpdates => !updates.isEmpty; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 162 return false; | 172 return false; |
| 163 } | 173 } |
| 164 | 174 |
| 165 bool canReuseScopeContainerElement( | 175 bool canReuseScopeContainerElement( |
| 166 ScopeContainerElement element, | 176 ScopeContainerElement element, |
| 167 ScopeContainerElement newElement) { | 177 ScopeContainerElement newElement) { |
| 168 List<Difference> differences = computeDifference(element, newElement); | 178 List<Difference> differences = computeDifference(element, newElement); |
| 169 logTime('Differences computed.'); | 179 logTime('Differences computed.'); |
| 170 for (Difference difference in differences) { | 180 for (Difference difference in differences) { |
| 171 logTime('Looking at difference: $difference'); | 181 logTime('Looking at difference: $difference'); |
| 172 if (difference.before == null || difference.after == null) { | 182 |
| 173 cannotReuse(difference, "Can't reuse; Scope changed."); | 183 if (difference.before == null && difference.after is PartialElement) { |
| 184 canReuseAddedElement(difference.after); | |
| 185 continue; | |
| 186 } | |
| 187 if (difference.after == null && difference.before is PartialElement) { | |
| 188 canReuseRemovedElement(difference.before); | |
| 174 continue; | 189 continue; |
| 175 } | 190 } |
| 176 Token diffToken = difference.token; | 191 Token diffToken = difference.token; |
| 177 if (diffToken == null) { | 192 if (diffToken == null) { |
| 178 cannotReuse(difference, "No difference token."); | 193 cannotReuse(difference, "No difference token."); |
| 179 continue; | 194 continue; |
| 180 } | 195 } |
| 181 if (difference.after is! PartialElement && | 196 if (difference.after is! PartialElement && |
| 182 difference.before is! PartialElement) { | 197 difference.before is! PartialElement) { |
| 183 cannotReuse(difference, "Don't know how to recompile."); | 198 cannotReuse(difference, "Don't know how to recompile."); |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 198 } | 213 } |
| 199 if (!reuser(diffToken, before, after)) { | 214 if (!reuser(diffToken, before, after)) { |
| 200 assert(!_failedUpdates.isEmpty); | 215 assert(!_failedUpdates.isEmpty); |
| 201 continue; | 216 continue; |
| 202 } | 217 } |
| 203 } | 218 } |
| 204 | 219 |
| 205 return _failedUpdates.isEmpty; | 220 return _failedUpdates.isEmpty; |
| 206 } | 221 } |
| 207 | 222 |
| 223 bool canReuseAddedElement(PartialElement element) { | |
| 224 return cannotReuse(element, "Scope changed, element added."); | |
| 225 } | |
| 226 | |
| 227 bool canReuseRemovedElement(PartialElement element) { | |
| 228 if (element is PartialFunctionElement) { | |
| 229 return canReuseRemovedFunction(element); | |
| 230 } | |
| 231 return cannotReuse( | |
| 232 element, "Removed element that isn't a method."); | |
| 233 } | |
| 234 | |
| 235 bool canReuseRemovedFunction(PartialFunctionElement element) { | |
| 236 if (!element.isInstanceMember) { | |
| 237 return cannotReuse( | |
| 238 element, "Removed function that isn't an instance method."); | |
| 239 } | |
| 240 logVerbose("Removed instance method $element."); | |
| 241 | |
| 242 PartialClassElement cls = element.enclosingClass; | |
| 243 for (ScopeContainerElement scope in scopesAffectedBy(element, cls)) { | |
| 244 scanSites(scope, (Element member, DeclarationSite site) { | |
| 245 // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior. | |
| 246 Map<String, List<String>> names = qualifiedNamesIn(site); | |
| 247 if (canNamesResolveTo(names, element, cls)) { | |
| 248 _elementsToInvalidate.add(member); | |
| 249 } | |
| 250 }); | |
| 251 } | |
| 252 | |
| 253 // TODO(ahe): Don't modify the class here, instead use an instance of | |
| 254 // Update. | |
| 255 Link<Element> localMembersReversed = const Link<Element>(); | |
| 256 cls.forEachLocalMember((member) { | |
| 257 if (member != element) { | |
| 258 localMembersReversed = localMembersReversed.prepend(member); | |
| 259 } | |
| 260 }); | |
| 261 cls.localMembersCache = null; | |
| 262 cls.localMembersReversed = localMembersReversed; | |
| 263 cls.localScope.contents.remove(element.name); | |
| 264 | |
| 265 // TODO(ahe): Also compute a patch which removes the function, e.g., | |
| 266 // "delete GlobalObject.MyClass.prototype.memberName". | |
| 267 | |
| 268 // TODO(ahe): Also forget [element]. | |
| 269 | |
| 270 return true; | |
| 271 } | |
| 272 | |
| 273 void scanSites( | |
| 274 Element element, | |
| 275 void f(ElementX element, DeclarationSite site)) { | |
| 276 DeclarationSite site = declarationSite(element); | |
| 277 if (site != null) { | |
| 278 f(element, site); | |
| 279 } | |
| 280 if (element is ScopeContainerElement) { | |
| 281 element.forEachLocalMember((member) { scanSites(member, f); }); | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 List<ScopeContainerElement> scopesAffectedBy( | |
| 286 Element element, | |
| 287 ClassElement cls) { | |
| 288 // TODO(ahe): Use library export graph to compute this. | |
| 289 // TODO(ahe): Should return all user-defined libraries and packages. | |
| 290 LibraryElement library = element.library; | |
| 291 List<ScopeContainerElement> result = <ScopeContainerElement>[library]; | |
| 292 | |
| 293 if (cls == null) return result; | |
| 294 | |
| 295 var externalSubtypes = | |
| 296 compiler.world.subtypesOf(cls).where((e) => e.library != library); | |
| 297 | |
| 298 return result..addAll(externalSubtypes); | |
| 299 } | |
| 300 | |
| 208 /// Returns true if function [before] can be reused to reflect the changes in | 301 /// Returns true if function [before] can be reused to reflect the changes in |
| 209 /// [after]. | 302 /// [after]. |
| 210 /// | 303 /// |
| 211 /// If [before] can be reused, an update (patch) is added to [updates]. | 304 /// If [before] can be reused, an update (patch) is added to [updates]. |
| 212 bool canReuseFunction( | 305 bool canReuseFunction( |
| 213 Token diffToken, | 306 Token diffToken, |
| 214 PartialFunctionElement before, | 307 PartialFunctionElement before, |
| 215 PartialFunctionElement after) { | 308 PartialFunctionElement after) { |
| 216 FunctionExpression node = | 309 FunctionExpression node = |
| 217 after.parseNode(compiler).asFunctionExpression(); | 310 after.parseNode(compiler).asFunctionExpression(); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 268 after, | 361 after, |
| 269 'Unhandled change:' | 362 'Unhandled change:' |
| 270 ' ${before} (${before.runtimeType} -> ${after.runtimeType}).'); | 363 ' ${before} (${before.runtimeType} -> ${after.runtimeType}).'); |
| 271 } | 364 } |
| 272 | 365 |
| 273 List<Element> applyUpdates() { | 366 List<Element> applyUpdates() { |
| 274 if (!_failedUpdates.isEmpty) { | 367 if (!_failedUpdates.isEmpty) { |
| 275 throw new StateError( | 368 throw new StateError( |
| 276 "Can't compute update.\n\n${_failedUpdates.join('\n\n')}"); | 369 "Can't compute update.\n\n${_failedUpdates.join('\n\n')}"); |
| 277 } | 370 } |
| 278 return updates.map((Update update) => update.apply()).toList(); | 371 for (ElementX element in _elementsToInvalidate) { |
| 372 compiler.forgetElement(element); | |
| 373 element.reuseElement(); | |
| 374 } | |
| 375 return updates.map((Update update) => update.apply()).toList() | |
| 376 ..addAll(_elementsToInvalidate); | |
| 279 } | 377 } |
| 280 | 378 |
| 281 String computeUpdateJs() { | 379 String computeUpdateJs() { |
| 282 List<Element> updatedElements = applyUpdates(); | 380 List<Element> updatedElements = applyUpdates(); |
| 283 if (compiler.progress != null) { | 381 if (compiler.progress != null) { |
| 284 compiler.progress.reset(); | 382 compiler.progress.reset(); |
| 285 } | 383 } |
| 286 for (Element element in updatedElements) { | 384 for (Element element in updatedElements) { |
| 287 compiler.enqueuer.resolution.addToWorkList(element); | 385 compiler.enqueuer.resolution.addToWorkList(element); |
| 288 } | 386 } |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 315 if (info == null) { | 413 if (info == null) { |
| 316 compiler.internalError(element, '${element.runtimeType}'); | 414 compiler.internalError(element, '${element.runtimeType}'); |
| 317 } | 415 } |
| 318 String name = info.name; | 416 String name = info.name; |
| 319 jsAst.Node function = info.code; | 417 jsAst.Node function = info.code; |
| 320 List<jsAst.Statement> statements = <jsAst.Statement>[]; | 418 List<jsAst.Statement> statements = <jsAst.Statement>[]; |
| 321 if (element.isInstanceMember) { | 419 if (element.isInstanceMember) { |
| 322 jsAst.Node elementAccess = namer.elementAccess(element.enclosingClass); | 420 jsAst.Node elementAccess = namer.elementAccess(element.enclosingClass); |
| 323 statements.add( | 421 statements.add( |
| 324 js.statement('#.prototype.# = f', [elementAccess, name])); | 422 js.statement('#.prototype.# = f', [elementAccess, name])); |
| 423 | |
| 424 if (backend.isAliasedSuperMember(element)) { | |
| 425 String superName = namer.getNameOfAliasedSuperMember(element); | |
| 426 statements.add( | |
| 427 js.statement('#.prototype.# = f', [elementAccess, superName])); | |
|
ahe
2014/11/10 16:53:27
Stephan, does this look reasonable?
herhut
2014/11/13 10:55:08
Looks good.
| |
| 428 } | |
| 325 } else { | 429 } else { |
| 326 jsAst.Node elementAccess = namer.elementAccess(element); | 430 jsAst.Node elementAccess = namer.elementAccess(element); |
| 327 jsAst.Expression globalFunctionsAccess = | 431 jsAst.Expression globalFunctionsAccess = |
| 328 emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS); | 432 emitter.generateEmbeddedGlobalAccess(embeddedNames.GLOBAL_FUNCTIONS); |
| 329 statements.add( | 433 statements.add( |
| 330 js.statement( | 434 js.statement( |
| 331 '#.# = # = f', | 435 '#.# = # = f', |
| 332 [globalFunctionsAccess, name, elementAccess])); | 436 [globalFunctionsAccess, name, elementAccess])); |
| 333 if (info.canTearOff) { | 437 if (info.canTearOff) { |
| 334 String globalName = namer.globalObjectFor(element); | 438 String globalName = namer.globalObjectFor(element); |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 396 before.getOrSet = after.getOrSet; | 500 before.getOrSet = after.getOrSet; |
| 397 } | 501 } |
| 398 | 502 |
| 399 /// Reset various caches and remove this element from the compiler's internal | 503 /// Reset various caches and remove this element from the compiler's internal |
| 400 /// state. | 504 /// state. |
| 401 void reuseElement() { | 505 void reuseElement() { |
| 402 compiler.forgetElement(before); | 506 compiler.forgetElement(before); |
| 403 before.reuseElement(); | 507 before.reuseElement(); |
| 404 } | 508 } |
| 405 } | 509 } |
| 510 | |
| 511 Map<String, List<String>> qualifiedNamesIn(PartialElement element) { | |
| 512 Token beginToken = element.beginToken; | |
| 513 Token endToken = element.endToken; | |
| 514 Token token = beginToken; | |
| 515 if (element is PartialClassElement) { | |
| 516 ClassNode node = element.cachedNode; | |
| 517 if (node != null) { | |
| 518 NodeList body = node.body; | |
| 519 if (body != null) { | |
| 520 endToken = body.beginToken; | |
| 521 } | |
| 522 } | |
| 523 } | |
| 524 Map<String, List<String>> names = new Map<String, List<String>>(); | |
| 525 List<List<Token>> qualifieds = <List<Token>>[]; | |
| 526 do { | |
| 527 if (token.isIdentifier()) { | |
| 528 List<String> name = names.putIfAbsent(token.value, () => <String>[]); | |
| 529 while (identical('.', token.next.stringValue) && | |
| 530 token.next.next.isIdentifier()) { | |
| 531 token = token.next.next; | |
| 532 name.add(token.value); | |
| 533 } | |
| 534 } | |
| 535 token = token.next; | |
| 536 } while (token.kind != EOF_TOKEN && token != endToken); | |
| 537 return names; | |
| 538 } | |
| 539 | |
| 540 bool canNamesResolveTo( | |
| 541 Map<String, List<String>> names, | |
| 542 Element element, | |
| 543 ClassElement cls) { | |
| 544 if (names.containsKey(element.name)) { | |
| 545 return true; | |
| 546 } | |
| 547 if (cls != null) { | |
| 548 List<String> rest = names[cls.name]; | |
| 549 if (rest != null && rest.contains(element.name)) { | |
| 550 // [names] contains C.m, where C is the name of [cls], and m is the name | |
| 551 // of [element]. | |
| 552 return true; | |
| 553 } | |
| 554 } | |
| 555 return false; | |
| 556 } | |
| 557 | |
| 558 DeclarationSite declarationSite(Element element) { | |
| 559 return element is ElementX ? element.declarationSite : null; | |
| 560 } | |
| OLD | NEW |