Chromium Code Reviews| 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 '../compiler.dart' show Compiler; |
| 9 import '../elements/resolution_types.dart' | 9 import '../elements/types.dart' show DartType, FunctionType; |
| 10 show ResolutionDartType, ResolutionFunctionType; | 10 import '../elements/entities.dart'; |
| 11 import '../elements/elements.dart' | |
| 12 show | |
| 13 ClassElement, | |
| 14 Element, | |
| 15 FunctionElement, | |
| 16 FunctionSignature, | |
| 17 MethodElement, | |
| 18 ParameterElement; | |
| 19 import '../js/js.dart' as jsAst; | 11 import '../js/js.dart' as jsAst; |
| 20 import '../js/js.dart' show js; | 12 import '../js/js.dart' show js; |
| 21 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 13 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
| 22 import '../js_backend/js_backend.dart' show JavaScriptBackend; | 14 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| 23 import 'full_emitter/emitter.dart' as full_js_emitter; | 15 import '../universe/world_builder.dart' show CodegenWorldBuilder; |
| 24 | 16 |
| 25 import 'code_emitter_task.dart' show CodeEmitterTask; | 17 import 'code_emitter_task.dart' show CodeEmitterTask; |
| 26 import 'model.dart'; | 18 import 'model.dart'; |
| 27 | 19 |
| 28 class NativeEmitter { | 20 class NativeEmitter { |
| 29 // TODO(floitsch): the native-emitter should not know about ClassBuilders. | |
| 30 final Map<Element, full_js_emitter.ClassBuilder> cachedBuilders; | |
| 31 | |
| 32 final CodeEmitterTask emitterTask; | 21 final CodeEmitterTask emitterTask; |
| 33 | 22 |
| 34 // Whether the application contains native classes. | 23 // Whether the application contains native classes. |
| 35 bool hasNativeClasses = false; | 24 bool hasNativeClasses = false; |
| 36 | 25 |
| 37 // Caches the native subtypes of a native class. | 26 // Caches the native subtypes of a native class. |
| 38 Map<ClassElement, List<ClassElement>> subtypes; | 27 Map<ClassEntity, List<ClassEntity>> subtypes = |
| 28 <ClassEntity, List<ClassEntity>>{}; | |
| 39 | 29 |
| 40 // Caches the direct native subtypes of a native class. | 30 // Caches the direct native subtypes of a native class. |
| 41 Map<ClassElement, List<ClassElement>> directSubtypes; | 31 Map<ClassEntity, List<ClassEntity>> directSubtypes = |
| 32 <ClassEntity, List<ClassEntity>>{}; | |
| 42 | 33 |
| 43 // Caches the methods that have a native body. | 34 // Caches the methods that have a native body. |
| 44 Set<FunctionElement> nativeMethods; | 35 Set<FunctionEntity> nativeMethods = new Set<FunctionEntity>(); |
| 45 | 36 |
| 46 NativeEmitter(CodeEmitterTask emitterTask) | 37 NativeEmitter(CodeEmitterTask emitterTask) : this.emitterTask = emitterTask; |
| 47 : this.emitterTask = emitterTask, | |
| 48 subtypes = new Map<ClassElement, List<ClassElement>>(), | |
| 49 directSubtypes = new Map<ClassElement, List<ClassElement>>(), | |
| 50 nativeMethods = new Set<FunctionElement>(), | |
| 51 cachedBuilders = emitterTask.compiler.cacheStrategy.newMap(); | |
| 52 | 38 |
| 53 Compiler get compiler => emitterTask.compiler; | 39 Compiler get compiler => emitterTask.compiler; |
| 54 | 40 |
| 55 JavaScriptBackend get backend => compiler.backend; | 41 JavaScriptBackend get backend => compiler.backend; |
| 56 | 42 |
| 57 BackendHelpers get helpers => backend.helpers; | 43 BackendHelpers get helpers => backend.helpers; |
| 58 | 44 |
| 45 CodegenWorldBuilder get worldBuilder => compiler.codegenWorldBuilder; | |
| 46 | |
| 59 /** | 47 /** |
| 60 * Prepares native classes for emission. Returns the unneeded classes. | 48 * Prepares native classes for emission. Returns the unneeded classes. |
| 61 * | 49 * |
| 62 * Removes trivial classes (that can be represented by a super type) and | 50 * Removes trivial classes (that can be represented by a super type) and |
| 63 * generates properties that have to be added to classes (native or not). | 51 * generates properties that have to be added to classes (native or not). |
| 64 * | 52 * |
| 65 * Updates the `nativeLeafTags`, `nativeNonLeafTags` and `nativeExtensions` | 53 * Updates the `nativeLeafTags`, `nativeNonLeafTags` and `nativeExtensions` |
| 66 * fields of the given classes. This data must be emitted with the | 54 * fields of the given classes. This data must be emitted with the |
| 67 * corresponding classes. | 55 * corresponding classes. |
| 68 * | 56 * |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 81 * of native classes. | 69 * of native classes. |
| 82 * | 70 * |
| 83 * [interceptorClassesNeededByConstants] contains the interceptors that are | 71 * [interceptorClassesNeededByConstants] contains the interceptors that are |
| 84 * referenced by constants. | 72 * referenced by constants. |
| 85 * | 73 * |
| 86 * [classesModifiedByEmitRTISupport] contains the list of classes that must | 74 * [classesModifiedByEmitRTISupport] contains the list of classes that must |
| 87 * exist, because runtime-type support adds information to the class. | 75 * exist, because runtime-type support adds information to the class. |
| 88 */ | 76 */ |
| 89 Set<Class> prepareNativeClasses( | 77 Set<Class> prepareNativeClasses( |
| 90 List<Class> classes, | 78 List<Class> classes, |
| 91 Set<ClassElement> interceptorClassesNeededByConstants, | 79 Set<ClassEntity> interceptorClassesNeededByConstants, |
| 92 Set<ClassElement> classesModifiedByEmitRTISupport) { | 80 Set<ClassEntity> classesModifiedByEmitRTISupport) { |
| 93 assert(classes.every((Class cls) => cls != null)); | 81 assert(classes.every((Class cls) => cls != null)); |
| 94 | 82 |
| 95 hasNativeClasses = classes.isNotEmpty; | 83 hasNativeClasses = classes.isNotEmpty; |
| 96 | 84 |
| 97 // Compute a pre-order traversal of the subclass forest. We actually want a | 85 // Compute a pre-order traversal of the subclass forest. We actually want a |
| 98 // post-order traversal but it is easier to compute the pre-order and use it | 86 // post-order traversal but it is easier to compute the pre-order and use it |
| 99 // in reverse. | 87 // in reverse. |
| 100 List<Class> preOrder = <Class>[]; | 88 List<Class> preOrder = <Class>[]; |
| 101 Set<Class> seen = new Set<Class>(); | 89 Set<Class> seen = new Set<Class>(); |
| 102 | 90 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 125 // needed class. | 113 // needed class. |
| 126 | 114 |
| 127 Set<Class> neededClasses = new Set<Class>(); | 115 Set<Class> neededClasses = new Set<Class>(); |
| 128 Set<Class> nonLeafClasses = new Set<Class>(); | 116 Set<Class> nonLeafClasses = new Set<Class>(); |
| 129 | 117 |
| 130 Map<Class, List<Class>> extensionPoints = computeExtensionPoints(preOrder); | 118 Map<Class, List<Class>> extensionPoints = computeExtensionPoints(preOrder); |
| 131 | 119 |
| 132 neededClasses.add(objectClass); | 120 neededClasses.add(objectClass); |
| 133 | 121 |
| 134 for (Class cls in preOrder.reversed) { | 122 for (Class cls in preOrder.reversed) { |
| 135 ClassElement classElement = cls.element; | 123 ClassEntity classElement = cls.element; |
| 136 // Post-order traversal ensures we visit the subclasses before their | 124 // Post-order traversal ensures we visit the subclasses before their |
| 137 // superclass. This makes it easy to tell if a class is needed because a | 125 // superclass. This makes it easy to tell if a class is needed because a |
| 138 // subclass is needed. | 126 // subclass is needed. |
| 139 bool needed = false; | 127 bool needed = false; |
| 140 if (!cls.isNative) { | 128 if (!cls.isNative) { |
| 141 // Mixin applications (native+mixin) are non-native, so [classElement] | 129 // Mixin applications (native+mixin) are non-native, so [classElement] |
| 142 // has already been emitted as a regular class. Mark [classElement] as | 130 // has already been emitted as a regular class. Mark [classElement] as |
| 143 // 'needed' to ensure the native superclass is needed. | 131 // 'needed' to ensure the native superclass is needed. |
| 144 needed = true; | 132 needed = true; |
| 145 } else if (!isTrivialClass(cls)) { | 133 } else if (!isTrivialClass(cls)) { |
| 146 needed = true; | 134 needed = true; |
| 147 } else if (interceptorClassesNeededByConstants.contains(classElement)) { | 135 } else if (interceptorClassesNeededByConstants.contains(classElement)) { |
| 148 needed = true; | 136 needed = true; |
| 149 } else if (classesModifiedByEmitRTISupport.contains(classElement)) { | 137 } else if (classesModifiedByEmitRTISupport.contains(classElement)) { |
| 150 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer | 138 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
| 151 // adds information to a class prototype or constructor. | 139 // adds information to a class prototype or constructor. |
| 152 needed = true; | 140 needed = true; |
| 153 } else if (extensionPoints.containsKey(cls)) { | 141 } else if (extensionPoints.containsKey(cls)) { |
| 154 needed = true; | 142 needed = true; |
| 155 } | 143 } |
| 156 if (backend.isJsInterop(classElement)) { | 144 if (backend.isJsInteropClass(classElement)) { |
| 157 needed = true; // TODO(jacobr): we don't need all interop classes. | 145 needed = true; // TODO(jacobr): we don't need all interop classes. |
| 158 } else if (cls.isNative && | 146 } else if (cls.isNative && |
| 159 backend.nativeData.hasNativeTagsForcedNonLeaf(classElement)) { | 147 backend.nativeData.hasNativeTagsForcedNonLeaf(classElement)) { |
| 160 needed = true; | 148 needed = true; |
| 161 nonLeafClasses.add(cls); | 149 nonLeafClasses.add(cls); |
| 162 } | 150 } |
| 163 | 151 |
| 164 if (needed || neededClasses.contains(cls)) { | 152 if (needed || neededClasses.contains(cls)) { |
| 165 neededClasses.add(cls); | 153 neededClasses.add(cls); |
| 166 neededClasses.add(cls.superclass); | 154 neededClasses.add(cls.superclass); |
| 167 nonLeafClasses.add(cls.superclass); | 155 nonLeafClasses.add(cls.superclass); |
| 168 } | 156 } |
| 169 } | 157 } |
| 170 | 158 |
| 171 // Collect all the tags that map to each native class. | 159 // Collect all the tags that map to each native class. |
| 172 | 160 |
| 173 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); | 161 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); |
| 174 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); | 162 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); |
| 175 | 163 |
| 176 for (Class cls in classes) { | 164 for (Class cls in classes) { |
| 177 if (!cls.isNative) continue; | 165 if (!cls.isNative) continue; |
| 178 ClassElement element = cls.element; | 166 ClassEntity element = cls.element; |
| 179 if (backend.isJsInterop(element)) continue; | 167 if (backend.isJsInteropClass(element)) continue; |
| 180 List<String> nativeTags = | 168 List<String> nativeTags = |
| 181 backend.nativeData.getNativeTagsOfClass(cls.element); | 169 backend.nativeData.getNativeTagsOfClass(cls.element); |
| 182 | 170 |
| 183 if (nonLeafClasses.contains(cls) || extensionPoints.containsKey(cls)) { | 171 if (nonLeafClasses.contains(cls) || extensionPoints.containsKey(cls)) { |
| 184 nonleafTags | 172 nonleafTags |
| 185 .putIfAbsent(cls, () => new Set<String>()) | 173 .putIfAbsent(cls, () => new Set<String>()) |
| 186 .addAll(nativeTags); | 174 .addAll(nativeTags); |
| 187 } else { | 175 } else { |
| 188 Class sufficingInterceptor = cls; | 176 Class sufficingInterceptor = cls; |
| 189 while (!neededClasses.contains(sufficingInterceptor)) { | 177 while (!neededClasses.contains(sufficingInterceptor)) { |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 268 } | 256 } |
| 269 | 257 |
| 270 return cls.methods.isEmpty && | 258 return cls.methods.isEmpty && |
| 271 cls.isChecks.isEmpty && | 259 cls.isChecks.isEmpty && |
| 272 cls.callStubs.isEmpty && | 260 cls.callStubs.isEmpty && |
| 273 !cls.superclass.isMixinApplication && | 261 !cls.superclass.isMixinApplication && |
| 274 !cls.fields.any(needsAccessor); | 262 !cls.fields.any(needsAccessor); |
| 275 } | 263 } |
| 276 | 264 |
| 277 void potentiallyConvertDartClosuresToJs(List<jsAst.Statement> statements, | 265 void potentiallyConvertDartClosuresToJs(List<jsAst.Statement> statements, |
| 278 FunctionElement member, List<jsAst.Parameter> stubParameters) { | 266 FunctionEntity member, List<jsAst.Parameter> stubParameters) { |
| 279 FunctionSignature parameters = member.functionSignature; | 267 FunctionEntity converter = helpers.closureConverter; |
| 280 MethodElement converter = helpers.closureConverter; | |
| 281 jsAst.Expression closureConverter = | 268 jsAst.Expression closureConverter = |
| 282 emitterTask.staticFunctionAccess(converter); | 269 emitterTask.staticFunctionAccess(converter); |
| 283 parameters.forEachParameter((ParameterElement parameter) { | 270 worldBuilder.forEachParameter(member, (DartType type, String name) { |
| 284 String name = parameter.name; | |
| 285 // If [name] is not in [stubParameters], then the parameter is an optional | 271 // If [name] is not in [stubParameters], then the parameter is an optional |
| 286 // parameter that was not provided for this stub. | 272 // parameter that was not provided for this stub. |
| 287 for (jsAst.Parameter stubParameter in stubParameters) { | 273 for (jsAst.Parameter stubParameter in stubParameters) { |
| 288 if (stubParameter.name == name) { | 274 if (stubParameter.name == name) { |
| 289 ResolutionDartType type = parameter.type.unaliased; | 275 type = type.unaliased; |
| 290 if (type is ResolutionFunctionType) { | 276 if (type.isFunctionType) { |
| 291 // The parameter type is a function type either directly or through | 277 // The parameter type is a function type either directly or through |
| 292 // typedef(s). | 278 // typedef(s). |
| 293 ResolutionFunctionType functionType = type; | 279 FunctionType functionType = type; |
| 294 int arity = functionType.computeArity(); | 280 int arity = functionType.parameterTypes.length; |
| 295 statements.add(js | 281 statements.add(js |
| 296 .statement('# = #(#, $arity)', [name, closureConverter, name])); | 282 .statement('# = #(#, $arity)', [name, closureConverter, name])); |
| 297 break; | 283 break; |
| 298 } | 284 } |
| 299 } | 285 } |
| 300 } | 286 } |
| 301 }); | 287 }); |
| 302 } | 288 } |
| 303 | 289 |
| 304 List<jsAst.Statement> generateParameterStubStatements( | 290 List<jsAst.Statement> generateParameterStubStatements( |
| 305 FunctionElement member, | 291 FunctionEntity member, |
| 306 bool isInterceptedMethod, | 292 bool isInterceptedMethod, |
| 307 jsAst.Name invocationName, | 293 jsAst.Name invocationName, |
| 308 List<jsAst.Parameter> stubParameters, | 294 List<jsAst.Parameter> stubParameters, |
| 309 List<jsAst.Expression> argumentsBuffer, | 295 List<jsAst.Expression> argumentsBuffer, |
| 310 int indexOfLastOptionalArgumentInParameters) { | 296 int indexOfLastOptionalArgumentInParameters) { |
| 311 // The target JS function may check arguments.length so we need to | 297 // The target JS function may check arguments.length so we need to |
| 312 // make sure not to pass any unspecified optional arguments to it. | 298 // make sure not to pass any unspecified optional arguments to it. |
| 313 // For example, for the following Dart method: | 299 // For example, for the following Dart method: |
| 314 // foo({x, y, z}); | 300 // foo({x, y, z}); |
| 315 // The call: | 301 // The call: |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 331 | 317 |
| 332 if (isInterceptedMethod) { | 318 if (isInterceptedMethod) { |
| 333 receiver = argumentsBuffer[0]; | 319 receiver = argumentsBuffer[0]; |
| 334 arguments = argumentsBuffer.sublist( | 320 arguments = argumentsBuffer.sublist( |
| 335 1, indexOfLastOptionalArgumentInParameters + 1); | 321 1, indexOfLastOptionalArgumentInParameters + 1); |
| 336 } else { | 322 } else { |
| 337 // Native methods that are not intercepted must be static. | 323 // Native methods that are not intercepted must be static. |
| 338 assert(invariant(member, member.isStatic)); | 324 assert(invariant(member, member.isStatic)); |
| 339 arguments = argumentsBuffer.sublist( | 325 arguments = argumentsBuffer.sublist( |
| 340 0, indexOfLastOptionalArgumentInParameters + 1); | 326 0, indexOfLastOptionalArgumentInParameters + 1); |
| 341 if (backend.isJsInterop(member)) { | 327 if (backend.isJsInteropMethod(member)) { |
| 342 // fixedBackendPath is allowed to have the form foo.bar.baz for | 328 // fixedBackendPath is allowed to have the form foo.bar.baz for |
| 343 // interop. This template is uncached to avoid possibly running out of | 329 // interop. This template is uncached to avoid possibly running out of |
| 344 // memory when Dart2Js is run in server mode. In reality the risk of | 330 // memory when Dart2Js is run in server mode. In reality the risk of |
| 345 // caching these templates causing an issue is very low as each class | 331 // caching these templates causing an issue is very low as each class |
| 346 // and library that uses typed JavaScript interop will create only 1 | 332 // and library that uses typed JavaScript interop will create only 1 |
| 347 // unique template. | 333 // unique template. |
| 348 receiver = js | 334 receiver = js |
| 349 .uncachedExpressionTemplate(backend.namer.fixedBackendPath(member)) | 335 .uncachedExpressionTemplate( |
| 336 backend.namer.fixedBackendMethodPath(member)) | |
| 350 .instantiate([]); | 337 .instantiate([]); |
| 351 } else { | 338 } else { |
| 352 receiver = js('this'); | 339 receiver = js('this'); |
| 353 } | 340 } |
| 354 } | 341 } |
| 355 statements | 342 statements |
| 356 .add(js.statement('return #.#(#)', [receiver, target, arguments])); | 343 .add(js.statement('return #.#(#)', [receiver, target, arguments])); |
| 357 | 344 |
| 358 return statements; | 345 return statements; |
| 359 } | 346 } |
| 360 | 347 |
| 361 bool isSupertypeOfNativeClass(ClassElement element) { | 348 bool isSupertypeOfNativeClass(ClassEntity element) { |
| 362 if (backend.classesMixedIntoInterceptedClasses.contains(element)) { | 349 if (backend.classesMixedIntoInterceptedClasses.contains(element)) { |
| 363 return true; | 350 return true; |
| 364 } | 351 } |
| 365 | 352 |
| 366 return subtypes[element] != null; | 353 return subtypes[element] != null; |
| 367 } | 354 } |
| 368 | 355 |
| 369 bool requiresNativeIsCheck(Element element) { | 356 bool requiresNativeIsCheck(ClassEntity element) { |
| 370 // TODO(sra): Remove this function. It determines if a native type may | 357 // TODO(sra): Remove this function. It determines if a native type may |
| 371 // satisfy a check against [element], in which case an interceptor must be | 358 // satisfy a check against [element], in which case an interceptor must be |
| 372 // used. We should also use an interceptor if the check can't be satisfied | 359 // used. We should also use an interceptor if the check can't be satisfied |
| 373 // by a native class in case we get a native instance that tries to spoof | 360 // by a native class in case we get a native instance that tries to spoof |
| 374 // the type info. i.e the criteria for whether or not to use an interceptor | 361 // the type info. i.e the criteria for whether or not to use an interceptor |
| 375 // is whether the receiver can be native, not the type of the test. | 362 // is whether the receiver can be native, not the type of the test. |
| 376 if (element == null || !element.isClass) return false; | 363 //if (element == null || !element.isClass) return false; |
|
Siggi Cherem (dart-lang)
2017/01/20 17:00:15
uncomment?
Johnni Winther
2017/01/23 10:00:09
Removed. The test is moot.
| |
| 377 ClassElement cls = element; | 364 ClassEntity cls = element; |
| 378 if (backend.isNativeOrExtendsNative(cls)) return true; | 365 if (backend.isNativeOrExtendsNative(cls)) return true; |
| 379 return isSupertypeOfNativeClass(element); | 366 return isSupertypeOfNativeClass(element); |
| 380 } | 367 } |
| 381 } | 368 } |
| OLD | NEW |