| 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/implementation/dart2jslib.dart' show | 15 import 'package:compiler/implementation/dart2jslib.dart' show |
| 16 Compiler, | 16 Compiler, |
| 17 Script; | 17 Script; |
| 18 | 18 |
| 19 import 'package:compiler/implementation/elements/elements.dart' show | 19 import 'package:compiler/implementation/elements/elements.dart' show |
| 20 Element, | 20 Element, |
| 21 FunctionElement, | 21 FunctionElement, |
| 22 LibraryElement; | 22 LibraryElement, |
| 23 ScopeContainerElement; |
| 23 | 24 |
| 24 import 'package:compiler/implementation/scanner/scannerlib.dart' show | 25 import 'package:compiler/implementation/scanner/scannerlib.dart' show |
| 25 EOF_TOKEN, | 26 EOF_TOKEN, |
| 27 PartialClassElement, |
| 26 PartialElement, | 28 PartialElement, |
| 27 PartialFunctionElement, | 29 PartialFunctionElement, |
| 28 Token; | 30 Token; |
| 29 | 31 |
| 30 import 'package:compiler/implementation/source_file.dart' show | 32 import 'package:compiler/implementation/source_file.dart' show |
| 31 StringSourceFile; | 33 StringSourceFile; |
| 32 | 34 |
| 33 import 'package:compiler/implementation/tree/tree.dart' show | 35 import 'package:compiler/implementation/tree/tree.dart' show |
| 34 FunctionExpression; | 36 ClassNode, |
| 37 FunctionExpression, |
| 38 NodeList; |
| 35 | 39 |
| 36 import 'package:compiler/implementation/js/js.dart' show | 40 import 'package:compiler/implementation/js/js.dart' show |
| 37 js; | 41 js; |
| 38 | 42 |
| 39 import 'package:compiler/implementation/js/js.dart' as jsAst; | 43 import 'package:compiler/implementation/js/js.dart' as jsAst; |
| 40 | 44 |
| 41 import 'package:compiler/implementation/js_emitter/js_emitter.dart' show | 45 import 'package:compiler/implementation/js_emitter/js_emitter.dart' show |
| 42 CodeEmitterTask, | 46 CodeEmitterTask, |
| 43 MemberInfo; | 47 MemberInfo; |
| 44 | 48 |
| 45 import 'package:compiler/js_lib/shared/embedded_names.dart' as embeddedNames; | 49 import 'package:compiler/js_lib/shared/embedded_names.dart' as embeddedNames; |
| 46 | 50 |
| 47 import 'package:compiler/implementation/js_backend/js_backend.dart' show | 51 import 'package:compiler/implementation/js_backend/js_backend.dart' show |
| 48 JavaScriptBackend, | 52 JavaScriptBackend, |
| 49 Namer; | 53 Namer; |
| 50 | 54 |
| 51 import 'diff.dart' show | 55 import 'diff.dart' show |
| 52 Difference, | 56 Difference, |
| 53 computeDifference; | 57 computeDifference; |
| 54 | 58 |
| 55 typedef void Logger(message); | 59 typedef void Logger(message); |
| 56 | 60 |
| 61 typedef bool Reuser( |
| 62 Token diffToken, |
| 63 PartialElement before, |
| 64 PartialElement after); |
| 65 |
| 57 // TODO(ahe): Generalize this class. For now only works for Compiler.mainApp, | 66 // TODO(ahe): Generalize this class. For now only works for Compiler.mainApp, |
| 58 // and only if that library has exactly one compilation unit. | 67 // and only if that library has exactly one compilation unit. |
| 59 class LibraryUpdater { | 68 class LibraryUpdater { |
| 60 final Compiler compiler; | 69 final Compiler compiler; |
| 61 | 70 |
| 62 final api.CompilerInputProvider inputProvider; | 71 final api.CompilerInputProvider inputProvider; |
| 63 | 72 |
| 64 final Logger logTime; | 73 final Logger logTime; |
| 65 | 74 |
| 66 final Logger logVerbose; | 75 final Logger logVerbose; |
| 67 | 76 |
| 68 // TODO(ahe): Get rid of this field. It assumes that only one library has | 77 // TODO(ahe): Get rid of this field. It assumes that only one library has |
| 69 // changed. | 78 // changed. |
| 70 final Uri uri; | 79 final Uri uri; |
| 71 | 80 |
| 72 // When [true], updates must be applied (using [applyUpdates]) before the | 81 // When [true], updates must be applied (using [applyUpdates]) before the |
| 73 // [compiler]'s state correctly reflects the updated program. | 82 // [compiler]'s state correctly reflects the updated program. |
| 74 bool hasPendingUpdates = false; | 83 bool hasPendingUpdates = false; |
| 75 | 84 |
| 85 bool onlySimpleUpdates = true; |
| 86 |
| 76 final List<Update> updates = <Update>[]; | 87 final List<Update> updates = <Update>[]; |
| 77 | 88 |
| 78 LibraryUpdater( | 89 LibraryUpdater( |
| 79 this.compiler, | 90 this.compiler, |
| 80 this.inputProvider, | 91 this.inputProvider, |
| 81 this.uri, | 92 this.uri, |
| 82 this.logTime, | 93 this.logTime, |
| 83 this.logVerbose); | 94 this.logVerbose); |
| 84 | 95 |
| 85 JavaScriptBackend get backend => compiler.backend; | 96 JavaScriptBackend get backend => compiler.backend; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 return true; | 130 return true; |
| 120 } | 131 } |
| 121 | 132 |
| 122 logTime("Source did change"); | 133 logTime("Source did change"); |
| 123 Script sourceScript = new Script( | 134 Script sourceScript = new Script( |
| 124 uri, uri, new StringSourceFile('$uri', newSource)); | 135 uri, uri, new StringSourceFile('$uri', newSource)); |
| 125 var dartPrivacyIsBroken = compiler.libraryLoader; | 136 var dartPrivacyIsBroken = compiler.libraryLoader; |
| 126 LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync( | 137 LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync( |
| 127 null, sourceScript, uri); | 138 null, sourceScript, uri); |
| 128 logTime('New library synthesized.'); | 139 logTime('New library synthesized.'); |
| 129 List<Difference> differences = computeDifference(library, newLibrary); | 140 return canReuseScopeContainerElement(library, newLibrary); |
| 141 } |
| 142 |
| 143 bool canReuseScopeContainerElement( |
| 144 ScopeContainerElement element, |
| 145 ScopeContainerElement newElement) { |
| 146 List<Difference> differences = computeDifference(element, newElement); |
| 130 logTime('Differences computed.'); | 147 logTime('Differences computed.'); |
| 131 for (Difference difference in differences) { | 148 for (Difference difference in differences) { |
| 132 logTime('Looking at difference: $difference'); | 149 logTime('Looking at difference: $difference'); |
| 133 if (difference.before == null || difference.after == null) { | 150 if (difference.before == null || difference.after == null) { |
| 134 logVerbose('Scope changed in $difference'); | 151 logVerbose('Scope changed in $difference'); |
| 135 // Scope changed, don't reuse library. | 152 // Scope changed, don't reuse library. |
| 153 onlySimpleUpdates = false; |
| 136 return false; | 154 return false; |
| 137 } | 155 } |
| 138 Token diffToken = difference.token; | 156 Token diffToken = difference.token; |
| 139 if (diffToken == null) { | 157 if (diffToken == null) { |
| 140 logVerbose('No token stored in difference.'); | 158 logVerbose('No token stored in difference.'); |
| 159 onlySimpleUpdates = false; |
| 141 return false; | 160 return false; |
| 142 } | 161 } |
| 143 if (difference.after is! PartialElement && | 162 if (difference.after is! PartialElement && |
| 144 difference.before is! PartialElement) { | 163 difference.before is! PartialElement) { |
| 145 logVerbose('Not a PartialElement: $difference'); | 164 logVerbose('Not a PartialElement: $difference'); |
| 146 // Don't know how to recompile element. | 165 // Don't know how to recompile element. |
| 166 onlySimpleUpdates = false; |
| 147 return false; | 167 return false; |
| 148 } | 168 } |
| 149 PartialElement before = difference.before; | 169 PartialElement before = difference.before; |
| 150 PartialElement after = difference.after; | 170 PartialElement after = difference.after; |
| 151 | 171 |
| 172 Reuser reuser; |
| 173 |
| 152 if (before is PartialFunctionElement && after is PartialFunctionElement) { | 174 if (before is PartialFunctionElement && after is PartialFunctionElement) { |
| 153 if (!canReuseFunction(diffToken, before, after)) { | 175 reuser = canReuseFunction; |
| 154 return false; | 176 } else if (before is PartialClassElement && |
| 155 } | 177 after is PartialClassElement) { |
| 178 reuser = canReuseClass; |
| 156 } else { | 179 } else { |
| 157 // Unhandled kind of element. | 180 reuser = cannotReuse; |
| 181 } |
| 182 if (!reuser(diffToken, before, after)) { |
| 183 onlySimpleUpdates = false; |
| 158 return false; | 184 return false; |
| 159 } | 185 } |
| 160 } | 186 } |
| 161 hasPendingUpdates = true; | 187 hasPendingUpdates = true; |
| 162 | 188 |
| 163 return true; | 189 return true; |
| 164 } | 190 } |
| 165 | 191 |
| 166 /// Returns true if function [before] can be reused to reflect the changes in | 192 /// Returns true if function [before] can be reused to reflect the changes in |
| 167 /// [after]. | 193 /// [after]. |
| 168 /// | 194 /// |
| 169 /// If [before] can be reused, an update (patch) is added to [updates]. | 195 /// If [before] can be reused, an update (patch) is added to [updates]. |
| 170 bool canReuseFunction( | 196 bool canReuseFunction( |
| 171 Token diffToken, | 197 Token diffToken, |
| 172 PartialFunctionElement before, | 198 PartialFunctionElement before, |
| 173 PartialFunctionElement after) { | 199 PartialFunctionElement after) { |
| 174 FunctionExpression node = | 200 FunctionExpression node = |
| 175 after.parseNode(compiler).asFunctionExpression(); | 201 after.parseNode(compiler).asFunctionExpression(); |
| 176 if (node == null) { | 202 if (node == null) { |
| 177 print('Not a function expression.'); | 203 logVerbose('Not a function expression.'); |
| 178 return false; | 204 return false; |
| 179 } | 205 } |
| 180 Token last = after.endToken; | 206 Token last = after.endToken; |
| 181 if (node.body != null) { | 207 if (node.body != null) { |
| 182 last = node.body.getBeginToken(); | 208 last = node.body.getBeginToken(); |
| 183 } | 209 } |
| 184 Token token = after.beginToken; | 210 if (isTokenBetween(diffToken, after.beginToken, last)) { |
| 185 while (token != last && token.kind != EOF_TOKEN) { | 211 logVerbose('Signature changed.'); |
| 186 if (token == diffToken) { | 212 return false; |
| 187 logVerbose('Signature changed'); | |
| 188 return false; | |
| 189 } | |
| 190 token = token.next; | |
| 191 } | 213 } |
| 192 print('Simple modification of ${after} detected'); | 214 logVerbose('Simple modification of ${after} detected'); |
| 193 updates.add(new FunctionUpdate(compiler, before, after)); | 215 updates.add(new FunctionUpdate(compiler, before, after)); |
| 194 return true; | 216 return true; |
| 195 } | 217 } |
| 196 | 218 |
| 219 bool canReuseClass( |
| 220 Token diffToken, |
| 221 PartialClassElement before, |
| 222 PartialClassElement after) { |
| 223 ClassNode node = after.parseNode(compiler).asClassNode(); |
| 224 if (node == null) { |
| 225 logVerbose('Not a ClassNode.'); |
| 226 return false; |
| 227 } |
| 228 NodeList body = node.body; |
| 229 if (body == null) { |
| 230 logVerbose('Class has no body.'); |
| 231 return false; |
| 232 } |
| 233 if (isTokenBetween(diffToken, node.beginToken, body.beginToken)) { |
| 234 logVerbose('Class header changed.'); |
| 235 return false; |
| 236 } |
| 237 logVerbose('Simple modification of ${after} detected'); |
| 238 return canReuseScopeContainerElement(before, after); |
| 239 } |
| 240 |
| 241 bool isTokenBetween(Token token, Token first, Token last) { |
| 242 Token current = first; |
| 243 while (current != last && current.kind != EOF_TOKEN) { |
| 244 if (current == token) { |
| 245 return true; |
| 246 } |
| 247 current = current.next; |
| 248 } |
| 249 return false; |
| 250 } |
| 251 |
| 252 bool cannotReuse( |
| 253 Token diffToken, |
| 254 PartialElement before, |
| 255 PartialElement after) { |
| 256 logVerbose( |
| 257 'Unhandled change:' |
| 258 ' ${before} (${before.runtimeType} -> ${after.runtimeType}).'); |
| 259 return false; |
| 260 } |
| 261 |
| 197 List<Element> applyUpdates() { | 262 List<Element> applyUpdates() { |
| 263 if (!onlySimpleUpdates) { |
| 264 throw new StateError("Can't compute update."); |
| 265 } |
| 198 return updates.map((Update update) => update.apply()).toList(); | 266 return updates.map((Update update) => update.apply()).toList(); |
| 199 } | 267 } |
| 200 | 268 |
| 201 String computeUpdateJs() { | 269 String computeUpdateJs() { |
| 202 List<Element> updatedElements = applyUpdates(); | 270 List<Element> updatedElements = applyUpdates(); |
| 203 if (compiler.progress != null) { | 271 if (compiler.progress != null) { |
| 204 compiler.progress.reset(); | 272 compiler.progress.reset(); |
| 205 } | 273 } |
| 206 for (Element element in updatedElements) { | 274 for (Element element in updatedElements) { |
| 207 compiler.enqueuer.resolution.addToWorkList(element); | 275 compiler.enqueuer.resolution.addToWorkList(element); |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 310 before.getOrSet = after.getOrSet; | 378 before.getOrSet = after.getOrSet; |
| 311 } | 379 } |
| 312 | 380 |
| 313 /// Reset various caches and remove this element from the compiler's internal | 381 /// Reset various caches and remove this element from the compiler's internal |
| 314 /// state. | 382 /// state. |
| 315 void reuseElement() { | 383 void reuseElement() { |
| 316 compiler.forgetElement(before); | 384 compiler.forgetElement(before); |
| 317 before.reuseElement(); | 385 before.reuseElement(); |
| 318 } | 386 } |
| 319 } | 387 } |
| OLD | NEW |