OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library dart2js_incremental.library_updater; |
| 6 |
| 7 import 'dart:async' show |
| 8 Future; |
| 9 |
| 10 import 'dart:convert' show |
| 11 UTF8; |
| 12 |
| 13 import 'package:compiler/compiler.dart' as api; |
| 14 |
| 15 import 'package:compiler/implementation/dart2jslib.dart' show |
| 16 Compiler, |
| 17 Script; |
| 18 |
| 19 import 'package:compiler/implementation/elements/elements.dart' show |
| 20 LibraryElement; |
| 21 |
| 22 import 'package:compiler/implementation/scanner/scannerlib.dart' show |
| 23 EOF_TOKEN, |
| 24 PartialElement, |
| 25 PartialFunctionElement, |
| 26 Token; |
| 27 |
| 28 import 'package:compiler/implementation/source_file.dart' show |
| 29 StringSourceFile; |
| 30 |
| 31 import 'package:compiler/implementation/tree/tree.dart' show |
| 32 FunctionExpression; |
| 33 |
| 34 import 'diff.dart' show |
| 35 Difference, |
| 36 computeDifference; |
| 37 |
| 38 typedef void Logger(message); |
| 39 |
| 40 // TODO(ahe): Generalize this class. For now only works for Compiler.mainApp, |
| 41 // and only if that library has exactly one compilation unit. |
| 42 class LibraryUpdater { |
| 43 final Compiler compiler; |
| 44 |
| 45 final api.CompilerInputProvider inputProvider; |
| 46 |
| 47 final Logger logTime; |
| 48 |
| 49 final Logger logVerbose; |
| 50 |
| 51 // TODO(ahe): Get rid of this field. It assumes that only one library has |
| 52 // changed. |
| 53 final Uri uri; |
| 54 |
| 55 final List<Update> updates = <Update>[]; |
| 56 |
| 57 LibraryUpdater( |
| 58 this.compiler, |
| 59 this.inputProvider, |
| 60 this.uri, |
| 61 this.logTime, |
| 62 this.logVerbose); |
| 63 |
| 64 /// Used as tear-off passed to [LibraryLoaderTask.resetAsync]. |
| 65 Future<bool> reuseLibrary(LibraryElement library) { |
| 66 assert(compiler != null); |
| 67 if (library.isPlatformLibrary || library.isPackageLibrary) { |
| 68 logTime('Reusing $library.'); |
| 69 return new Future.value(true); |
| 70 } else if (library != compiler.mainApp) { |
| 71 return new Future.value(false); |
| 72 } |
| 73 return inputProvider(uri).then((List<int> bytes) { |
| 74 if (canReuseLibrary(library, bytes)) { |
| 75 // TODO(ahe): Temporary. Since we don't yet apply the updates, the |
| 76 // library cannot be reused if there are updates. |
| 77 return updates.isEmpty; |
| 78 } else { |
| 79 return false; |
| 80 } |
| 81 }); |
| 82 } |
| 83 |
| 84 /// Returns true if [library] can be reused. |
| 85 /// |
| 86 /// This methods also computes the [updates] (patches) needed to have |
| 87 /// [library] reflect the modifications in [bytes]. |
| 88 bool canReuseLibrary(LibraryElement library, List<int> bytes) { |
| 89 logTime('Attempting to reuse mainApp.'); |
| 90 String newSource = UTF8.decode(bytes); |
| 91 logTime('Decoded UTF8'); |
| 92 |
| 93 // TODO(ahe): Can't use compiler.mainApp in general. |
| 94 if (false && newSource == compiler.mainApp.compilationUnit.script.text) { |
| 95 // TODO(ahe): Need to update the compilationUnit's source code when |
| 96 // doing incremental analysis for this to work. |
| 97 logTime("Source didn't change"); |
| 98 return true; |
| 99 } |
| 100 |
| 101 logTime("Source did change"); |
| 102 Script sourceScript = new Script( |
| 103 uri, uri, new StringSourceFile('$uri', newSource)); |
| 104 var dartPrivacyIsBroken = compiler.libraryLoader; |
| 105 LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync( |
| 106 null, sourceScript, uri); |
| 107 logTime('New library synthesized.'); |
| 108 List<Difference> differences = computeDifference(library, newLibrary); |
| 109 logTime('Differences computed.'); |
| 110 for (Difference difference in differences) { |
| 111 logTime('Looking at difference: $difference'); |
| 112 if (difference.before == null || difference.after == null) { |
| 113 logVerbose('Scope changed in $difference'); |
| 114 // Scope changed, don't reuse library. |
| 115 return false; |
| 116 } |
| 117 Token diffToken = difference.token; |
| 118 if (diffToken == null) { |
| 119 logVerbose('No token stored in difference.'); |
| 120 return false; |
| 121 } |
| 122 if (difference.after is! PartialElement && |
| 123 difference.before is! PartialElement) { |
| 124 logVerbose('Not a PartialElement: $difference'); |
| 125 // Don't know how to recompile element. |
| 126 return false; |
| 127 } |
| 128 PartialElement before = difference.before; |
| 129 PartialElement after = difference.after; |
| 130 |
| 131 if (before is PartialFunctionElement && after is PartialFunctionElement) { |
| 132 if (!canReuseFunction(diffToken, before, after)) { |
| 133 return false; |
| 134 } |
| 135 } else { |
| 136 // Unhandled kind of element. |
| 137 return false; |
| 138 } |
| 139 } |
| 140 |
| 141 return true; |
| 142 } |
| 143 |
| 144 /// Returns true if function [before] can be reused to reflect the changes in |
| 145 /// [after]. |
| 146 /// |
| 147 /// If [before] can be reused, an update (patch) is added to [updates]. |
| 148 bool canReuseFunction( |
| 149 Token diffToken, |
| 150 PartialFunctionElement before, |
| 151 PartialFunctionElement after) { |
| 152 FunctionExpression node = |
| 153 after.parseNode(compiler).asFunctionExpression(); |
| 154 if (node == null) { |
| 155 print('Not a function expression.'); |
| 156 return false; |
| 157 } |
| 158 Token last = after.endToken; |
| 159 if (node.body != null) { |
| 160 last = node.body.getBeginToken(); |
| 161 } |
| 162 Token token = after.beginToken; |
| 163 while (token != last && token.kind != EOF_TOKEN) { |
| 164 if (token == diffToken) { |
| 165 logVerbose('Signature changed'); |
| 166 return false; |
| 167 } |
| 168 token = token.next; |
| 169 } |
| 170 print('Simple modification of ${after} detected'); |
| 171 updates.add(new FunctionUpdate(compiler, before, after)); |
| 172 return true; |
| 173 } |
| 174 } |
| 175 |
| 176 /// Represents an update (aka patch) of [before] to [after]. We use the word |
| 177 /// "update" to avoid confusion with the compiler feature of "patch" methods. |
| 178 abstract class Update { |
| 179 final Compiler compiler; |
| 180 |
| 181 PartialElement get before; |
| 182 |
| 183 PartialElement get after; |
| 184 |
| 185 Update(this.compiler); |
| 186 } |
| 187 |
| 188 /// Represents an update of a function element. |
| 189 class FunctionUpdate extends Update { |
| 190 final PartialFunctionElement before; |
| 191 |
| 192 final PartialFunctionElement after; |
| 193 |
| 194 FunctionUpdate(Compiler compiler, this.before, this.after) |
| 195 : super(compiler); |
| 196 } |
OLD | NEW |