| 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 '../common.dart'; | 8 import '../common.dart'; |
| 9 import '../common/registry.dart' show Registry; | 9 import '../common/registry.dart' show Registry; |
| 10 import '../compiler.dart' show Compiler; | 10 import '../compiler.dart' show Compiler; |
| 11 import '../constants/values.dart' show | 11 import '../constants/values.dart' |
| 12 ConstantValue, | 12 show |
| 13 ConstructedConstantValue, | 13 ConstantValue, |
| 14 ListConstantValue, | 14 ConstructedConstantValue, |
| 15 NullConstantValue, | 15 ListConstantValue, |
| 16 StringConstantValue, | 16 NullConstantValue, |
| 17 TypeConstantValue; | 17 StringConstantValue, |
| 18 TypeConstantValue; |
| 18 import '../dart_types.dart' show DartType; | 19 import '../dart_types.dart' show DartType; |
| 19 import '../elements/elements.dart' show | 20 import '../elements/elements.dart' |
| 20 ClassElement, | 21 show |
| 21 Element, | 22 ClassElement, |
| 22 Elements, | 23 Element, |
| 23 FieldElement, | 24 Elements, |
| 24 FunctionElement, | 25 FieldElement, |
| 25 FunctionSignature, | 26 FunctionElement, |
| 26 LibraryElement, | 27 FunctionSignature, |
| 27 VariableElement; | 28 LibraryElement, |
| 29 VariableElement; |
| 28 import 'js_backend.dart' show JavaScriptBackend; | 30 import 'js_backend.dart' show JavaScriptBackend; |
| 29 import '../dart_types.dart' show DynamicType, InterfaceType; | 31 import '../dart_types.dart' show DynamicType, InterfaceType; |
| 30 import 'package:pub_semver/pub_semver.dart'; | 32 import 'package:pub_semver/pub_semver.dart'; |
| 31 | 33 |
| 32 /// An analysis and optimization to remove unused entries from a `LookupMap`. | 34 /// An analysis and optimization to remove unused entries from a `LookupMap`. |
| 33 /// | 35 /// |
| 34 /// `LookupMaps` are defined in `package:lookup_map/lookup_map.dart`. They are | 36 /// `LookupMaps` are defined in `package:lookup_map/lookup_map.dart`. They are |
| 35 /// simple maps that contain constant expressions as keys, and that only support | 37 /// simple maps that contain constant expressions as keys, and that only support |
| 36 /// the lookup operation. | 38 /// the lookup operation. |
| 37 /// | 39 /// |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 136 } | 138 } |
| 137 | 139 |
| 138 /// Initializes this analysis by providing the resolved library. This is | 140 /// Initializes this analysis by providing the resolved library. This is |
| 139 /// invoked during resolution when the `lookup_map` library is discovered. | 141 /// invoked during resolution when the `lookup_map` library is discovered. |
| 140 void init(LibraryElement library) { | 142 void init(LibraryElement library) { |
| 141 lookupMapLibrary = library; | 143 lookupMapLibrary = library; |
| 142 // We will enable the lookupMapAnalysis as long as we get a known version of | 144 // We will enable the lookupMapAnalysis as long as we get a known version of |
| 143 // the lookup_map package. We otherwise produce a warning. | 145 // the lookup_map package. We otherwise produce a warning. |
| 144 lookupMapVersionVariable = library.implementation.findLocal('_version'); | 146 lookupMapVersionVariable = library.implementation.findLocal('_version'); |
| 145 if (lookupMapVersionVariable == null) { | 147 if (lookupMapVersionVariable == null) { |
| 146 reporter.reportInfo(library, | 148 reporter.reportInfo( |
| 147 MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP); | 149 library, MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP); |
| 148 } else { | 150 } else { |
| 149 backend.compiler.enqueuer.resolution.addToWorkList( | 151 backend.compiler.enqueuer.resolution |
| 150 lookupMapVersionVariable); | 152 .addToWorkList(lookupMapVersionVariable); |
| 151 } | 153 } |
| 152 } | 154 } |
| 153 | 155 |
| 154 /// Checks if the version of lookup_map is valid, and if so, enable this | 156 /// Checks if the version of lookup_map is valid, and if so, enable this |
| 155 /// analysis during codegen. | 157 /// analysis during codegen. |
| 156 void onCodegenStart() { | 158 void onCodegenStart() { |
| 157 _inCodegen = true; | 159 _inCodegen = true; |
| 158 if (lookupMapVersionVariable == null) return; | 160 if (lookupMapVersionVariable == null) return; |
| 159 | 161 |
| 160 // At this point, the lookupMapVersionVariable should be resolved and it's | 162 // At this point, the lookupMapVersionVariable should be resolved and it's |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 192 /// Whether [constant] is an instance of a `LookupMap`. | 194 /// Whether [constant] is an instance of a `LookupMap`. |
| 193 bool isLookupMap(ConstantValue constant) => | 195 bool isLookupMap(ConstantValue constant) => |
| 194 _isEnabled && | 196 _isEnabled && |
| 195 constant is ConstructedConstantValue && | 197 constant is ConstructedConstantValue && |
| 196 constant.type.asRaw().element.isSubclassOf(typeLookupMapClass); | 198 constant.type.asRaw().element.isSubclassOf(typeLookupMapClass); |
| 197 | 199 |
| 198 /// Registers an instance of a lookup-map with the analysis. | 200 /// Registers an instance of a lookup-map with the analysis. |
| 199 void registerLookupMapReference(ConstantValue lookupMap) { | 201 void registerLookupMapReference(ConstantValue lookupMap) { |
| 200 if (!_isEnabled || !_inCodegen) return; | 202 if (!_isEnabled || !_inCodegen) return; |
| 201 assert(isLookupMap(lookupMap)); | 203 assert(isLookupMap(lookupMap)); |
| 202 _lookupMaps.putIfAbsent(lookupMap, | 204 _lookupMaps.putIfAbsent( |
| 203 () => new _LookupMapInfo(lookupMap, this).._updateUsed()); | 205 lookupMap, () => new _LookupMapInfo(lookupMap, this).._updateUsed()); |
| 204 } | 206 } |
| 205 | 207 |
| 206 /// Whether [key] is a constant value whose type overrides equals. | 208 /// Whether [key] is a constant value whose type overrides equals. |
| 207 bool _overridesEquals(ConstantValue key) { | 209 bool _overridesEquals(ConstantValue key) { |
| 208 if (key is ConstructedConstantValue) { | 210 if (key is ConstructedConstantValue) { |
| 209 ClassElement element = key.type.element; | 211 ClassElement element = key.type.element; |
| 210 return _typesWithEquals.putIfAbsent(element, () => | 212 return _typesWithEquals.putIfAbsent( |
| 211 !element.lookupMember('==').enclosingClass.isObject); | 213 element, () => !element.lookupMember('==').enclosingClass.isObject); |
| 212 } | 214 } |
| 213 return false; | 215 return false; |
| 214 } | 216 } |
| 215 | 217 |
| 216 /// Whether we need to preserve [key]. This is true for keys that are not | 218 /// Whether we need to preserve [key]. This is true for keys that are not |
| 217 /// candidates for tree-shaking in the first place (primitives and non-type | 219 /// candidates for tree-shaking in the first place (primitives and non-type |
| 218 /// const values overriding equals) and keys that we have seen in the program. | 220 /// const values overriding equals) and keys that we have seen in the program. |
| 219 bool _shouldKeep(ConstantValue key) => | 221 bool _shouldKeep(ConstantValue key) => |
| 220 key.isPrimitive || _inUse.contains(key) || _overridesEquals(key); | 222 key.isPrimitive || _inUse.contains(key) || _overridesEquals(key); |
| 221 | 223 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 } | 294 } |
| 293 | 295 |
| 294 /// Callback from the backend, invoked when reaching the end of the enqueuing | 296 /// Callback from the backend, invoked when reaching the end of the enqueuing |
| 295 /// process, but before emitting the code. At this moment we have discovered | 297 /// process, but before emitting the code. At this moment we have discovered |
| 296 /// all types used in the program and we can tree-shake anything that is | 298 /// all types used in the program and we can tree-shake anything that is |
| 297 /// unused. | 299 /// unused. |
| 298 void onQueueClosed() { | 300 void onQueueClosed() { |
| 299 if (!_isEnabled || !_inCodegen) return; | 301 if (!_isEnabled || !_inCodegen) return; |
| 300 | 302 |
| 301 _lookupMaps.values.forEach((info) { | 303 _lookupMaps.values.forEach((info) { |
| 302 assert (!info.emitted); | 304 assert(!info.emitted); |
| 303 info.emitted = true; | 305 info.emitted = true; |
| 304 info._prepareForEmission(); | 306 info._prepareForEmission(); |
| 305 }); | 307 }); |
| 306 | 308 |
| 307 // When --verbose is passed, we show the total number and set of keys that | 309 // When --verbose is passed, we show the total number and set of keys that |
| 308 // were tree-shaken from lookup maps. | 310 // were tree-shaken from lookup maps. |
| 309 Compiler compiler = backend.compiler; | 311 Compiler compiler = backend.compiler; |
| 310 if (compiler.options.verbose) { | 312 if (compiler.options.verbose) { |
| 311 var sb = new StringBuffer(); | 313 var sb = new StringBuffer(); |
| 312 int count = 0; | 314 int count = 0; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 349 /// Whether we have already emitted this constant. | 351 /// Whether we have already emitted this constant. |
| 350 bool emitted = false; | 352 bool emitted = false; |
| 351 | 353 |
| 352 /// Whether the `LookupMap` constant was built using the `LookupMap.pair` | 354 /// Whether the `LookupMap` constant was built using the `LookupMap.pair` |
| 353 /// constructor. | 355 /// constructor. |
| 354 bool singlePair; | 356 bool singlePair; |
| 355 | 357 |
| 356 /// Entries in the lookup map whose keys have not been seen in the rest of the | 358 /// Entries in the lookup map whose keys have not been seen in the rest of the |
| 357 /// program. | 359 /// program. |
| 358 Map<ConstantValue, ConstantValue> unusedEntries = | 360 Map<ConstantValue, ConstantValue> unusedEntries = |
| 359 <ConstantValue, ConstantValue> {}; | 361 <ConstantValue, ConstantValue>{}; |
| 360 | 362 |
| 361 /// Entries that have been used, and thus will be part of the generated code. | 363 /// Entries that have been used, and thus will be part of the generated code. |
| 362 Map<ConstantValue, ConstantValue> usedEntries = | 364 Map<ConstantValue, ConstantValue> usedEntries = |
| 363 <ConstantValue, ConstantValue> {}; | 365 <ConstantValue, ConstantValue>{}; |
| 364 | 366 |
| 365 /// Creates and initializes the information containing all keys of the | 367 /// Creates and initializes the information containing all keys of the |
| 366 /// original map marked as unused. | 368 /// original map marked as unused. |
| 367 _LookupMapInfo(this.original, this.analysis) { | 369 _LookupMapInfo(this.original, this.analysis) { |
| 368 ConstantValue key = original.fields[analysis.keyField]; | 370 ConstantValue key = original.fields[analysis.keyField]; |
| 369 singlePair = !key.isNull; | 371 singlePair = !key.isNull; |
| 370 | 372 |
| 371 if (singlePair) { | 373 if (singlePair) { |
| 372 unusedEntries[key] = original.fields[analysis.valueField]; | 374 unusedEntries[key] = original.fields[analysis.valueField]; |
| 373 | 375 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 405 } | 407 } |
| 406 | 408 |
| 407 /// Marks that [key] has been seen, and thus, the corresponding entry in this | 409 /// Marks that [key] has been seen, and thus, the corresponding entry in this |
| 408 /// map should be considered reachable. | 410 /// map should be considered reachable. |
| 409 _markUsed(ConstantValue key) { | 411 _markUsed(ConstantValue key) { |
| 410 assert(!emitted); | 412 assert(!emitted); |
| 411 assert(unusedEntries.containsKey(key)); | 413 assert(unusedEntries.containsKey(key)); |
| 412 assert(!usedEntries.containsKey(key)); | 414 assert(!usedEntries.containsKey(key)); |
| 413 ConstantValue constant = unusedEntries.remove(key); | 415 ConstantValue constant = unusedEntries.remove(key); |
| 414 usedEntries[key] = constant; | 416 usedEntries[key] = constant; |
| 415 analysis.backend.registerCompileTimeConstant(constant, | 417 analysis.backend.registerCompileTimeConstant( |
| 416 analysis.backend.compiler.globalDependencies); | 418 constant, analysis.backend.compiler.globalDependencies); |
| 417 } | 419 } |
| 418 | 420 |
| 419 /// Restores [original] to contain all of the entries marked as possibly used. | 421 /// Restores [original] to contain all of the entries marked as possibly used. |
| 420 void _prepareForEmission() { | 422 void _prepareForEmission() { |
| 421 ListConstantValue originalEntries = original.fields[analysis.entriesField]; | 423 ListConstantValue originalEntries = original.fields[analysis.entriesField]; |
| 422 DartType listType = originalEntries.type; | 424 DartType listType = originalEntries.type; |
| 423 List<ConstantValue> keyValuePairs = <ConstantValue>[]; | 425 List<ConstantValue> keyValuePairs = <ConstantValue>[]; |
| 424 usedEntries.forEach((key, value) { | 426 usedEntries.forEach((key, value) { |
| 425 keyValuePairs.add(key); | 427 keyValuePairs.add(key); |
| 426 keyValuePairs.add(value); | 428 keyValuePairs.add(value); |
| 427 }); | 429 }); |
| 428 | 430 |
| 429 // Note: we are restoring the entries here, see comment in [original]. | 431 // Note: we are restoring the entries here, see comment in [original]. |
| 430 if (singlePair) { | 432 if (singlePair) { |
| 431 assert (keyValuePairs.length == 0 || keyValuePairs.length == 2); | 433 assert(keyValuePairs.length == 0 || keyValuePairs.length == 2); |
| 432 if (keyValuePairs.length == 2) { | 434 if (keyValuePairs.length == 2) { |
| 433 original.fields[analysis.keyField] = keyValuePairs[0]; | 435 original.fields[analysis.keyField] = keyValuePairs[0]; |
| 434 original.fields[analysis.valueField] = keyValuePairs[1]; | 436 original.fields[analysis.valueField] = keyValuePairs[1]; |
| 435 } | 437 } |
| 436 } else { | 438 } else { |
| 437 original.fields[analysis.entriesField] = | 439 original.fields[analysis.entriesField] = |
| 438 new ListConstantValue(listType, keyValuePairs); | 440 new ListConstantValue(listType, keyValuePairs); |
| 439 } | 441 } |
| 440 } | 442 } |
| 441 } | 443 } |
| 442 | 444 |
| 443 final _validLookupMapVersionConstraint = | 445 final _validLookupMapVersionConstraint = new VersionConstraint.parse('^0.0.1'); |
| 444 new VersionConstraint.parse('^0.0.1'); | |
| OLD | NEW |