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 |