Chromium Code Reviews| 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 |