| 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 |