OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 /// Analysis to determine how to generate code for `LookupMap`s. | 5 /// Analysis to determine how to generate code for `LookupMap`s. |
6 library compiler.src.js_backend.lookup_map_analysis; | 6 library compiler.src.js_backend.lookup_map_analysis; |
7 | 7 |
8 import 'package:pub_semver/pub_semver.dart'; | 8 import 'package:pub_semver/pub_semver.dart'; |
9 | 9 |
10 import '../common.dart'; | 10 import '../common.dart'; |
11 import '../common_elements.dart'; | 11 import '../common_elements.dart'; |
12 import '../common/backend_api.dart'; | 12 import '../common/backend_api.dart'; |
13 import '../compile_time_constants.dart'; | |
14 import '../constants/constant_system.dart'; | |
13 import '../constants/values.dart' | 15 import '../constants/values.dart' |
14 show | 16 show |
15 ConstantValue, | 17 ConstantValue, |
16 ConstructedConstantValue, | 18 ConstructedConstantValue, |
17 ListConstantValue, | 19 ListConstantValue, |
18 NullConstantValue, | 20 NullConstantValue, |
19 StringConstantValue, | 21 StringConstantValue, |
20 TypeConstantValue; | 22 TypeConstantValue; |
21 import '../elements/elements.dart' show ClassElement, FieldElement; | 23 import '../elements/elements.dart' show ClassElement, FieldElement; |
22 import '../elements/entities.dart'; | 24 import '../elements/entities.dart'; |
23 import '../elements/resolution_types.dart' show ResolutionInterfaceType; | 25 import '../elements/resolution_types.dart' show ResolutionInterfaceType; |
24 import '../options.dart'; | 26 import '../options.dart'; |
25 import '../universe/use.dart' show ConstantUse, StaticUse; | 27 import '../universe/use.dart' show ConstantUse, StaticUse; |
26 import '../universe/world_impact.dart' | 28 import '../universe/world_impact.dart' |
27 show WorldImpact, StagedWorldImpactBuilder; | 29 show WorldImpact, StagedWorldImpactBuilder; |
28 import 'backend.dart' show JavaScriptBackend; | |
29 import 'backend_helpers.dart'; | 30 import 'backend_helpers.dart'; |
30 | 31 |
31 /// Lookup map handling for resolution. | 32 /// Lookup map handling for resolution. |
32 class LookupMapLibraryAccess { | 33 class LookupMapLibraryAccess { |
33 static final Uri PACKAGE_LOOKUP_MAP = | 34 static final Uri PACKAGE_LOOKUP_MAP = |
34 new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart'); | 35 new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart'); |
35 | 36 |
36 /// Reference the diagnostic reporting system for logging and reporting issues | 37 /// Reference the diagnostic reporting system for logging and reporting issues |
37 /// to the end-user. | 38 /// to the end-user. |
38 final DiagnosticReporter _reporter; | 39 final DiagnosticReporter _reporter; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
106 /// | 107 /// |
107 /// * Include all generic-type arguments, if the program uses type | 108 /// * Include all generic-type arguments, if the program uses type |
108 /// variables in expressions such as `class A<T> { Type get extract => T }`. | 109 /// variables in expressions such as `class A<T> { Type get extract => T }`. |
109 /// | 110 /// |
110 // TODO(sigmund): add support for const expressions, currently this | 111 // TODO(sigmund): add support for const expressions, currently this |
111 // implementation only supports Type literals. To support const expressions we | 112 // implementation only supports Type literals. To support const expressions we |
112 // need to change some of the invariants below (e.g. we can no longer use the | 113 // need to change some of the invariants below (e.g. we can no longer use the |
113 // ClassElement of a type to refer to keys we need to discover). | 114 // ClassElement of a type to refer to keys we need to discover). |
114 // TODO(sigmund): detect uses of mirrors | 115 // TODO(sigmund): detect uses of mirrors |
115 class LookupMapAnalysis { | 116 class LookupMapAnalysis { |
117 const LookupMapAnalysis._(); | |
118 | |
119 factory LookupMapAnalysis( | |
120 DiagnosticReporter reporter, | |
121 ConstantSystem constantSystem, | |
122 ConstantEnvironment constants, | |
123 ElementEnvironment elementEnvironment, | |
124 CommonElements commonElements, | |
125 BackendHelpers helpers, | |
126 BackendClasses backendClasses, | |
127 LookupMapLibraryAccess analysis) { | |
128 /// Checks if the version of lookup_map is valid, and if so, enable this | |
129 /// analysis during codegen. | |
130 FieldElement lookupMapVersionVariable = analysis.lookupMapVersionVariable; | |
131 if (lookupMapVersionVariable == null) return const LookupMapAnalysis._(); | |
132 | |
133 // At this point, the lookupMapVersionVariable should be resolved and it's | |
134 // constant value should be available. | |
135 StringConstantValue value = | |
136 constants.getConstantValue(lookupMapVersionVariable.constant); | |
137 if (value == null) { | |
138 reporter.reportHintMessage(lookupMapVersionVariable, | |
139 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP); | |
140 return const LookupMapAnalysis._(); | |
141 } | |
142 | |
143 // TODO(sigmund): add proper version resolution using the pub_semver package | |
144 // when we introduce the next version. | |
145 Version version; | |
146 try { | |
147 version = new Version.parse(value.primitiveValue.slowToString()); | |
148 } catch (e) {} | |
149 | |
150 if (version == null || !_validLookupMapVersionConstraint.allows(version)) { | |
151 reporter.reportHintMessage(lookupMapVersionVariable, | |
152 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP); | |
153 return const LookupMapAnalysis._(); | |
154 } | |
155 | |
156 ClassEntity typeLookupMapClass = | |
157 elementEnvironment.lookupClass(analysis.lookupMapLibrary, 'LookupMap'); | |
158 FieldElement entriesField = | |
159 elementEnvironment.lookupClassMember(typeLookupMapClass, '_entries'); | |
160 FieldElement keyField = | |
161 elementEnvironment.lookupClassMember(typeLookupMapClass, '_key'); | |
162 FieldElement valueField = | |
163 elementEnvironment.lookupClassMember(typeLookupMapClass, '_value'); | |
164 // TODO(sigmund): Maybe inline nested maps to make the output code smaller? | |
165 | |
166 return new LookupMapAnalysisImpl(constantSystem, commonElements, helpers, | |
167 backendClasses, entriesField, keyField, valueField, typeLookupMapClass); | |
168 } | |
169 | |
170 /// Compute the [WorldImpact] for the constants registered since last flush. | |
171 WorldImpact flush() => const WorldImpact(); | |
172 | |
173 /// Whether [constant] is an instance of a `LookupMap`. | |
174 bool isLookupMap(ConstantValue constant) => false; | |
175 | |
176 /// Registers an instance of a lookup-map with the analysis. | |
177 void registerLookupMapReference(ConstantValue lookupMap) {} | |
178 | |
179 /// Callback from the enqueuer, invoked when [element] is instantiated. | |
180 void registerInstantiatedClass(ClassElement element) {} | |
181 | |
182 /// Callback from the enqueuer, invoked when [type] is instantiated. | |
183 void registerInstantiatedType(ResolutionInterfaceType type) {} | |
184 | |
185 /// Callback from the codegen enqueuer, invoked when a constant (which is | |
186 /// possibly a const key or a type literal) is used in the program. | |
187 void registerTypeConstant(ClassElement element) {} | |
188 | |
189 void registerConstantKey(ConstantValue constant) {} | |
190 | |
191 void logSummary(void log(String message)) {} | |
192 | |
193 void onQueueClosed() {} | |
194 } | |
195 | |
196 class LookupMapAnalysisImpl implements LookupMapAnalysis { | |
Siggi Cherem (dart-lang)
2017/03/14 05:47:00
maybe give it a private name instead?
Johnni Winther
2017/03/15 13:38:00
Done.
| |
116 static final Uri PACKAGE_LOOKUP_MAP = | 197 static final Uri PACKAGE_LOOKUP_MAP = |
117 new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart'); | 198 new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart'); |
118 | 199 |
119 /// Reference to [JavaScriptBackend] to be able to enqueue work when we | 200 final ConstantSystem _constantSystem; |
120 /// discover that a key in a map is potentially used. | |
121 final JavaScriptBackend _backend; | |
122 | |
123 final CompilerOptions _options; | |
124 | |
125 /// Reference the diagnostic reporting system for logging and reporting issues | |
126 /// to the end-user. | |
127 final DiagnosticReporter _reporter; | |
128 | |
129 final ElementEnvironment _elementEnvironment; | |
130 | 201 |
131 final CommonElements _commonElements; | 202 final CommonElements _commonElements; |
132 | 203 |
133 final BackendHelpers _helpers; | 204 final BackendHelpers _helpers; |
134 | 205 |
135 final BackendClasses _backendClasses; | 206 final BackendClasses _backendClasses; |
136 | 207 |
137 /// The resolved [ClassElement] associated with `LookupMap`. | 208 /// The resolved [ClassElement] associated with `LookupMap`. |
138 ClassElement typeLookupMapClass; | 209 final ClassElement _typeLookupMapClass; |
139 | 210 |
140 /// The resolved [FieldElement] for `LookupMap._entries`. | 211 /// The resolved [FieldElement] for `LookupMap._entries`. |
141 FieldElement entriesField; | 212 final FieldElement _entriesField; |
142 | 213 |
143 /// The resolved [FieldElement] for `LookupMap._key`. | 214 /// The resolved [FieldElement] for `LookupMap._key`. |
144 FieldElement keyField; | 215 final FieldElement _keyField; |
145 | 216 |
146 /// The resolved [FieldElement] for `LookupMap._value`. | 217 /// The resolved [FieldElement] for `LookupMap._value`. |
147 FieldElement valueField; | 218 final FieldElement _valueField; |
148 | 219 |
149 /// Constant instances of `LookupMap` and information about them tracked by | 220 /// Constant instances of `LookupMap` and information about them tracked by |
150 /// this analysis. | 221 /// this analysis. |
151 final Map<ConstantValue, _LookupMapInfo> _lookupMaps = {}; | 222 final Map<ConstantValue, _LookupMapInfo> _lookupMaps = {}; |
152 | 223 |
153 /// Keys that we have discovered to be in use in the program. | 224 /// Keys that we have discovered to be in use in the program. |
154 final _inUse = new Set<ConstantValue>(); | 225 final _inUse = new Set<ConstantValue>(); |
155 | 226 |
156 /// Internal helper to memoize the mapping between class elements and their | 227 /// Internal helper to memoize the mapping between class elements and their |
157 /// corresponding type constants. | 228 /// corresponding type constants. |
158 final _typeConstants = <ClassElement, TypeConstantValue>{}; | 229 final _typeConstants = <ClassElement, TypeConstantValue>{}; |
159 | 230 |
160 /// Internal helper to memoize which classes (ignoring Type) override equals. | 231 /// Internal helper to memoize which classes (ignoring Type) override equals. |
161 /// | 232 /// |
162 /// Const keys of these types will not be tree-shaken because we can't | 233 /// Const keys of these types will not be tree-shaken because we can't |
163 /// statically guarantee that the program doesn't produce an equivalent key at | 234 /// statically guarantee that the program doesn't produce an equivalent key at |
164 /// runtime. Technically if we limit lookup-maps to check for identical keys, | 235 /// runtime. Technically if we limit lookup-maps to check for identical keys, |
165 /// we could allow const instances of these types. However, we internally use | 236 /// we could allow const instances of these types. However, we internally use |
166 /// a hash map within lookup-maps today, so we need this restriction. | 237 /// a hash map within lookup-maps today, so we need this restriction. |
167 final _typesWithEquals = <ClassElement, bool>{}; | 238 final _typesWithEquals = <ClassElement, bool>{}; |
168 | 239 |
169 /// Pending work to do if we discover that a new key is in use. For each key | 240 /// Pending work to do if we discover that a new key is in use. For each key |
170 /// that we haven't seen, we record the list of lookup-maps that contain an | 241 /// that we haven't seen, we record the list of lookup-maps that contain an |
171 /// entry with that key. | 242 /// entry with that key. |
172 final _pending = <ConstantValue, List<_LookupMapInfo>>{}; | 243 final _pending = <ConstantValue, List<_LookupMapInfo>>{}; |
173 | 244 |
174 final StagedWorldImpactBuilder _impactBuilder = | 245 final StagedWorldImpactBuilder _impactBuilder = |
175 new StagedWorldImpactBuilder(); | 246 new StagedWorldImpactBuilder(); |
176 | 247 |
177 /// Whether the backend is currently processing the codegen queue. | 248 LookupMapAnalysisImpl( |
178 bool _inCodegen = false; | 249 this._constantSystem, |
179 | |
180 LookupMapAnalysis( | |
181 this._backend, | |
182 this._options, | |
183 this._reporter, | |
184 this._elementEnvironment, | |
185 this._commonElements, | 250 this._commonElements, |
186 this._helpers, | 251 this._helpers, |
187 this._backendClasses); | 252 this._backendClasses, |
253 this._entriesField, | |
254 this._keyField, | |
255 this._valueField, | |
256 this._typeLookupMapClass); | |
188 | 257 |
189 /// Compute the [WorldImpact] for the constants registered since last flush. | 258 /// Compute the [WorldImpact] for the constants registered since last flush. |
190 WorldImpact flush() { | 259 WorldImpact flush() { |
191 return _impactBuilder.flush(); | 260 return _impactBuilder.flush(); |
192 } | 261 } |
193 | 262 |
194 /// Whether this analysis and optimization is enabled. | |
195 bool get _isEnabled { | |
196 // `lookupMap==off` kept here to make it easy to test disabling this feature | |
197 if (const String.fromEnvironment('lookupMap') == 'off') return false; | |
198 return typeLookupMapClass != null; | |
199 } | |
200 | |
201 /// Checks if the version of lookup_map is valid, and if so, enable this | |
202 /// analysis during codegen. | |
203 void onCodegenStart(LookupMapLibraryAccess analysis) { | |
204 _inCodegen = true; | |
205 FieldElement lookupMapVersionVariable = analysis.lookupMapVersionVariable; | |
206 if (lookupMapVersionVariable == null) return; | |
207 | |
208 // At this point, the lookupMapVersionVariable should be resolved and it's | |
209 // constant value should be available. | |
210 StringConstantValue value = | |
211 _backend.constants.getConstantValue(lookupMapVersionVariable.constant); | |
212 if (value == null) { | |
213 _reporter.reportHintMessage(lookupMapVersionVariable, | |
214 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP); | |
215 return; | |
216 } | |
217 | |
218 // TODO(sigmund): add proper version resolution using the pub_semver package | |
219 // when we introduce the next version. | |
220 Version version; | |
221 try { | |
222 version = new Version.parse(value.primitiveValue.slowToString()); | |
223 } catch (e) {} | |
224 | |
225 if (version == null || !_validLookupMapVersionConstraint.allows(version)) { | |
226 _reporter.reportHintMessage(lookupMapVersionVariable, | |
227 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP); | |
228 return; | |
229 } | |
230 | |
231 ClassEntity cls = | |
232 _elementEnvironment.lookupClass(analysis.lookupMapLibrary, 'LookupMap'); | |
233 entriesField = _elementEnvironment.lookupClassMember(cls, '_entries'); | |
234 keyField = _elementEnvironment.lookupClassMember(cls, '_key'); | |
235 valueField = _elementEnvironment.lookupClassMember(cls, '_value'); | |
236 // TODO(sigmund): Maybe inline nested maps to make the output code smaller? | |
237 typeLookupMapClass = cls; | |
238 } | |
239 | |
240 /// Whether [constant] is an instance of a `LookupMap`. | 263 /// Whether [constant] is an instance of a `LookupMap`. |
241 bool isLookupMap(ConstantValue constant) { | 264 bool isLookupMap(ConstantValue constant) { |
242 if (_isEnabled && constant is ConstructedConstantValue) { | 265 if (constant is ConstructedConstantValue) { |
243 ResolutionInterfaceType type = constant.type; | 266 ResolutionInterfaceType type = constant.type; |
244 return type.element.isSubclassOf(typeLookupMapClass); | 267 return type.element.isSubclassOf(_typeLookupMapClass); |
245 } | 268 } |
246 return false; | 269 return false; |
247 } | 270 } |
248 | 271 |
249 /// Registers an instance of a lookup-map with the analysis. | 272 /// Registers an instance of a lookup-map with the analysis. |
250 void registerLookupMapReference(ConstantValue lookupMap) { | 273 void registerLookupMapReference(ConstantValue lookupMap) { |
251 if (!_isEnabled || !_inCodegen) return; | |
252 assert(isLookupMap(lookupMap)); | 274 assert(isLookupMap(lookupMap)); |
253 _lookupMaps.putIfAbsent( | 275 _lookupMaps.putIfAbsent( |
254 lookupMap, () => new _LookupMapInfo(lookupMap, this).._updateUsed()); | 276 lookupMap, () => new _LookupMapInfo(lookupMap, this).._updateUsed()); |
255 } | 277 } |
256 | 278 |
257 /// Whether [key] is a constant value whose type overrides equals. | 279 /// Whether [key] is a constant value whose type overrides equals. |
258 bool _overridesEquals(ConstantValue key) { | 280 bool _overridesEquals(ConstantValue key) { |
259 if (key is ConstructedConstantValue) { | 281 if (key is ConstructedConstantValue) { |
260 ClassElement element = key.type.element; | 282 ClassElement element = key.type.element; |
261 return _typesWithEquals.putIfAbsent( | 283 return _typesWithEquals.putIfAbsent( |
262 element, () => !element.lookupMember('==').enclosingClass.isObject); | 284 element, () => !element.lookupMember('==').enclosingClass.isObject); |
263 } | 285 } |
264 return false; | 286 return false; |
265 } | 287 } |
266 | 288 |
267 /// Whether we need to preserve [key]. This is true for keys that are not | 289 /// Whether we need to preserve [key]. This is true for keys that are not |
268 /// candidates for tree-shaking in the first place (primitives and non-type | 290 /// candidates for tree-shaking in the first place (primitives and non-type |
269 /// const values overriding equals) and keys that we have seen in the program. | 291 /// const values overriding equals) and keys that we have seen in the program. |
270 bool _shouldKeep(ConstantValue key) => | 292 bool _shouldKeep(ConstantValue key) => |
271 key.isPrimitive || _inUse.contains(key) || _overridesEquals(key); | 293 key.isPrimitive || _inUse.contains(key) || _overridesEquals(key); |
272 | 294 |
273 void _addClassUse(ClassElement cls) { | 295 void _addClassUse(ClassElement cls) { |
274 ConstantValue key = _typeConstants.putIfAbsent( | 296 ConstantValue key = _typeConstants.putIfAbsent( |
275 cls, | 297 cls, |
276 () => _backend.constantSystem | 298 () => _constantSystem.createType( |
277 .createType(_commonElements, _backendClasses, cls.rawType)); | 299 _commonElements, _backendClasses, cls.rawType)); |
278 _addUse(key); | 300 _addUse(key); |
279 } | 301 } |
280 | 302 |
281 /// Record that [key] is used and update every lookup map that contains it. | 303 /// Record that [key] is used and update every lookup map that contains it. |
282 void _addUse(ConstantValue key) { | 304 void _addUse(ConstantValue key) { |
283 if (_inUse.add(key)) { | 305 if (_inUse.add(key)) { |
284 _pending[key]?.forEach((info) => info._markUsed(key)); | 306 _pending[key]?.forEach((info) => info._markUsed(key)); |
285 _pending.remove(key); | 307 _pending.remove(key); |
286 } | 308 } |
287 } | 309 } |
288 | 310 |
289 /// If [key] is a type, cache it in [_typeConstants]. | 311 /// If [key] is a type, cache it in [_typeConstants]. |
290 _registerTypeKey(ConstantValue key) { | 312 _registerTypeKey(ConstantValue key) { |
291 if (key is TypeConstantValue && | 313 if (key is TypeConstantValue && |
292 key.representedType is ResolutionInterfaceType) { | 314 key.representedType is ResolutionInterfaceType) { |
293 ResolutionInterfaceType type = key.representedType; | 315 ResolutionInterfaceType type = key.representedType; |
294 _typeConstants[type.element] = key; | 316 _typeConstants[type.element] = key; |
295 } else { | 317 } else { |
296 // TODO(sigmund): report error? | 318 // TODO(sigmund): report error? |
297 } | 319 } |
298 } | 320 } |
299 | 321 |
300 /// Callback from the enqueuer, invoked when [element] is instantiated. | 322 /// Callback from the enqueuer, invoked when [element] is instantiated. |
301 void registerInstantiatedClass(ClassElement element) { | 323 void registerInstantiatedClass(ClassElement element) { |
302 if (!_isEnabled || !_inCodegen) return; | |
303 // TODO(sigmund): only add if .runtimeType is ever used | 324 // TODO(sigmund): only add if .runtimeType is ever used |
304 _addClassUse(element); | 325 _addClassUse(element); |
305 } | 326 } |
306 | 327 |
307 /// Callback from the enqueuer, invoked when [type] is instantiated. | 328 /// Callback from the enqueuer, invoked when [type] is instantiated. |
308 void registerInstantiatedType(ResolutionInterfaceType type) { | 329 void registerInstantiatedType(ResolutionInterfaceType type) { |
309 if (!_isEnabled || !_inCodegen) return; | |
310 // TODO(sigmund): only add if .runtimeType is ever used | 330 // TODO(sigmund): only add if .runtimeType is ever used |
311 _addClassUse(type.element); | 331 _addClassUse(type.element); |
312 // TODO(sigmund): only do this when type-argument expressions are used? | 332 // TODO(sigmund): only do this when type-argument expressions are used? |
313 _addGenerics(type); | 333 _addGenerics(type); |
314 } | 334 } |
315 | 335 |
316 /// Records generic type arguments in [type], in case they are retrieved and | 336 /// Records generic type arguments in [type], in case they are retrieved and |
317 /// returned using a type-argument expression. | 337 /// returned using a type-argument expression. |
318 void _addGenerics(ResolutionInterfaceType type) { | 338 void _addGenerics(ResolutionInterfaceType type) { |
319 if (!type.isGeneric) return; | 339 if (!type.isGeneric) return; |
320 for (var arg in type.typeArguments) { | 340 for (var arg in type.typeArguments) { |
321 if (arg is ResolutionInterfaceType) { | 341 if (arg is ResolutionInterfaceType) { |
322 _addClassUse(arg.element); | 342 _addClassUse(arg.element); |
323 // Note: this call was needed to generate correct code for | 343 // Note: this call was needed to generate correct code for |
324 // type_lookup_map/generic_type_test | 344 // type_lookup_map/generic_type_test |
325 // TODO(sigmund): can we get rid of this? | 345 // TODO(sigmund): can we get rid of this? |
326 _impactBuilder.registerStaticUse(new StaticUse.staticInvoke( | 346 _impactBuilder.registerStaticUse(new StaticUse.staticInvoke( |
327 // TODO(johnniwinther): Find the right [CallStructure]. | 347 // TODO(johnniwinther): Find the right [CallStructure]. |
328 _helpers.createRuntimeType, | 348 _helpers.createRuntimeType, |
329 null)); | 349 null)); |
330 _addGenerics(arg); | 350 _addGenerics(arg); |
331 } | 351 } |
332 } | 352 } |
333 } | 353 } |
334 | 354 |
335 /// Callback from the codegen enqueuer, invoked when a constant (which is | 355 /// Callback from the codegen enqueuer, invoked when a constant (which is |
336 /// possibly a const key or a type literal) is used in the program. | 356 /// possibly a const key or a type literal) is used in the program. |
337 void registerTypeConstant(ClassElement element) { | 357 void registerTypeConstant(ClassElement element) { |
338 if (!_isEnabled || !_inCodegen) return; | |
339 _addClassUse(element); | 358 _addClassUse(element); |
340 } | 359 } |
341 | 360 |
342 void registerConstantKey(ConstantValue constant) { | 361 void registerConstantKey(ConstantValue constant) { |
343 if (!_isEnabled || !_inCodegen) return; | |
344 if (constant.isPrimitive || _overridesEquals(constant)) return; | 362 if (constant.isPrimitive || _overridesEquals(constant)) return; |
345 _addUse(constant); | 363 _addUse(constant); |
346 } | 364 } |
347 | 365 |
366 void logSummary(void log(String message)) { | |
367 // When --verbose is passed, we show the total number and set of keys that | |
368 // were tree-shaken from lookup maps. | |
369 var sb = new StringBuffer(); | |
370 int count = 0; | |
371 for (var info in _lookupMaps.values) { | |
372 for (var key in info.unusedEntries.keys) { | |
373 if (count != 0) sb.write(','); | |
374 sb.write(key.toDartText()); | |
375 count++; | |
376 } | |
377 } | |
378 log(count == 0 | |
379 ? 'lookup-map: nothing was tree-shaken' | |
380 : 'lookup-map: found $count unused keys ($sb)'); | |
381 } | |
382 | |
348 /// Callback from the backend, invoked when reaching the end of the enqueuing | 383 /// Callback from the backend, invoked when reaching the end of the enqueuing |
349 /// process, but before emitting the code. At this moment we have discovered | 384 /// process, but before emitting the code. At this moment we have discovered |
350 /// all types used in the program and we can tree-shake anything that is | 385 /// all types used in the program and we can tree-shake anything that is |
351 /// unused. | 386 /// unused. |
352 void onQueueClosed() { | 387 void onQueueClosed() { |
353 if (!_isEnabled || !_inCodegen) return; | |
354 | |
355 _lookupMaps.values.forEach((info) { | 388 _lookupMaps.values.forEach((info) { |
356 assert(!info.emitted); | 389 assert(!info.emitted); |
357 info.emitted = true; | 390 info.emitted = true; |
358 info._prepareForEmission(); | 391 info._prepareForEmission(); |
359 }); | 392 }); |
360 | 393 |
361 // When --verbose is passed, we show the total number and set of keys that | |
362 // were tree-shaken from lookup maps. | |
363 if (_options.verbose) { | |
364 var sb = new StringBuffer(); | |
365 int count = 0; | |
366 for (var info in _lookupMaps.values) { | |
367 for (var key in info.unusedEntries.keys) { | |
368 if (count != 0) sb.write(','); | |
369 sb.write(key.toDartText()); | |
370 count++; | |
371 } | |
372 } | |
373 _reporter.log(count == 0 | |
374 ? 'lookup-map: nothing was tree-shaken' | |
375 : 'lookup-map: found $count unused keys ($sb)'); | |
376 } | |
377 | |
378 // Release resources. | 394 // Release resources. |
379 _lookupMaps.clear(); | 395 _lookupMaps.clear(); |
380 _pending.clear(); | 396 _pending.clear(); |
381 _inUse.clear(); | 397 _inUse.clear(); |
382 } | 398 } |
383 } | 399 } |
384 | 400 |
385 /// Internal information about the entries on a lookup-map. | 401 /// Internal information about the entries on a lookup-map. |
386 class _LookupMapInfo { | 402 class _LookupMapInfo { |
387 /// The original reference to the constant value. | 403 /// The original reference to the constant value. |
388 /// | 404 /// |
389 /// This reference will be mutated in place to remove it's entries when the | 405 /// This reference will be mutated in place to remove it's entries when the |
390 /// map is first seen during codegen, and to restore them (or a subset of | 406 /// map is first seen during codegen, and to restore them (or a subset of |
391 /// them) when we have finished discovering which entries are used. This has | 407 /// them) when we have finished discovering which entries are used. This has |
392 /// the side-effect that `orignal.getDependencies()` will be empty during | 408 /// the side-effect that `orignal.getDependencies()` will be empty during |
393 /// most of codegen until we are ready to emit the constants. However, | 409 /// most of codegen until we are ready to emit the constants. However, |
394 /// restoring the entries before emitting code lets us keep the emitter logic | 410 /// restoring the entries before emitting code lets us keep the emitter logic |
395 /// agnostic of this optimization. | 411 /// agnostic of this optimization. |
396 final ConstructedConstantValue original; | 412 final ConstructedConstantValue original; |
397 | 413 |
398 /// Reference to the lookup map analysis to be able to refer to data shared | 414 /// Reference to the lookup map analysis to be able to refer to data shared |
399 /// accross infos. | 415 /// accross infos. |
400 final LookupMapAnalysis analysis; | 416 final LookupMapAnalysisImpl analysis; |
401 | 417 |
402 /// Whether we have already emitted this constant. | 418 /// Whether we have already emitted this constant. |
403 bool emitted = false; | 419 bool emitted = false; |
404 | 420 |
405 /// Whether the `LookupMap` constant was built using the `LookupMap.pair` | 421 /// Whether the `LookupMap` constant was built using the `LookupMap.pair` |
406 /// constructor. | 422 /// constructor. |
407 bool singlePair; | 423 bool singlePair; |
408 | 424 |
409 /// Entries in the lookup map whose keys have not been seen in the rest of the | 425 /// Entries in the lookup map whose keys have not been seen in the rest of the |
410 /// program. | 426 /// program. |
411 Map<ConstantValue, ConstantValue> unusedEntries = | 427 Map<ConstantValue, ConstantValue> unusedEntries = |
412 <ConstantValue, ConstantValue>{}; | 428 <ConstantValue, ConstantValue>{}; |
413 | 429 |
414 /// Entries that have been used, and thus will be part of the generated code. | 430 /// Entries that have been used, and thus will be part of the generated code. |
415 Map<ConstantValue, ConstantValue> usedEntries = | 431 Map<ConstantValue, ConstantValue> usedEntries = |
416 <ConstantValue, ConstantValue>{}; | 432 <ConstantValue, ConstantValue>{}; |
417 | 433 |
418 /// Creates and initializes the information containing all keys of the | 434 /// Creates and initializes the information containing all keys of the |
419 /// original map marked as unused. | 435 /// original map marked as unused. |
420 _LookupMapInfo(this.original, this.analysis) { | 436 _LookupMapInfo(this.original, this.analysis) { |
421 ConstantValue key = original.fields[analysis.keyField]; | 437 ConstantValue key = original.fields[analysis._keyField]; |
422 singlePair = !key.isNull; | 438 singlePair = !key.isNull; |
423 | 439 |
424 if (singlePair) { | 440 if (singlePair) { |
425 unusedEntries[key] = original.fields[analysis.valueField]; | 441 unusedEntries[key] = original.fields[analysis._valueField]; |
426 | 442 |
427 // Note: we modify the constant in-place, see comment in [original]. | 443 // Note: we modify the constant in-place, see comment in [original]. |
428 original.fields[analysis.keyField] = new NullConstantValue(); | 444 original.fields[analysis._keyField] = new NullConstantValue(); |
429 original.fields[analysis.valueField] = new NullConstantValue(); | 445 original.fields[analysis._valueField] = new NullConstantValue(); |
430 } else { | 446 } else { |
431 ListConstantValue list = original.fields[analysis.entriesField]; | 447 ListConstantValue list = original.fields[analysis._entriesField]; |
432 List<ConstantValue> keyValuePairs = list.entries; | 448 List<ConstantValue> keyValuePairs = list.entries; |
433 for (int i = 0; i < keyValuePairs.length; i += 2) { | 449 for (int i = 0; i < keyValuePairs.length; i += 2) { |
434 ConstantValue key = keyValuePairs[i]; | 450 ConstantValue key = keyValuePairs[i]; |
435 unusedEntries[key] = keyValuePairs[i + 1]; | 451 unusedEntries[key] = keyValuePairs[i + 1]; |
436 } | 452 } |
437 | 453 |
438 // Note: we modify the constant in-place, see comment in [original]. | 454 // Note: we modify the constant in-place, see comment in [original]. |
439 original.fields[analysis.entriesField] = | 455 original.fields[analysis._entriesField] = |
440 new ListConstantValue(list.type, []); | 456 new ListConstantValue(list.type, []); |
441 } | 457 } |
442 } | 458 } |
443 | 459 |
444 /// Check every key in unusedEntries and mark it as used if the analysis has | 460 /// Check every key in unusedEntries and mark it as used if the analysis has |
445 /// already discovered them. This is meant to be called once to finalize | 461 /// already discovered them. This is meant to be called once to finalize |
446 /// initialization after constructing an instance of this class. Afterwards, | 462 /// initialization after constructing an instance of this class. Afterwards, |
447 /// we call [_markUsed] on each individual key as it gets discovered. | 463 /// we call [_markUsed] on each individual key as it gets discovered. |
448 void _updateUsed() { | 464 void _updateUsed() { |
449 // Note: we call toList because `_markUsed` modifies the map. | 465 // Note: we call toList because `_markUsed` modifies the map. |
(...skipping 14 matching lines...) Expand all Loading... | |
464 assert(unusedEntries.containsKey(key)); | 480 assert(unusedEntries.containsKey(key)); |
465 assert(!usedEntries.containsKey(key)); | 481 assert(!usedEntries.containsKey(key)); |
466 ConstantValue constant = unusedEntries.remove(key); | 482 ConstantValue constant = unusedEntries.remove(key); |
467 usedEntries[key] = constant; | 483 usedEntries[key] = constant; |
468 analysis._impactBuilder | 484 analysis._impactBuilder |
469 .registerConstantUse(new ConstantUse.lookupMap(constant)); | 485 .registerConstantUse(new ConstantUse.lookupMap(constant)); |
470 } | 486 } |
471 | 487 |
472 /// Restores [original] to contain all of the entries marked as possibly used. | 488 /// Restores [original] to contain all of the entries marked as possibly used. |
473 void _prepareForEmission() { | 489 void _prepareForEmission() { |
474 ListConstantValue originalEntries = original.fields[analysis.entriesField]; | 490 ListConstantValue originalEntries = original.fields[analysis._entriesField]; |
475 ResolutionInterfaceType listType = originalEntries.type; | 491 ResolutionInterfaceType listType = originalEntries.type; |
476 List<ConstantValue> keyValuePairs = <ConstantValue>[]; | 492 List<ConstantValue> keyValuePairs = <ConstantValue>[]; |
477 usedEntries.forEach((key, value) { | 493 usedEntries.forEach((key, value) { |
478 keyValuePairs.add(key); | 494 keyValuePairs.add(key); |
479 keyValuePairs.add(value); | 495 keyValuePairs.add(value); |
480 }); | 496 }); |
481 | 497 |
482 // Note: we are restoring the entries here, see comment in [original]. | 498 // Note: we are restoring the entries here, see comment in [original]. |
483 if (singlePair) { | 499 if (singlePair) { |
484 assert(keyValuePairs.length == 0 || keyValuePairs.length == 2); | 500 assert(keyValuePairs.length == 0 || keyValuePairs.length == 2); |
485 if (keyValuePairs.length == 2) { | 501 if (keyValuePairs.length == 2) { |
486 original.fields[analysis.keyField] = keyValuePairs[0]; | 502 original.fields[analysis._keyField] = keyValuePairs[0]; |
487 original.fields[analysis.valueField] = keyValuePairs[1]; | 503 original.fields[analysis._valueField] = keyValuePairs[1]; |
488 } | 504 } |
489 } else { | 505 } else { |
490 original.fields[analysis.entriesField] = | 506 original.fields[analysis._entriesField] = |
491 new ListConstantValue(listType, keyValuePairs); | 507 new ListConstantValue(listType, keyValuePairs); |
492 } | 508 } |
493 } | 509 } |
494 } | 510 } |
495 | 511 |
496 final _validLookupMapVersionConstraint = new VersionConstraint.parse('^0.0.1'); | 512 final _validLookupMapVersionConstraint = new VersionConstraint.parse('^0.0.1'); |
OLD | NEW |