OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.js_emitter.native_emitter; | 5 library dart2js.js_emitter.native_emitter; |
6 | 6 |
7 import '../common.dart'; | 7 import '../common.dart'; |
8 import '../compiler.dart' show Compiler; | 8 import '../common_elements.dart' show CommonElements; |
9 import '../elements/types.dart' show DartType, FunctionType; | 9 import '../elements/types.dart' show DartType, FunctionType; |
10 import '../elements/entities.dart'; | 10 import '../elements/entities.dart'; |
11 import '../js/js.dart' as jsAst; | 11 import '../js/js.dart' as jsAst; |
12 import '../js/js.dart' show js; | 12 import '../js/js.dart' show js; |
13 import '../js_backend/js_backend.dart' show JavaScriptBackend, Namer; | |
14 import '../js_backend/interceptor_data.dart'; | 13 import '../js_backend/interceptor_data.dart'; |
15 import '../js_backend/native_data.dart'; | 14 import '../js_backend/native_data.dart'; |
| 15 import '../native/enqueue.dart' show NativeCodegenEnqueuer; |
16 import '../universe/world_builder.dart' show CodegenWorldBuilder; | 16 import '../universe/world_builder.dart' show CodegenWorldBuilder; |
| 17 import '../world.dart' show ClosedWorld; |
17 | 18 |
18 import 'code_emitter_task.dart' show CodeEmitterTask; | 19 import 'code_emitter_task.dart' show CodeEmitterTask; |
19 import 'model.dart'; | 20 import 'model.dart'; |
20 | 21 |
21 class NativeEmitter { | 22 class NativeEmitter { |
22 final CodeEmitterTask emitterTask; | 23 final CodeEmitterTask _emitterTask; |
23 final NativeData nativeData; | 24 final ClosedWorld _closedWorld; |
24 final InterceptorData interceptorData; | 25 final CodegenWorldBuilder _worldBuilder; |
| 26 final NativeCodegenEnqueuer _nativeCodegenEnqueuer; |
25 | 27 |
26 // Whether the application contains native classes. | 28 // Whether the application contains native classes. |
27 bool hasNativeClasses = false; | 29 bool hasNativeClasses = false; |
28 | 30 |
29 // Caches the native subtypes of a native class. | 31 // Caches the native subtypes of a native class. |
30 Map<ClassEntity, List<ClassEntity>> subtypes = | 32 Map<ClassEntity, List<ClassEntity>> subtypes = |
31 <ClassEntity, List<ClassEntity>>{}; | 33 <ClassEntity, List<ClassEntity>>{}; |
32 | 34 |
33 // Caches the direct native subtypes of a native class. | 35 // Caches the direct native subtypes of a native class. |
34 Map<ClassEntity, List<ClassEntity>> directSubtypes = | 36 Map<ClassEntity, List<ClassEntity>> directSubtypes = |
35 <ClassEntity, List<ClassEntity>>{}; | 37 <ClassEntity, List<ClassEntity>>{}; |
36 | 38 |
37 // Caches the methods that have a native body. | 39 // Caches the methods that have a native body. |
38 Set<FunctionEntity> nativeMethods = new Set<FunctionEntity>(); | 40 Set<FunctionEntity> nativeMethods = new Set<FunctionEntity>(); |
39 | 41 |
40 NativeEmitter(this.emitterTask, this.nativeData, this.interceptorData); | 42 NativeEmitter(this._emitterTask, this._closedWorld, this._worldBuilder, |
| 43 this._nativeCodegenEnqueuer); |
41 | 44 |
42 Compiler get compiler => emitterTask.compiler; | 45 CommonElements get _commonElements => _closedWorld.commonElements; |
43 | 46 NativeData get _nativeData => _closedWorld.nativeData; |
44 JavaScriptBackend get backend => compiler.backend; | 47 InterceptorData get _interceptorData => _closedWorld.interceptorData; |
45 | |
46 CodegenWorldBuilder get worldBuilder => compiler.codegenWorldBuilder; | |
47 | |
48 Namer get namer => backend.namer; | |
49 | 48 |
50 /** | 49 /** |
51 * Prepares native classes for emission. Returns the unneeded classes. | 50 * Prepares native classes for emission. Returns the unneeded classes. |
52 * | 51 * |
53 * Removes trivial classes (that can be represented by a super type) and | 52 * Removes trivial classes (that can be represented by a super type) and |
54 * generates properties that have to be added to classes (native or not). | 53 * generates properties that have to be added to classes (native or not). |
55 * | 54 * |
56 * Updates the `nativeLeafTags`, `nativeNonLeafTags` and `nativeExtensions` | 55 * Updates the `nativeLeafTags`, `nativeNonLeafTags` and `nativeExtensions` |
57 * fields of the given classes. This data must be emitted with the | 56 * fields of the given classes. This data must be emitted with the |
58 * corresponding classes. | 57 * corresponding classes. |
(...skipping 29 matching lines...) Expand all Loading... |
88 // Compute a pre-order traversal of the subclass forest. We actually want a | 87 // Compute a pre-order traversal of the subclass forest. We actually want a |
89 // post-order traversal but it is easier to compute the pre-order and use it | 88 // post-order traversal but it is easier to compute the pre-order and use it |
90 // in reverse. | 89 // in reverse. |
91 List<Class> preOrder = <Class>[]; | 90 List<Class> preOrder = <Class>[]; |
92 Set<Class> seen = new Set<Class>(); | 91 Set<Class> seen = new Set<Class>(); |
93 | 92 |
94 Class objectClass = null; | 93 Class objectClass = null; |
95 Class jsInterceptorClass = null; | 94 Class jsInterceptorClass = null; |
96 | 95 |
97 void walk(Class cls) { | 96 void walk(Class cls) { |
98 if (cls.element == compiler.commonElements.objectClass) { | 97 if (cls.element == _commonElements.objectClass) { |
99 objectClass = cls; | 98 objectClass = cls; |
100 return; | 99 return; |
101 } | 100 } |
102 if (cls.element == compiler.commonElements.jsInterceptorClass) { | 101 if (cls.element == _commonElements.jsInterceptorClass) { |
103 jsInterceptorClass = cls; | 102 jsInterceptorClass = cls; |
104 return; | 103 return; |
105 } | 104 } |
106 if (seen.contains(cls)) return; | 105 if (seen.contains(cls)) return; |
107 seen.add(cls); | 106 seen.add(cls); |
108 walk(cls.superclass); | 107 walk(cls.superclass); |
109 preOrder.add(cls); | 108 preOrder.add(cls); |
110 } | 109 } |
111 | 110 |
112 classes.forEach(walk); | 111 classes.forEach(walk); |
(...skipping 24 matching lines...) Expand all Loading... |
137 needed = true; | 136 needed = true; |
138 } else if (interceptorClassesNeededByConstants.contains(classElement)) { | 137 } else if (interceptorClassesNeededByConstants.contains(classElement)) { |
139 needed = true; | 138 needed = true; |
140 } else if (classesModifiedByEmitRTISupport.contains(classElement)) { | 139 } else if (classesModifiedByEmitRTISupport.contains(classElement)) { |
141 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer | 140 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
142 // adds information to a class prototype or constructor. | 141 // adds information to a class prototype or constructor. |
143 needed = true; | 142 needed = true; |
144 } else if (extensionPoints.containsKey(cls)) { | 143 } else if (extensionPoints.containsKey(cls)) { |
145 needed = true; | 144 needed = true; |
146 } | 145 } |
147 if (nativeData.isJsInteropClass(classElement)) { | 146 if (_nativeData.isJsInteropClass(classElement)) { |
148 needed = true; // TODO(jacobr): we don't need all interop classes. | 147 needed = true; // TODO(jacobr): we don't need all interop classes. |
149 } else if (cls.isNative && | 148 } else if (cls.isNative && |
150 nativeData.hasNativeTagsForcedNonLeaf(classElement)) { | 149 _nativeData.hasNativeTagsForcedNonLeaf(classElement)) { |
151 needed = true; | 150 needed = true; |
152 nonLeafClasses.add(cls); | 151 nonLeafClasses.add(cls); |
153 } | 152 } |
154 | 153 |
155 if (needed || neededClasses.contains(cls)) { | 154 if (needed || neededClasses.contains(cls)) { |
156 neededClasses.add(cls); | 155 neededClasses.add(cls); |
157 neededClasses.add(cls.superclass); | 156 neededClasses.add(cls.superclass); |
158 nonLeafClasses.add(cls.superclass); | 157 nonLeafClasses.add(cls.superclass); |
159 } | 158 } |
160 } | 159 } |
161 | 160 |
162 // Collect all the tags that map to each native class. | 161 // Collect all the tags that map to each native class. |
163 | 162 |
164 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); | 163 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); |
165 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); | 164 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); |
166 | 165 |
167 for (Class cls in classes) { | 166 for (Class cls in classes) { |
168 if (!cls.isNative) continue; | 167 if (!cls.isNative) continue; |
169 ClassEntity element = cls.element; | 168 ClassEntity element = cls.element; |
170 if (nativeData.isJsInteropClass(element)) continue; | 169 if (_nativeData.isJsInteropClass(element)) continue; |
171 List<String> nativeTags = nativeData.getNativeTagsOfClass(cls.element); | 170 List<String> nativeTags = _nativeData.getNativeTagsOfClass(cls.element); |
172 | 171 |
173 if (nonLeafClasses.contains(cls) || extensionPoints.containsKey(cls)) { | 172 if (nonLeafClasses.contains(cls) || extensionPoints.containsKey(cls)) { |
174 nonleafTags | 173 nonleafTags |
175 .putIfAbsent(cls, () => new Set<String>()) | 174 .putIfAbsent(cls, () => new Set<String>()) |
176 .addAll(nativeTags); | 175 .addAll(nativeTags); |
177 } else { | 176 } else { |
178 Class sufficingInterceptor = cls; | 177 Class sufficingInterceptor = cls; |
179 while (!neededClasses.contains(sufficingInterceptor)) { | 178 while (!neededClasses.contains(sufficingInterceptor)) { |
180 sufficingInterceptor = sufficingInterceptor.superclass; | 179 sufficingInterceptor = sufficingInterceptor.superclass; |
181 } | 180 } |
(...skipping 14 matching lines...) Expand all Loading... |
196 cls.nativeLeafTags = leafTags[cls].toList(growable: false); | 195 cls.nativeLeafTags = leafTags[cls].toList(growable: false); |
197 } | 196 } |
198 if (nonleafTags[cls] != null) { | 197 if (nonleafTags[cls] != null) { |
199 cls.nativeNonLeafTags = nonleafTags[cls].toList(growable: false); | 198 cls.nativeNonLeafTags = nonleafTags[cls].toList(growable: false); |
200 } | 199 } |
201 cls.nativeExtensions = extensionPoints[cls]; | 200 cls.nativeExtensions = extensionPoints[cls]; |
202 } | 201 } |
203 | 202 |
204 // Add properties containing the information needed to construct maps used | 203 // Add properties containing the information needed to construct maps used |
205 // by getNativeInterceptor and custom elements. | 204 // by getNativeInterceptor and custom elements. |
206 if (backend.nativeCodegenEnqueuer.hasInstantiatedNativeClasses) { | 205 if (_nativeCodegenEnqueuer.hasInstantiatedNativeClasses) { |
207 fillNativeInfo(jsInterceptorClass); | 206 fillNativeInfo(jsInterceptorClass); |
208 for (Class cls in classes) { | 207 for (Class cls in classes) { |
209 if (!cls.isNative || neededClasses.contains(cls)) { | 208 if (!cls.isNative || neededClasses.contains(cls)) { |
210 fillNativeInfo(cls); | 209 fillNativeInfo(cls); |
211 } | 210 } |
212 } | 211 } |
213 } | 212 } |
214 | 213 |
215 // TODO(sra): Issue #13731- this is commented out as part of custom | 214 // TODO(sra): Issue #13731- this is commented out as part of custom |
216 // element constructor work. | 215 // element constructor work. |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 | 258 |
260 return cls.methods.isEmpty && | 259 return cls.methods.isEmpty && |
261 cls.isChecks.isEmpty && | 260 cls.isChecks.isEmpty && |
262 cls.callStubs.isEmpty && | 261 cls.callStubs.isEmpty && |
263 !cls.superclass.isMixinApplication && | 262 !cls.superclass.isMixinApplication && |
264 !cls.fields.any(needsAccessor); | 263 !cls.fields.any(needsAccessor); |
265 } | 264 } |
266 | 265 |
267 void potentiallyConvertDartClosuresToJs(List<jsAst.Statement> statements, | 266 void potentiallyConvertDartClosuresToJs(List<jsAst.Statement> statements, |
268 FunctionEntity member, List<jsAst.Parameter> stubParameters) { | 267 FunctionEntity member, List<jsAst.Parameter> stubParameters) { |
269 FunctionEntity converter = compiler.commonElements.closureConverter; | 268 FunctionEntity converter = _commonElements.closureConverter; |
270 jsAst.Expression closureConverter = | 269 jsAst.Expression closureConverter = |
271 emitterTask.staticFunctionAccess(converter); | 270 _emitterTask.staticFunctionAccess(converter); |
272 worldBuilder.forEachParameter(member, (DartType type, String name) { | 271 _worldBuilder.forEachParameter(member, (DartType type, String name) { |
273 // If [name] is not in [stubParameters], then the parameter is an optional | 272 // If [name] is not in [stubParameters], then the parameter is an optional |
274 // parameter that was not provided for this stub. | 273 // parameter that was not provided for this stub. |
275 for (jsAst.Parameter stubParameter in stubParameters) { | 274 for (jsAst.Parameter stubParameter in stubParameters) { |
276 if (stubParameter.name == name) { | 275 if (stubParameter.name == name) { |
277 type = type.unaliased; | 276 type = type.unaliased; |
278 if (type.isFunctionType) { | 277 if (type.isFunctionType) { |
279 // The parameter type is a function type either directly or through | 278 // The parameter type is a function type either directly or through |
280 // typedef(s). | 279 // typedef(s). |
281 FunctionType functionType = type; | 280 FunctionType functionType = type; |
282 int arity = functionType.parameterTypes.length; | 281 int arity = functionType.parameterTypes.length; |
(...skipping 25 matching lines...) Expand all Loading... |
308 List<jsAst.Statement> statements = <jsAst.Statement>[]; | 307 List<jsAst.Statement> statements = <jsAst.Statement>[]; |
309 potentiallyConvertDartClosuresToJs(statements, member, stubParameters); | 308 potentiallyConvertDartClosuresToJs(statements, member, stubParameters); |
310 | 309 |
311 String target; | 310 String target; |
312 jsAst.Expression receiver; | 311 jsAst.Expression receiver; |
313 List<jsAst.Expression> arguments; | 312 List<jsAst.Expression> arguments; |
314 | 313 |
315 assert(invariant(member, nativeMethods.contains(member))); | 314 assert(invariant(member, nativeMethods.contains(member))); |
316 // When calling a JS method, we call it with the native name, and only the | 315 // When calling a JS method, we call it with the native name, and only the |
317 // arguments up until the last one provided. | 316 // arguments up until the last one provided. |
318 target = nativeData.getFixedBackendName(member); | 317 target = _nativeData.getFixedBackendName(member); |
319 | 318 |
320 if (isInterceptedMethod) { | 319 if (isInterceptedMethod) { |
321 receiver = argumentsBuffer[0]; | 320 receiver = argumentsBuffer[0]; |
322 arguments = argumentsBuffer.sublist( | 321 arguments = argumentsBuffer.sublist( |
323 1, indexOfLastOptionalArgumentInParameters + 1); | 322 1, indexOfLastOptionalArgumentInParameters + 1); |
324 } else { | 323 } else { |
325 // Native methods that are not intercepted must be static. | 324 // Native methods that are not intercepted must be static. |
326 assert(invariant(member, member.isStatic)); | 325 assert(invariant(member, member.isStatic)); |
327 arguments = argumentsBuffer.sublist( | 326 arguments = argumentsBuffer.sublist( |
328 0, indexOfLastOptionalArgumentInParameters + 1); | 327 0, indexOfLastOptionalArgumentInParameters + 1); |
329 if (nativeData.isJsInteropMember(member)) { | 328 if (_nativeData.isJsInteropMember(member)) { |
330 // fixedBackendPath is allowed to have the form foo.bar.baz for | 329 // fixedBackendPath is allowed to have the form foo.bar.baz for |
331 // interop. This template is uncached to avoid possibly running out of | 330 // interop. This template is uncached to avoid possibly running out of |
332 // memory when Dart2Js is run in server mode. In reality the risk of | 331 // memory when Dart2Js is run in server mode. In reality the risk of |
333 // caching these templates causing an issue is very low as each class | 332 // caching these templates causing an issue is very low as each class |
334 // and library that uses typed JavaScript interop will create only 1 | 333 // and library that uses typed JavaScript interop will create only 1 |
335 // unique template. | 334 // unique template. |
336 receiver = js | 335 receiver = js |
337 .uncachedExpressionTemplate( | 336 .uncachedExpressionTemplate( |
338 nativeData.getFixedBackendMethodPath(member)) | 337 _nativeData.getFixedBackendMethodPath(member)) |
339 .instantiate([]); | 338 .instantiate([]); |
340 } else { | 339 } else { |
341 receiver = js('this'); | 340 receiver = js('this'); |
342 } | 341 } |
343 } | 342 } |
344 statements | 343 statements |
345 .add(js.statement('return #.#(#)', [receiver, target, arguments])); | 344 .add(js.statement('return #.#(#)', [receiver, target, arguments])); |
346 | 345 |
347 return statements; | 346 return statements; |
348 } | 347 } |
349 | 348 |
350 bool isSupertypeOfNativeClass(ClassEntity element) { | 349 bool isSupertypeOfNativeClass(ClassEntity element) { |
351 if (interceptorData.isMixedIntoInterceptedClass(element)) { | 350 if (_interceptorData.isMixedIntoInterceptedClass(element)) { |
352 return true; | 351 return true; |
353 } | 352 } |
354 | 353 |
355 return subtypes[element] != null; | 354 return subtypes[element] != null; |
356 } | 355 } |
357 | 356 |
358 bool requiresNativeIsCheck(ClassEntity element) { | 357 bool requiresNativeIsCheck(ClassEntity element) { |
359 // TODO(sra): Remove this function. It determines if a native type may | 358 // TODO(sra): Remove this function. It determines if a native type may |
360 // satisfy a check against [element], in which case an interceptor must be | 359 // satisfy a check against [element], in which case an interceptor must be |
361 // used. We should also use an interceptor if the check can't be satisfied | 360 // used. We should also use an interceptor if the check can't be satisfied |
362 // by a native class in case we get a native instance that tries to spoof | 361 // by a native class in case we get a native instance that tries to spoof |
363 // the type info. i.e the criteria for whether or not to use an interceptor | 362 // the type info. i.e the criteria for whether or not to use an interceptor |
364 // is whether the receiver can be native, not the type of the test. | 363 // is whether the receiver can be native, not the type of the test. |
365 ClassEntity cls = element; | 364 ClassEntity cls = element; |
366 if (nativeData.isNativeOrExtendsNative(cls)) return true; | 365 if (_nativeData.isNativeOrExtendsNative(cls)) return true; |
367 return isSupertypeOfNativeClass(element); | 366 return isSupertypeOfNativeClass(element); |
368 } | 367 } |
369 } | 368 } |
OLD | NEW |