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 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 76 import 'package:compiler/src/universe/universe.dart' show | 76 import 'package:compiler/src/universe/universe.dart' show |
| 77 Selector; | 77 Selector; |
| 78 | 78 |
| 79 import 'package:compiler/src/constants/values.dart' show | 79 import 'package:compiler/src/constants/values.dart' show |
| 80 ConstantValue; | 80 ConstantValue; |
| 81 | 81 |
| 82 import 'diff.dart' show | 82 import 'diff.dart' show |
| 83 Difference, | 83 Difference, |
| 84 computeDifference; | 84 computeDifference; |
| 85 | 85 |
| 86 import 'dart2js_incremental.dart' show | |
| 87 IncrementalCompiler; | |
| 88 | |
| 86 typedef void Logger(message); | 89 typedef void Logger(message); |
| 87 | 90 |
| 88 typedef bool Reuser( | 91 typedef bool Reuser( |
| 89 Token diffToken, | 92 Token diffToken, |
| 90 PartialElement before, | 93 PartialElement before, |
| 91 PartialElement after); | 94 PartialElement after); |
| 92 | 95 |
| 93 class FailedUpdate { | 96 class FailedUpdate { |
| 94 /// Either an [Element] or a [Difference]. | 97 /// Either an [Element] or a [Difference]. |
| 95 final context; | 98 final context; |
| 96 final String message; | 99 final String message; |
| 97 | 100 |
| 98 FailedUpdate(this.context, this.message); | 101 FailedUpdate(this.context, this.message); |
| 99 | 102 |
| 100 String toString() { | 103 String toString() { |
| 101 if (context == null) return '$message'; | 104 if (context == null) return '$message'; |
| 102 return 'In $context:\n $message'; | 105 return 'In $context:\n $message'; |
| 103 } | 106 } |
| 104 } | 107 } |
| 105 | 108 |
| 106 // TODO(ahe): Generalize this class. For now only works for Compiler.mainApp, | 109 abstract class _IncrementalCompilerContext { |
| 107 // and only if that library has exactly one compilation unit. | 110 IncrementalCompiler incrementalCompiler; |
| 111 | |
| 112 Set<ClassElementX> _emittedClasses; | |
| 113 | |
| 114 Set<ClassElementX> _directlyInstantiatedClasses; | |
| 115 | |
| 116 Set<ConstantValue> _compiledConstants; | |
| 117 } | |
| 118 | |
| 119 class IncrementalCompilerContext extends _IncrementalCompilerContext { | |
| 120 final Set<Uri> _uriWithUpdates = new Set<Uri>(); | |
| 121 | |
| 122 void set incrementalCompiler(IncrementalCompiler value) { | |
| 123 if (super.incrementalCompiler != null) { | |
| 124 throw new StateError("Can't set [incrementalCompiler] more than once."); | |
| 125 } | |
|
Johnni Winther
2014/12/16 11:22:52
You need to do
super.incrementalCompiler = value
ahe
2014/12/16 12:04:31
Done.
| |
| 126 } | |
| 127 | |
| 128 void registerUriWithUpdates(Iterable<Uri> uris) { | |
| 129 _uriWithUpdates.addAll(uris); | |
| 130 } | |
| 131 | |
| 132 void _captureState(Compiler compiler) { | |
| 133 _emittedClasses = new Set.from(compiler.backend.emitter.neededClasses); | |
| 134 | |
| 135 _directlyInstantiatedClasses = | |
| 136 new Set.from(compiler.codegenWorld.directlyInstantiatedClasses); | |
| 137 | |
| 138 List<ConstantValue> constants = | |
| 139 compiler.backend.emitter.outputConstantLists[ | |
| 140 compiler.deferredLoadTask.mainOutputUnit]; | |
| 141 if (constants == null) constants = <ConstantValue>[]; | |
| 142 _compiledConstants = new Set<ConstantValue>.identity()..addAll(constants); | |
| 143 } | |
| 144 | |
| 145 bool _uriHasUpdate(Uri uri) => _uriWithUpdates.contains(uri); | |
| 146 } | |
| 147 | |
| 108 class LibraryUpdater extends JsFeatures { | 148 class LibraryUpdater extends JsFeatures { |
| 109 final Compiler compiler; | 149 final Compiler compiler; |
| 110 | 150 |
| 111 final api.CompilerInputProvider inputProvider; | 151 final api.CompilerInputProvider inputProvider; |
| 112 | 152 |
| 113 final Logger logTime; | 153 final Logger logTime; |
| 114 | 154 |
| 115 final Logger logVerbose; | 155 final Logger logVerbose; |
| 116 | 156 |
| 117 // TODO(ahe): Get rid of this field. It assumes that only one library has | |
| 118 // changed. | |
| 119 final Uri uri; | |
| 120 | |
| 121 final List<Update> updates = <Update>[]; | 157 final List<Update> updates = <Update>[]; |
| 122 | 158 |
| 123 final List<FailedUpdate> _failedUpdates = <FailedUpdate>[]; | 159 final List<FailedUpdate> _failedUpdates = <FailedUpdate>[]; |
| 124 | 160 |
| 125 final Set<ElementX> _elementsToInvalidate = new Set<ElementX>(); | 161 final Set<ElementX> _elementsToInvalidate = new Set<ElementX>(); |
| 126 | 162 |
| 127 final Set<ElementX> _removedElements = new Set<ElementX>(); | 163 final Set<ElementX> _removedElements = new Set<ElementX>(); |
| 128 | 164 |
| 129 final Set<ClassElementX> _classesWithSchemaChanges = | 165 final Set<ClassElementX> _classesWithSchemaChanges = |
| 130 new Set<ClassElementX>(); | 166 new Set<ClassElementX>(); |
| 131 | 167 |
| 132 final Set<ClassElementX> _emittedClasses; | 168 final IncrementalCompilerContext _context; |
| 133 | |
| 134 final Set<ClassElementX> _directlyInstantiatedClasses; | |
| 135 | |
| 136 final Set<ConstantValue> _compiledConstants; | |
| 137 | 169 |
| 138 bool _hasComputedNeeds = false; | 170 bool _hasComputedNeeds = false; |
| 139 | 171 |
| 172 bool _hasCapturedCompilerState = false; | |
| 173 | |
| 140 LibraryUpdater( | 174 LibraryUpdater( |
| 141 Compiler compiler, | 175 this.compiler, |
| 142 this.inputProvider, | 176 this.inputProvider, |
| 143 this.uri, | |
| 144 this.logTime, | 177 this.logTime, |
| 145 this.logVerbose) | 178 this.logVerbose, |
| 146 : this.compiler = compiler, | 179 this._context) { |
| 147 _emittedClasses = _getEmittedClasses(compiler), | 180 // TODO(ahe): Would like to remove this from the constructor. However, the |
| 148 _directlyInstantiatedClasses = | 181 // state must be captured before calling [reuseCompiler]. |
| 149 _getDirectlyInstantiatedClasses(compiler), | 182 // Proper solution might be: [reuseCompiler] should not clear the sets that |
| 150 _compiledConstants = _getEmittedConstants(compiler); | 183 // are captured in [IncrementalCompilerContext._captureState]. |
| 184 _ensureCompilerStateCaptured(); | |
| 185 } | |
| 151 | 186 |
| 152 /// Returns the classes emitted by [compiler]. | 187 /// Returns the classes emitted by [compiler]. |
| 153 static Set<ClassElementX> _getEmittedClasses(Compiler compiler) { | 188 Set<ClassElementX> get _emittedClasses => _context._emittedClasses; |
| 154 if (compiler == null) return null; | |
| 155 return new Set.from(compiler.backend.emitter.neededClasses); | |
| 156 } | |
| 157 | 189 |
| 158 /// Returns the directly instantantiated classes seen by [compiler]. | 190 /// Returns the directly instantantiated classes seen by [compiler] (this |
| 159 static Set<ClassElementX> _getDirectlyInstantiatedClasses(Compiler compiler) { | 191 /// includes interfaces and may be different from [_emittedClasses] that only |
| 160 if (compiler == null) return null; | 192 /// includes interfaces used in type tests). |
| 161 return new Set.from(compiler.codegenWorld.directlyInstantiatedClasses); | 193 Set<ClassElementX> get _directlyInstantiatedClasses { |
| 194 return _context._directlyInstantiatedClasses; | |
| 162 } | 195 } |
| 163 | 196 |
| 164 /// Returns the constants emitted by [compiler]. | 197 /// Returns the constants emitted by [compiler]. |
| 165 static Set<ConstantValue> _getEmittedConstants(Compiler compiler) { | 198 Set<ConstantValue> get _compiledConstants => _context._compiledConstants; |
| 166 if (compiler != null) { | |
| 167 List<ConstantValue> constants = | |
| 168 compiler.backend.emitter.outputConstantLists[ | |
| 169 compiler.deferredLoadTask.mainOutputUnit]; | |
| 170 if (constants != null) { | |
| 171 return new Set<ConstantValue>.identity()..addAll(constants); | |
| 172 } | |
| 173 } | |
| 174 return null; | |
| 175 } | |
| 176 | 199 |
| 177 /// When [true], updates must be applied (using [applyUpdates]) before the | 200 /// When [true], updates must be applied (using [applyUpdates]) before the |
| 178 /// [compiler]'s state correctly reflects the updated program. | 201 /// [compiler]'s state correctly reflects the updated program. |
| 179 bool get hasPendingUpdates => !updates.isEmpty; | 202 bool get hasPendingUpdates => !updates.isEmpty; |
| 180 | 203 |
| 181 bool get failed => !_failedUpdates.isEmpty; | 204 bool get failed => !_failedUpdates.isEmpty; |
| 182 | 205 |
| 183 /// Used as tear-off passed to [LibraryLoaderTask.resetAsync]. | 206 /// Used as tear-off passed to [LibraryLoaderTask.resetAsync]. |
| 184 Future<bool> reuseLibrary(LibraryElement library) { | 207 Future<bool> reuseLibrary(LibraryElement library) { |
| 208 _ensureCompilerStateCaptured(); | |
| 185 assert(compiler != null); | 209 assert(compiler != null); |
| 186 if (library.isPlatformLibrary || library.isPackageLibrary) { | 210 if (library.isPlatformLibrary) { |
| 187 logTime('Reusing $library.'); | 211 logTime('Reusing $library (assumed read-only).'); |
| 188 return new Future.value(true); | 212 return new Future.value(true); |
| 189 } else if (library != compiler.mainApp) { | |
| 190 return new Future.value(false); | |
| 191 } | 213 } |
| 192 return inputProvider(uri).then((bytes) { | 214 for (CompilationUnitElementX unit in library.compilationUnits) { |
| 193 return canReuseLibrary(library, bytes); | 215 Uri uri = unit.script.resourceUri; |
| 194 }); | 216 if (_context._uriHasUpdate(uri)) { |
| 217 if (!library.compilationUnits.tail.isEmpty) { | |
| 218 // TODO(ahe): Remove this restriction. | |
| 219 cannotReuse(library, "Multiple compilation units not supported."); | |
| 220 return new Future.value(true); | |
| 221 } | |
| 222 return inputProvider(uri).then((bytes) { | |
| 223 return canReuseLibrary(library, bytes); | |
| 224 }); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 logTime("Reusing $library, source didn't change."); | |
| 229 // Source code of [library] wasn't changed. | |
| 230 return new Future.value(true); | |
| 231 } | |
| 232 | |
| 233 void _ensureCompilerStateCaptured() { | |
| 234 // TODO(ahe): [compiler] shouldn't be null, remove the following line. | |
| 235 if (compiler == null) return; | |
| 236 | |
| 237 if (_hasCapturedCompilerState) return; | |
| 238 _context._captureState(compiler); | |
| 239 _hasCapturedCompilerState = true; | |
| 195 } | 240 } |
| 196 | 241 |
| 197 /// Returns true if [library] can be reused. | 242 /// Returns true if [library] can be reused. |
| 198 /// | 243 /// |
| 199 /// This methods also computes the [updates] (patches) needed to have | 244 /// This methods also computes the [updates] (patches) needed to have |
| 200 /// [library] reflect the modifications in [bytes]. | 245 /// [library] reflect the modifications in [bytes]. |
| 201 bool canReuseLibrary(LibraryElement library, bytes) { | 246 bool canReuseLibrary(LibraryElement library, bytes) { |
| 202 logTime('Attempting to reuse mainApp.'); | 247 logTime('Attempting to reuse ${library}.'); |
| 203 String newSource = bytes is String ? bytes : UTF8.decode(bytes); | 248 String newSource = bytes is String ? bytes : UTF8.decode(bytes); |
| 204 logTime('Decoded UTF8'); | 249 logTime('Decoded UTF8'); |
| 205 | 250 |
| 206 // TODO(ahe): Can't use compiler.mainApp in general. | 251 Uri uri = library.entryCompilationUnit.script.resourceUri; |
| 207 if (false && newSource == compiler.mainApp.compilationUnit.script.text) { | |
| 208 // TODO(ahe): Need to update the compilationUnit's source code when | |
| 209 // doing incremental analysis for this to work. | |
| 210 logTime("Source didn't change"); | |
| 211 return true; | |
| 212 } | |
| 213 | |
| 214 logTime("Source did change"); | |
| 215 Script sourceScript = new Script( | 252 Script sourceScript = new Script( |
| 216 uri, uri, new StringSourceFile('$uri', newSource)); | 253 uri, uri, new StringSourceFile('$uri', newSource)); |
| 217 var dartPrivacyIsBroken = compiler.libraryLoader; | 254 var dartPrivacyIsBroken = compiler.libraryLoader; |
| 218 LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync( | 255 LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync( |
| 219 null, sourceScript, uri); | 256 null, sourceScript, uri); |
| 220 logTime('New library synthesized.'); | 257 logTime('New library synthesized.'); |
| 221 return canReuseScopeContainerElement(library, newLibrary); | 258 return canReuseScopeContainerElement(library, newLibrary); |
| 222 } | 259 } |
| 223 | 260 |
| 224 bool cannotReuse(context, String message) { | 261 bool cannotReuse(context, String message) { |
| (...skipping 1070 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1295 List<String> computeFields(ClassElement cls) { | 1332 List<String> computeFields(ClassElement cls) { |
| 1296 // TODO(ahe): Rewrite for new emitter. | 1333 // TODO(ahe): Rewrite for new emitter. |
| 1297 ClassBuilder builder = new ClassBuilder(cls, namer); | 1334 ClassBuilder builder = new ClassBuilder(cls, namer); |
| 1298 classEmitter.emitFields(cls, builder); | 1335 classEmitter.emitFields(cls, builder); |
| 1299 return builder.fields; | 1336 return builder.fields; |
| 1300 } | 1337 } |
| 1301 } | 1338 } |
| 1302 | 1339 |
| 1303 // TODO(ahe): Remove this method. | 1340 // TODO(ahe): Remove this method. |
| 1304 NO_WARN(x) => x; | 1341 NO_WARN(x) => x; |
| OLD | NEW |