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 part of dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
6 | 6 |
7 class NativeEmitter { | 7 class NativeEmitter { |
8 | |
9 // TODO(floitsch): the native-emitter should not know about ClassBuilders. | 8 // TODO(floitsch): the native-emitter should not know about ClassBuilders. |
10 final Map<Element, full_js_emitter.ClassBuilder> cachedBuilders; | 9 final Map<Element, full_js_emitter.ClassBuilder> cachedBuilders; |
11 | 10 |
12 final CodeEmitterTask emitterTask; | 11 final CodeEmitterTask emitterTask; |
13 | 12 |
14 // Whether the application contains native classes. | 13 // Whether the application contains native classes. |
15 bool hasNativeClasses = false; | 14 bool hasNativeClasses = false; |
16 | 15 |
17 // Caches the native subtypes of a native class. | 16 // Caches the native subtypes of a native class. |
18 Map<ClassElement, List<ClassElement>> subtypes; | 17 Map<ClassElement, List<ClassElement>> subtypes; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
64 * | 63 * |
65 * [classes] contains native classes, mixin applications, and user subclasses | 64 * [classes] contains native classes, mixin applications, and user subclasses |
66 * of native classes. | 65 * of native classes. |
67 * | 66 * |
68 * [interceptorClassesNeededByConstants] contains the interceptors that are | 67 * [interceptorClassesNeededByConstants] contains the interceptors that are |
69 * referenced by constants. | 68 * referenced by constants. |
70 * | 69 * |
71 * [classesModifiedByEmitRTISupport] contains the list of classes that must | 70 * [classesModifiedByEmitRTISupport] contains the list of classes that must |
72 * exist, because runtime-type support adds information to the class. | 71 * exist, because runtime-type support adds information to the class. |
73 */ | 72 */ |
74 Set<Class> prepareNativeClasses(List<Class> classes, | 73 Set<Class> prepareNativeClasses( |
| 74 List<Class> classes, |
75 Set<ClassElement> interceptorClassesNeededByConstants, | 75 Set<ClassElement> interceptorClassesNeededByConstants, |
76 Set<ClassElement> classesModifiedByEmitRTISupport) { | 76 Set<ClassElement> classesModifiedByEmitRTISupport) { |
77 assert(classes.every((Class cls) => cls != null)); | 77 assert(classes.every((Class cls) => cls != null)); |
78 | 78 |
79 hasNativeClasses = classes.isNotEmpty; | 79 hasNativeClasses = classes.isNotEmpty; |
80 | 80 |
81 // Compute a pre-order traversal of the subclass forest. We actually want a | 81 // Compute a pre-order traversal of the subclass forest. We actually want a |
82 // post-order traversal but it is easier to compute the pre-order and use it | 82 // post-order traversal but it is easier to compute the pre-order and use it |
83 // in reverse. | 83 // in reverse. |
84 List<Class> preOrder = <Class>[]; | 84 List<Class> preOrder = <Class>[]; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 } else if (interceptorClassesNeededByConstants.contains(classElement)) { | 130 } else if (interceptorClassesNeededByConstants.contains(classElement)) { |
131 needed = true; | 131 needed = true; |
132 } else if (classesModifiedByEmitRTISupport.contains(classElement)) { | 132 } else if (classesModifiedByEmitRTISupport.contains(classElement)) { |
133 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer | 133 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
134 // adds information to a class prototype or constructor. | 134 // adds information to a class prototype or constructor. |
135 needed = true; | 135 needed = true; |
136 } else if (extensionPoints.containsKey(cls)) { | 136 } else if (extensionPoints.containsKey(cls)) { |
137 needed = true; | 137 needed = true; |
138 } | 138 } |
139 if (backend.isJsInterop(classElement)) { | 139 if (backend.isJsInterop(classElement)) { |
140 needed = true; // TODO(jacobr): we don't need all interop classes. | 140 needed = true; // TODO(jacobr): we don't need all interop classes. |
141 } else if (cls.isNative && | 141 } else if (cls.isNative && |
142 backend.nativeData.hasNativeTagsForcedNonLeaf(classElement)) { | 142 backend.nativeData.hasNativeTagsForcedNonLeaf(classElement)) { |
143 needed = true; | 143 needed = true; |
144 nonLeafClasses.add(cls); | 144 nonLeafClasses.add(cls); |
145 } | 145 } |
146 | 146 |
147 if (needed || neededClasses.contains(cls)) { | 147 if (needed || neededClasses.contains(cls)) { |
148 neededClasses.add(cls); | 148 neededClasses.add(cls); |
149 neededClasses.add(cls.superclass); | 149 neededClasses.add(cls.superclass); |
150 nonLeafClasses.add(cls.superclass); | 150 nonLeafClasses.add(cls.superclass); |
151 } | 151 } |
152 } | 152 } |
153 | 153 |
154 // Collect all the tags that map to each native class. | 154 // Collect all the tags that map to each native class. |
155 | 155 |
156 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); | 156 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); |
157 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); | 157 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); |
158 | 158 |
159 for (Class cls in classes) { | 159 for (Class cls in classes) { |
160 if (!cls.isNative) continue; | 160 if (!cls.isNative) continue; |
161 if (backend.isJsInterop(cls.element)) continue; | 161 if (backend.isJsInterop(cls.element)) continue; |
162 List<String> nativeTags = | 162 List<String> nativeTags = |
163 backend.nativeData.getNativeTagsOfClass(cls.element); | 163 backend.nativeData.getNativeTagsOfClass(cls.element); |
164 | 164 |
165 if (nonLeafClasses.contains(cls) || | 165 if (nonLeafClasses.contains(cls) || extensionPoints.containsKey(cls)) { |
166 extensionPoints.containsKey(cls)) { | |
167 nonleafTags | 166 nonleafTags |
168 .putIfAbsent(cls, () => new Set<String>()) | 167 .putIfAbsent(cls, () => new Set<String>()) |
169 .addAll(nativeTags); | 168 .addAll(nativeTags); |
170 } else { | 169 } else { |
171 Class sufficingInterceptor = cls; | 170 Class sufficingInterceptor = cls; |
172 while (!neededClasses.contains(sufficingInterceptor)) { | 171 while (!neededClasses.contains(sufficingInterceptor)) { |
173 sufficingInterceptor = sufficingInterceptor.superclass; | 172 sufficingInterceptor = sufficingInterceptor.superclass; |
174 } | 173 } |
175 if (sufficingInterceptor == objectClass) { | 174 if (sufficingInterceptor == objectClass) { |
176 sufficingInterceptor = jsInterceptorClass; | 175 sufficingInterceptor = jsInterceptorClass; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 Class nativeAncestorOf(Class cls) { | 229 Class nativeAncestorOf(Class cls) { |
231 return nativeSuperclassOf(cls.superclass); | 230 return nativeSuperclassOf(cls.superclass); |
232 } | 231 } |
233 | 232 |
234 Map<Class, List<Class>> map = new Map<Class, List<Class>>(); | 233 Map<Class, List<Class>> map = new Map<Class, List<Class>>(); |
235 | 234 |
236 for (Class cls in classes) { | 235 for (Class cls in classes) { |
237 if (cls.isNative) continue; | 236 if (cls.isNative) continue; |
238 Class nativeAncestor = nativeAncestorOf(cls); | 237 Class nativeAncestor = nativeAncestorOf(cls); |
239 if (nativeAncestor != null) { | 238 if (nativeAncestor != null) { |
240 map | 239 map.putIfAbsent(nativeAncestor, () => <Class>[]).add(cls); |
241 .putIfAbsent(nativeAncestor, () => <Class>[]) | |
242 .add(cls); | |
243 } | 240 } |
244 } | 241 } |
245 return map; | 242 return map; |
246 } | 243 } |
247 | 244 |
248 bool isTrivialClass(Class cls) { | 245 bool isTrivialClass(Class cls) { |
249 bool needsAccessor(Field field) { | 246 bool needsAccessor(Field field) { |
250 return field.needsGetter || | 247 return field.needsGetter || |
251 field.needsUncheckedSetter || | 248 field.needsUncheckedSetter || |
252 field.needsCheckedSetter; | 249 field.needsCheckedSetter; |
253 } | 250 } |
254 | 251 |
255 return | 252 return cls.methods.isEmpty && |
256 cls.methods.isEmpty && | |
257 cls.isChecks.isEmpty && | 253 cls.isChecks.isEmpty && |
258 cls.callStubs.isEmpty && | 254 cls.callStubs.isEmpty && |
259 !cls.superclass.isMixinApplication && | 255 !cls.superclass.isMixinApplication && |
260 !cls.fields.any(needsAccessor); | 256 !cls.fields.any(needsAccessor); |
261 } | 257 } |
262 | 258 |
263 void potentiallyConvertDartClosuresToJs( | 259 void potentiallyConvertDartClosuresToJs(List<jsAst.Statement> statements, |
264 List<jsAst.Statement> statements, | 260 FunctionElement member, List<jsAst.Parameter> stubParameters) { |
265 FunctionElement member, | |
266 List<jsAst.Parameter> stubParameters) { | |
267 FunctionSignature parameters = member.functionSignature; | 261 FunctionSignature parameters = member.functionSignature; |
268 Element converter = helpers.closureConverter; | 262 Element converter = helpers.closureConverter; |
269 jsAst.Expression closureConverter = | 263 jsAst.Expression closureConverter = |
270 emitterTask.staticFunctionAccess(converter); | 264 emitterTask.staticFunctionAccess(converter); |
271 parameters.forEachParameter((ParameterElement parameter) { | 265 parameters.forEachParameter((ParameterElement parameter) { |
272 String name = parameter.name; | 266 String name = parameter.name; |
273 // If [name] is not in [stubParameters], then the parameter is an optional | 267 // If [name] is not in [stubParameters], then the parameter is an optional |
274 // parameter that was not provided for this stub. | 268 // parameter that was not provided for this stub. |
275 for (jsAst.Parameter stubParameter in stubParameters) { | 269 for (jsAst.Parameter stubParameter in stubParameters) { |
276 if (stubParameter.name == name) { | 270 if (stubParameter.name == name) { |
277 DartType type = parameter.type.unaliased; | 271 DartType type = parameter.type.unaliased; |
278 if (type is FunctionType) { | 272 if (type is FunctionType) { |
279 // The parameter type is a function type either directly or through | 273 // The parameter type is a function type either directly or through |
280 // typedef(s). | 274 // typedef(s). |
281 FunctionType functionType = type; | 275 FunctionType functionType = type; |
282 int arity = functionType.computeArity(); | 276 int arity = functionType.computeArity(); |
283 statements.add( | 277 statements.add(js |
284 js.statement('# = #(#, $arity)', | 278 .statement('# = #(#, $arity)', [name, closureConverter, name])); |
285 [name, closureConverter, name])); | |
286 break; | 279 break; |
287 } | 280 } |
288 } | 281 } |
289 } | 282 } |
290 }); | 283 }); |
291 } | 284 } |
292 | 285 |
293 List<jsAst.Statement> generateParameterStubStatements( | 286 List<jsAst.Statement> generateParameterStubStatements( |
294 FunctionElement member, | 287 FunctionElement member, |
295 bool isInterceptedMethod, | 288 bool isInterceptedMethod, |
(...skipping 17 matching lines...) Expand all Loading... |
313 jsAst.Expression receiver; | 306 jsAst.Expression receiver; |
314 List<jsAst.Expression> arguments; | 307 List<jsAst.Expression> arguments; |
315 | 308 |
316 assert(invariant(member, nativeMethods.contains(member))); | 309 assert(invariant(member, nativeMethods.contains(member))); |
317 // When calling a JS method, we call it with the native name, and only the | 310 // When calling a JS method, we call it with the native name, and only the |
318 // arguments up until the last one provided. | 311 // arguments up until the last one provided. |
319 target = backend.nativeData.getFixedBackendName(member); | 312 target = backend.nativeData.getFixedBackendName(member); |
320 | 313 |
321 if (isInterceptedMethod) { | 314 if (isInterceptedMethod) { |
322 receiver = argumentsBuffer[0]; | 315 receiver = argumentsBuffer[0]; |
323 arguments = argumentsBuffer.sublist(1, | 316 arguments = argumentsBuffer.sublist( |
324 indexOfLastOptionalArgumentInParameters + 1); | 317 1, indexOfLastOptionalArgumentInParameters + 1); |
325 } else { | 318 } else { |
326 // Native methods that are not intercepted must be static. | 319 // Native methods that are not intercepted must be static. |
327 assert(invariant(member, member.isStatic)); | 320 assert(invariant(member, member.isStatic)); |
328 arguments = argumentsBuffer.sublist(0, | 321 arguments = argumentsBuffer.sublist( |
329 indexOfLastOptionalArgumentInParameters + 1); | 322 0, indexOfLastOptionalArgumentInParameters + 1); |
330 if (backend.isJsInterop(member)) { | 323 if (backend.isJsInterop(member)) { |
331 // fixedBackendPath is allowed to have the form foo.bar.baz for | 324 // fixedBackendPath is allowed to have the form foo.bar.baz for |
332 // interop. This template is uncached to avoid possibly running out of | 325 // interop. This template is uncached to avoid possibly running out of |
333 // memory when Dart2Js is run in server mode. In reality the risk of | 326 // memory when Dart2Js is run in server mode. In reality the risk of |
334 // caching these templates causing an issue is very low as each class | 327 // caching these templates causing an issue is very low as each class |
335 // and library that uses typed JavaScript interop will create only 1 | 328 // and library that uses typed JavaScript interop will create only 1 |
336 // unique template. | 329 // unique template. |
337 receiver = js.uncachedExpressionTemplate( | 330 receiver = js |
338 backend.namer.fixedBackendPath(member)).instantiate([]); | 331 .uncachedExpressionTemplate(backend.namer.fixedBackendPath(member)) |
| 332 .instantiate([]); |
339 } else { | 333 } else { |
340 receiver = js('this'); | 334 receiver = js('this'); |
341 } | 335 } |
342 } | 336 } |
343 statements.add( | 337 statements |
344 js.statement('return #.#(#)', [receiver, target, arguments])); | 338 .add(js.statement('return #.#(#)', [receiver, target, arguments])); |
345 | 339 |
346 return statements; | 340 return statements; |
347 } | 341 } |
348 | 342 |
349 bool isSupertypeOfNativeClass(ClassElement element) { | 343 bool isSupertypeOfNativeClass(ClassElement element) { |
350 if (backend.classesMixedIntoInterceptedClasses.contains(element)) { | 344 if (backend.classesMixedIntoInterceptedClasses.contains(element)) { |
351 return true; | 345 return true; |
352 } | 346 } |
353 | 347 |
354 return subtypes[element] != null; | 348 return subtypes[element] != null; |
355 } | 349 } |
356 | 350 |
357 bool requiresNativeIsCheck(Element element) { | 351 bool requiresNativeIsCheck(Element element) { |
358 // TODO(sra): Remove this function. It determines if a native type may | 352 // TODO(sra): Remove this function. It determines if a native type may |
359 // satisfy a check against [element], in which case an interceptor must be | 353 // satisfy a check against [element], in which case an interceptor must be |
360 // used. We should also use an interceptor if the check can't be satisfied | 354 // used. We should also use an interceptor if the check can't be satisfied |
361 // by a native class in case we get a native instance that tries to spoof | 355 // by a native class in case we get a native instance that tries to spoof |
362 // the type info. i.e the criteria for whether or not to use an interceptor | 356 // the type info. i.e the criteria for whether or not to use an interceptor |
363 // is whether the receiver can be native, not the type of the test. | 357 // is whether the receiver can be native, not the type of the test. |
364 if (element == null || !element.isClass) return false; | 358 if (element == null || !element.isClass) return false; |
365 ClassElement cls = element; | 359 ClassElement cls = element; |
366 if (backend.isNativeOrExtendsNative(cls)) return true; | 360 if (backend.isNativeOrExtendsNative(cls)) return true; |
367 return isSupertypeOfNativeClass(element); | 361 return isSupertypeOfNativeClass(element); |
368 } | 362 } |
369 } | 363 } |
OLD | NEW |