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