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 import 'package:front_end/src/fasta/scanner.dart' show BeginGroupToken, Token; | |
6 import 'package:front_end/src/fasta/scanner.dart' as Tokens show EOF_TOKEN; | |
7 | |
5 import '../common.dart'; | 8 import '../common.dart'; |
6 import '../common/backend_api.dart' show ForeignResolver; | 9 import '../common/backend_api.dart'; |
7 import '../common/resolution.dart' show Resolution; | 10 import '../common/resolution.dart' show Resolution; |
8 import '../compiler.dart' show Compiler; | 11 import '../compiler.dart' show Compiler; |
9 import '../constants/values.dart'; | |
10 import '../common_elements.dart' show CommonElements; | 12 import '../common_elements.dart' show CommonElements; |
11 import '../elements/elements.dart'; | 13 import '../elements/elements.dart'; |
12 import '../elements/entities.dart'; | 14 import '../elements/entities.dart'; |
13 import '../elements/modelx.dart' show FunctionElementX; | |
14 import '../elements/resolution_types.dart'; | 15 import '../elements/resolution_types.dart'; |
15 import '../elements/types.dart'; | 16 import '../elements/types.dart'; |
16 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 17 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
17 import '../js_backend/backend_usage.dart' show BackendUsageBuilder; | 18 import '../js_backend/backend_usage.dart' show BackendUsageBuilder; |
18 import '../js_backend/js_backend.dart'; | 19 import '../js_backend/js_backend.dart'; |
19 import '../js_backend/native_data.dart' show NativeBasicDataBuilder; | 20 import '../js_backend/native_data.dart' show NativeBasicData, NativeData; |
20 import '../js_emitter/js_emitter.dart' show CodeEmitterTask, NativeEmitter; | 21 import '../js_emitter/js_emitter.dart' show CodeEmitterTask, NativeEmitter; |
21 import 'package:front_end/src/fasta/scanner.dart' show BeginGroupToken, Token; | |
22 import 'package:front_end/src/fasta/scanner.dart' as Tokens show EOF_TOKEN; | |
23 import '../tree/tree.dart'; | |
24 import '../universe/use.dart' show StaticUse, TypeUse; | 22 import '../universe/use.dart' show StaticUse, TypeUse; |
25 import '../universe/world_impact.dart' | 23 import '../universe/world_impact.dart' |
26 show WorldImpact, WorldImpactBuilder, WorldImpactBuilderImpl; | 24 show WorldImpact, WorldImpactBuilder, WorldImpactBuilderImpl; |
27 import 'behavior.dart'; | 25 import 'behavior.dart'; |
28 | 26 |
29 /** | 27 /** |
30 * This could be an abstract class but we use it as a stub for the dart_backend. | 28 * This could be an abstract class but we use it as a stub for the dart_backend. |
31 */ | 29 */ |
32 class NativeEnqueuer { | 30 class NativeEnqueuer { |
33 /// Called when a [type] has been instantiated natively. | 31 /// Called when a [type] has been instantiated natively. |
34 void onInstantiatedType(InterfaceType type) {} | 32 void onInstantiatedType(InterfaceType type) {} |
35 | 33 |
36 /// Initial entry point to native enqueuer. | 34 /// Initial entry point to native enqueuer. |
37 WorldImpact processNativeClasses(Iterable<LibraryElement> libraries) => | 35 WorldImpact processNativeClasses(Iterable<LibraryElement> libraries) => |
38 const WorldImpact(); | 36 const WorldImpact(); |
39 | 37 |
40 /// Registers the [nativeBehavior]. Adds the liveness of its instantiated | 38 /// Registers the [nativeBehavior]. Adds the liveness of its instantiated |
41 /// types to the world. | 39 /// types to the world. |
42 void registerNativeBehavior( | 40 void registerNativeBehavior( |
43 WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) {} | 41 WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) {} |
44 | 42 |
45 /// Returns whether native classes are being used. | 43 /// Returns whether native classes are being used. |
46 bool get hasInstantiatedNativeClasses => false; | 44 bool get hasInstantiatedNativeClasses => false; |
47 | 45 |
48 /// Emits a summary information using the [log] function. | 46 /// Emits a summary information using the [log] function. |
49 void logSummary(log(message)) {} | 47 void logSummary(log(message)) {} |
50 } | 48 } |
51 | 49 |
52 abstract class NativeEnqueuerBase implements NativeEnqueuer { | 50 abstract class NativeEnqueuerBase implements NativeEnqueuer { |
Johnni Winther
2017/03/28 09:30:28
Weird diff. Code is moved from NativeEnqueuerBase
| |
51 final Set<ClassElement> _registeredClasses = new Set<ClassElement>(); | |
52 final Set<ClassElement> _unusedClasses = new Set<ClassElement>(); | |
53 | |
54 bool get hasInstantiatedNativeClasses => !_registeredClasses.isEmpty; | |
55 | |
56 final Compiler _compiler; | |
57 final bool enableLiveTypeAnalysis; | |
58 | |
59 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. | |
60 NativeEnqueuerBase(this._compiler, this.enableLiveTypeAnalysis); | |
61 | |
62 JavaScriptBackend get _backend => _compiler.backend; | |
63 BackendHelpers get _helpers => _backend.helpers; | |
64 Resolution get _resolution => _compiler.resolution; | |
65 | |
66 DiagnosticReporter get _reporter => _compiler.reporter; | |
67 CommonElements get _commonElements => _compiler.commonElements; | |
68 | |
69 NativeBasicData get _nativeBasicData => _backend.nativeBasicData; | |
70 | |
71 BackendClasses get _backendClasses => _backend.backendClasses; | |
72 | |
73 void onInstantiatedType(InterfaceType type) { | |
74 if (_unusedClasses.remove(type.element)) { | |
75 _registeredClasses.add(type.element); | |
76 } | |
77 } | |
78 | |
79 /// Register [classes] as natively instantiated in [impactBuilder]. | |
80 void _registerTypeUses( | |
81 WorldImpactBuilder impactBuilder, Set<ClassElement> classes, cause) { | |
82 for (ClassElement cls in classes) { | |
83 if (!_unusedClasses.contains(cls)) { | |
84 // No need to add [classElement] to [impactBuilder]: it has already been | |
85 // instantiated and we don't track origins of native instantiations | |
86 // precisely. | |
87 continue; | |
88 } | |
89 cls.ensureResolved(_resolution); | |
90 impactBuilder | |
91 .registerTypeUse(new TypeUse.nativeInstantiation(cls.rawType)); | |
92 } | |
93 } | |
94 | |
95 void registerNativeBehavior( | |
96 WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) { | |
97 _processNativeBehavior(impactBuilder, nativeBehavior, cause); | |
98 } | |
99 | |
100 void _processNativeBehavior( | |
101 WorldImpactBuilder impactBuilder, NativeBehavior behavior, cause) { | |
102 void registerInstantiation(ResolutionInterfaceType type) { | |
103 impactBuilder.registerTypeUse(new TypeUse.nativeInstantiation(type)); | |
104 } | |
105 | |
106 int unusedBefore = _unusedClasses.length; | |
107 Set<ClassElement> matchingClasses = new Set<ClassElement>(); | |
108 for (var type in behavior.typesInstantiated) { | |
109 if (type is SpecialType) { | |
110 if (type == SpecialType.JsObject) { | |
111 registerInstantiation(_commonElements.objectType); | |
112 } | |
113 continue; | |
114 } | |
115 if (type is ResolutionInterfaceType) { | |
116 if (type == _commonElements.numType) { | |
117 registerInstantiation(_commonElements.doubleType); | |
118 registerInstantiation(_commonElements.intType); | |
119 } else if (type == _commonElements.intType || | |
120 type == _commonElements.doubleType || | |
121 type == _commonElements.stringType || | |
122 type == _commonElements.nullType || | |
123 type == _commonElements.boolType || | |
124 type.asInstanceOf(_backendClasses.listClass) != null) { | |
125 registerInstantiation(type); | |
126 } | |
127 // TODO(johnniwinther): Improve spec string precision to handle type | |
128 // arguments and implements relations that preserve generics. Currently | |
129 // we cannot distinguish between `List`, `List<dynamic>`, and | |
130 // `List<int>` and take all to mean `List<E>`; in effect not including | |
131 // any native subclasses of generic classes. | |
132 // TODO(johnniwinther,sra): Find and replace uses of `List` with the | |
133 // actual implementation classes such as `JSArray` et al. | |
134 matchingClasses | |
135 .addAll(_findUnusedClassesMatching((ClassElement nativeClass) { | |
136 ResolutionInterfaceType nativeType = nativeClass.thisType; | |
137 ResolutionInterfaceType specType = type.element.thisType; | |
138 return _compiler.types.isSubtype(nativeType, specType); | |
139 })); | |
140 } else if (type.isDynamic) { | |
141 matchingClasses.addAll(_unusedClasses); | |
142 } else { | |
143 assert(type is ResolutionVoidType); | |
144 } | |
145 } | |
146 if (matchingClasses.isNotEmpty && _registeredClasses.isEmpty) { | |
147 matchingClasses.addAll(_onFirstNativeClass(impactBuilder)); | |
148 } | |
149 _registerTypeUses(impactBuilder, matchingClasses, cause); | |
150 | |
151 // Give an info so that library developers can compile with -v to find why | |
152 // all the native classes are included. | |
153 if (unusedBefore > 0 && unusedBefore == matchingClasses.length) { | |
154 _reporter.log('All native types marked as used due to $cause.'); | |
155 } | |
156 } | |
157 | |
158 Iterable<ClassElement> _findUnusedClassesMatching( | |
159 bool predicate(classElement)) { | |
160 return _unusedClasses.where(predicate); | |
161 } | |
162 | |
163 void _registerBackendUse(FunctionEntity element) {} | |
164 | |
165 Iterable<ClassElement> _onFirstNativeClass(WorldImpactBuilder impactBuilder) { | |
166 void staticUse(FunctionEntity element) { | |
167 impactBuilder.registerStaticUse(new StaticUse.implicitInvoke(element)); | |
168 _registerBackendUse(element); | |
169 } | |
170 | |
171 staticUse(_helpers.defineProperty); | |
172 staticUse(_helpers.toStringForNativeObject); | |
173 staticUse(_helpers.hashCodeForNativeObject); | |
174 staticUse(_helpers.closureConverter); | |
175 return _findNativeExceptions(); | |
176 } | |
177 | |
178 Iterable<ClassElement> _findNativeExceptions() { | |
179 return _findUnusedClassesMatching((classElement) { | |
180 // TODO(sra): Annotate exception classes in dart:html. | |
181 String name = classElement.name; | |
182 if (name.contains('Exception')) return true; | |
183 if (name.contains('Error')) return true; | |
184 return false; | |
185 }); | |
186 } | |
187 } | |
188 | |
189 class NativeResolutionEnqueuer extends NativeEnqueuerBase { | |
53 /// The set of all native classes. Each native class is in [nativeClasses] | 190 /// The set of all native classes. Each native class is in [nativeClasses] |
54 /// and exactly one of [unusedClasses] and [registeredClasses]. | 191 /// and exactly one of [unusedClasses] and [registeredClasses]. |
55 final Set<ClassElement> _nativeClasses = new Set<ClassElement>(); | 192 final Set<ClassElement> _nativeClasses = new Set<ClassElement>(); |
56 | 193 |
57 final Set<ClassElement> _registeredClasses = new Set<ClassElement>(); | 194 Map<String, ClassElement> tagOwner = new Map<String, ClassElement>(); |
58 final Set<ClassElement> _unusedClasses = new Set<ClassElement>(); | 195 |
59 | 196 NativeResolutionEnqueuer(Compiler compiler) |
60 bool get hasInstantiatedNativeClasses => !_registeredClasses.isEmpty; | 197 : super(compiler, compiler.options.enableNativeLiveTypeAnalysis); |
61 | 198 |
62 final Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); | 199 BackendUsageBuilder get _backendUsageBuilder => _backend.backendUsageBuilder; |
63 | 200 |
64 final Compiler compiler; | 201 void _registerBackendUse(FunctionEntity element) { |
65 final bool enableLiveTypeAnalysis; | 202 _backendUsageBuilder.registerBackendFunctionUse(element); |
66 | 203 _backendUsageBuilder.registerGlobalFunctionDependency(element); |
67 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. | |
68 NativeEnqueuerBase(Compiler compiler, this.enableLiveTypeAnalysis) | |
69 : this.compiler = compiler; | |
70 | |
71 JavaScriptBackend get backend => compiler.backend; | |
72 BackendHelpers get helpers => backend.helpers; | |
73 Resolution get resolution => compiler.resolution; | |
74 | |
75 DiagnosticReporter get reporter => compiler.reporter; | |
76 CommonElements get commonElements => compiler.commonElements; | |
77 | |
78 void onInstantiatedType(ResolutionInterfaceType type) { | |
79 if (_unusedClasses.remove(type.element)) { | |
80 _registeredClasses.add(type.element); | |
81 } | |
82 } | 204 } |
83 | 205 |
84 WorldImpact processNativeClasses(Iterable<LibraryElement> libraries) { | 206 WorldImpact processNativeClasses(Iterable<LibraryElement> libraries) { |
85 WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl(); | 207 WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl(); |
86 _processNativeClasses(impactBuilder, libraries); | 208 Set<ClassElement> nativeClasses = new Set<ClassElement>(); |
87 return impactBuilder; | 209 libraries.forEach((l) => _processNativeClassesInLibrary(l, nativeClasses)); |
88 } | 210 if (_helpers.isolateHelperLibrary != null) { |
89 | 211 _processNativeClassesInLibrary( |
90 void _processNativeClasses( | 212 _helpers.isolateHelperLibrary, nativeClasses); |
91 WorldImpactBuilder impactBuilder, Iterable<LibraryElement> libraries) { | 213 } |
92 libraries.forEach(processNativeClassesInLibrary); | 214 _processSubclassesOfNativeClasses(libraries, nativeClasses); |
93 if (helpers.isolateHelperLibrary != null) { | 215 _nativeClasses.addAll(nativeClasses); |
94 processNativeClassesInLibrary(helpers.isolateHelperLibrary); | 216 _unusedClasses.addAll(nativeClasses); |
95 } | |
96 processSubclassesOfNativeClasses(libraries); | |
97 if (!enableLiveTypeAnalysis) { | 217 if (!enableLiveTypeAnalysis) { |
98 _registerTypeUses(impactBuilder, _nativeClasses, 'forced'); | 218 _registerTypeUses(impactBuilder, _nativeClasses, 'forced'); |
99 } | 219 } |
100 } | 220 return impactBuilder; |
101 | 221 } |
102 void processNativeClassesInLibrary(LibraryElement library) { | 222 |
223 void _processNativeClassesInLibrary( | |
224 LibraryElement library, Set<ClassElement> nativeClasses) { | |
103 // Use implementation to ensure the inclusion of injected members. | 225 // Use implementation to ensure the inclusion of injected members. |
104 library.implementation.forEachLocalMember((Element element) { | 226 library.implementation.forEachLocalMember((Element element) { |
105 if (element.isClass) { | 227 if (element.isClass) { |
106 ClassElement cls = element; | 228 ClassElement cls = element; |
107 if (backend.nativeBasicData.isNativeClass(cls)) { | 229 if (_nativeBasicData.isNativeClass(cls)) { |
108 processNativeClass(element); | 230 _processNativeClass(element, nativeClasses); |
109 } | 231 } |
110 } | 232 } |
111 }); | 233 }); |
112 } | 234 } |
113 | 235 |
114 void processNativeClass(ClassElement classElement) { | 236 void _processNativeClass( |
115 _nativeClasses.add(classElement); | 237 ClassElement classElement, Set<ClassElement> nativeClasses) { |
116 _unusedClasses.add(classElement); | 238 nativeClasses.add(classElement); |
117 // Resolve class to ensure the class has valid inheritance info. | 239 // Resolve class to ensure the class has valid inheritance info. |
118 classElement.ensureResolved(resolution); | 240 classElement.ensureResolved(_resolution); |
119 } | 241 // Js Interop interfaces do not have tags. |
120 | 242 if (_nativeBasicData.isJsInteropClass(classElement)) return; |
121 void processSubclassesOfNativeClasses(Iterable<LibraryElement> libraries) { | 243 // Since we map from dispatch tags to classes, a dispatch tag must be used |
244 // on only one native class. | |
245 for (String tag in _nativeBasicData.getNativeTagsOfClass(classElement)) { | |
246 ClassElement owner = tagOwner[tag]; | |
247 if (owner != null) { | |
248 if (owner != classElement) { | |
249 _reporter.internalError( | |
250 classElement, "Tag '$tag' already in use by '${owner.name}'"); | |
251 } | |
252 } else { | |
253 tagOwner[tag] = classElement; | |
254 } | |
255 } | |
256 } | |
257 | |
258 void _processSubclassesOfNativeClasses( | |
259 Iterable<LibraryElement> libraries, Set<ClassElement> nativeClasses) { | |
260 Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); | |
122 // Collect potential subclasses, e.g. | 261 // Collect potential subclasses, e.g. |
123 // | 262 // |
124 // class B extends foo.A {} | 263 // class B extends foo.A {} |
125 // | 264 // |
126 // String "A" has a potential subclass B. | 265 // String "A" has a potential subclass B. |
127 | 266 |
128 var potentialExtends = new Map<String, Set<ClassElement>>(); | 267 var potentialExtends = new Map<String, Set<ClassElement>>(); |
129 | 268 |
130 libraries.forEach((library) { | 269 libraries.forEach((library) { |
131 library.implementation.forEachLocalMember((element) { | 270 library.implementation.forEachLocalMember((element) { |
132 if (element.isClass) { | 271 if (element.isClass) { |
133 String extendsName = findExtendsNameOfClass(element); | 272 String extendsName = _findExtendsNameOfClass(element); |
134 if (extendsName != null) { | 273 if (extendsName != null) { |
135 Set<ClassElement> potentialSubclasses = potentialExtends | 274 Set<ClassElement> potentialSubclasses = potentialExtends |
136 .putIfAbsent(extendsName, () => new Set<ClassElement>()); | 275 .putIfAbsent(extendsName, () => new Set<ClassElement>()); |
137 potentialSubclasses.add(element); | 276 potentialSubclasses.add(element); |
138 } | 277 } |
139 } | 278 } |
140 }); | 279 }); |
141 }); | 280 }); |
142 | 281 |
143 // Resolve all the native classes and any classes that might extend them in | 282 // Resolve all the native classes and any classes that might extend them in |
144 // [potentialExtends], and then check that the properly resolved class is in | 283 // [potentialExtends], and then check that the properly resolved class is in |
145 // fact a subclass of a native class. | 284 // fact a subclass of a native class. |
146 | 285 |
147 ClassElement nativeSuperclassOf(ClassElement classElement) { | 286 ClassElement nativeSuperclassOf(ClassElement classElement) { |
148 if (backend.nativeBasicData.isNativeClass(classElement)) | 287 if (_nativeBasicData.isNativeClass(classElement)) return classElement; |
149 return classElement; | |
150 if (classElement.superclass == null) return null; | 288 if (classElement.superclass == null) return null; |
151 return nativeSuperclassOf(classElement.superclass); | 289 return nativeSuperclassOf(classElement.superclass); |
152 } | 290 } |
153 | 291 |
154 void walkPotentialSubclasses(ClassElement element) { | 292 void walkPotentialSubclasses(ClassElement element) { |
155 if (nativeClassesAndSubclasses.contains(element)) return; | 293 if (nativeClassesAndSubclasses.contains(element)) return; |
156 element.ensureResolved(resolution); | 294 element.ensureResolved(_resolution); |
157 ClassElement nativeSuperclass = nativeSuperclassOf(element); | 295 ClassElement nativeSuperclass = nativeSuperclassOf(element); |
158 if (nativeSuperclass != null) { | 296 if (nativeSuperclass != null) { |
159 nativeClassesAndSubclasses.add(element); | 297 nativeClassesAndSubclasses.add(element); |
160 Set<ClassElement> potentialSubclasses = potentialExtends[element.name]; | 298 Set<ClassElement> potentialSubclasses = potentialExtends[element.name]; |
161 if (potentialSubclasses != null) { | 299 if (potentialSubclasses != null) { |
162 potentialSubclasses.forEach(walkPotentialSubclasses); | 300 potentialSubclasses.forEach(walkPotentialSubclasses); |
163 } | 301 } |
164 } | 302 } |
165 } | 303 } |
166 | 304 |
167 _nativeClasses.forEach(walkPotentialSubclasses); | 305 nativeClasses.forEach(walkPotentialSubclasses); |
168 | 306 nativeClasses.addAll(nativeClassesAndSubclasses); |
169 _nativeClasses.addAll(nativeClassesAndSubclasses); | |
170 _unusedClasses.addAll(nativeClassesAndSubclasses); | |
171 } | 307 } |
172 | 308 |
173 /** | 309 /** |
174 * Returns the source string of the class named in the extends clause, or | 310 * Returns the source string of the class named in the extends clause, or |
175 * `null` if there is no extends clause. | 311 * `null` if there is no extends clause. |
176 */ | 312 */ |
177 String findExtendsNameOfClass(ClassElement classElement) { | 313 String _findExtendsNameOfClass(ClassElement classElement) { |
178 if (classElement.isResolved) { | 314 if (classElement.isResolved) { |
179 ClassElement superClass = classElement.superclass; | 315 ClassElement superClass = classElement.superclass; |
180 while (superClass != null) { | 316 while (superClass != null) { |
181 if (!superClass.isUnnamedMixinApplication) { | 317 if (!superClass.isUnnamedMixinApplication) { |
182 return superClass.name; | 318 return superClass.name; |
183 } | 319 } |
184 superClass = superClass.superclass; | 320 superClass = superClass.superclass; |
185 } | 321 } |
186 return null; | 322 return null; |
187 } | 323 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
228 token = token.next; | 364 token = token.next; |
229 if (token.stringValue != '.') break; | 365 if (token.stringValue != '.') break; |
230 token = token.next; | 366 token = token.next; |
231 if (!token.isIdentifier()) return null; | 367 if (!token.isIdentifier()) return null; |
232 id = token; | 368 id = token; |
233 } | 369 } |
234 // Should be at '{', 'with', 'implements', '<' or 'native'. | 370 // Should be at '{', 'with', 'implements', '<' or 'native'. |
235 return id.lexeme; | 371 return id.lexeme; |
236 } | 372 } |
237 | 373 |
238 return reporter.withCurrentElement(classElement, () { | 374 return _reporter.withCurrentElement(classElement, () { |
239 return scanForExtendsName(classElement.position); | 375 return scanForExtendsName(classElement.position); |
240 }); | 376 }); |
241 } | 377 } |
242 | 378 |
243 /// Register [classes] as natively instantiated in [impactBuilder]. | |
244 void _registerTypeUses( | |
245 WorldImpactBuilder impactBuilder, Set<ClassElement> classes, cause) { | |
246 for (ClassElement cls in classes) { | |
247 if (!_unusedClasses.contains(cls)) { | |
248 // No need to add [classElement] to [impactBuilder]: it has already been | |
249 // instantiated and we don't track origins of native instantiations | |
250 // precisely. | |
251 continue; | |
252 } | |
253 cls.ensureResolved(resolution); | |
254 impactBuilder | |
255 .registerTypeUse(new TypeUse.nativeInstantiation(cls.rawType)); | |
256 } | |
257 } | |
258 | |
259 void registerNativeBehavior( | |
260 WorldImpactBuilder impactBuilder, NativeBehavior nativeBehavior, cause) { | |
261 _processNativeBehavior(impactBuilder, nativeBehavior, cause); | |
262 } | |
263 | |
264 void _processNativeBehavior( | |
265 WorldImpactBuilder impactBuilder, NativeBehavior behavior, cause) { | |
266 void registerInstantiation(ResolutionInterfaceType type) { | |
267 impactBuilder.registerTypeUse(new TypeUse.nativeInstantiation(type)); | |
268 } | |
269 | |
270 int unusedBefore = _unusedClasses.length; | |
271 Set<ClassElement> matchingClasses = new Set<ClassElement>(); | |
272 for (var type in behavior.typesInstantiated) { | |
273 if (type is SpecialType) { | |
274 if (type == SpecialType.JsObject) { | |
275 registerInstantiation(compiler.commonElements.objectType); | |
276 } | |
277 continue; | |
278 } | |
279 if (type is ResolutionInterfaceType) { | |
280 if (type == commonElements.numType) { | |
281 registerInstantiation(commonElements.doubleType); | |
282 registerInstantiation(commonElements.intType); | |
283 } else if (type == commonElements.intType || | |
284 type == commonElements.doubleType || | |
285 type == commonElements.stringType || | |
286 type == commonElements.nullType || | |
287 type == commonElements.boolType || | |
288 type.asInstanceOf(backend.backendClasses.listClass) != null) { | |
289 registerInstantiation(type); | |
290 } | |
291 // TODO(johnniwinther): Improve spec string precision to handle type | |
292 // arguments and implements relations that preserve generics. Currently | |
293 // we cannot distinguish between `List`, `List<dynamic>`, and | |
294 // `List<int>` and take all to mean `List<E>`; in effect not including | |
295 // any native subclasses of generic classes. | |
296 // TODO(johnniwinther,sra): Find and replace uses of `List` with the | |
297 // actual implementation classes such as `JSArray` et al. | |
298 matchingClasses | |
299 .addAll(_findUnusedClassesMatching((ClassElement nativeClass) { | |
300 ResolutionInterfaceType nativeType = nativeClass.thisType; | |
301 ResolutionInterfaceType specType = type.element.thisType; | |
302 return compiler.types.isSubtype(nativeType, specType); | |
303 })); | |
304 } else if (type.isDynamic) { | |
305 matchingClasses.addAll(_unusedClasses); | |
306 } else { | |
307 assert(type is ResolutionVoidType); | |
308 } | |
309 } | |
310 if (matchingClasses.isNotEmpty && _registeredClasses.isEmpty) { | |
311 matchingClasses.addAll(_onFirstNativeClass(impactBuilder)); | |
312 } | |
313 _registerTypeUses(impactBuilder, matchingClasses, cause); | |
314 | |
315 // Give an info so that library developers can compile with -v to find why | |
316 // all the native classes are included. | |
317 if (unusedBefore > 0 && unusedBefore == matchingClasses.length) { | |
318 reporter.log('All native types marked as used due to $cause.'); | |
319 } | |
320 } | |
321 | |
322 Iterable<ClassElement> _findUnusedClassesMatching( | |
323 bool predicate(classElement)) { | |
324 return _unusedClasses.where(predicate); | |
325 } | |
326 | |
327 void registerBackendUse(MethodElement element) {} | |
328 | |
329 Iterable<ClassElement> _onFirstNativeClass(WorldImpactBuilder impactBuilder) { | |
330 void staticUse(MethodElement element) { | |
331 impactBuilder.registerStaticUse(new StaticUse.implicitInvoke(element)); | |
332 registerBackendUse(element); | |
333 } | |
334 | |
335 staticUse(helpers.defineProperty); | |
336 staticUse(helpers.toStringForNativeObject); | |
337 staticUse(helpers.hashCodeForNativeObject); | |
338 staticUse(helpers.closureConverter); | |
339 return _findNativeExceptions(); | |
340 } | |
341 | |
342 Iterable<ClassElement> _findNativeExceptions() { | |
343 return _findUnusedClassesMatching((classElement) { | |
344 // TODO(sra): Annotate exception classes in dart:html. | |
345 String name = classElement.name; | |
346 if (name.contains('Exception')) return true; | |
347 if (name.contains('Error')) return true; | |
348 return false; | |
349 }); | |
350 } | |
351 } | |
352 | |
353 class NativeResolutionEnqueuer extends NativeEnqueuerBase { | |
354 Map<String, ClassElement> tagOwner = new Map<String, ClassElement>(); | |
355 | |
356 NativeResolutionEnqueuer(Compiler compiler) | |
357 : super(compiler, compiler.options.enableNativeLiveTypeAnalysis); | |
358 | |
359 BackendUsageBuilder get _backendUsageBuilder => backend.backendUsageBuilder; | |
360 | |
361 void registerBackendUse(MethodElement element) { | |
362 _backendUsageBuilder.registerBackendFunctionUse(element); | |
363 _backendUsageBuilder.registerGlobalFunctionDependency(element); | |
364 } | |
365 | |
366 void processNativeClass(ClassElement classElement) { | |
367 super.processNativeClass(classElement); | |
368 | |
369 // Js Interop interfaces do not have tags. | |
370 if (backend.nativeBasicData.isJsInteropClass(classElement)) return; | |
371 // Since we map from dispatch tags to classes, a dispatch tag must be used | |
372 // on only one native class. | |
373 for (String tag | |
374 in backend.nativeBasicData.getNativeTagsOfClass(classElement)) { | |
375 ClassElement owner = tagOwner[tag]; | |
376 if (owner != null) { | |
377 if (owner != classElement) { | |
378 reporter.internalError( | |
379 classElement, "Tag '$tag' already in use by '${owner.name}'"); | |
380 } | |
381 } else { | |
382 tagOwner[tag] = classElement; | |
383 } | |
384 } | |
385 } | |
386 | |
387 void logSummary(log(message)) { | 379 void logSummary(log(message)) { |
388 log('Resolved ${_registeredClasses.length} native elements used, ' | 380 log('Resolved ${_registeredClasses.length} native elements used, ' |
389 '${_unusedClasses.length} native elements dead.'); | 381 '${_unusedClasses.length} native elements dead.'); |
390 } | 382 } |
391 } | 383 } |
392 | 384 |
393 class NativeCodegenEnqueuer extends NativeEnqueuerBase { | 385 class NativeCodegenEnqueuer extends NativeEnqueuerBase { |
394 final CodeEmitterTask emitter; | 386 final CodeEmitterTask emitter; |
395 | 387 |
396 final Set<ClassElement> doneAddSubtypes = new Set<ClassElement>(); | 388 final Set<ClassElement> doneAddSubtypes = new Set<ClassElement>(); |
397 | 389 |
398 final NativeResolutionEnqueuer _resolutionEnqueuer; | 390 final NativeResolutionEnqueuer _resolutionEnqueuer; |
399 | 391 |
400 NativeCodegenEnqueuer( | 392 NativeCodegenEnqueuer( |
401 Compiler compiler, this.emitter, this._resolutionEnqueuer) | 393 Compiler compiler, this.emitter, this._resolutionEnqueuer) |
402 : super(compiler, compiler.options.enableNativeLiveTypeAnalysis); | 394 : super(compiler, compiler.options.enableNativeLiveTypeAnalysis) {} |
403 | 395 |
404 void _processNativeClasses( | 396 NativeData get _nativeData => _backend.nativeData; |
405 WorldImpactBuilder impactBuilder, Iterable<LibraryElement> libraries) { | 397 |
406 super._processNativeClasses(impactBuilder, libraries); | 398 WorldImpact processNativeClasses(Iterable<LibraryElement> libraries) { |
399 WorldImpactBuilderImpl impactBuilder = new WorldImpactBuilderImpl(); | |
400 _unusedClasses.addAll(_resolutionEnqueuer._nativeClasses); | |
401 | |
402 if (!enableLiveTypeAnalysis) { | |
403 _registerTypeUses( | |
404 impactBuilder, _resolutionEnqueuer._nativeClasses, 'forced'); | |
405 } | |
407 | 406 |
408 // HACK HACK - add all the resolved classes. | 407 // HACK HACK - add all the resolved classes. |
409 Set<ClassElement> matchingClasses = new Set<ClassElement>(); | 408 Set<ClassElement> matchingClasses = new Set<ClassElement>(); |
410 for (final classElement in _resolutionEnqueuer._registeredClasses) { | 409 for (final classElement in _resolutionEnqueuer._registeredClasses) { |
411 if (_unusedClasses.contains(classElement)) { | 410 if (_unusedClasses.contains(classElement)) { |
412 matchingClasses.add(classElement); | 411 matchingClasses.add(classElement); |
413 } | 412 } |
414 } | 413 } |
415 if (matchingClasses.isNotEmpty && _registeredClasses.isEmpty) { | 414 if (matchingClasses.isNotEmpty && _registeredClasses.isEmpty) { |
416 matchingClasses.addAll(_onFirstNativeClass(impactBuilder)); | 415 matchingClasses.addAll(_onFirstNativeClass(impactBuilder)); |
417 } | 416 } |
418 _registerTypeUses(impactBuilder, matchingClasses, 'was resolved'); | 417 _registerTypeUses(impactBuilder, matchingClasses, 'was resolved'); |
418 return impactBuilder; | |
419 } | 419 } |
420 | 420 |
421 void _registerTypeUses( | 421 void _registerTypeUses( |
422 WorldImpactBuilder impactBuilder, Set<ClassElement> classes, cause) { | 422 WorldImpactBuilder impactBuilder, Set<ClassElement> classes, cause) { |
423 super._registerTypeUses(impactBuilder, classes, cause); | 423 super._registerTypeUses(impactBuilder, classes, cause); |
424 | 424 |
425 for (ClassElement classElement in classes) { | 425 for (ClassElement classElement in classes) { |
426 // Add the information that this class is a subtype of its supertypes. The | 426 // Add the information that this class is a subtype of its supertypes. The |
427 // code emitter and the ssa builder use that information. | 427 // code emitter and the ssa builder use that information. |
428 _addSubtypes(classElement, emitter.nativeEmitter); | 428 _addSubtypes(classElement, emitter.nativeEmitter); |
429 } | 429 } |
430 } | 430 } |
431 | 431 |
432 void _addSubtypes(ClassElement cls, NativeEmitter emitter) { | 432 void _addSubtypes(ClassElement cls, NativeEmitter emitter) { |
433 if (!backend.nativeData.isNativeClass(cls)) return; | 433 if (!_nativeData.isNativeClass(cls)) return; |
434 if (doneAddSubtypes.contains(cls)) return; | 434 if (doneAddSubtypes.contains(cls)) return; |
435 doneAddSubtypes.add(cls); | 435 doneAddSubtypes.add(cls); |
436 | 436 |
437 // Walk the superclass chain since classes on the superclass chain might not | 437 // Walk the superclass chain since classes on the superclass chain might not |
438 // be instantiated (abstract or simply unused). | 438 // be instantiated (abstract or simply unused). |
439 _addSubtypes(cls.superclass, emitter); | 439 _addSubtypes(cls.superclass, emitter); |
440 | 440 |
441 for (ResolutionInterfaceType type in cls.allSupertypes) { | 441 for (ResolutionInterfaceType type in cls.allSupertypes) { |
442 List<ClassEntity> subtypes = | 442 List<ClassEntity> subtypes = |
443 emitter.subtypes.putIfAbsent(type.element, () => <ClassEntity>[]); | 443 emitter.subtypes.putIfAbsent(type.element, () => <ClassEntity>[]); |
444 subtypes.add(cls); | 444 subtypes.add(cls); |
445 } | 445 } |
446 | 446 |
447 // Skip through all the mixin applications in the super class | 447 // Skip through all the mixin applications in the super class |
448 // chain. That way, the direct subtypes set only contain the | 448 // chain. That way, the direct subtypes set only contain the |
449 // natives classes. | 449 // natives classes. |
450 ClassElement superclass = cls.superclass; | 450 ClassElement superclass = cls.superclass; |
451 while (superclass != null && superclass.isMixinApplication) { | 451 while (superclass != null && superclass.isMixinApplication) { |
452 assert(!backend.nativeData.isNativeClass(superclass)); | 452 assert(!_nativeData.isNativeClass(superclass)); |
453 superclass = superclass.superclass; | 453 superclass = superclass.superclass; |
454 } | 454 } |
455 | 455 |
456 List<ClassEntity> directSubtypes = | 456 List<ClassEntity> directSubtypes = |
457 emitter.directSubtypes.putIfAbsent(superclass, () => <ClassEntity>[]); | 457 emitter.directSubtypes.putIfAbsent(superclass, () => <ClassEntity>[]); |
458 directSubtypes.add(cls); | 458 directSubtypes.add(cls); |
459 } | 459 } |
460 | 460 |
461 void logSummary(log(message)) { | 461 void logSummary(log(message)) { |
462 log('Compiled ${_registeredClasses.length} native classes, ' | 462 log('Compiled ${_registeredClasses.length} native classes, ' |
463 '${_unusedClasses.length} native classes omitted.'); | 463 '${_unusedClasses.length} native classes omitted.'); |
464 } | 464 } |
465 } | 465 } |
OLD | NEW |